在用CST仿真对模型进行扫参时,一般会产生大量数据,我们需要把产生的数据导出以用于后续的分析使用,而且在扫参时我们可能需要指定特定的扫参方式或随机生成一些参数,使用CST自带的扫参功能很不方便,特别是在数据导出阶段很麻烦。由于CST自带了VBA(visual basic)编程环境,可以使用VBA脚本编程可以控制CST实现自动仿真以及数据导出。而CST-2020以上的版本提供了Python编程接口,也提供了在Python环境中执行VBA脚本的接口,我们可以利用Python强大的编程功能实现控制CST自动扫参以及数据的导出,还可以直接对导出的数据进行处理。
打开CST帮助文档,在Automation and Scripting目录下有Visual Basic和Python目录,点开Python目录下的CST Python Libraries,可以看到关于python控制接口的详细介绍。此处CST版本为2022,已支持Python 3.9,如其他版本请注意CST支持的Python版本。
在帮助文档中可以看到Python与CST的接口函数以及CST的VBA函数,如需使用其他功能在Python端运行对应的VBA函数即可。
在高级系统设置的环境变量处新建系统环境变量,设置变量名为PYTHONPATH,变量值为CST安装目录下的AMD64\python_cst_libraries。
在每个需要调用CST的代码前添加这三行代码,路径为CST安装目录下的AMD64\python_cst_libraries。
import sys
cst_lib_path = r"E:\CST Studio Suite 2022\AMD64\python_cst_libraries"
sys.path.append(cst_lib_path)
设置好环境变量后,输入如下代码。
import sys
cst_lib_path = r"E:\CST Studio Suite 2022\AMD64\python_cst_libraries"
sys.path.append(cst_lib_path)
import cst
print(cst.__file__)
运行不报错且打印成功,则环境变量配置成功
导入cst.interface以使用内部接口方法与CST交互,导入os是由于内部涉及很多文件和路径的操作。
import sys
cst_lib_path = r"E:\CST Studio Suite 2022\AMD64\python_cst_libraries"
sys.path.append(cst_lib_path)
import cst
import cst.interface
import os
先判断已经打开的文件中有没有文件是我们需要操作的,如果有的话直接返回此文件的句柄以供后续操作,若没有则创建一个cst环境,打开所需要的cst文件。
参考cst的帮助文档
def opencst(self):
allpids = cst.interface.running_design_environments()
is_open = False
for pid in allpids:
current_DE = cst.interface.DesignEnvironment.connect(pid)
for project_path in current_DE.list_open_projects():
# print(project_path)
if self.cst_full_path == project_path:
current_project = MyCurrentDE.get_open_project(project_path)
is_open = True
break
if is_open == False:
current_DE = cst.interface.DesignEnvironment()
current_project = current_DE.open_project(self.cst_full_path)
is_open = True
return current_DE, current_project
执行需要的VBA代码,比如更改模型参数,设置仿真频率范围,导出仿真数据等。需传入需要操作的cst文件句柄,即步骤2中返回的第二个参数。command即为需要执行的VBA代码。
def excute_vba(self, CurrentProject, command):
command = self.line_break.join(command)
vba = CurrentProject.schematic
res = vba.execute_vba_code(command)
return res
需要传入参数名和需要修改的值;
def change_para(self, CurrentProject, para_name, para_value):
command = ['Sub Main', 'StoreDoubleParameter("%s","%d")' % (para_name, para_value),
'RebuildOnParametricChange(False, True)', 'End Sub']
res = self.excute_vba(CurrentProject, command)
return res
第一个参数为需操作的文件句柄;第二个参数flag标志导出是需要多个频率连续导出还是导出当个频率下的数据,为1时导出多个连续频率下数据,为0是仅导出start_frequency频率下数据;start_frequency和end_frequency为连续导出数据时的开始和结束频率(结束频率需比需要导出的最后一个频率大一个步长);step为连续导出时的频率步长;file_path为文件保存路径;mode为“E”时表示需要导出电场数据,为“I”表示导出电流数据,需要导出其他数据可自行添加。
def export_3D_datas(self, CurrentProject, flag, start_frequency, end_frequency, step, file_path, mode):
if (mode == 'E'):
if (flag):
command = ['Sub Main',
'Dim monitorff As Double',
'For monitorff = %.2f To %.2f STEP %.2f' % (start_frequency, end_frequency, step),
r'SelectTreeItem("2D/3D Results\E-Field\e-field (f="&CStr(monitorff)&") [pw]")',
'With ASCIIExport',
'.Reset',
'.FileName ("%s" & "E-field-" & CStr(monitorff) & "GHz" & ".txt")' % file_path,
'.Execute',
'End With',
'Next monitorff',
'End Sub']
else:
command = ['Sub Main',
r'SelectTreeItem("2D/3D Results\E-Field\e-field (f=%3s) [pw]")' % start_frequency,
'With ASCIIExport',
'.Reset',
'.FileName ("%s" & "E-field-" & "%s" & "GHz" & ".txt")' %
(file_path, start_frequency),
'.Execute',
'End With',
'End Sub']
res = self.excute_vba(CurrentProject, command)
elif (mode == 'I'):
if (flag):
command = ['Sub Main',
'Dim monitorff As Double',
'For monitorff = %.2f To %.2f STEP %.2f' % (start_frequency, end_frequency, step),
r'SelectTreeItem("2D/3D Results\Surface Current\surface current (f="&CStr(monitorff)&") [pw]")',
'With ASCIIExport',
'.Reset',
'.FileName ("%s" & "Surface-Current-" & CStr(monitorff) & "GHz" & ".txt")' % file_path,
'.Execute',
'End With',
'Next monitorff',
'End Sub']
else:
command = ['Sub Main',
r'SelectTreeItem("2D/3D Results\Surface Current\surface current (f=%3s) [pw]")' % start_frequency,
'With ASCIIExport',
'.Reset',
'.FileName ("%s" & "Surface-Current-" & "%s" & "GHz" & ".txt")' %
(file_path, start_frequency),
'.Execute',
'End With',
'End Sub']
res = self.excute_vba(CurrentProject, command)
else:
print('Error mode(E or I only)!')
res = False
return res
传入需要设置的频率范围。
def change_frequency(self, CurrentProject, min_frequency, max_frequency):
command = ['Sub Main', 'Solver.FrequencyRange "%s", "%s"' % (min_frequency, max_frequency), 'End Sub']
res = self.excute_vba(CurrentProject, command)
return res
可设置电场和磁场的监视器,可指定频率范围以及需要设定的个数,脚本会根据频率范围和需要设置的个数设置需要的监视器。
def set_monitors(self, CurrentProject, start_frequency, end_frequency, nums, mode):
command = ['Sub Main',
'With Monitor',
'.Reset ',
'.Domain "Frequency"',
'.FieldType "%sfield"' % mode.upper(),
'.Dimension "Volume" ',
'.UseSubvolume "False"',
'.Coordinates "Structure"',
'.SetSubvolume "-301.705", "301.705", "-301.705", "301.705", "-10", "610"',
'.SetSubvolumeOffset "0.0", "0.0", "0.0", "0.0", "0.0", "0.0"',
'.SetSubvolumeInflateWithOffset "False" ',
'.CreateUsingLinearSamples "%s", "%s", "%s"' % (start_frequency, end_frequency, nums),
'End With',
'End Sub']
res = self.excute_vba(CurrentProject, command)
return res
在对应坐标设置探针,可设置电场和磁场探针。
def set_probe(self, CurrentProject, mode, x, y, z):
# mode = H/E
if (mode != 'H' and mode != 'E'):
print("Error mode(H/E Only)!")
res = False
else:
command = ['Sub Main',
'With Probe',
'.Reset',
'.ID %s' % id,
'.AutoLabel 1',
'.Field "%sfield"' % mode,
'.Orientation "All"',
'.Xpos "%s"' % x,
'.Ypos "%s"' % y,
'.Zpos "%s"' % z,
'.Create',
'End With',
'End Sub']
id += 1
res = self.excute_vba(CurrentProject, command)
return res
利用探针id删除探针。
def delete_probe(self, CurrentProject, id):
command = ['Sub Main',
'With Probe',
'.DeleteById "%s"' % id,
'End With',
'End Sub']
res = self.excute_vba(CurrentProject, command)
return res
由于我是根据不同面以及面上的不同id命名的probe,所以在传参时使用的是face和id,如果使用的是默认的位置命名此处可以自行修改为位置的参数。此处只导出Abs的数据,如需导出X Y Z的数据请自行添加。
def export_probe_data(self, CurrentProject, file_path, mode, face, id):
command = ['Sub Main',
r'SelectTreeItem("1D Results\Probes\%s-Field\%s-Field (%s-%s)(Abs) [pw]")' %
(mode.upper(), mode.upper(), face, id),
'With ASCIIExport',
'.Reset',
'.FileName ("%s" & "%s-field-" & "(%s-%s)(Abs)" & ".txt")' %
(file_path, mode.upper(), face, id),
'.Execute',
'End With',
'End Sub']
res = self.excute_vba(CurrentProject, command)
return res
由于voltage命名一般是voltage0..1..2..3..4,所以此处指定开始的编号以及需要导出的个数,自行导出。
def export_voltage_data(self, CurrentProject, start_num, nums, file_path):
for i in range(start_num, start_num + nums):
name = i
command = ['Sub Main',
r'SelectTreeItem("1D Results\Voltage Monitors\voltage%s [pw]")' % i,
'With ASCIIExport',
'.Reset',
'.FileName ("%s" & "voltage-" & "%s" & ".txt")' % (file_path, name),
'.Execute',
'End With',
'End Sub']
res = self.excute_vba(CurrentProject, command)
return res
使用run_solver()开始cst的仿真。
def start_stimulate(self, CurrentProject):
model = CurrentProject.modeler
res = model.run_solver()
return res
a = (-100, -80)
b = (10, 40)
c = (240, 340)
d = (20, 39)
e = (80, 150)
f = (160, 260)
for w in range(2):
my_cst.change_para(MyCurrentProject, 'a', a[w])
for e in range(2):
my_cst.change_para(MyCurrentProject, 'b',
b[e])
for r in range(2):
my_cst.change_para(MyCurrentProject, 'c', c[r])
for t in range(2):
my_cst.change_para(MyCurrentProject, 'd',
d[t])
for u in range(2):
my_cst.change_para(MyCurrentProject, 'e',
e[u])
for q in range(2):
my_cst.change_para(MyCurrentProject, 'f', f[q])