2022年8月7日 星期日

重新排列S參數檔案ports順序並輸出檔案

以下範例讀取32port S參數檔案,重新輸出16組s2p檔案。

import re, itertools


class SNP:
def __init__(self, touchstone_path):
with open(touchstone_path) as f:
text = f.readlines()

self.data = []
self.ports = {}
for line in text:
try:
[float(i) for i in line.strip().split()]
self.data += line.strip().split()
except:
if line.startswith('#'):
self.header = line
else:
m = re.search('!\sPort\[(\d+)\]\s=\s(.*)$', line)
if m:
num = int(m.group(1))
name = m.group(2)
self.ports[num] = name

def reorder(self, order, touchstone_path):
num = len(self.ports)
frequencies = self.data[::2 * num ** 2 + 1]

matrix = list(itertools.product(order, repeat=2))
new_touchstone = [self.header]

touchstone_path += '.s{}p'.format(len(order))
print('\n' + touchstone_path)
for n, i in enumerate(order, 1):
port = '! Port[{}] = {}'.format(n, self.ports[i])
print(port)
new_touchstone.append(port)

for n, f in enumerate(frequencies):
line = [self.data[n * (num ** 2 * 2 + 1)]]
for i, j in matrix:
i -= 1
j -= 1
line.append(self.data[n * (num ** 2 * 2 + 1) + 1 + (2 * num * i) + (2 * j)])
line.append(self.data[n * (num ** 2 * 2 + 1) + 1 + (2 * num * i) + (2 * j + 1)])

new_touchstone.append(' '.join(line))

with open(touchstone_path, 'w') as f:
f.write('\n'.join(new_touchstone))


snp = SNP(r"D:\demo\dq.s32p")
snp.ports
snp.reorder((23, 9), 'd:/demo/dq0')
snp.reorder((21, 11), 'd:/demo/dq1')
snp.reorder((24, 10), 'd:/demo/dq2')
snp.reorder((19, 12), 'd:/demo/dq3')
snp.reorder((17, 14), 'd:/demo/dq4')
snp.reorder((20, 16), 'd:/demo/dq5')
snp.reorder((18, 13), 'd:/demo/dq6')
snp.reorder((22, 15), 'd:/demo/dq7')
snp.reorder((31, 1), 'd:/demo/dq8')
snp.reorder((27, 3), 'd:/demo/dq9')
snp.reorder((29, 2), 'd:/demo/dq10')
snp.reorder((26, 4), 'd:/demo/dq11')
snp.reorder((32, 6), 'd:/demo/dq12')
snp.reorder((25, 8), 'd:/demo/dq13')
snp.reorder((30, 5), 'd:/demo/dq14')
snp.reorder((28, 7), 'd:/demo/dq15')

2022年7月29日 星期五

輸出Report所有維度的獨立變數

 

from win32com import client

oApp = client.Dispatch("Ansoft.ElectronicsDesktop.2022.2")
oDesktop = oApp.GetAppDesktop()
oDesktop.RestoreWindow()
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()

oReportSetup = oDesign.GetModule("ReportSetup")
oSolutions = oDesign.GetModule("Solutions")

report_solution = {}
solutions = []
display = {}
context = {}
category = {}
quantities = {}
variations = {}
solve_range = {}

for report_type in oReportSetup.GetAvailableReportTypes():
display[report_type] = oReportSetup.GetAvailableDisplayTypes(report_type)
report_solution[report_type] = oReportSetup.GetAvailableSolutions(report_type)
for s in report_solution[report_type]:
if s not in solutions:
solutions.append(s)

for _report_type, _display in display.items():
for d in _display:
for _solution in report_solution[_report_type]:
ctxt = oReportSetup.GetSolutionContexts(_report_type, d, _solution)
context[(_report_type, d, _solution)] = ctxt
for _ctxt in ctxt:
cate = oReportSetup.GetAllCategories(_report_type, d, _solution, _ctxt)
category[(_report_type, d, _solution, _ctxt)] = cate
for _cate in cate:
q = oReportSetup.GetAllQuantities(_report_type, d, _solution, _ctxt, _cate)
quantities[(_report_type, d, _solution, _ctxt, _cate)] = q

for sol in solutions:
solve_range[sol] = oSolutions.GetSolveRangeInfo(sol)
variations[sol] = oSolutions.GetAvailableVariations(sol)



2022年7月26日 星期二

Q3D輸出Transion Region頻率範圍

 選擇金屬厚度邊,執行腳本即可輸出到訊息視窗。

import math
import ScriptEnv

oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.SetActiveEditor("3D Modeler")

try:
selected = oEditor.GetSelections()
edge_id = selected[0].replace('Edge', '')

obj = oEditor.GetObjectNameByEdgeID(edge_id)
material = oEditor.GetPropertyValue('Geometry3DAttributeTab', obj, 'Material').replace('"', '')
AddWarningMessage('Material: {}'.format(material))

unit_map = {'meter': 1, 'mm': 1e-3, 'um': 1e-6, 'nm': 1e-9, 'mil': 2.54e-5, 'in': 2.54e-2}
freq_map = [(1, 'Hz'), (1e3, 'KHz'), (1e6, 'MHz'), (1e9, 'GHz'), (1e12, 'THz')]

unit = oEditor.GetModelUnits()
thickness = float(oEditor.GetEdgeLength(edge_id)) * unit_map[unit]
AddWarningMessage('Thicknetss: {}(m)'.format(thickness))

oDefinitionManager = oProject.GetDefinitionManager()
conductivity = float(
oDefinitionManager.GetPropertyValue('MaterialPropTab', 'Materials:' + material, 'Bulk Conductivity'))
permeability = float(
oDefinitionManager.GetPropertyValue('MaterialPropTab', 'Materials:' + material, 'Relative Permeability'))
AddWarningMessage('Conductivity: {}'.format(conductivity))
AddWarningMessage('Relative Permeability: {}'.format(permeability))

u0 = 4 * math.pi * 1e-7

ac_high = 9 / (math.pi * conductivity * permeability * u0 * thickness ** 2)

for n, (scale, unit) in enumerate(freq_map):
if ac_high / scale < 1:
scale, unit = freq_map[n - 1]
_ac_low = str(round(ac_high / 9 / scale, 3)) + unit
_ac_high = str(round(ac_high / scale, 3)) + unit
break

AddWarningMessage("Transition Region: {} - {}".format(_ac_low, _ac_high))

except:
AddErrorMessage("Please Select Metal Edge!")







2022年7月4日 星期一

SIwave API設定Pin Group Port

 

from win32com import client

oApp = client.Dispatch("SIwave.Application.2022.1")
oApp.RestoreWindow()
oDoc = oApp.GetActiveProject()

# ScrPlaceCircuitElement(string givenElementName,
# string givenPartName,
# int circuitElementType,
# int posTermConnectionType,
# string posTermParam1,
# string posTermParam2,
# string posTermParam3,
# int refTermConnectionType,
# string refTermParam1,
# string refTermParam2,
# string refTermParam3,
# double capVal,
# double indVal,
# double resVal,
# double refZRe,
# double mag,
# double phase) -> short

oDoc.ScrPlaceCircuitElement('ABC',
'DEF',
3,
1,
'FCHIP',
'FCHIP',
'FCHIP_VDD_15_Group',
1,
'FCHIP',
'FCHIP',
'FCHIP_VSS_Group',
0,
0,
0,
50,
0,
0)



2022年6月13日 星期一

類別屬性檢查


class test:
def __init__(self):
self._score = 60

@property
def score(self):
return self._score

@score.setter
def score(self, value):
if value < 0:
self._score = 0
if value > 100:
self._score = 100


x = test()
print(x.score)
x.score = 110
print(x.score)
x.score = -10
print(x.score)

 








2022年5月16日 星期一

傅立葉轉換

時域訊號經過傅立葉轉換到頻域,再轉回時域做比較

import numpy as np

tstop = 10
tstep = 0.01

fstart = -1 / tstep / 2
fstop = 1 / tstep / 2
fstep = 1 / tstop

t = np.arange(0, tstop, tstep)
y0 = np.sin(20 * t)

import matplotlib.pyplot as plt

scale = np.sqrt(2 * np.pi * len(t))
plt.plot(t, y0)
plt.show()
y1 = []
f = np.arange(fstart, fstop, fstep)
for w in f:
y1.append(np.sum(np.exp(-1j * w * t) * y0) / scale)

plt.plot(f, np.abs(y1))
plt.show()

y2 = []
for t0 in t:
y2.append(np.sum(np.exp(1j * t0 * f) * y1) / scale)

plt.plot(t, np.real(y2))
plt.show()

plt.plot(t, y0)
plt.plot(t, y2, c='r', linewidth=1)
plt.show()




2022年4月21日 星期四

在Spyder當中修改padstack definition某一層的屬性

在Spyder當中修改padstack definition某一層的屬性

import clr, os, sys, System
from distutils.dir_util import copy_tree

AnsysEM_Path = 'C:/Program Files/AnsysEM/v221/Win64/'
sys.path.append(AnsysEM_Path)
os.environ['PATH'] += ';' + AnsysEM_Path

clr.AddReference('Ansys.Ansoft.Edb')
clr.AddReference('Ansys.Ansoft.SimSetupData')
import Ansys.Ansoft.Edb as edb

edb.Database.SetRunAsStandAlone(True)

aedb_path = "D:\demo2\Galileo_G87173_20431_12.aedb"
cell_name = 'Galileo_G87173_204'
layer_name = 'UNNAMED_004'
up_height = '10mil'
low_height = '12mil'

# %%----------------------------------------------------------------------------
n = 1
while True:
new_aedb_path = aedb_path.replace('.aedb', '_{}.aedb'.format(n))
if not os.path.isdir(new_aedb_path):
break
n += 1
copy_tree(aedb_path, new_aedb_path)

middle_layer_name = layer_name + '_1'
low_layer_name = layer_name + '_2'

DB = edb.Database.Open(new_aedb_path, False)

try:
cells = [i for i in DB.CircuitCells]
for cell in cells:
if cell.GetName() == cell_name:
break

layout = cell.GetLayout()
padstack_instances = [i for i in layout.PadstackInstances]
padstack_def = {}
for i in padstack_instances:
if i.GetPadstackDef().GetName() not in padstack_def:
padstack_def[i.GetPadstackDef().GetName()] = i.GetPadstackDef()

dimension = System.Collections.Generic.List[edb.Utility.Value]()
dimension.Add(edb.Utility.Value(0))

for i, d in padstack_def.items():
new_data = edb.Definition.PadstackDefData(d.GetData())

new_data.SetPadParameters('Default',
edb.Definition.PadType.RegularPad,
edb.Definition.PadGeometryType.NoGeometry,
dimension,
edb.Utility.Value(0),
edb.Utility.Value(0),
edb.Utility.Value(0))
new_data.SetPadParameters('Default',
edb.Definition.PadType.AntiPad,
edb.Definition.PadGeometryType.NoGeometry,
dimension,
edb.Utility.Value(0),
edb.Utility.Value(0),
edb.Utility.Value(0))
d.SetData(new_data)
DB.Save()
except:
raise
finally:
DB.Close()



2022年4月18日 星期一

在Spyder當中用EDB在stackup當中插入新的layers

 注意需要將layer複製,才能修改屬性。

import clr, os, sys, System
from distutils.dir_util import copy_tree

AnsysEM_Path = 'C:/Program Files/AnsysEM/v221/Win64/'
sys.path.append(AnsysEM_Path)
os.environ['PATH'] += ';' + AnsysEM_Path

clr.AddReference('Ansys.Ansoft.Edb')
clr.AddReference('Ansys.Ansoft.SimSetupData')
import Ansys.Ansoft.Edb as edb

edb.Database.SetRunAsStandAlone(True)

aedb_path = "D:\demo2\Galileo_G87173_20431.aedb"
cell_name = 'Galileo_G87173_204'
layer_name = 'UNNAMED_004'
up_height = '10mil'
low_height = '12mil'

# %%----------------------------------------------------------------------------
n = 1
while True:
new_aedb_path = aedb_path.replace('.aedb', '_{}.aedb'.format(n))
if not os.path.isdir(new_aedb_path):
break
n += 1
copy_tree(aedb_path, new_aedb_path)

middle_layer_name = layer_name + '_1'
low_layer_name = layer_name + '_2'

DB = edb.Database.Open(new_aedb_path, False)

try:
cells = [i for i in DB.CircuitCells]
for cell in cells:
if cell.GetName() == cell_name:
break

layout = cell.GetLayout()
raw_layercollection = layout.GetLayerCollection()
new_layercollection = edb.Cell.LayerCollection(raw_layercollection)

all_layers = raw_layercollection.Layers(edb.Cell.LayerTypeSet.AllLayerSet)

for layer in all_layers:
if layer.GetName() == layer_name:
layer_material = layer.GetMaterial()
layer2 = edb.Cell.StackupLayer(middle_layer_name, edb.Cell.LayerType.SignalLayer, edb.Utility.Value('0'),
edb.Utility.Value(0), "COPPER")
layer3 = edb.Cell.StackupLayer(low_layer_name, edb.Cell.LayerType.DielectricLayer,
edb.Utility.Value(low_height), edb.Utility.Value(0), layer_material)
new_layercollection.AddLayerBelow(layer2, layer_name)
new_layercollection.AddLayerBelow(layer3, middle_layer_name)

empty_layercollection = edb.Cell.LayerCollection()
for layer in new_layercollection.Layers(edb.Cell.LayerTypeSet.AllLayerSet):
if layer.GetName() == layer_name:
layer = layer.Clone()
layer.SetThickness(edb.Utility.Value(up_height))
empty_layercollection.AddLayerBottom(layer)
else:
empty_layercollection.AddLayerBottom(layer)

layout.SetLayerCollection(empty_layercollection)

DB.Save()
except:
raise
finally:
DB.Close()


2022年3月16日 星期三

如何在AEDT Console執行Script

 可以使用execfile()指令如下,搭配input()可做為一個簡單互動的UI。



利用批次檔執行AEDT Classical API Script

 test.bat

PATH="C:\Program Files\AnsysEM\v221\Win64\common\IronPython";%PATH%

ipy64 .\test.py
pause

test.py

import sys
sys.path.append(r'C:\Program Files\AnsysEM\v221\Win64\PythonFiles\DesktopPlugin')
sys.path.append(r'C:\Program Files\AnsysEM\v221\Win64')

import ScriptEnv
ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.NewProject()

2022年2月25日 星期五

如何在編輯器環境編寫Classical AEDT API腳本

Spyder當中直接編輯Classical AEDT  API代碼,pip install pypiwin32

from win32com import client

oApp = client.Dispatch("Ansoft.ElectronicsDesktop.2022.1")
oDesktop = oApp.GetAppDesktop()
oDesktop.RestoreWindow()
oProject = oDesktop.NewProject()
oDesign = oProject.InsertDesign("HFSS", "HFSSDesign1", "HFSS Terminal Network", "")
oEditor = oDesign.SetActiveEditor("3D Modeler")
oEditor.CreateCylinder(
[
"NAME:CylinderParameters",
"XCenter:=" , "0mm",
"YCenter:=" , "-0.3mm",
"ZCenter:=" , "0mm",
"Radius:=" , "0.282842712474619mm",
"Height:=" , "0.8mm",
"WhichAxis:=" , "Z",
"NumSides:=" , "0"
],
[
"NAME:Attributes",
"Name:=" , "Cylinder1",
"Flags:=" , "",
"Color:=" , "(143 175 143)",
"Transparency:=" , 0,
"PartCoordinateSystem:=", "Global",
"UDMId:=" , "",
"MaterialValue:=" , "\"vacuum\"",
"SurfaceMaterialValue:=", "\"\"",
"SolveInside:=" , True,
"ShellElement:=" , False,
"ShellElementThickness:=", "0mm",
"IsMaterialEditable:=" , True,
"UseMaterialAppearance:=", False,
"IsLightweight:=" , False
])
#oDesktop.QuitApplication()



如何在編輯器環境編寫SIwave腳本

在Spyder當中使用win32com可以直接編輯SIwave代碼。可以pip install pypiwin32安裝win32com模組。

from win32com import client


oApp = client.Dispatch("SIwave.Application.2022.1")
oApp.RestoreWindow()
oDoc = oApp.GetActiveProject()
oDoc.ScrDrawCircle(10, 0, 20, 'METAL-1', 'NET-1', 'mm')
oDoc.ScrSaveProjectAs ('d:/demo/test215.siw')
oApp.Quit()



2022年2月24日 星期四

pyAEDT如何呼叫舊的API所寫的函數

假使我們之前已經用classical API開發了很多的自動化程式,能否在pyAEDT當中呼叫舊的程式碼,省去從頭開發的時間?答案是可以的,比方說,classical.py當中用舊的API開發的createBoxArray函數,我們可以在pyAEDT程式當中將其匯入,並使用該函數。底下範例使用non-graphical模式在不啟用GUI的狀況底下完成建模。

classical.py

def createBoxArray(m, n, spacing=1):
for i in range(m):
for j in range(n):
oEditor.CreateBox(
[
"NAME:BoxParameters",
"XPosition:=" , "{}mm".format(i*spacing),
"YPosition:=" , "{}mm".format(j*spacing),
"ZPosition:=" , "0mm",
"XSize:=" , "0.3mm",
"YSize:=" , "0.3mm",
"ZSize:=" , "0.4mm"
],
[
"NAME:Attributes",
"Name:=" , "Box1",
"Flags:=" , "",
"Color:=" , "(143 175 143)",
"Transparency:=" , 0,
"PartCoordinateSystem:=", "Global",
"UDMId:=" , "",
"MaterialValue:=" , "\"vacuum\"",
"SurfaceMaterialValue:=", "\"\"",
"SolveInside:=" , True,
"ShellElement:=" , False,
"ShellElementThickness:=", "0mm",
"IsMaterialEditable:=" , True,
"UseMaterialAppearance:=", False,
"IsLightweight:=" , False
])

new.py

from pyaedt import Hfss

hfss = Hfss(specified_version='2022.1', non_graphical=True)

import classical
classical.oEditor = hfss.modeler.oeditor
classical.createBoxArray(4, 8, 1)

hfss.plot()





2022年2月20日 星期日

inheritance Example

 類別繼承基本

class mylist(list):
def __init__(self, x):
super().__init__(x)

def multiply(self, x):
temp = [i for i in self]
self.clear()
for i in temp:
self.append(i * x)


x = mylist([1, 2, 3])
x.append(4)
x.multiply(3)
print(x)

2022年2月19日 星期六

Dynamically Add Method To Object

 動態加入方法到物件當中

class foo:
def __init__(self, v):
self.value = v


def new_method(self, v=None):
self.value += v


x = foo(3)
setattr(x, 'add', lambda v: new_method(x, v))

x.add(4)
print(x.value)

2022年1月4日 星期二

利用程式物件來快速定義拓樸

使用者可以指定每個格點的材料來定義馬達截面的拓樸結構。可搭配PyAEDT快速生成Maxwell2D的設計並完成模擬。

from matplotlib.patches import Wedge
from matplotlib.collections import PatchCollection
import matplotlib.pyplot as plt

m1 = 90
m2 = 45

class topology():
def __init__(self):
self.materials = {}
self.wedges = []
dx = 90 / 20
dy = 0.5 / 20

for i in range(1, 21):
for j in range(1, 20 + 1):
self.materials[i, j] = 0
w = Wedge((0, 0), 0.5 + j * dy, (i-1) * dx, i * dx, dy)
self.wedges.append(w)

def getmaterials(self):
result = []
for i in range(1, 21):
for j in range(1, 20 + 1):
result.append(self.materials[i, j])
return result

def show(self):
cmap = plt.cm.get_cmap('jet')
p = PatchCollection(s.wedges, alpha=1, cmap=cmap, )
p.set_clim(0, 100)
p.set_array(self.getmaterials())
p.set_color('w')
fig, ax = plt.subplots(figsize=(15, 15))
ax.add_collection(p)

s = topology()
s.materials[1, 20] = m1
s.materials[10, 10] = m2
s.materials[20, 1] = m2
for i in [5, 6, 7, 8]:
s.materials[i, 14] = m1
s.show()