跳到主要内容Python 驱动的 ADS 自动化仿真框架与 API 实战指南 | 极客日志Python算法
Python 驱动的 ADS 自动化仿真框架与 API 实战指南
Python 脚本结合 ADS API 实现批量仿真。通过模板方法模式解耦参数更新与流程控制。支持多格式输入、智能结果提取及异常处理。适用于射频电路参数扫描与优化。
lzdxwyh1 浏览 1. 自动化数据提取工具库详解
为了降低 ADS 程控开发的门槛,本框架封装了一个通用的自动化工具库 auto_simulator.py。它覆盖了从环境配置、参数更新、仿真运行到结果提取的全流程,开发者只需关注'如何将参数应用到电路'这一核心逻辑。
"""
通用自动化仿真模块
提供通用的 ADS 仿真自动化框架,支持批量参数扫描和结果提取。
用户只需实现参数更新接口即可使用。
"""
import pandas as pd
import numpy as np
import json
import os
from pathlib import Path
from abc import ABC, abstractmethod
from typing import Dict, List, Any, Optional, Callable
from keysight.ads.de import db_uu as db
from keysight.ads import dataset
import utils
class ParameterUpdater(ABC):
"""参数更新抽象接口"""
@abstractmethod
def update_parameters(self, design: db.Design, param_row: pd.Series) -> None:
"""
更新电路参数
Args:
design: ADS 设计对象
param_row: 包含参数值的 pandas Series
"""
pass
class AutoSimulator:
"""通用自动化仿真器"""
def __init__(self, parameter_updater: ParameterUpdater):
"""
初始化仿真器
Args:
parameter_updater: 参数更新器实例
"""
.parameter_updater = parameter_updater
.design =
() -> pd.DataFrame:
filepath = Path(filepath)
filepath.exists():
FileNotFoundError()
filepath.suffix.lower() == :
(filepath, , encoding=) f:
data = json.load(f)
(data, ):
data:
pd.DataFrame(data[])
:
pd.DataFrame([data])
(data, ):
pd.DataFrame(data)
:
ValueError()
filepath.suffix.lower() == :
pd.read_csv(filepath)
filepath.suffix.lower() [, ]:
pd.read_excel(filepath)
:
ValueError()
() -> :
()
:
workspace = utils.get_workspace(workspace_path)
library = utils.get_library(workspace, library_name)
.design = utils.get_design(library, design_name, utils.ViewType.SCHEMATIC)
()
Exception e:
RuntimeError()
() -> [, ]:
extraction_results = {}
debug:
()
var_name target_variables:
extraction_results[var_name] =
:
vb_name results.varblock_names:
:
varblock = results[vb_name]
dependent_vars = []
independent_vars = []
(varblock, ) (varblock, ):
:
independent_vars = [ivar.name ivar varblock.ivars]
dependent_vars = [dvar.name dvar varblock.dvars]
Exception e:
debug:
()
debug:
()
()
search_vars = (dependent_vars dependent_vars (independent_vars + dependent_vars))
target_var target_variables:
extraction_results[target_var] :
target_var search_vars:
:
df = varblock.to_dataframe(dvar_names=[target_var])
target_var df.columns (df) > :
data_series = df[target_var]
(data_series) == :
value = (data_series.iloc[])
extraction_results[target_var] = value
debug:
()
:
value_array = data_series.values.tolist()
extraction_results[target_var] = value_array
debug:
()
Exception e:
debug:
()
Exception e:
debug:
()
debug:
successful_vars = [var var, val extraction_results.items() val ]
failed_vars = [var var, val extraction_results.items() val ]
()
()
failed_vars:
()
extraction_results
Exception e:
debug:
()
extraction_results
() -> [, ]:
:
sim_manager = utils.SimulationManager(.design)
sim_manager.run_design_simulation()
sim_manager.get_simulation_results() results:
.extract_simulation_results(results, target_variables, debug=debug)
Exception e:
debug:
()
{var: var target_variables}
() -> pd.DataFrame:
()
()
()
()
()
()
df = .load_parameter_samples(data_filepath)
()
max_samples :
df = df.head(max_samples)
()
.setup_ads_environment(workspace_path, library_name, design_name)
simulation_results = []
idx, row df.iterrows():
sample_id = row.get(, idx + )
()
:
.parameter_updater.update_parameters(.design, row)
()
old_data_path = os.path.join(.design.cell.path, , design_name + )
os.path.exists(old_data_path):
os.remove(old_data_path)
results = .run_simulation_and_extract_results(target_variables)
result_processor:
processed_results = result_processor(results)
:
processed_results = results
simulation_results.append(processed_results)
var_name, value processed_results.items():
value :
(value, (, )):
()
:
()
Exception e:
()
simulation_results.append({var: var target_variables})
goal_variables :
goal_variables = target_variables
save_to_original:
._save_results_to_original_file(df, simulation_results, goal_variables, data_filepath)
:
df[] = simulation_results
._print_simulation_statistics(df, goal_variables)
()
df
() -> :
()
new_formatted_results = []
result_dict simulation_results:
new_result = {}
var_name goal_variables:
value = result_dict.get(var_name)
value :
processed_value = ._process_value_for_json(value)
new_result[] = processed_value
:
new_result[] =
new_formatted_results.append(new_result)
merged_results = []
i, (_, row) (df.iterrows()):
existing_results = row.get(, {})
(existing_results, ):
existing_results = {}
new_result = (new_formatted_results[i] i < (new_formatted_results) {})
merged_result = existing_results.copy()
merged_result.update(new_result)
merged_results.append(merged_result)
df[] = merged_results
(data_filepath, , encoding=) f:
json.dump(df.to_dict(orient=), f, indent=, ensure_ascii=)
()
() -> :
(value, ):
{: (value.real), : (value.imag), : ((value)), : (np.angle(value, deg=))}
(value, (, np.ndarray)):
(value) == :
[]
(value) == :
._process_value_for_json(value[])
:
processed_list = []
item value:
processed_list.append(._process_value_for_json(item))
processed_list
(value, np.number):
np.iscomplex(value):
._process_value_for_json((value))
:
(value)
(value, ):
np.isnan(value) np.isinf(value):
:
value
:
value
() -> :
()
()
successful_count =
results df[]:
(results.get(var) var target_variables):
successful_count +=
()
()
var_name target_variables:
values = []
results df[]:
value = results.get(var_name)
value (value, (, )):
values.append(value)
values:
values = np.array(values)
()
()
self
self
None
def
load_parameter_samples
self, filepath: str
"""加载参数样本数据,仅支持输出 JSON 格式"""
if
not
raise
f"参数文件不存在:{filepath}"
if
".json"
with
open
"r"
"utf-8"
as
if
isinstance
dict
if
"samples"
in
return
"samples"
else
return
elif
isinstance
list
return
else
raise
"JSON 数据格式不支持,需要字典或列表格式"
elif
".csv"
return
elif
in
".xlsx"
".xls"
return
else
raise
f"不支持的文件格式:{filepath.suffix}"
def
setup_ads_environment
self, workspace_path: str, library_name: str, design_name: str
None
"""设置 ADS 工作环境"""
print
"设置 ADS 工作环境..."
try
self
print
"ADS 环境设置成功"
except
as
raise
f"ADS 环境设置失败:{e}"
def
extract_simulation_results
self, results: dataset.Dataset, target_variables: List[str], debug: bool = True
Dict
str
Any
"""
通用的仿真结果提取函数
Args:
results: ADS 仿真结果对象
target_variables: 目标变量名列表
debug: 是否输出调试信息
Returns:
dict: 提取结果字典,格式为 {变量名:值/数组,...}
"""
if
print
f"开始提取仿真结果,目标变量:{target_variables}"
for
in
None
try
for
in
try
if
hasattr
"ivars"
and
hasattr
"dvars"
try
for
in
for
in
except
as
if
print
f"使用 ivars/dvars 失败:{e}"
if
print
f"检查变量块:{vb_name}"
print
f"依赖变量:{dependent_vars}"
if
else
for
in
if
is
not
None
continue
if
in
try
if
in
and
len
0
if
len
1
float
0
if
print
f"✓ 提取单值变量:{vb_name}.{target_var} = {value:.3f}"
else
if
print
f"✓ 提取数组变量:{vb_name}.{target_var} (长度:{len(value_array)})"
except
as
if
print
f"❌ 提取 {vb_name}.{target_var} 失败:{e}"
continue
except
as
if
print
f"❌ 访问变量块 {vb_name} 失败:{e}"
continue
if
for
in
if
is
not
None
for
in
if
is
None
print
"=== 提取结果摘要 ==="
print
f"成功提取:{successful_vars}"
if
print
f"未找到变量:{failed_vars}"
return
except
as
if
print
f"❌ 仿真结果提取异常:{e}"
return
def
run_simulation_and_extract_results
self, target_variables: List[str], debug: bool = True
Dict
str
Any
"""
运行仿真并提取多个目标变量的结果
Args:
target_variables: 目标变量名列表
debug: 是否输出调试信息
Returns:
dict: 提取结果字典
"""
try
self
with
as
return
self
except
as
if
print
f"仿真失败:{e}"
return
None
for
in
def
run_batch_simulation
self, data_filepath: str, workspace_path: str, library_name: str, design_name: str, target_variables: List[str], goal_variables: List[str] = None, result_processor: Optional[Callable] = None, max_samples: Optional[int] = None, save_to_original: bool = True,
"""
运行批量仿真
Args:
data_filepath: 参数数据文件路径
workspace_path: ADS 工作空间路径
library_name: 库名称
design_name: 设计名称
target_variables: 仿真提取变量列表
goal_variables: 目标变量列表
result_processor: 结果处理函数
max_samples: 最大样本数限制
save_to_original: 是否保存结果到原始数据文件
Returns:
包含仿真结果的 DataFrame
"""
print
"=== 自动化仿真开始 ==="
print
f"数据文件:{data_filepath}"
print
f"工作空间:{workspace_path}"
print
f"库名称:{library_name}"
print
f"设计名称:{design_name}"
print
f"加载参数数据:{data_filepath}"
self
print
f"成功加载 {len(df)} 个样本"
if
is
not
None
print
f"测试模式:只处理前 {len(df)} 个样本"
self
for
in
"sample_id"
1
print
f"\n处理样本 {sample_id} ({idx+1}/{len(df)})..."
try
self
self
print
"参数更新完成"
self
"data"
".ds"
if
self
if
else
for
in
if
is
not
None
if
isinstance
int
float
print
f"{var_name} = {value:.3f}"
else
print
f"{var_name} = {type(value).__name__}(长度:{len(value) if hasattr(value, '__len__') else 'N/A'})"
except
as
print
f"样本 {sample_id} 处理失败:{e}"
None
for
in
if
is
None
if
self
else
"simulation_results"
self
print
"=== 自动化仿真完成 ==="
return
def
_save_results_to_original_file
self, df: pd.DataFrame, simulation_results: List[Dict[str, Any]], goal_variables: List[str], data_filepath: str
None
"""将仿真结果保存到原始数据文件中,追加到现有的 simulation_results 中"""
print
"\n保存结果..."
for
in
for
in
if
is
not
None
self
f"{var_name}"
else
f"{var_name}"
None
for
in
enumerate
"simulation_results"
if
not
isinstance
dict
if
len
else
"simulation_results"
with
open
"w"
"utf-8"
as
"records"
2
False
print
f"结果已保存到:{data_filepath}"
def
_process_value_for_json
self, value: Any
Any
"""处理值以确保 JSON 可序列化"""
if
isinstance
complex
return
"real"
float
"imag"
float
"magnitude"
float
abs
"phase_deg"
float
True
elif
isinstance
list
if
len
0
return
elif
len
1
return
self
0
else
for
in
self
return
elif
isinstance
if
return
self
complex
else
return
float
elif
isinstance
float
if
or
return
None
else
return
else
return
def
_print_simulation_statistics
self, df: pd.DataFrame, target_variables: List[str]
None
"""打印仿真统计信息"""
print
"\n=== 仿真统计 ==="
print
f"总样本数:{len(df)}"
0
for
in
"simulation_results"
if
any
is
not
None
for
in
1
print
f"成功仿真:{successful_count}"
print
f"失败样本:{len(df)- successful_count}"
for
in
for
in
"simulation_results"
if
is
not
None
and
isinstance
int
float
if
print
f"{var_name}范围:{values.min():.3f} ~ {values.max():.3f}"
print
f"{var_name}均值:{values.mean():.3f}"
AutoSimulator 采用了 模板方法模式 (Template Method Pattern) 进行设计:
- 不变的部分:ADS 工作空间的打开/关闭、仿真器的实例化、结果文件的遍历与读取、异常处理、数据保存(追加到原始 JSON)。这些通用逻辑被封装在基类和工具函数中。
- 变化的部分:不同的电路有不同的参数需要更新(如电阻值、信号文件路径、偏置电压)。这部分逻辑通过
ParameterUpdater 接口暴露给用户实现。
1.2 核心类说明
utils.auto_simulator.py 中主要包含两个核心类:ParameterUpdater 和 AutoSimulator。
1. ParameterUpdater (抽象基类)
设计意图:这是一个策略接口(Strategy Pattern),将'如何更新电路参数'这一变化点从框架中剥离出来。由于每个 ADS 设计的元件名称、变量控件结构、参数命名规则都不同,这部分逻辑无法通用化,必须由用户根据自己的电路原理图来实现。
from abc import ABC, abstractmethod
from keysight.ads.de import db_uu as db
import pandas as pd
class ParameterUpdater(ABC):
"""参数更新抽象接口"""
@abstractmethod
def update_parameters(self, design: db.Design, param_row: pd.Series) -> None:
"""
更新电路参数(用户必须实现)
Args:
design: ADS 设计对象,指向当前打开的原理图
param_row: 包含本次仿真所需全部参数的 Pandas Series
"""
pass
| 方法签名 | update_parameters(self, design: db.Design, param_row: pd.Series) -> None |
|---|
| 调用时机 | AutoSimulator 在处理每一行参数样本时,会在执行仿真前调用此方法 |
| design 参数 | 由 setup_ads_environment 打开的设计对象,类型为 keysight.ads.de.db_uu.Design。通过它可以访问原理图中的所有元件实例 |
| param_row 参数 | Pandas Series 对象,键为参数名(如 "R1", "Vbias"),值为参数数值。这些键名对应输入 JSON/CSV 文件中的列名 |
| 返回值 | 无(None)。参数更新通过直接修改 design 对象完成 |
from keysight.ads.de.db import Transaction
with Transaction(design) as transaction:
inst1.vars.update({...})
inst2.parameters["C"].value = "10 pF"
transaction.commit()
inst.update_item_annotation()
更新元件参数(用于电阻、电容、信号源等具体元件):
inst.parameters["参数名"].value = "值(字符串,可带单位)"
更新 VAR 控件中的变量(用于 VAR / VAR2 等变量定义控件):
inst.vars.update({"变量名 1":"值(字符串)","变量名 2":"值(字符串)",})
inst = design.get_instance("VAR1")
- 所有参数值必须转换为字符串格式,ADS 内部会自行解析单位
- 文件路径类参数(如
DAC1 的 File 参数)需要用双引号包裹,即 "'path'"
- 若更新多个元件,建议包裹在
Transaction 中,确保要么全部成功、要么全部回滚
- 更新后调用
update_item_annotation() 可让原理图界面同步显示新值(非必需但推荐)
2. AutoSimulator (核心控制器)
职责:这是自动化仿真的主引擎(Driver)。它不关心电路的具体细节,只负责流程控制:加载数据 -> 设置环境 -> 循环仿真 -> 提取结果 -> 保存文件。
__init__(self, parameter_updater: ParameterUpdater)
构造函数,负责将用户实现的参数更新器'注册'到控制器中。
| 参数 | 类型 | 说明 |
|---|
parameter_updater | ParameterUpdater | 用户自定义的参数更新器实例,必须实现 update_parameters 方法 |
内部行为:将 parameter_updater 保存为成员变量,同时初始化 self.design = None(后续由 setup_ads_environment 填充)。
load_parameter_samples(self, filepath: str) -> pd.DataFrame
从文件加载参数样本数据,统一转换为 Pandas DataFrame 供后续遍历使用。
| 参数 | 类型 | 说明 |
|---|
filepath | str | 参数数据文件的完整路径 |
| 扩展名 | 处理逻辑 |
|---|
.json | 使用 json.load 读取。支持三种结构:① 纯列表 [{...}, {...}];② 字典包裹 {"samples": [{...}]};③ 单条记录 {...}(自动包装为单行 DataFrame) |
.csv | 调用 pd.read_csv(filepath) |
.xlsx / .xls | 调用 pd.read_excel(filepath) |
FileNotFoundError:文件路径不存在
ValueError:JSON 内容既非 dict 也非 list,或扩展名不在上述列表中
注意:虽然加载支持多种格式,但目前结果保存仅实现了 JSON 格式(见 _save_results_to_original_file)。如需将结果回写到 CSV/Excel,用户可继承 AutoSimulator 并重写保存逻辑。
setup_ads_environment(self, workspace_path: str, library_name: str, design_name: str) -> None
初始化 ADS 工作环境,完成工作空间 -> 库 -> 设计的三级打开流程。
| 参数 | 类型 | 说明 |
|---|
workspace_path | str | ADS 工作空间目录路径,如 E:/ADS2026_wrk/MyProject_wrk |
library_name | str | 库名称,如 MyAmplifier_lib |
design_name | str | 原理图设计名称(不含扩展名),如 Amp_Sim_Schematic |
utils.get_workspace(workspace_path) —— 打开/连接工作空间
utils.get_library(workspace, library_name) —— 加载指定库
utils.get_design(library, design_name, ViewType.SCHEMATIC) —— 打开原理图视图
成功后 self.design 将指向该设计对象,后续的参数更新和仿真都基于此对象进行。
异常情况:路径错误、库/设计不存在时抛出 RuntimeError。
extract_simulation_results(self, results: dataset.Dataset, target_variables: List[str], debug: bool = True) -> Dict[str, Any]
从仿真结果数据集中智能提取用户指定的目标变量,是本工具库最核心的数据处理函数。
| 参数 | 类型 | 说明 |
|---|
results | dataset.Dataset | ADS 仿真结果对象(由 SimulationManager.get_simulation_results() 获取) |
target_variables | List[str] | 想要提取的变量名列表,如 ["S[2,1]", "Gain_dB", "OUT"] |
debug | bool | 是否打印调试信息,默认 True |
- 遍历
results.varblock_names 获取所有 VariableBlock
- 对每个块,通过
varblock.ivars / varblock.dvars 获取其独立变量和依赖变量名列表
- 若目标变量出现在
dvars 中,调用 varblock.to_dataframe(dvar_names=[target_var]) 精准提取该列
- 根据数据长度自动判断类型:
- 长度 = 1 → 标量,转为 Python
float
- 长度 > 1 → 向量,转为 Python
list
返回值:Dict[str, Any],格式为 {变量名:值或列表}。未找到的变量对应值为 None。
run_simulation_and_extract_results(self, target_variables: List[str], debug: bool = True) -> Dict[str, Any]
执行单次仿真并提取结果的快捷方法,内部封装了仿真管理器的创建与结果获取。
| 参数 | 类型 | 说明 |
|---|
target_variables | List[str] | 想要提取的变量名列表 |
debug | bool | 是否打印调试信息 |
- 创建
SimulationManager(self.design)
- 调用
sim_manager.run_design_simulation() 执行仿真
- 使用上下文管理器
with sim_manager.get_simulation_results() as results 获取结果
- 调用
extract_simulation_results(results, target_variables, debug) 提取数据
异常处理:若仿真过程出错(如不收敛、License 问题),捕获异常并返回 {var: None for var in target_variables},不会中断程序。
run_batch_simulation(self, data_filepath, workspace_path, library_name, design_name, target_variables, goal_variables=None, result_processor=None, max_samples=None, save_to_original=True) -> pd.DataFrame
批量仿真的主入口,串联整个自动化流程。大多数情况下用户只需调用此方法。
| 参数 | 类型 | 默认值 | 说明 |
|---|
data_filepath | str | — | 参数数据文件路径 |
workspace_path | str | — | ADS 工作空间路径 |
library_name | str | — | 库名称 |
design_name | str | — | 设计名称 |
target_variables | List[str] | — | 要提取的仿真结果变量列表 |
goal_variables | List[str] | None | 写入文件时的字段名集合,若为 None 则等于 target_variables |
result_processor | Callable | None | 可选的结果后处理回调函数,签名为 (Dict) -> Dict |
max_samples | int | None | 限制处理的样本数量,用于调试;None 表示处理全部 |
save_to_original | bool | True | 是否将结果合并回写到原始数据文件 |
- 加载数据:调用
load_parameter_samples(data_filepath) 读取参数表
- 限制样本:若设置了
max_samples,取前 N 行
- 初始化环境:调用
setup_ads_environment(...) 打开 ADS 设计
- 循环处理每个样本:
- 调用
parameter_updater.update_parameters(design, row) 更新电路参数
- 删除旧仿真数据:移除
{cell_path}/data/{design_name}.ds,防止读到历史缓存
- 调用
run_simulation_and_extract_results(target_variables) 执行仿真并提取结果
- 若提供了
result_processor,对结果进行后处理
- 将结果追加到列表
- 保存结果:若
save_to_original=True,调用 _save_results_to_original_file 合并写回
- 打印统计:调用
_print_simulation_statistics 输出成功/失败计数与数值范围
返回值:带有 simulation_results 列的 DataFrame。
_save_results_to_original_file(self, df, simulation_results, goal_variables, data_filepath) -> None
将当前批次的仿真结果与原文件中已有的 simulation_results 字段合并后写回,支持增量覆盖(新结果覆盖同名旧结果,旧结果中不重名的字段保留)。
- 遍历
simulation_results 列表,对每条记录中的 goal_variables 调用 _process_value_for_json 转换
- 遍历原 DataFrame,取出每行的
simulation_results(若不存在则初始化为空字典)
- 使用
dict.update() 将新结果合并进去
- 最终以
json.dump(df.to_dict(orient="records"), ...) 写回文件
限制:目前仅支持 JSON 格式输出。如需 CSV/Excel 输出,可重写此方法或在调用后手动导出。
_process_value_for_json(self, value) -> Any
递归处理各种数据类型,确保最终结果可被 json.dump 序列化。
| 输入类型 | 输出格式 |
|---|
complex | {"real": float, "imag": float, "magnitude": float, "phase_deg": float} |
list / np.ndarray | 递归处理每个元素;若长度为 1 则解包为标量 |
np.number | 转为 Python float(复数走 complex 分支) |
float 且为 NaN/Inf | 转为 None |
| 其他 | 原样返回 |
_print_simulation_statistics(self, df, target_variables) -> None
在批量仿真结束后打印统计摘要,便于快速评估整体质量。
- 总样本数 / 成功数 / 失败数
- 对于每个
target_variable,若为数值型则显示最小值、最大值、均值
1.3 实战示例:开发一个自定义仿真器
本节以一个射频放大器增益仿真为例,完整演示从数据准备到结果提取的全流程。
1.3.1 输入数据格式
首先准备参数样本文件(JSON 格式),每条记录代表一组待仿真的电路参数:
[{"sample_id":1,"Vbias":3.3,"R_source":50,"C_coupling":100,"L_match":2.2},{"sample_id":2,"Vbias":3.0,"R_source":75,"C_coupling":150,"L_match":1.8}]
sample_id:样本编号,用于日志输出和结果追踪(可选,若不提供则自动使用行索引)
- 其余字段:与原理图中需要更新的变量/参数一一对应
1.3.2 实现参数更新器
根据你的 ADS 原理图结构,继承 ParameterUpdater 并实现 update_parameters 方法:
"""amplifier_simulator.py 射频放大器批量仿真脚本"""
import sys
from pathlib import Path
project_root = Path(__file__).parent.parent
sys.path.append(str(project_root))
import pandas as pd
from utils.auto_simulator import AutoSimulator, ParameterUpdater
from keysight.ads.de import db_uu as db
from keysight.ads.de.db import Transaction
class AmplifierUpdater(ParameterUpdater):
"""
放大器参数更新器
假设原理图中包含:
- VAR1 控件:定义 Vbias(偏置电压)、R_source(源阻抗)
- C1 元件:耦合电容
- L1 元件:匹配电感
"""
def update_parameters(self, design: db.Design, param_row: pd.Series) -> None:
"""
将 param_row 中的参数值写入 ADS 原理图
Args:
design: ADS 设计对象
param_row: 当前样本的参数(Pandas Series)
"""
with Transaction(design) as transaction:
inst_var = design.get_instance("VAR1")
inst_var.vars.update({
"Vbias": str(param_row["Vbias"]),
"R_source": str(param_row["R_source"]),
})
inst_var.update_item_annotation()
inst_c1 = design.get_instance("C1")
inst_c1.parameters["C"].value = f"{param_row['C_coupling']} pF"
inst_c1.update_item_annotation()
inst_l1 = design.get_instance("L1")
inst_l1.parameters["L"].value = f"{param_row['L_match']} nH"
inst_l1.update_item_annotation()
transaction.commit()
- Transaction 上下文管理器:将所有修改包裹在事务中,调用
commit() 后才真正生效
- VAR 控件 vs 元件参数:VAR 控件用
.vars.update(),普通元件用 .parameters["名称"].value
- 单位处理:电容用
pF、电感用 nH,ADS 会自动解析
- 字符串转换:所有值必须是字符串,使用
str() 或 f-string
1.3.3 编写主控制脚本
def main():
"""主函数:配置参数并启动批量仿真"""
config = {
"data_filepath": "E:/project/data/amplifier_samples.json",
"workspace_path": "E:/ADS2026_wrk/MyAmplifier_wrk",
"library_name": "MyAmplifier_lib",
"design_name": "Amp_Gain_Schematic",
"target_variables": [
"S[2,1]",
"S[1,1]",
"Gain_dB",
"NF_dB",
],
"max_samples": None,
}
print("="*60)
print("射频放大器批量仿真")
print("="*60)
print(f"参数文件:{config['data_filepath']}")
print(f"目标变量:{config['target_variables']}")
print()
try:
updater = AmplifierUpdater()
simulator = AutoSimulator(updater)
final_df = simulator.run_batch_simulation(
data_filepath=config["data_filepath"],
workspace_path=config["workspace_path"],
library_name=config["library_name"],
design_name=config["design_name"],
target_variables=config["target_variables"],
max_samples=config["max_samples"],
save_to_original=True,
)
print()
print("="*60)
print(f"仿真完成!共处理 {len(final_df)} 个样本")
print(f"结果已保存到:{config['data_filepath']}")
print("="*60)
except Exception as e:
print(f"\n❌ 仿真过程发生错误:{e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
1.3.4 输出结果格式
仿真完成后,原始 JSON 文件会被追加 simulation_results 字段:
[{"sample_id":1,"Vbias":3.3,"R_source":50,"C_coupling":100,"L_match":2.2,"simulation_results":{"S[2,1]":{"real":2.51,"imag":1.33,"magnitude":2.84,"phase_deg":27.9},"S[1,1]":{"real":-0.12,"imag":0.05,"magnitude":0.13,"phase_deg":157.4},"Gain_dB":9.07,"NF_dB":2.3}},{"sample_id":2,"Vbias":3.0,"R_source":75,"C_coupling":150,"L_match":1.8,"simulation_results":{"S[2,1]":{...},"S[1,1]":{...},"Gain_dB":8.45,"NF_dB":null}}]
- 复数类型(如 S 参数):自动拆分为
real、imag、magnitude、phase_deg
- 标量类型(如
Gain_dB):直接存储数值
- 提取失败:对应字段值为
null
1.3.5 进阶:自定义结果后处理
若需要对原始提取结果进行计算(如将复数 S21 转为 dB),可传入 result_processor 回调:
import numpy as np
def process_results(raw_results: dict) -> dict:
"""
自定义结果后处理函数
Args:
raw_results: extract_simulation_results 返回的原始字典
Returns:
处理后的结果字典
"""
processed = raw_results.copy()
s21 = raw_results.get("S[2,1]")
if s21 is not None and isinstance(s21, complex):
processed["S21_dB"] = 20 * np.log10(abs(s21))
return processed
final_df = simulator.run_batch_simulation(..., result_processor=process_results)
1.4 为什么推荐使用此模式?
- 稳健性:内部统一捕获仿真异常,单个样本失败不会拖垮全流程。
- 断点续传:结果实时写回原始文件,结合简单的跳过逻辑即可实现增量仿真。
- 结果对齐:自动将输入参数与输出结果一一对应,便于后续数据分析与模型训练。
1.5 辅助工具库
工具库内部还封装了以下辅助函数,简化了工作空间、库和设计的操作:
import os
import keysight.ads.dataset as dataset
from keysight.ads import de
from keysight.ads.de import db_uu as db
from enum import Enum
from typing import Literal, Union
from keysight.edatoolbox import ads
from keysight.ads.de.experimental.text_maker import TextMaker
class viewType(Enum):
SCHEMATIC = "schematic"
SYMBOL = "symbol"
LAYOUT = "layout"
def __str__(self) -> str:
return self.value
ViewNameLiteral = Literal["schematic", "symbol", "layout"]
ViewNameType = Union[ViewType, ViewNameLiteral]
def design_exists(library: de.Library, name: str, view_type: ViewNameType = ViewType.SCHEMATIC) -> bool:
design_ref = f"{library.name}:{name}:{view_type.value}"
return de.design_exists(design_ref)
def get_workspace(workspace_path: str) -> de.Workspace:
if de.workspace_is_open():
de.close_workspace()
if os.path.exists(workspace_path):
print(f"Workspace directory already exists: {workspace_path}")
de.open_workspace(workspace_path)
return de.active_workspace()
workspace = de.create_workspace(workspace_path)
print(f"Workspace created: {workspace_path}")
workspace.open()
return workspace
def get_library(workspace: de.Workspace, name: str) -> de.Library:
assert workspace.is_open
library_name = name
library_path = workspace.path / library_name
if os.path.exists(library_path):
if de.library_is_open(library_name):
de.close_library(library_name)
print(f"Library directory already exists: {library_path}")
return workspace.open_library(library_name, library_path, de.LibraryMode.SHARED)
de.create_new_library(library_name, library_path)
print(f"Library created: {library_path}")
workspace.add_library(library_name, library_path, de.LibraryMode.SHARED)
lib = workspace.open_library(library_name, library_path, de.LibraryMode.SHARED)
return lib
def get_design(library: de.Library, name: str, view_type: ViewNameType = ViewType.SCHEMATIC, mode: db.DesignMode = db.DesignMode.APPEND,) -> db.Design:
"""创建设计,如果已存在则打开现有设计"""
design_ref = f"{library.name}:{name}:{view_type.value}"
if design_exists(library, name, view_type):
return db.open_design(design_ref, mode=mode)
else:
if view_type == ViewType.SCHEMATIC:
design = db.create_schematic(design_ref)
elif view_type == ViewType.SYMBOL:
design = db.create_symbol(design_ref)
elif view_type == ViewType.LAYOUT:
design = db.create_layout(design_ref)
else:
raise ValueError(f"Invalid view type: {view_type}")
print(f"Design created: {design_ref}")
return design
def add_measeqn(design: db.Design, eq_name: str, eq_list: list[str], origin: tuple[float, float] = (0, 0), angle: float = 0,) -> None:
measeqn = design.add_instance(("ads_simulation", "MeasEqn", "symbol"), origin, name=eq_name, angle=angle)
measeqn.parameters["Meas"].value = [eq_list[0]]
for i in range(len(eq_list) - 1):
measeqn.parameters["Meas"].repeats.append(db.ParamItemString("Meas", "SingleTextLine", eq_list[i + 1]))
measeqn.update_item_annotation()
class ManagedCircuitSimulator:
"""带有完整资源管理的电路仿真器"""
def __init__(self, hpeesof_dir=None):
self.simulator = ads.CircuitSimulator(hpeesof_dir)
def run_netlist(self, netlist: str, output_dir: str, **kwargs) -> None:
working_dir = kwargs.get("working_dir", output_dir)
try:
return self.simulator.run_netlist(netlist=netlist, output_dir=output_dir, **kwargs)
finally:
self._cleanup_all_temp_files(working_dir)
def _cleanup_all_temp_files(self, directory) -> None:
"""Clean up all ADS temporary files in directory"""
import glob
patterns = ["circ*.ckt", "circ*.out", "*.tmp"]
for pattern in patterns:
for temp_file in glob.glob(os.path.join(directory, pattern)):
try:
os.remove(temp_file)
print(f"Cleaned temporary file: {os.path.basename(temp_file)}")
except OSError:
pass
class SimulationManager:
"""Complete simulation manager with temporary file cleanup and path safety handling"""
def __init__(self, design: db.Design):
self.design = design
self.output_dir = os.path.join(self.design.cell.path, "data")
self.netlist = None
self.result = None
self.result_path = os.path.join(self.output_dir, f"{self.design.cell_name}.ds")
self.managed_simulator = ManagedCircuitSimulator()
self.data_file_is_exist = os.path.exists(self.result_path)
if self.data_file_is_exist:
self.result = dataset.open(self.result_path)
def validate_design_name(self) -> bool:
"""Validate design name, check for uppercase letters that may cause path encoding issues"""
cell_name = self.design.cell_name
has_uppercase = any(c.isupper() for c in cell_name)
if has_uppercase:
print(f"Warning: Cell name '{cell_name}' contains uppercase letters")
print(" ADS maps uppercase letters to %letter format, which may cause path issues")
print(" Recommend using lowercase letters for Cell names")
return False
return True
def run_design_simulation(self, output_dir: Union[str, None] = None, output_file: str = None, validate_name: bool = True, **sim_kwargs,) -> None:
"""Run design simulation with automatic temporary file management"""
if output_dir is None:
output_dir = self.output_dir
os.makedirs(output_dir, exist_ok=True)
print(f"=== Running simulation: {self.design.design_name} ===")
print(f"Output directory: {output_dir}")
if validate_name:
self.validate_design_name()
try:
print("Generating netlist...")
self.netlist = self.design.generate_netlist()
if not self.netlist or len(self.netlist.strip()) == 0:
raise RuntimeError("Generated netlist is empty")
print(f"Netlist generated successfully, length: {len(self.netlist)} characters")
print("Running simulation...")
self.managed_simulator.run_netlist(netlist=self.netlist, output_dir=output_dir, output_file=output_file, **sim_kwargs,)
print("Simulation executed successfully")
print(f"Simulation completed, result file: {self.result_path}")
self.data_file_is_exist = True
except Exception as e:
print(f"Simulation failed: {e}")
raise
def get_simulation_results(self) -> dataset.Dataset:
"""Get simulation results
Returns a context manager, should be used like this:
with sim_mgr.get_simulation_results() as results:
print(results.varblock_names) # Process results...
# Dataset will be automatically closed
"""
if not self.data_file_is_exist:
raise RuntimeError("Simulation has not been run or failed, cannot get results")
return dataset.open(self.result_path)
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online