2021年5月28日 星期五

開發Windows/Linux皆可用的AEDT GUI

用WPF開發視窗雖然方便,可惜只能支援Windows。用Windows Forms控件則可以做出Windows/Linux皆可用的GUI,可以透過Viual Studio C#視窗編程先拖拉出GUI外觀,再將其生成之程式碼做一個修改即可使用在IronPyrhon。

simpleGUI.py

import clr
clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')

from System import Drawing
from System.Windows.Forms import *

class MyForm(Form):
def __init__(self):
self.textBox1 = TextBox()
self.button1 = Button()
self.label1 = Label()
self.SuspendLayout()


self.textBox1.Location = Drawing.Point(12, 46)
self.textBox1.Name = "textBox1"
self.textBox1.Size = Drawing.Size(309, 30)
self.textBox1.TabIndex = 0

self.button1.BackColor = Drawing.SystemColors.HotTrack
self.button1.Font = Drawing.Font("Microsoft Sans Serif", 12)
self.button1.ForeColor = Drawing.Color.GhostWhite
self.button1.Location = Drawing.Point(216, 116)
self.button1.Name = "button1"
self.button1.Size = Drawing.Size(99, 53)
self.button1.TabIndex = 1
self.button1.Text = "Print"
self.button1.UseVisualStyleBackColor = False
self.button1.Click += self.button1_Click_1

self.label1.AutoSize = True
self.label1.Font = Drawing.Font("Microsoft Sans Serif", 12)
self.label1.Location = Drawing.Point(12, 9)
self.label1.Name = "label1"
self.label1.Size = Drawing.Size(55, 25)
self.label1.TabIndex = 2
self.label1.Text = "Input"

self.AutoScaleDimensions = Drawing.SizeF(8, 16)

self.AutoSize = True
self.ClientSize = Drawing.Size(333, 178)
self.Controls.Add(self.label1)
self.Controls.Add(self.button1)
self.Controls.Add(self.textBox1)
self.FormBorderStyle = FormBorderStyle.FixedSingle
self.MaximizeBox = False
self.MinimizeBox = False
self.Name = "Form1"
self.Text = "Simpe Linux Window"
self.TopMost = True
self.ResumeLayout(False)
self.PerformLayout()

def button1_Click_1(self, sender, e):
AddWarningMessage(self.textBox1.Text)

form = MyForm()
Application.Run(form)


(圖一) Linux執行

(圖二) Windows執行




可用在AEDT Linux(CentOS)環境的開啟檔案對話框代碼

由於在CentOS AEDT開啟檔案之後,視窗無效又無法關閉,因此加上最後兩行代碼來關閉視窗。

import sys
import clr
clr.AddReference("System.Windows.Forms")

from System.Windows.Forms import DialogResult, OpenFileDialog
dialog = OpenFileDialog()
dialog.Filter = "text files (*.txt)|*.txt"

if dialog.ShowDialog() == DialogResult.OK:
txt_path = dialog.FileName
AddWarningMessage(txt_path)
else:
pass

dialog = OpenFileDialog()
dialog.Dispose()
(圖一) Linux環境開啟OpenFileDialog視窗

2021年5月26日 星期三

如何根據Pin Group自動設Circuit Port

以下代碼會在設有Pin Group的元件上設circuit ports。

create_circuit_port.py

import ScriptEnv

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.GetActiveEditor()

import clr

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

DB = edb.Database.Attach(int(oProject.GetEDBHandle()))
cells = list(DB.TopCircuitCells)

for i in cells:
if i.GetName() == (oDesign.GetName() if ';' not in oDesign.GetName() else oDesign.GetName().split(';')[1]):
layout = i.GetLayout()
break

all_ports = oEditor.FindObjects('Type', 'Port')

for pg in layout.PinGroups:
pg_name = pg.GetName()

pins = pg.GetPins()
comp_name = pins[0].GetComponent().GetName()
pg_net = pins[0].GetNet().GetName()

pinnames_to_set = []
portnames_to_set = []

for pin_name in oEditor.GetComponentPins(comp_name):
short_pin_name = oEditor.GetPropertyValue('BaseElementTab', pin_name, 'Component Pin')
pin_net = oEditor.GetPropertyValue('BaseElementTab', pin_name, 'Net')

if pin_net == pg_net:
continue

if pin_net == '':
for port_name in ('Port{}'.format(i) for i in range(1, 1000000)):
if port_name not in all_ports:
all_ports.append(port_name)
break
else:
port_name = '{}.{}.{}'.format(comp_name, short_pin_name, pin_net)

pinnames_to_set.append(pin_name)
portnames_to_set.append(port_name)

try:
oEditor.ToggleViaPin(["NAME:elements", ] + pinnames_to_set)
oEditor.AddPinGroupRefPort(portnames_to_set, [pg_name])
AddWarningMessage('{} excitations are added in {}!'.format(len(portnames_to_set), comp_name))

except:
pass
(圖一)自動加上circuit ports


2021年5月24日 星期一

AEDT簡單的輸入視窗(Windows + Linux)

SimpleInputDialog.py

import clr
clr.AddReference("ANS.UI.Toolkit")
clr.AddReference("ANS.UI.Toolkit.Base")
import Ansys.UI.Toolkit as ui


class mainwindow(ui.Dialog):
def __init__(self):
self.Size = ui.Drawing.Size(400, 200)
self.Text = 'Simple Input Dialog'
self.mainPanel = ui.TableLayoutPanel()
self.mainPanel.Rows.Add(ui.TableLayoutSizeType.Absolute, 30)
self.mainPanel.Rows.Add(ui.TableLayoutSizeType.Absolute, 30)
self.mainPanel.Rows.Add(ui.TableLayoutSizeType.Absolute, 30)
self.mainPanel.Columns.Add(ui.TableLayoutSizeType.Absolute, 10)
self.mainPanel.Columns.Add(ui.TableLayoutSizeType.Absolute, 300)
self.mainPanel.Columns.Add(ui.TableLayoutSizeType.Absolute, 60)

self.SetControl(self.mainPanel)

self.tb = ui.TextBox()
self.mainPanel.Controls.Add(self.tb, 1, 1, 1, 1)

self.bt = ui.Button('Run')

self.bt.Click += ui.EventDelegate(self.click)
self.mainPanel.Controls.Add(self.bt, 1, 2, 1, 1)

def click(self, sender, args):
if self.tb.Text:
AddWarningMessage(self.tb.Text)
else:
AddWarningMessage('No Input!')


gui = mainwindow()
gui.ShowDialog()





2021年5月20日 星期四

如何複製檔案到剪貼簿

 之前介紹過複製字串到剪貼簿,這裡是複製檔案到剪貼簿。

copyFileswithTXT.bat

if not DEFINED IS_MINIMIZED set IS_MINIMIZED=1 && start "" /min "%~dpnx0" %* && exit

SET PATH=C:\Program Files\AnsysEM\AnsysEM20
.2\Win64\common\IronPython\
ipy64 copyFileswithTXT.py


copyFileswithTXT.py
# coding=UTF-8
import re
import os
import clr
from shutil import copyfile

clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import Clipboard, MessageBox
import System
paths = System.Collections.Specialized.StringCollection()

input = Clipboard.GetText()
os.system('start d:/demo')

for src in input.splitlines():
try:
src = src.strip().strip('"')

dst = 'd:/demo/{}.txt'.format(os.path.basename(src))
print(src, dst)
with open(src) as f:
text = f.read()
with open(dst, 'w') as f:
f.write(text)
paths.Add(dst)
except:
pass

Clipboard.SetFileDropList(paths)
print(paths)

2021年5月14日 星期五

如何取得3D Layout當中Library所有資料

 import ScriptEnv

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.GetActiveEditor()

oDefinitionManager = oProject.GetDefinitionManager()
lib = ['Component', 'Material', 'Padstack', 'Bondwire', 'Model', 'Symbol', 'Footprint', 'Script',]
result = {}
for i in lib:
oManager = oDefinitionManager.GetManager(i)
result[i] = {}
for j in oManager.GetNames():
result[i][j] = oManager.GetData(j)

AddWarningMessage(str(result))
(圖一)取得Library的所有資料



從一段文字抓出郵件地址到剪貼簿當中

queryEmail.bat
if not DEFINED IS_MINIMIZED set IS_MINIMIZED=1 && start "" /min "%~dpnx0" %* && exit

SET PATH=C:\Program Files\AnsysEM\AnsysEM20.2\Win64\common\IronPython\
ipy64 queryEmail.py


queryEmail.py
# coding=UTF-8
import re
import clr

clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import Clipboard, MessageBox

input = Clipboard.GetText()
emails = set([i.lower() for i in re.findall('[\w\.\-]+@[\w\.]+', input)])
try:
result = '; '.join(emails)
Clipboard.SetText(result)
MessageBox.Show("{} 郵件地址到剪貼簿!".format(len(emails)))
except:
MessageBox.Show("找不到郵件地址!")




2021年5月12日 星期三

如何在HFSS當中快速設置差分對

在此範例當中,同一差分隊的同一側命名只有L跟H的差異,利用這一點可以使用腳本快速將所有符合規則的ports設置為differential pair。如果不是L跟H的差異,使用者可以修改腳本來適應命名規則。

DifferentialPairing.py

import ScriptEnv


ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()


def setdiffpair(i, p, n):
result = [
"NAME:Pair1",
"PosBoundary:=" , p,
"NegBoundary:=" , n,
"CommonName:=" , "Comm_" + str(i),
"CommonRefZ:=" , "25ohm",
"DiffName:=" , "Diff_" + str(i),
"DiffRefZ:=" , "100ohm",
"IsActive:=" , True,
"UseMatched:=" , False
]
return result

oModule = oDesign.GetModule("BoundarySetup")
result = {}
for i in oModule.GetExcitations()[::2]:
key = i.lower().replace('_l_', '').replace('_h_', '')
if key not in result:
result[key] = [i]
else:
result[key] += [i]

pairs = []
n = 1
for i in sorted(result.keys()):
result[i].sort()
if len(result[i]) == 2:
pairs.append(setdiffpair(i, *result[i]))
n += 1
AddWarningMessage('{}, {}'.format(*result[i]))
else:
pass

oModule.EditDiffPairs(["NAME:EditDiffPairs" ,] + pairs)
AddInfoMessage("Differential pairing completed!")

(圖一) 自動差分對設置


2021年5月11日 星期二

計算波束聚合遠場

 # coding=UTF-8

# User Input--------------------------------------------------------
solution = "Setup1 : LastAdaptive"
freq = "28e9"
code_dir = "D:/demo/code1"

# Don't Revise Code Below-------------------------------------------
from math import log10
import os
import time
import json
import itertools
import ScriptEnv

t0 = time.time()

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oDesktop.ClearMessages("", "", 2)

try:
oModule.DeleteAllReports()
except:
pass


def getrE():
oModule = oDesign.GetModule("ReportSetup")
arr = oModule.GetSolutionDataPerVariation(
"Far Fields",
solution,
[
"Context:=" , "RG"
],
['Freq:=', [freq]],
["RealizedGainTotal"])

rETotal = [x for x in arr[0].GetRealDataValues("RealizedGainTotal")]
if len(rETotal) != 65341:
raise Exception("Theta, Phi範圍錯誤!")
return rETotal

def setExcitation(csv_path):
oModule = oDesign.GetModule("BoundarySetup")
ports = [i.replace(':1', '') for i in oModule.GetExcitations()[::2]]
x = {name: ("0W", "0deg") for name in ports}

try:
with open(csv_path) as f:
text = f.readlines()

for i in text[1:]:
try:
source, magnitude, phase = i.split(',')
x[source.replace(':1', '')] = (magnitude, phase)
except:
pass

oModule = oDesign.GetModule("Solutions")
y = []
for name in x:
magnitude, phase = x[name]
y.append([
"Name:=" , name,
"Magnitude:=" , magnitude,
"Phase:=" , phase
])

oModule.EditSources(
[
[
"IncludePortPostProcessing:=", False,
"SpecifySystemPower:=" , False
],
] + y)

for name in x:
magnitude, phase = x[name]
AddWarningMessage('Load "{}" successfully!'.format(csv_path))

except:
AddErrorMessage('Load "{}" failed!'.format(csv_path))

oModule = oDesign.GetModule("RadField")

if "RG" not in oModule.GetChildNames():
oModule.InsertInfiniteSphereSetup(
[
"NAME:RG",
"UseCustomRadiationSurface:=", False,
"CSDefinition:=" , "Theta-Phi",
"Polarization:=" , "Linear",
"ThetaStart:=" , "0deg",
"ThetaStop:=" , "180deg",
"ThetaStep:=" , "1deg",
"PhiStart:=" , "-180deg",
"PhiStop:=" , "180deg",
"PhiStep:=" , "1deg",
"UseLocalCS:=" , False
])




data = []
for i in os.listdir(code_dir):
csv_path = os.path.join(code_dir, i)
setExcitation(csv_path)
data.append(getrE())

max_table = list(map(max, zip(*data)))

with open('RealizedGain.tab', 'w') as f:
f.writelines('Phi[deg],Theta[deg],RealizedGain[db/sr]\n')

for Er, (phi, theta) in zip(max_table, itertools.product(range(-180 ,181), range(0 ,181) ,)):
# maxU = 10*log10(Er**2/377/2) + 30
f.writelines('{}\t{}\t{}\n'.format(phi, theta, Er))

oModule = oDesign.GetModule("ReportSetup")
avaiulable_solution = oModule.GetAvailableSolutions("Modal")

oModule = oDesign.GetModule("Solutions")

if "RG : Table" in avaiulable_solution:
oModule.DeleteImportData(["RG:Table"])

oProject.Save()
oModule.ImportTable('RealizedGain.tab', "RG", "Table", True, True, ["Phi", "Theta", "RealizedGain"], [True, True, False])
oModule = oDesign.GetModule("ReportSetup")
oModule.CreateReport("RG Plot", "Modal Solution Data", "3D Polar Plot", "RG : Table", [],
[
"Tb(Phi):=" , ["All"],
"Tb(Theta):=" , ["All"]
],
[
"Phi Component:=" , "Tb(Phi)",
"Theta Component:=" , "Tb(Theta)",
"Mag Component:=" , ["Tb(RealizedGain)"]
])
(圖一)計算波束聚合遠場


2021年5月9日 星期日

如何將2 Ports元件(R, L, C)設為Edge Ports

滑鼠框選範圍,範圍當中的2 port元件會被disable並設為Edge Ports

(程式碼仍有瑕疵,僅供參考)

# ----------------------------------------------
# Script Recorded by ANSYS Electronics Desktop Version 2021.1.0
# 8:17:33 May 09, 2021
# ----------------------------------------------
from math import atan, degrees
import ScriptEnv

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)

oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.GetActiveEditor()
oModule = oDesign.GetModule('Excitations')


def getPadInfo():
oDefinitionManager = oProject.GetDefinitionManager()
oPadstackManager = oDefinitionManager.GetManager("Padstack")
scale = 0.0000254
x = oPadstackManager.GetNames()
result = {}
for i in x:
try:
info = oPadstackManager.GetData(i)
if info[9][9][1][6][1] == 'Rct':
x, y = info[9][9][1][6][3]
result[i] = (float(x[:-3]) / 2 * scale, float(y[:-3]) / 2 * scale)
if info[9][9][1][6][1] == 'Sq':
x = info[9][9][1][6][3][0]
result[i] = (float(x[:-3]) / 2 * scale, float(x[:-3]) / 2 * scale)
except:
pass
return result


padinfo = getPadInfo()

def getLayerID():
result = {}
for i in oEditor.GetStackupLayerNames():
x = oEditor.GetLayerInfo(i)
result[i] = int(x[10].split(':')[1])
return result


ID = getLayerID()


def disableModel(cmp_name):
oEditor.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:BaseElementTab",
[
"NAME:PropServers",
cmp_name
],
[
"NAME:ChangedProps",
[
"NAME:Model Info",
[
"NAME:Model",
"RLCProp:=",
["CompPropEnabled:=", False, "Pid:=", -1, "Pmo:=", "0", "CompPropType:=", 0, "PinPairRLC:=",
["RLCModelType:=", 0, "ppr:=", ["p1:=", "1", "p2:=", "2", "rlc:=",
["r:=", "240ohm", "re:=", True, "l:=", "0", "le:=", False,
"c:=", "0", "ce:=", False, "p:=", False, "lyr:=", 1]]]],
"CompType:=", 1
]
]
]
]
])


def createPort(sx0, sy0, ex0, ey0, sx1, sy1, ex1, ey1):
ports_old = oModule.GetAllPortsList()
oEditor.CreateEdgePort(
[
"NAME:Contents",
"edge:=",
["et:=", "pse", "sel:=", "{}-2".format(i), "layer:=", layerid, "sx:=", sx0, "sy:=", sy0, "ex:=", ex0,
"ey:=", ey0, "h:=", 0, "rad:=", 0],
"external:=", True,
"btype:=", 0
])
ports_new = oModule.GetAllPortsList()

portname = list(set(ports_new) - set(ports_old))
oEditor.AddRefPort(portname,
[
"NAME:Contents",
"edge:=",
["et:=", "pse", "sel:=", "{}-1".format(i), "layer:=", layerid, "sx:=", sx1, "sy:=", sy1,
"ex:=", ex1, "ey:=", ey1, "h:=", 0, "rad:=", 0]
])
renamePort(portname[0], 'port_{}'.format(i))


def renamePort(old, new):
oDesign.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:EM Design",
[
"NAME:PropServers",
"Excitations:{}".format(old)
],
[
"NAME:ChangedProps",
[
"NAME:Port",
"Value:=" , new
]
]
]
])

def getAngle(x, y):
x = 1e-12 if x == 0 else x
z = round(degrees(atan(y/x)))
if x < 0:
return (z-90)%360-180
else:
return (z-90)%360

allcmps = oEditor.FindObjects('type', 'component')
for i in oEditor.GetSelections():

if i not in allcmps:
continue
if len(oEditor.GetComponentPins(i)) != 2:
continue

try:
pds = oEditor.GetPropertyValue('BaseElementTab', i +'-1', 'Padstack Definition')
W, H = padinfo[pds]
except:
AddErrorMessage('{} does not belong to "square" or "rectangle"!'.format(i))
continue

layername = oEditor.GetPropertyValue('BaseElementTab', i, 'PlacementLayer')
layerid = ID[layername]

try:
disableModel(i)
except:
pass

angle = oEditor.GetPropertyValue('BaseElementTab', i + '-1', 'Angle')
angle = int(float(angle[:-3]))
loc1 = oEditor.GetPropertyValue('BaseElementTab', i + '-1', 'Location')
loc2 = oEditor.GetPropertyValue('BaseElementTab', i + '-2', 'Location')
x1, y1 = [float(j) for j in loc1.split(',')]
x2, y2 = [float(j) for j in loc2.split(',')]
dx, dy = x1 - x2, y1 - y2
orien = getAngle(dx, dy)

theta = int(angle - orien)%360
if theta == 0:
createPort(W, H, -W, H, W, -H, -W, -H)
elif theta == 90:
createPort(W, -H, W, H, -W, -H, -W, H)
elif theta == 180:
createPort(W, -H, -W, -H, W, H, -W, H)
elif theta == 270:
createPort(-W, -H, -W, H, W, -H, W, H)
else:
pass

AddWarningMessage(str(theta))
AddWarningMessage('port_{}'.format(i))

(圖一)框選的2 port元件設為Edge Ports


2021年5月7日 星期五

如何移除3D Layout錄製腳本當中函數參數之間多餘空白

 3D Layout錄製的腳本函數的參數中間有多個空白導致閱讀困難,先Ctrl+C選擇腳本所有程式碼,在AEDT底下執行下面腳本removeDummySpaces.py,在Ctrl+V貼回程式碼即可。

removeDummySpaces.py

import re
import clr

clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import Clipboard

x = Clipboard.GetText()

result = ''
for i in x.splitlines():
i = i.replace('\t', ' ')
i = re.sub(',\s+', ', ', i)
i = re.sub('\s+,', ',', i)
i = re.sub('\[\s+', '[', i)
result += (i + '\n')

Clipboard.SetText(result)

(圖一) 參數之間多餘空白被移除


2021年5月5日 星期三

逐個激發port並記錄每個面的近場到單一pickle檔案

# coding=UTF-8
import pickle
import ScriptEnv
import time

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.SetActiveEditor("3D Modeler")


class PortIterator():
def __init__(self):
oModule = oDesign.GetModule("BoundarySetup")
self.ports = [i.replace(':1', '') for i in oModule.GetExcitations()[::2]]
self.max_num = len(self.ports)
self.index = -1

def __iter__(self):
return self

def next(self):
oModule = oDesign.GetModule("Solutions")
self.index += 1
y = []
if self.index == self.max_num:
raise StopIteration

for i in range(self.max_num):
if i == self.index:
y.append(["Name:=", self.ports[i], "Magnitude:=", "1W", "Phase:=", "0deg"])
else:
y.append(["Name:=", self.ports[i], "Magnitude:=", "0W", "Phase:=", "0deg"])

oModule.EditSources([["IncludePortPostProcessing:=", False, "SpecifySystemPower:=", False], ] + y)
return self.ports[self.index]


def getSheets():
if oDesktop.GetVersion() == '2020.1.0':
seg_string = '{}:CreatePolyline:2:Segment{}'
else:
seg_string = '{}:CreatePolyline:1:Segment{}'

result = {}
for obj in oEditor.GetObjectsInGroup('Sheets'):
try:
num = oEditor.GetPropertyValue('Geometry3DCmdTab', '{}:CreatePolyline:1'.format(obj), 'Number of curves')
result[obj] = []
for i in range(int(num)):
cs = oEditor.GetPropertyValue('Geometry3DPolylineTab', seg_string.format(obj, i), 'Point1')
result[obj].append(map(float, cs.split(','))[0:2])
result[obj].append(result[obj][0])
except:
pass

for i in result:
result[i] = zip(*result[i])

return result


def getNFRectangle():
result = []
radiation = oDesign.GetChildObject('Radiation')
for i in radiation.GetChildNames():
if oDesign.GetPropertyValue('RadFieldSetupTab', 'RadField:{}'.format(i), 'Type') == 'Rectangle':
result.append(i)
return result


def getNearEH(solution, freq, nf):
oModule = oDesign.GetModule("ReportSetup")
EH = ["NearEX", "NearEY", "NearEZ", "NearHX", "NearHY", "NearHZ"]
try:
arr = oModule.GetSolutionDataPerVariation("Near Fields", solution, ["Context:=", nf], ['Freq:=', [freq]], EH)
except:
AddErrorMessage("無模擬資料可輸出!")
result = {}
result["v"] = list(arr[0].GetSweepValues("_v"))
x = list(arr[0].GetSweepValues("_u"))
result["u"] = x[0:int(len(x) / len(result["v"]))]

for i in EH:
result[i] = [complex(x, y) for x, y in zip(arr[0].GetRealDataValues(i), arr[0].GetImagDataValues(i))]
return result


def outputPickle(solution, freq, pickle_path):
result = {'sheet': getSheets()}

for portname in PortIterator():
result[portname] = {}
AddWarningMessage("激發端口: {}".format(portname))
for nf in getNFRectangle():
result[portname][nf] = getNearEH(solution, freq, nf)

with open(pickle_path, 'wb') as f:
pickle.dump(result, f)
AddInfoMessage("檔案輸出: {}".format(pickle_path))


outputPickle("Setup1 : LastAdaptive", "28GHz", 'd:/demo/data.pickle')

2021年5月4日 星期二

如何計算多個beam所合成的EIRP輻射場型

腳本讀取每個beam並記錄其對應的rETotal。完成特定角度最大值運算之後會輸出.tab檔。在HFSS當中匯入.tab檔即可畫出其3D幅射場型及2D contour。

# coding=UTF-8
# User Input--------------------------------------------------------
solution = "Setup1 : LastAdaptive"
freq = "28e9"
code_dir = "D:/demo/code"
output_path = 'd:/demo/EIRP.tab'

# Don't Revise Code Below-------------------------------------------
from math import log10
import os
import time
import json
import itertools
import ScriptEnv

t0 = time.time()

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oDesktop.ClearMessages("", "", 2)
oModule = oDesign.GetModule("ReportSetup")
try:
oModule.DeleteAllReports()
except:
pass


def getrE():
arr = oModule.GetSolutionDataPerVariation(
"Far Fields",
solution,
[
"Context:=" , "3D"
],
['Freq:=', [freq]],
["rETotal"])

rETotal = [x for x in arr[0].GetRealDataValues("rETotal")]
if len(rETotal) != 65341:
raise Exception("Theta, Phi範圍錯誤!")
return rETotal

def setExcitation(csv_path):
oModule = oDesign.GetModule("BoundarySetup")
ports = [i.replace(':1', '') for i in oModule.GetExcitations()[::2]]
x = {name: ("0W", "0deg") for name in ports}

try:
with open(csv_path) as f:
text = f.readlines()

for i in text[1:]:
try:
source, magnitude, phase = i.split(',')
x[source.replace(':1', '')] = (magnitude, phase)
except:
pass

oModule = oDesign.GetModule("Solutions")
y = []
for name in x:
magnitude, phase = x[name]
y.append([
"Name:=" , name,
"Magnitude:=" , magnitude,
"Phase:=" , phase
])

oModule.EditSources(
[
[
"IncludePortPostProcessing:=", False,
"SpecifySystemPower:=" , False
],
] + y)

for name in x:
magnitude, phase = x[name]
# AddInfoMessage("{}: {}, {}".format(name, magnitude, phase))
AddWarningMessage('Load "{}" successfully!'.format(csv_path))

except:
AddErrorMessage('Load "{}" failed!'.format(csv_path))

data = []
for i in os.listdir(code_dir):
csv_path = os.path.join(code_dir, i)
setExcitation(csv_path)
data.append(getrE())

max_table = list(map(max, zip(*data)))

with open(output_path, 'w') as f:
f.writelines('Phi[deg],Theta[deg],EIRP[dbm/sr]\n')

for Er, (phi, theta) in zip(max_table, itertools.product(range(-180 ,181), range(0 ,181) ,)):
maxU = 10*log10(Er**2/377/2)+30
f.writelines('{}\t{}\t{}\n'.format(phi, theta, maxU))

AddWarningMessage(str(time.time( )-t0))

(圖一) 3D角度設定範圍及解析度


(圖二) EIRP