使用python-cst实现角锥喇叭的建模仿真
内容
完全使用python-cst联合仿真脚本模式,从0开始实现角锥喇叭的建模、端口添加、远场监视器添加,以及模型仿真。喇叭的形状如图所示:
一,创建cst项目
# 创建CST文件,文件路径名为cst_path
def creat_cst(cst_path):
allpids = cst.interface.running_design_environments() # 以列表形式返回运行的cst环境名(PIDs)
DE_is_open = False # 判断是否有打开的DE
for pid in allpids:
my_DE = cst.interface.DesignEnvironment.connect(pid) # 连接到指定PID的DE
DE_is_open = True
break
if not DE_is_open: # DE不存在,则新建一个DE,否则使用上面已打开的最后一个DE
my_DE = cst.interface.DesignEnvironment()
my_project = my_DE.new_mws()
my_project.save(cst_path)
return my_DE, my_project
二,定义VBA执行(添加历史书)
# 执行VBA代码并添加历史书
def add_vba_history(my_modeler, sCommand, history_name):
line_break = '\n' # 用于VBA代码的拼接
sCommand = line_break.join(sCommand)
my_modeler.add_to_history(history_name, sCommand)
return my_modeler
三,设置变量和仿真频率
# 设置参数
class set_parameter(object):
def __init__(self, my_modeler):
super().__init__()
self.parameter_list = {
'taper_angle': 7, # 嘴部抬头角度
'horn_length': 166.71, # 喇叭嘴部长
'wall_thickness': 1.8, # 喇叭厚度
'waveguide_height': 24, # 喇叭尾部高
'waveguide_width': 12, # 喇叭尾部宽
'waveguide_length': 51.4, # # 喇叭尾部长
'theta': 0, # unit cell下设置的入射电磁波角度
'phi': 0
}
self.frq = [12, 14]
self.my_modeler = my_modeler
def add_parameter(self):
for par_key, par_value in self.parameter_list.items():
self.my_modeler.add_to_history('StoreParameter', f'MakeSureParameterExists("{par_key}", {par_value})')
return self.my_modeler
# 添加频率
def set_frq(self):
sCommand = 'Solver.FrequencyRange %f, %f' % (self.frq[0], self.frq[1])
self.my_modeler.add_to_history('define FrequencyRange', sCommand)
return self.my_modeler
四,设置Units
# 单位初始化
def init_unit(my_modeler):
sCommand = ['With Units',
'.Geometry "mm"',
'.Frequency "GHz"',
'.Voltage "V"',
'.Resistance "Ohm"',
'.Inductance "H"',
'.TemperatureUnit "Kelvin"',
'.Time "s"',
'.Current "A"',
'.Conductance "Siemens"',
'.Capacitance "F"',
'End With']
history_name = 'define Units'
my_modeler = add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
五,设置Background
# 设置背景材料
def set_background(my_modeler):
sCommand = ['With Background',
'.ResetBackground',
'.Type "Normal"',
'.XminSpace "0.0"',
'.XmaxSpace "0.0"',
'.YminSpace "0.0"',
'.YmaxSpace "0.0"',
'.ZminSpace "0.0"',
'.ZmaxSpace "0.0"',
'.ApplyInAllDirections "False"',
'End With']
history_name = 'define Background'
my_modeler = add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
六,设置Boundaries
这里设置的open(add space)边界条件,如果是设置成unit cell,则将注释部分的注释取消,其中周期边界角度设置为了theta, phi。
def set_boundary(my_modeler):
sCommand = ['With Boundary',
'.Xmin "expanded open"',
'.Xmax "expanded open"',
'.Ymin "expanded open"',
'.Ymax "expanded open"',
'.Zmin "expanded open"',
'.Zmax "expanded open"',
'.Xsymmetry "none"',
'.Ysymmetry "none"',
'.Zsymmetry "none"',
'.ApplyInAllDirections "False"',
'.OpenAddSpaceFactor "0.5"',
# '.XPeriodicShift "0.0"',
# '.YPeriodicShift "0.0"',
# '.ZPeriodicShift "0.0"',
# '.PeriodicUseConstantAngles "True"',
# '.SetPeriodicBoundaryAngles "theta", "phi"',
# '.SetPeriodicBoundaryAnglesDirection "outward"',
# '.UnitCellFitToBoundingBox "True"',
# '.UnitCellDs1 "0.0"',
# '.UnitCellDs2 "0.0"',
# '.UnitCellAngle "90.0"',
'End With']
history_name = 'define boundaries'
my_modeler = add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
七,喇叭建模
为了方便查错管理,这里是将建模所需要的操作都分别定义好,之后只进行调用即可。
1, 创建新component
在使用新的component时,CST不会自动创建目标组,因此需要提前创建。
def add_new_component(my_modeler, component_name):
sCommand = [f'Component.New "{component_name}"']
history_name = f'new component: {component_name}'
add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
2,brick创建
def brick_model(my_modeler, name, component, material, xrange, yrange, zrange):
sCommand = ['With Brick',
'.Reset',
'.Name "%s"' % name,
'.Component "%s"' % component,
'.Material "%s"' % material,
f'.Xrange {xrange[0]}, {xrange[1]}',
f'.Yrange {yrange[0]}, {yrange[1]}',
f'.Zrange {zrange[0]}, {zrange[1]}',
'.Create',
'End With']
history_name = 'define brick:%s:%s' % (component, name)
add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
3,pick face操作
其中的value表示选中面所代表的序号,目前我还没搞懂这个值与模型的面的联系,只能在cst界面交互后实验下才知道。
def pick_face(my_modeler, component, obj_name, value):
sCommand = [f'Pick.PickFaceFromId "{component}:{obj_name}", "{value}"']
history_name = 'pick face'
add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
4,Extrude face操作
在进行Extrude face操作之前,需要先进行pick face选中一个操作面。
def extrude_face(my_modeler, component, name, material):
sCommand = ['With Extrude',
f'.Name "{name}"',
f'.Component "{component}"',
f'.Material "{material}"',
'.Mode "Picks"',
'.Height "horn_length"',
'.Twist "0.0"',
'.Taper "taper_angle"',
'.UsePicksForHeight "False"',
'.DeleteBaseFaceSolid "False"',
'.ClearPickedFace "True"',
'.Create',
'End With']
history_name = f'define extrude: {component}:{name}'
add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
5,Add操作
class boolean(object):
def __init__(self, my_modeler, obj_name1, obj_component1, obj_name2, obj_component2):
super().__init__()
self.my_modeler = my_modeler
self.name1 = obj_component1 + ':' + obj_name1
self.name2 = obj_component2 + ':' + obj_name2
def add(self): # +
sCommand = [f'Solid.Add "{self.name1}", "{self.name2}"']
history_name = f'boolean add shapes: {self.name1}, {self.name2}'
add_vba_history(self.my_modeler, sCommand, history_name)
return self.my_modeler
6,挖空操作
在使用此操作之前,需要先使用pick face操作分别选中两个面。
def shell(my_modeler, component, name):
sCommand = ['With Solid',
f'.AdvancedShell "{component}:{name}", "Outside", "wall_thickness"',
'End With']
history_name = f'shell object: {component}, {name}'
add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
7,transform操作
obj_component, obj_name:操作对象的组和名
num:操作次数
save_component:目标操作完成后,得到的新的对象的保存component,默认与目标对象一致
# 模型翻转操作
class transform(object): # 输入为操作对象分组,对象名,操作次数,保存分组
def __init__(self, my_modeler, obj_component, obj_name=None, num=1, save_component="", is_copy=True):
super().__init__()
self.my_modeler = my_modeler
if obj_name is None: # 对整个分组进行操作
self.name = obj_component
else:
self.name = obj_component + ':' + obj_name
self.num = num # 操作次数
self.save_component = save_component
self.is_copy = is_copy
# 镜像
def mirrow(self, plane_normal): # 输入为镜像的方向向量
sCommand = ['With Transform',
'.Reset',
f'.Name "{self.name}"',
'.Origin "Free"',
'.Center "0", "0", "0"',
f'.PlaneNormal "{plane_normal[0]}", "{plane_normal[1]}", "{plane_normal[2]}"',
f'.MultipleObjects "{self.is_copy}"',
'.GroupObjects "False"',
f'.Repetitions "{self.num}"',
'.MultipleSelection "False"',
f'.Destination "{self.save_component}"',
'.Material ""',
'.Transform "Shape", "Mirror"',
'End With']
history_name = f'transform: mirrow {self.name}'
add_vba_history(self.my_modeler, sCommand, history_name)
return self.my_modeler
# 平移,输入为:移动的方向向量
def translate(self, tran_vector):
sCommand = ['With Transform',
'.Reset',
f'.Name "{self.name}"',
f'.Vector "{tran_vector[0]}", "{tran_vector[1]}", "{tran_vector[2]}"',
'.UsePickedPoints "False"',
'.InvertPickedPoints "False"',
f'.MultipleObjects "{self.is_copy}"',
'.GroupObjects "False"',
f'.Repetitions "{self.num}"',
'.MultipleSelection "False"',
'.Transform "Shape", "Translate"',
'End With']
history_name = f'transform: translate {self.name}'
add_vba_history(self.my_modeler, sCommand, history_name)
return self.my_modeler
8,喇叭建模
def horn_model(my_modeler):
material = 'PEC'
component = 'component1'
add_new_component(my_modeler, component)
# 尾部波导
waveguide_name = 'waveguide'
xrange = ["-waveguide_width/2", "waveguide_width/2"]
yrange = ["-waveguide_height/2", "waveguide_height/2"]
zrange = ["0", "waveguide_length"]
brick_model(my_modeler, waveguide_name, component, material,
xrange, yrange, zrange)
# 喇叭嘴部
pick_face(my_modeler, component, waveguide_name, 1)
horn_name = 'horn'
extrude_face(my_modeler, component, horn_name, material)
boolean(my_modeler, horn_name, component, waveguide_name, component).add()
# 挖空
pick_face(my_modeler, component, horn_name, 2)
pick_face(my_modeler, component, horn_name, 11)
shell(my_modeler, component, horn_name)
transform(my_modeler, component, horn_name, is_copy=False).mirrow([0, 0, 1])
transform(my_modeler, component, horn_name, is_copy=False).translate([0, 0, 'horn_length+waveguide_length'])
xrange = ["-waveguide_width/2", "waveguide_width/2"]
yrange = ["-waveguide_height/2", "waveguide_height/2"]
zrange = ['horn_length+waveguide_length', 'horn_length+waveguide_length']
pick_face(my_modeler, component, horn_name, '11')
set_port(my_modeler).waveguide_port(1, xrange, yrange, zrange, 'positive') # 设置端口
return my_modeler
八,修改频域求解
由于项目初始时求解器为时域求解器,因此需要将其修改为频域求解。
# 更改为频域求解器
def get_frq_solver(my_modeler):
sCommand = ['ChangeSolverType "HF Frequency Domain"']
history_name = 'change solver type'
add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
九,设置波导端口
这里是用于将喇叭的尾部设置为波导端口,使用时需要先进行pick face操作选中一个面。其中:
port_num: 波导端口编号
port_dir:波导方向,只能是"positive"和"negative"
modes_num:端口模式数,默认为1
class set_port(object):
def __init__(self, my_modeler):
super().__init__()
self.modeler = my_modeler
def waveguide_port(self, port_num, xrange, yrange, zrange, port_dir, modes_num=1):
sCommand = ['With Port',
'.Reset',
f'.PortNumber "{port_num}"',
'.Label ""',
'.Folder ""',
f'.NumberOfModes "{modes_num}"',
'.AdjustPolarization "False"',
'.PolarizationAngle "0.0"',
'.ReferencePlaneDistance "0"',
'.TextSize "50"',
'.TextMaxLimit "1"',
'.Coordinates "Picks"',
f'.Orientation "{port_dir}"',
'.PortOnBound "False"',
'.ClipPickedPortToBound "False"',
f'.Xrange "{xrange[0]}", "{xrange[1]}"',
f'.Yrange "{yrange[0]}", "{yrange[1]}"',
f'.Zrange "{zrange[0]}", "{zrange[1]}"',
'.XrangeAdd "0.0", "0.0"',
'.YrangeAdd "0.0", "0.0"',
'.ZrangeAdd "0.0", "0.0"',
'.SingleEnded "False"',
'.WaveguideMonitor "False"',
'.Create',
'End With']
history_name = f'define port:{port_num}'
add_vba_history(self.modeler, sCommand, history_name)
return self.modeler
十,添加远场监视器
这里是需要设置一个13GHz的远场监视器。
def set_monitor(my_modeler, frq, xrange, yrange, zrange):
sCommand = ['With Monitor',
'.Reset',
f'.Name "farfield (f={frq})"',
'.Domain "Frequency"',
'.FieldType "Farfield"',
f'.MonitorValue "{frq}"',
'.ExportFarfieldSource "False"',
'.UseSubvolume "False"',
'.Coordinates "Structure"',
f'.SetSubvolume "{xrange[0]}", "{xrange[1]}", "{yrange[0]}", "{yrange[1]}", "{zrange[0]}", "{zrange[1]}"',
'.SetSubvolumeOffset "10", "10", "10", "10", "10", "10"',
'.SetSubvolumeInflateWithOffset "False"',
'.SetSubvolumeOffsetType "FractionOfWavelength"',
'.EnableNearfieldCalculation "True"',
'.Create',
'End With']
history_name = f'define farfield monitor: farfield (f={frq})'
add_vba_history(my_modeler, sCommand, history_name)
return my_modeler
十一,模块调用
if __name__ == '__main__':
work_path = os.getcwd()
my_path = os.path.join(work_path, r'cst_file/horn.cst')
my_DE, my_project = creat_cst(my_path)
my_modeler = my_project.modeler
set_parameter(my_modeler).add_parameter()
set_parameter(my_modeler).set_frq()
init_unit(my_modeler)
set_background(my_modeler)
set_boundary(my_modeler)
horn_model(my_modeler)
get_frq_solver(my_modeler)
xrange = ["-28.282931833949", "28.282931833949"]
yrange = ["-34.282931833949", "34.282931833949"]
zrange = ["-2.8421709430404e-14", 'horn_length+waveguide_length']
set_monitor(my_modeler, 13, xrange, yrange, zrange)
# Zoom(my_modeler)
my_modeler.run_solver()
my_project.save()
代码执行效果
弹出窗口是CST2021及以上版本新加入的脚本(静态)模式/交互模式,即使不关闭弹窗,脚本也会执行下去,只是无法看到界面而已。
角锥喇叭仿真