摘要:本节主要介绍波导查值模块,以及HFSS调用模块的编写。
该部分要实现的功能主要是根据输入的工作频率,自动选择合适的标准波导尺寸,免去翻资料的麻烦,实现起来逻辑很简单,也就是单纯地根据条件来查值,查值方法有两种:
根据工作频点来判断;
根据波导名直接查询;
两种方式均可用简单的函数来实现,代码如下所示(wg.py):
"""
波导尺寸查询
"""
# 标准波导尺寸表
wg_list = (
('BJ3', 0.32, 0.49, 584.2, 292.1),
('BJ4', 0.35, 0.53, 533.4, 266.7),
('BJ5', 0.41, 0.62, 457.2, 228.6),
('BJ6', 0.49, 0.75, 381.0, 190.5),
('BJ8', 0.64, 0.98, 292.1, 146.05),
('BJ9', 0.76, 1.15, 247.65, 123.82),
('BJ12', 0.96, 1.46, 195.58, 97.79),
('BJ14', 1.13, 1.73, 165.10, 82.55),
('BJ18', 1.45, 2.20, 129.54, 64.77),
('BJ22', 1.72, 2.61, 109.22, 54.61),
('BJ26', 2.17, 3.30, 86.36, 43.18),
('BJ32', 2.60, 3.35, 72.14, 34.04),
('BJ40', 3.22, 4.90, 58.17, 29.08),
('BJ48', 3.94, 5.99, 47.549, 22.149),
('BJ58', 4.64, 7.05, 40.386, 20.193),
('BJ70', 5.38, 8.17, 34.849, 15.799),
('BJ84', 6.57, 9.99, 28.499, 12.624),
('BJ100', 8.20, 12.5, 22.86, 10.160),
('BJ120', 9.84, 15.0, 19.050, 9.525),
('BJ140', 11.9, 18.0, 15.799, 7.899),
('BJ180', 14.5, 22.0, 12.954, 6.477),
('BJ220', 17.6, 26.7, 10.668, 4.318),
('BJ260', 21.7, 33.0, 8.636, 4.318),
('BJ320', 26.3, 40.0, 7.112, 3.556),
('BJ400', 32.9, 50.1, 5.690, 2.845),
('BJ500', 39.2, 59.6, 4.775, 2.388),
('BJ620', 49.8, 75.8, 3.759, 1.880),
)
def check_by_freq(freq):
"""
通过输入频点查找标准波导尺寸
:param freq: 频点,GHz
:return: 波导的宽边a和短边b,mm
"""
a, b = None, None
for i in wg_list:
if i[1] <= freq <= i[2]:
a, b = i[3], i[4]
break
return a, b
def check_by_name(name):
"""
通过名称来查找标准波导尺寸
:param name: 波导名,如‘BJ100’
:return: 波导的宽边a和短边b,mm
"""
a, b = None, None
for i in wg_list:
if i[0] == name:
a, b = i[3], i[4]
break
return a, b
if __name__ == '__main__':
a1, b1 = check_by_freq(25)
a2, b2 = check_by_name('BJ320')
print(a1, b1)
print(a2, b2)
实际使用中第一种“自动判断”的场景要多一些,但有时候也会特别指定波导型号,故两者皆写好供调用。
HFSS调用模块
这一部分是稍微比较麻烦的,需要用到HFSS自己的“代码录制”功能和Python的win32com模块。
首先,众所周知,HFSS支持脚本控制,可将操作录制为一段脚本,便于自动化运行以及实现较为复杂的建模功能,该功能位于Tools——Record Script to File中,支持vbs和python两种格式(早期版本仅支持vbs)。
因此,要调用HFSS建模,自然会想到先用“代码录制”功能将建模操作录制下来,再进行简单修改,最后再想办法用python直接运行调用。
顺着这个思路,首先梳理下角锥喇叭的建模步骤,以及对应步骤抽象出来的封装函数(注:建模方法当然不止一种,以下仅为个人习惯方式):
设置变量。个人习惯先把变量建好,这一步操作应抽象并封装成函数:
设置变量(set_variable)——1
建喇叭天线内腔。画两个同尺寸矩形,connect而成立方体;再画两个不同尺寸矩形,connect而成锥台;两者unite而成联合体。这一步操作有:
画一个矩形(create_centered_rectangle)——2
联合两个面(connect)——3
布尔和(unite)——4
建喇叭天线体并设为金属。采用同上方法画一个更大一些的联合体,然后与第一步得到的联合体相减,最后设置材料为PEC,新操作有:
复制(copy_and_paste)——5
布尔差(subtract)——6
设置材料属性(set_material)——7
建空气盒子和辐射边界。操作有:
画一个区域(create_region)——8
设置辐射边界(assign_radiation_region)——9
插入方向图设置(insert_radiation_setup)——10
画端口并设置解算参数。操作有:
画一个端口(assign_port)——11
设置解算参数(insert_analysis_setup)——12
保存工程后仿真并生成报告。操作有:
保存(save_prj)——13
仿真(run)——14
生成仿真报告(create_reports)——15
梳理以后发现,完成一个喇叭建模,竟然大概要写15个函数。。。感觉不知比直接建模要麻烦到哪儿去了,但考虑到这些函数写好了,以后可以被大量复用,相当在于写一个小型API,故是一种先难后易的做法。
对于每一个封装函数的实现,操作步骤为先用HFSS录制脚本,在进行修改。以实现并封装set_variable这个函数为例,首先在HFSS中设一个名为w1,值为20mm的变量,把这段操作录制为py程序,打开后如下所示:
# ----------------------------------------------
# Script Recorded by ANSYS Electronics Desktop Version 2018.0.0
# 10:13:34 十一月 17, 2018
# ----------------------------------------------
import ScriptEnv
ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.SetActiveProject("Project2")
oDesign = oProject.SetActiveDesign("HFSSDesign1")
oDesign.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:LocalVariableTab",
[
"NAME:PropServers",
"LocalVariables"
],
[
"NAME:NewProps",
[
"NAME:w1",
"PropType:=" , "VariableProp",
"UserDef:=" , True,
"Value:=" , "20mm"
]
]
]
])
这段原始代码有以下几点要注意:
一个HFSS专属坑。一定得要先删除注释中的中文和中文符号!!!这点特别坑爹,HFSS不能正确识别任何带中文字符的路径和脚本,连它自己录制的也不行(希望HFSS下个版本能改掉这个万年bug);
引用模块调用无效。import ScriptEnv 在HFSS外部调用时没有意义,因为这不是一个通用的Python包,也就是说想在Python中写这么一段就调用HFSS是不可能的;
格式。格式有点杂乱,相当地不pythonic,这当然是不能忍的,应手动调整一下。
改写后的代码如下所示(将变量名与变量值设成了输入参数):
def set_variable(_var_name, _var_value):
_NAME = 'NAME:' _var_name
_VALUE = str(_var_value) 'mm'
oDesign.ChangeProperty(["NAME:AllTabs",
["NAME:LocalVariableTab",
["NAME:PropServers", "LocalVariables"],
["NAME:NewProps",
[_NAME, "PropType:=", "VariableProp", "UserDef:=", True, "Value:=", _VALUE]]]])
改写以后,是否感觉清爽了很多?采用这种思路,将15个函数统统编好,为了调用方便,再整体封装为一个对象HFSS,程序就比较像样了。
最后要强调的就是,通过查阅各种资料,我个人认为在Python中直接打开HFSS最好的方式还是要借助win32com这个模块,其介绍如下:
python模块:win32com用法详解 - 杜雪峰的个人页面 - 开源中国my.oschina.net
综合以上,形成HFSS调用模块,其代码如下(sim.py):
from win32com import client
import os
class HFSS:
def __init__(self):
self.oAnsoftApp = client.Dispatch('AnsoftHfss.HfssScriptInterface')
self.oDesktop = self.oAnsoftApp.GetAppDesktop()
self.oProject = self.oDesktop.NewProject()
self.oProject.InsertDesign('HFSS', 'HFSSDesign1', 'DrivenModal1', '')
self.oDesign = self.oProject.SetActiveDesign("HFSSDesign1")
self.oEditor = self.oDesign.SetActiveEditor("3D Modeler")
self.oModule = self.oDesign.GetModule('BoundarySetup')
self.transparency = 0.5
def set_variable(self, _var_name, _var_value):
_NAME = 'NAME:' _var_name
_VALUE = str(_var_value) 'mm'
self.oDesign.ChangeProperty(["NAME:AllTabs",
["NAME:LocalVariableTab",
["NAME:PropServers", "LocalVariables"],
["NAME:NewProps",
[_NAME, "PropType:=", "VariableProp", "UserDef:=", True, "Value:=", _VALUE]]]])
def create_centered_rectangle(self, _var_x, _var_y, _var_z, _name, _dir='Z'):
self.oEditor.CreateRectangle(
[
"NAME:RectangleParameters",
"IsCovered:=", True,
"XStart:=", '-' _var_x '/2',
"YStart:=", '-' _var_y '/2',
"ZStart:=", _var_z,
"Width:=", _var_x,
"Height:=", _var_y,
"WhichAxis:=", _dir
],
[
"NAME:Attributes",
"Name:=", _name,
"Flags:=", "",
"Color:=", "(143 175 143)",
"Transparency:=", 0,
"PartCoordinateSystem:=", "Global",
"UDMId:=", "",
"MaterialValue:=", "\"vacuum\"",
"SurfaceMaterialValue:=", "\"\"",
"SolveInside:=", True,
"IsMaterialEditable:=", True,
"UseMaterialAppearance:=", False
])
def connect(self, _obj1, _obj2):
self.oEditor.Connect(["NAME:Selections", "Selections:=", _obj1 ',' _obj2])
def unite(self, _obj1, _obj2):
self.oEditor.Unite(["NAME:Selections", "Selections:=", _obj1 ',' _obj2],
["NAME:UniteParameters", "KeepOriginals:=", False])
def subtract(self, _obj1, _obj2):
self.oEditor.Subtract(["NAME:Selections", "Blank Parts:=", _obj1, "Tool Parts:=", _obj2],
["NAME:SubtractParameters", "KeepOriginals:=", False])
def copy_and_paste(self, _obj):
self.oEditor.Copy(["NAME:Selections", "Selections:=", _obj])
self.oEditor.Paste()
def set_material(self, _obj, _mat='pec'):
self.oEditor.AssignMaterial(
[
"NAME:Selections",
"AllowRegionDependentPartSelectionForPMLCreation:=", True,
"AllowRegionSelectionForPMLCreation:=", True,
"Selections:=", _obj
],
[
"NAME:Attributes",
"MaterialValue:=", "\"" _mat "\"",
"SolveInside:=", False,
"IsMaterialEditable:=", True,
"UseMaterialAppearance:=", False
])
def assign_port(self, _obj):
self.oModule.AssignWavePort(["NAME:1", "Objects:=", [_obj],
"NumModes:=", 1, "RenormalizeAllTerminals:=", True,
"UseLineModeAlignment:=", False, "DoDeembed:=", False,
["NAME:Modes",
["NAME:Mode1", "ModeNum:=", 1, "UseIntLine:=", False, "CharImp:=", "Zpi"]],
"ShowReporterFilter:=", False,
"ReporterFilter:=", [True],
"UseAnalyticAlignment:=", False])
def create_region(self, _var_ab):
self.oEditor.CreateRegion(
[
"NAME:RegionParameters",
" XPaddingType:=", "Absolute Offset",
" XPadding:=", _var_ab,
"-XPaddingType:=", "Absolute Offset",
"-XPadding:=", _var_ab,
" YPaddingType:=", "Absolute Offset",
" YPadding:=", _var_ab,
"-YPaddingType:=", "Absolute Offset",
"-YPadding:=", _var_ab,
" ZPaddingType:=", "Absolute Offset",
" ZPadding:=", _var_ab,
"-ZPaddingType:=", "Absolute Offset",
"-ZPadding:=", _var_ab
],
[
"NAME:Attributes",
"Name:=", "Region",
"Flags:=", "Wireframe#",
"Color:=", "(143 175 143)",
"Transparency:=", 0,
"PartCoordinateSystem:=", "Global",
"UDMId:=", "",
"MaterialValue:=", "\"vacuum\"",
"SurfaceMaterialValue:=", "\"\"",
"SolveInside:=", True,
"IsMaterialEditable:=", True,
"UseMaterialAppearance:=", False
])
def assign_radiation_region(self):
self.oModule.AssignRadiation(
[
"NAME:Rad1",
"Objects:=", ["Region"],
"IsFssReference:=", False,
"IsForPML:=", False
])
def insert_radiation_setup(self):
mod = self.oDesign.GetModule('RadField')
mod.InsertFarFieldSphereSetup(
[
"NAME:Infinite Sphere1",
"UseCustomRadiationSurface:=", False,
"ThetaStart:=", "-180deg",
"ThetaStop:=", "180deg",
"ThetaStep:=", "1deg",
"PhiStart:=", "0deg",
"PhiStop:=", "90deg",
"PhiStep:=", "90deg",
"UseLocalCS:=", False
])
def insert_analysis_setup(self, _freq):
mod = self.oDesign.GetModule('AnalysisSetup')
mod.InsertSetup("HfssDriven",
[
"NAME:Setup1",
"AdaptMultipleFreqs:=", False,
"Frequency:=", str(_freq) 'GHz',
"MaxDeltaS:=", 0.02,
"PortsOnly:=", False,
"UseMatrixConv:=", False,
"MaximumPasses:=", 20,
"MinimumPasses:=", 1,
"MinimumConvergedPasses:=", 1,
"PercentRefinement:=", 30,
"IsEnabled:=", True,
"BasisOrder:=", 1,
"DoLambdaRefine:=", True,
"DoMaterialLambda:=", True,
"SetLambdaTarget:=", False,
"Target:=", 0.3333,
"UseMaxTetIncrease:=", False,
"PortAccuracy:=", 2,
"UseABCOnPort:=", False,
"SetPortMinMaxTri:=", False,
"UseDomains:=", False,
"UseIterativeSolver:=", False,
"SaveRadFieldsOnly:=", False,
"SaveAnyFields:=", True,
"IESolverType:=", "Auto",
"LambdaTargetForIESolver:=", 0.15,
"UseDefaultLambdaTgtForIESolver:=", True
])
def create_reports(self):
mod = self.oDesign.GetModule('ReportSetup')
mod.CreateReport("VSWR Plot 1", "Modal Solution Data", "Rectangular Plot", "Setup1 : LastAdaptive", [],
["Freq:=", ["All"], ],
["X Component:=", "Freq",
"Y Component:=", ["VSWR(1)"]], [])
mod.CreateReport("Realized Gain Plot 1", "Far Fields", "Rectangular Plot", "Setup1 : LastAdaptive",
["Context:=", "Infinite Sphere1"],
[
"Theta:=", ["All"],
"Phi:=", ["All"],
"Freq:=", ["All"],
],
[
"X Component:=", "Theta",
"Y Component:=", ["dB(RealizedGainTotal)"]
], [])
mod.AddTraceCharacteristics("Realized Gain Plot 1", "max", [], ["Full"])
mod.AddTraceCharacteristics("Realized Gain Plot 1", "xdb10Beamwidth", ["3"], ["Full"])
def save_prj(self):
_base_path = os.getcwd()
_prj_num = 1
while True:
_path = os.path.join(_base_path, 'Prj{}.aedt'.format(_prj_num))
if os.path.exists(_path):
_prj_num = 1
else:
break
self.oProject.SaveAs(_path, True)
def run(self):
self.oDesktop.RestoreWindow()
self.oDesign.Analyze('Setup1')
调用方法是首先实例化HFSS对象,再使用其方法,就可以顺利调用HFSS啦,例如:
if __name__ == '__main__':
h = HFSS()
# 设置变量
h.set_variable('wg_a', 22.86)
h.set_variable('wg_b', 10.16)
# 画个矩形
h.create_centered_rectangle('wg_a', 'wg_b', 0, 'wg_in')
# 保存一下
h.save_prj()
要注意的是,save_prj函数会将新建立的工程保存在与程序同样目录中,这个目录的路径照例不可以有任何中文符号,否则HFSS保存出错,至于文件名,save_prj函数中已作了简单的判重处理,不用担心覆盖,当然,有特别需求的请自行修改。
小结
本部分是相对比较麻烦的部分,相当于写了一个HFSS与Python之间的通讯接口,然而后续将会看到,有了这部分基础,主调程序才可以用相对清晰的逻辑来操作HFSS建模。
本篇介绍即到此结束,下一部分将会讲到主调用模块及GUI模块,谢谢各位观看(*^_^*)!
Peace!