首先在環境變數引入AEDT內建的CPython路徑,再用system()執行python "xxx.py"即可。
os.environ['PATH'] = oDesktop.GetExeDir() + "commonfiles/CPython/3_7/winx64/Release/python"
os.system('python "alignCS_Calculation.py"')
AEDT(ANSYS Electronics DeskTop)包含了HFSS, Designer, Q3D, Maxwell, Icepak, Simplorer, Mechanical等模擬軟體。本網誌分享如何利用Python編程,配合AEDT API(Application Programming Interface, 應用程式介面)來開發AEDT外掛工具。並介紹各類自動化模擬設定及模擬資料分析技巧。 版主:林鳴志(Lin, Ming Chih)
首先在環境變數引入AEDT內建的CPython路徑,再用system()執行python "xxx.py"即可。
os.environ['PATH'] = oDesktop.GetExeDir() + "commonfiles/CPython/3_7/winx64/Release/python"
os.system('python "alignCS_Calculation.py"')
前一篇的分散bondwires的落點地script無法很好處理兩排die pad bondwire問題。可以透過以下script來交換bondwire的落點以解決bondwire空中交錯的問題。將其設為熱鍵,可以簡化操作。
import ScriptEnv
ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oLayout = oDesign.GetActiveEditor()
seles = oLayout.GetSelections()
A, B = seles[0:2]
Ax, Ay = oLayout.GetPropertyValue('BaseElementTab', A, 'Pt1').split(',')
Bx, By = oLayout.GetPropertyValue('BaseElementTab', B, 'Pt1').split(',')
oLayout.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:BaseElementTab",
[
"NAME:PropServers",
A
],
[
"NAME:ChangedProps",
[
"NAME:Pt1",
"X:=" , str(Bx) + 'mm',
"Y:=" , str(By) + 'mm'
]
]
]
])
oLayout.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:BaseElementTab",
[
"NAME:PropServers",
B
],
[
"NAME:ChangedProps",
[
"NAME:Pt1",
"X:=" , str(Ax) + 'mm',
"Y:=" , str(Ay) + 'mm'
]
]
]
])
oLayout.Select(seles[0:2])
(圖一)切換前後排交叉 bondwires的落點來避免交叉 |
以下腳本對單排的diepad可以適用。首先選擇Pin,執行腳本,落在其上的bondwires的Pt1會分離到不同的位置。可持續執行腳本直到到滿意的位置。將腳本連接到熱鍵可更快速的執行工作。目前只能確保2D平面直線不會交叉,因為立體結構,仍可能有交叉,仍需使用者肉眼判斷。
# -*- coding: utf-8 -*-
from itertools import combinations
from math import sqrt
import random
def ccw(A, B, C):
Ax, Ay = A
Bx, By = B
Cx, Cy = C
return (Cy - Ay) * (Bx - Ax) > (By - Ay) * (Cx - Ax)
def intersect(A, B, C, D):
return ccw(A, C, D) != ccw(B, C, D) and ccw(A, B, C) != ccw(A, B, D)
def checkintersection(segments):
for (A, B), (C, D) in combinations(segments, 2):
if intersect(A, B, C, D):
return True
return False
import ScriptEnv
ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oLayout = oDesign.GetActiveEditor()
sele = oLayout.GetSelections()
AddWarningMessage(str(sele))
def getPkgGrid(pin_name):
layer = oLayout.GetPropertyValue('BaseElementTab', pin_name, 'Start Layer')
x0, y0 = oLayout.GetPropertyValue('BaseElementTab', pin_name, 'Location').split(',')
x0, y0 = float(x0), float(y0)
grid = []
for i in range(-10, 11):
for j in range(-10, 11):
x = (x0 + 0.05 * i) * 1e-3
y = (y0 + 0.05 * j) * 1e-3
pt = oLayout.Point()
pt.Set(x, y)
if pin_name in oLayout.FindObjectsByPoint(pt, layer):
x = (x0 + 0.04 * i) * 1e-3
y = (y0 + 0.04 * j) * 1e-3
grid.append((x, y))
return grid
pkg = getPkgGrid(sele[0])
AddWarningMessage('Pkg Locations: {}'.format(len(pkg)))
def getDieGrid(pin_name):
layer = oLayout.GetPropertyValue('BaseElementTab', pin_name, 'Start Layer')
grid = {}
for i in oLayout.FindObjects('Type', 'bondwire'):
p1 = oLayout.Point()
x, y = oLayout.GetPropertyValue('BaseElementTab', i, 'Pt1').split(',')
pt = p1.Set(float(x) * 1e-3, float(y) * 1e-3)
obj = oLayout.FindObjectsByPoint(p1, layer)
if pin_name in oLayout.FindObjectsByPoint(pt, layer):
x, y = oLayout.GetPropertyValue('BaseElementTab', i, 'Pt0').split(',')
x, y = float(x) * 1e-3, float(y) * 1e-3
grid[(x, y)] = i
return grid
die = getDieGrid(sele[0])
AddWarningMessage('die Locations: {}'.format(len(die)))
pair = {}
N = 0
while (True):
N += 1
if N > 1000000:
AddWarningMessage('Failed')
segments = []
break
segments = []
random.shuffle(pkg)
for (pt0, pt1) in zip(die.keys(), pkg[0:len(die)]):
segments.append((pt0, pt1))
if checkintersection(segments) == False:
AddWarningMessage('Successful')
break
for pt0, pt1 in segments:
pair[die[pt0]] = pt1
AddWarningMessage(str(pair))
try:
for bw_name in pair:
x, y = pair[bw_name]
oLayout.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:BaseElementTab",
[
"NAME:PropServers",
bw_name
],
[
"NAME:ChangedProps",
[
"NAME:Pt1",
"X:=" , str(x),
"Y:=" , str(y)
]
]
]
])
except:
pass
oLayout.Select(sele[0])
選擇位於'L1_pkg'層的Pin,執行下列腳本便可以選擇落在選中的Pin的所有Bondwires。這個功能可以用來協助開發分離bondwires腳位的工具(見上一篇)。
import ScriptEnv
ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oLayout = oDesign.GetActiveEditor()
sele = oLayout.GetSelections()
AddWarningMessage(str(sele))
#polygon1 = oLayout.GetPolygon(sele[0])
toselect = []
for i in oLayout.FindObjects('Type', 'bondwire'):
p1 = oLayout.Point()
x, y = oLayout.GetPropertyValue('BaseElementTab',i, 'Pt1').split(',')
p1 = p1.Set(float(x)*1e-3 ,float(y)*1e-3)
obj = oLayout.FindObjectsByPoint(p1, 'L1_pkg')
if set(obj).intersection(set(sele)):
AddWarningMessage(str(i))
toselect.append(i)
oLayout.Select(toselect)
(圖一)選擇Pin,接著執行腳本 |
(圖二)落在被選中的Pin上的bondwires被選擇出來 |
有人問是否能自動調整bondwire pt2的位置,避免bondwire重疊導致網格失敗。所以先簡化成2D直線問題。在已知die端的座標點及pkg端的座標點且Npkg>=Ndie的前提下,採用隨機算法+交叉判斷,七條以內的直線不交叉配對可以在1sec內完成。雖然不代表bond-wires不會碰撞(因立體且有厚度),但是或許可以從這裡出發來探索一個可行的方法。
from itertools import combinations
from math import sqrt
import random
w = 0.1
def getparallel(A, B, d):
x1, y1 = A
x2, y2 = B
r = sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
deltax = (d / r) * (y1 - y2)
deltay = (d / r) * (x2 - x1)
x3 = x1 + deltax
y3 = y1 + deltay
x4 = x2 + deltax
y4 = y2 + deltay
return (x3, y3), (x4, y4)
def ccw(A, B, C):
Ax, Ay = A
Bx, By = B
Cx, Cy = C
return (Cy - Ay) * (Bx - Ax) > (By - Ay) * (Cx - Ax)
def intersect(A, B, C, D):
return ccw(A, C, D) != ccw(B, C, D) and ccw(A, B, C) != ccw(A, B, D)
def checkintersection(segments):
for (A, B), (C, D) in combinations(segments, 2):
_A, _B = getparallel(A, B, w / 2)
A_, B_ = getparallel(A, B, -w / 2)
_C, _D = getparallel(C, D, w / 2)
C_, D_ = getparallel(C, D, -w / 2)
if intersect(_A, _B, C_, D_) or intersect(A_, B_, _C, _D) or intersect(_A, B_, C_, _D) or intersect(A_, _B, _C,
D_):
return True
return False
import matplotlib.pyplot as plt
y0 = -2
die = [(0, i) for i in range(y0, y0 + 8)]
for i, j in die:
plt.scatter(i, j, marker='s')
x0, y0 = 3, 0
ds1 = [-1, -0.5, 0, 0.5]
ds2 = [-0.4, 0, 0.4, ]
pkg = [(x0 + i, y0 + j) for i in ds1 for j in ds2]
for i, j in pkg:
plt.scatter(i, j)
while (True):
segments = []
random.shuffle(pkg)
for (pt1, pt2) in zip(die, pkg[0:len(die)]):
segments.append((pt1, pt2))
if checkintersection(segments) == False:
break
for pt1, pt2 in segments:
x1, y1 = pt1
x2, y2 = pt2
plt.plot([x1, x2], [y1, y2])
plt.show()
不管是透過Run Script或ToolKit啟動python script都需要多個步驟。我們可以在Definitions/Script加入一段VB腳本,當中再呼叫Python Script。AEDT環境當中透過Run Script執行該VB就可以執行Python Script了。VB碼如下:
Dim oAnsoftApp
Dim oDesktop
Set oAnsoftApp = CreateObject("Ansoft.ElectronicsDesktop")
Set oDesktop = oAnsoftApp.GetAppDesktop()
oDesktop.RunScript("d:/demo/test.py")
(圖一)建立VB Script |
(圖二)透過執行VB Script間接執行Python Script |
多謝 Gregory, Liao的糾錯,程式計算的解果與HFSS完全一致。
from math import tan, pi, sqrt, atan, log, exp
'''
3.85309+0.0265244*ln((2.53303e+22+Freq*Freq)/(6.10915e+09+Freq*Freq))
1e-12+2.95123e-12*Freq*(atan(Freq/78161.1)-atan(Freq/1.59155e+11))
'''
def Djordjevic_Sarkar(dk=4, df=0.02, f0=10e9):
epson0 = 8.8541878128e-12
epson1 = dk
delta1 = df
f1 = f0
fb = 159.15494e9
sigma_dc = 1e-12
K = (epson1 * delta1 - sigma_dc / (2 * pi * f1 * epson0)) / atan(fb / f1)
epson_inf = epson1 - K * log(sqrt(fb ** 2 + f1 ** 2) / (f1))
delta_epson = 10 * delta1 * epson_inf
fa = fb / exp(delta_epson / K)
formula1 = '{:.5f}+{:.7f}*ln(({:.5e}+Freq*Freq)/({:.5e}+Freq*Freq))'.format(epson_inf, K / 2, fb ** 2, fa ** 2)
formula2 = '{}+{:.5e}*Freq*(atan(Freq/{:.5e})-atan(Freq/{:.5e}))'.format(sigma_dc, 2 * pi * epson0 * K, fa, fb)
return formula1, formula2
dk, df = Djordjevic_Sarkar()
print(dk)
print(df)
(圖一)輸出Dk, Df字串 |
import os
import datetime
import pathlib
data = []
for path, subdirs, files in os.walk(r'C:\Program Files\AnsysEM\AnsysEM20.2\Win64'):
for name in files:
fname = pathlib.Path(os.path.join(path, name))
mtime = datetime.datetime.fromtimestamp(fname.stat().st_mtime)
data.append((mtime, fname))
data.sort()
print(data[0])
print(data[-1])
輸出結果
D:\Customer2020\pythonProject1\venv\Scripts\python.exe D:/Customer2020/pythonProject1/main.py (datetime.datetime(1999, 12, 31, 20, 0), WindowsPath('C:/Program Files/AnsysEM/AnsysEM20.2/Win64/common/ROMViewer/resources/app/services/System.Diagnostics.Process.dll')) (datetime.datetime(2021, 1, 4, 9, 18, 48, 61165), WindowsPath('C:/Program Files/AnsysEM/AnsysEM20.2/Win64/syslib/Toolkits/Icepak/Geometry/Packages/DUAL.py')) Process finished with exit code 0