20 KiB
PythonModels
PythonModels 用于承接 Modelica 和 AMESim 模型的 Python 平台移植。
目标不是逐行翻译源模型,而是建立可运行、可测试、可导出,并能与 OpenModelica 或 AMESim baseline 对比的 Python 仿真框架。
当前包含两条模型线:Testmodel 已有可运行的 ODE 近似和 OpenModelica 对比能力;test_mql 已形成 132 状态气动机械总闭包,正在按 AMESim baseline 做数值校准。
当前目录
core/: 通用基础设施 包含组件基类、状态与端口数据结构、介质模型、网络装配、积分入口。components/: 元件级 Python 实现 包含Cylinder、Tank、Pipe、Orifice、Tee,以及 AMESim 气动和机械组件原语。systems/: 系统级装配与闭合 包含旧TestModelSystem,以及当前主线TestMqlSystem的配置、拓扑、closure、snapshot、端口写回和 RHS。reporting/: 结果导出与对比 承接 OpenModelica 对比,以及 AMESim 结果读取、Data_Pathschema validation、comparison 和诊断报告。scripts/: 运行脚本 包含run_testmodel.py、run_test_mql.py和run_test_mql_full_state_comparison.py。baselines/: 提交进仓库的稳定基线 当前承接 Python 主变量基线和 Python 对 Modelica 的误差摘要基线。runs/: 每次实际运行的默认输出目录 当前脚本默认会在这里创建带时间戳的子目录,用来放这次运行生成的产物。
当前关键文件:
core/medium.py: 温度相关的理想气体近似介质IdealGasMediumcore/peng_robinson.py:test_mql使用的氦气 Peng-Robinson 物性core/network.py:SimulationNetwork,负责组件注册、连接拓扑和状态向量拼装core/solver.py:integrate_ode(),优先走SciPy solve_ivp,缺依赖时回退到内置 RK4,并支持t_start == t_stop的零时长返回components/pipe.py: 单阻容管道近似,入口压降 + 出口直连内容腔components/tee.py: 三通的最小 stream 混合 helpersystems/testmodel.py:Testmodel的系统装配壳与外部运行入口systems/testmodel_closure.py:Testmodel当前专用的闭合、初始化投影、分支求解与端口回写systems/test_mql.py:test_mql系统装配、132 状态总闭包和关键输出映射systems/test_mql_closure.py:test_mql气动网络 closure、snapshot、流量计算和端口写回reporting/testmodel_outputs.py:Testmodel的 CSV/SVG/对比摘要导出scripts/run_test_mql_full_state_comparison.py:test_mql短时域 AMESim comparison 和诊断入口scripts/run_testmodel.py: 基线运行与程序化执行入口tests/test_pythonmodels_regression.py: 当前 Python 基线回归测试
当前阶段进度
这一阶段原先有 4 件重点工作,现在的状态如下:
mytee1的 stream/焓传播语义:已完成当前阶段收紧 现在如果只有一条支路发生倒流,下游来流焓统一按tank.h处理,不再临时借另一条支路的焓来凑。- 下游初始化/约束处理:已完成当前阶段收口
之前是“直接改对象状态再开始积分”,现在已经收成显式的
consistent_initial_state_vector()初始化入口。当前这一步会在不改下游总质量、总内能的前提下,把几段直接相连的体积拉回同一个连接压力。 - 自动校验:已完成当前阶段首版
已经补了标准库
unittest回归测试,先把初始化投影是否守恒、是否污染原始状态,以及 4 个主变量的提交基线锁住。 - 更严格介质模型:已完成当前阶段首版
已经从固定
cp/cv的理想气体近似,推进到随温度变化的空气近似,并接上了内能反解和初始化求根。
如果只看结果,可以把这一阶段理解成:
- 连接器语义:首轮收紧已完成
- 初始化入口:首轮收口已完成
- 基线验证:首轮保护已完成
- 介质精化:首轮近似已完成
当前阶段收口
上一轮 N0-N3 已全部完成首版,当前可以简单理解为:
N0:系统层里最明显的流向/焓判断已经继续下沉到组件 helper。N1:模型参数和运行参数已经收口到配置对象。N2:运行接口已经分成“准备请求”和“执行请求”两层。N3:结果导出和命令行报告格式化已经统一收口到reporting/。
这一轮结束后,项目已经不缺“能不能跑”的能力,下一步更重要的是把后续开发最容易卡住的地方先处理掉。
本次推送更新
本次推送已经把上一轮建议里的 M2-M5 推进到下面这个状态:
-
M2:已完成当前阶段首版- 已把
Testmodel的专用闭合、初始化投影、分支入口流量求解、下游支路出口流量闭合、端口状态回写,从systems/testmodel.py拆到新的systems/testmodel_closure.py TestModelSystem现在主要承担组件装配、网络注册和对闭合器的委托,不再继续堆积系统级手写细节
- 已把
-
M3:已完成当前阶段首版- 已给两条支路入口流量固定点求解、下游公共压力投影补了显式诊断
- 诊断内容至少包含
converged / iterations / residual - 已支持严格模式;内部求解不收敛时可以直接抛错,而不是静默返回最后一个近似值
run_testmodel()的结构化结果和testmodel_run_report.txt已能带出最后一次内部闭合求解诊断
-
M4:已完成当前阶段首版- 自动测试已不再只盯最终主变量结果
- 现在已经覆盖:
- 改支路参数后,初始支路入口流量是否按预期变化
- 更偏激配置下,初始化和内部闭合是否仍然收敛
- 有无 Modelica 参考两种运行路径下,程序接口与产物行为是否一致
-
M5:已启动- 当前已经明确选择优先走“更容易扩展”的方向,而不是先追求更贴近 Modelica
- 已完成第一步:把闭合器内部原来大量写死的
upper/lower双支路逻辑,收成可复用的BranchClosureComponents / BranchClosureState结构 - 当前已继续推进到
G1-G5的首轮兼容层改造:snapshot已提供通用分支集合,系统层结果生成已拆成“通用键生成 + 旧键别名派生”两层,报告层已开始优先消费通用分支键,旧导出列名仍通过兼容映射保留,兼容测试已显式保护分支顺序和旧导出语义
下一阶段接手建议
如果继续往前推进,建议按下面顺序做,而不是再零散补功能:
-
G1:已完成当前阶段首轮兼容接入TestModelSnapshot已新增branches集合- 每个分支当前至少带
name / pipe / inlet_flow / outlet_flow / inlet_h / inlet_flow_diagnostics pipe_upper / pipe_lower / branch_inlet_flows / branch_outlet_flows目前仍保留为兼容属性,供旧调用方继续使用
-
G2:已完成当前阶段首轮内部迁移evaluate_solution()已改成从snapshot.branches读取数据,再通过显式分支名映射写回当前旧列名rhs()里的分支导数计算已改成通过通用 helper 按分支循环生成,再按当前状态向量顺序拼回- 当前外部导出列名仍保持兼容:
mypipe.pmypipe1.pbranch_upper.in/outbranch_lower.in/out
-
G3:已完成当前阶段首轮兼容测试- 当前测试已经显式保护:
branches顺序是否稳定snapshot新字段和兼容字段是否一致- 旧导出列名是否仍映射到正确分支语义
- 参数变化后
upper/lower的名字和顺序是否不会被打乱
- 当前测试已经显式保护:
-
G4:已完成当前阶段首轮兼容拆层evaluate_solution()现在会同时产出:- 通用分支键:
branch.<branch_name>.p/in/out - 旧兼容键:
mypipe.p、mypipe1.p、branch_upper.*、branch_lower.*
- 通用分支键:
- 报告层当前已开始优先读取通用分支键,旧键只作为兼容后备
- 当前已经把“内部统一表达”和“旧接口兼容导出”拆成两层,但还没有把所有报告/导出逻辑都迁干净
-
G5:已完成当前阶段首轮兼容收口evaluate_solution()当前会先生成通用分支键,再统一派生旧兼容键- 报告层当前已支持“通用键优先、旧键兼容后备”
- 当前已经把系统层和 reporting 层的主要旧专名读取入口收口到少量 helper 上,后续继续迁移不会再到处散改
-
P1:下一阶段建议从这里接手 当前更合适的下一步,不是继续深挖内核通用化,而是切回结果导向主线:- 定义一份稳定的外部输入参数 schema
- 明确这些结构化参数如何映射到
TestModelConfig / TestModelRunConfig - 建立“结构化参数 -> 仿真执行 -> 结果产物/摘要”的稳定接口
这样可以直接服务后续文档解析、网页入口和报告生成,而不是继续在
Testmodel内部做边际收益越来越低的抽象整理
-
P2:在P1完成后,再推进文档解析或报告生成链路 更现实的顺序应是:- 先把结构化输入跑通
- 再把结果摘要/产物组织成更接近最终产品的输出包
- 最后再接 Word 解析或页面入口
如果后续继续推进,这个 README 也要一起更新,不要长期保留已经失效的路线描述。
当前实现了什么
当前代码已经实现:
m、U作为动态元件主状态,p、T、rho、u、h作为派生量。Cylinder、Tank、Pipe的刚性绝热容腔近似。Orifice的压差开方流量关系。Tee的简化混合焓处理。Testmodel的系统级拓扑映射和一版可运行的rhs(t, x)。- 基于
solve_ivp的积分入口,以及 SciPy 不可用时的 RK4 回退。 - 温度相关空气近似介质,包括
cp(T)、h(T)、u(T)以及u -> T反解。 - 显式一致初值入口
consistent_initial_state_vector(),以及可迭代初始化器initialize_consistent_state()。 - Python 主变量结果导出:
mytank.p、mytank.T、mycylinder.p、mycylinder.T - 贮箱温度曲线导出:
testmodel_tank_temperature.csvtestmodel_tank_temperature.svg - 基于
ModelicaModels/Simulation/Testmodel_res.csv的逐时刻对比与误差摘要导出。 - 基于
unittest的自动回归测试,当前已覆盖初始化守恒、主变量基线、运行接口、内部闭合诊断、通用分支兼容层、通用结果键与旧键别名一致性,以及部分中间闭合过程行为。
当前没有实现:
- 通用 DAE 初始化器
Modelica.Media.Air.SimpleAir的严格复刻- 面向任意拓扑的通用 connector/stream 求解器
当前怎么运行
最小运行方式:
python3 -m PythonModels.scripts.run_testmodel
如果要改模型参数或运行参数,建议直接改配置对象,而不是改源码里的默认值。例如:
from PythonModels.core.solver import SolveIVPConfig
from PythonModels.scripts.run_testmodel import (
TestModelRunConfig,
TestModelSamplingConfig,
run_testmodel,
)
from PythonModels.systems.testmodel import (
BranchConfig,
CylinderConfig,
OrificeConfig,
PipeConfig,
TankConfig,
TestModelConfig,
)
run_config = TestModelRunConfig(
model=TestModelConfig(
cylinder=CylinderConfig(p0=30e6),
upper_branch=BranchConfig(
orifice=OrificeConfig(K=8e-6),
pipe=PipeConfig(length=6.0, diameter=0.03),
),
tank=TankConfig(volume=0.12),
),
solver=SolveIVPConfig(t_start=0.0, t_stop=10.0, method="BDF"),
sampling=TestModelSamplingConfig(step=0.05),
)
result = run_testmodel(run_config=run_config)
如果调用方想先确认“这次运行最后到底会用哪些路径、哪些采样点”,可以先准备请求,再执行:
from PythonModels.scripts.run_testmodel import (
prepare_testmodel_run,
run_prepared_testmodel,
TestModelRunConfig,
)
prepared = prepare_testmodel_run(run_config=TestModelRunConfig())
print(prepared.output_dir)
print(prepared.t_eval)
result = run_prepared_testmodel(prepared)
print(result.artifacts.primary_csv_path)
print(result.used_modelica_reference)
当前脚本会:
- 构建
TestModelSystem - 打印原始初值向量与约束一致后的初值向量
- 运行
0 s -> 20 s的仿真,默认采样间隔0.1 s - 将结果写入
PythonModels/runs/下本次运行专属的时间戳目录 - 若存在
ModelicaModels/Simulation/Testmodel_res.csv,自动生成 Python 与 OpenModelica 对比结果
当前脚本默认不会再把运行结果直接写到提交基线目录,而是会在 PythonModels/runs/ 下创建一个带时间戳的子目录,例如:
PythonModels/runs/testmodel_20260512_103000_123456/
该目录里通常会包含:
testmodel_primary_series.csvtestmodel_tank_temperature.csvtestmodel_tank_temperature.svgtestmodel_run_report.txttestmodel_modelica_comparison.csvtestmodel_modelica_comparison_summary.txt
基线结果
当前基线对比摘要来自: testmodel_modelica_comparison_summary.txt
当前四个主变量的最大误差为:
mytank.p:max_abs_error = 134.960857 Pa,max_rel_error = 0.006798%mytank.T:max_abs_error = 0.035507 K,max_rel_error = 0.009016%mycylinder.p:max_abs_error = 1391.986349 Pa,max_rel_error = 0.009447%mycylinder.T:max_abs_error = 0.009069 K,max_rel_error = 0.003870%
这说明在当前基线工况下,Python 版主变量已经能较好贴近 OpenModelica 结果。
AMESim test_mql 当前进度
test_mql 是从 AmesimModels/test_mql.ame 新增迁移的 AMESim 模型,当前只在独立路径下推进,不修改旧 testmodel。新增命名保持 AMESim 原始别名和 Data_Path,方便后续逐变量对齐。
当前已经完成:
- 解析 117 个组件、84 条 LINE 连接、直接组件接触、全局参数、仿真设置以及 AMESim 变量目录。
- 直接读取
.ame包内test_mql_.var和test_mql_.results;baseline 包含 1002 个时间点和 1116 个保存变量。 - 使用氦气 Peng-Robinson 物性,内部统一使用绝对压力,对外按 AMESim 表压和原始单位输出。
- 实现
PNCH023 / PNCH012 / PNOR001 / PNVO001,以及PNL0001 / PNL0002 / PNL0003 / PNL00R管路和PN3NODE2 / P4NODE2节点语义。 - 完成气动真实拓扑装配、canonical flow、端口写回、snapshot 和 112 状态气动 RHS。
- 实现
PNRP17 / MECMAS21 / LSTP00A / LMECHN1 / UD00 / FORC当前工况可确认的机械行为,并形成 20 状态机械闭包。 - 将气动和机械部分组合成 132 状态总闭包,接入活塞体积反馈、气动力、外力、端止动和质量约束,可通过现有 solver 短时积分。
- 建立关键
Data_Path序列导出、output schema、validation、AMESim 插值比较、误差排序、端点诊断和 PNCH012 RHS 项拆解。
当前确认的关键细节:
PNRP17活塞腔体积使用环形有效面积piston_area - rod_area。LSTP00A的gap观测单位是 mm,计算接触力前必须转换为 m。PNCH023固定气室初始压力来自P0=153 bar的绝对压力;AMESimpress输出为相对101300 Pa的表压。PNCH012变容腔初始压力对齐 AMESim 的1 bar绝对压力,vol输出单位为 cm3,且末端体积等于基础死容积加对应活塞vol1。MECMAS21的x1dup / v1dup / acc1dup是第二机械端口观测,相对x1 / v1 / acc1为反号,不是重复同值。- 本算例中
MECMAS21的Fmin / Fmax / Fvisc / Ffric在 AMESim 结果里为零;当前只把这一工况能验证的部分写入测试,没有硬猜未激活碰撞/摩擦状态机。
当前默认 0 -> 1e-5 s comparison 已定位最大偏差为 press@pn_c1_8:初值对齐,但末值绝对误差约 9.22849 Pa。RHS 拆解显示边界体积功约 0.026 W,端口焓流约 32722 W,因此当前首要工作是比较 Python 的 p4_port3_remote_chamber_to_line_flow 与 AMESim 的 dm1@pneumatic_69,检查单位、符号、PNL0001 阻力和 pnnode4_16 节点平衡。
当前还不能宣称 test_mql 的 Python 时域仿真已经和 AMESim 全局一致。完整说明、运行命令和下一步校准路径见 AmesimModels/test_mql/README.md。
Testmodel 当前架构判断
如果按“组件正确 -> 网络闭合 -> 积分可跑 -> 结果对齐 -> 去近似”来看,当前大致处于:
- 组件级:已完成首版
- 系统闭合:已完成首版
- 积分入口:已完成首版
- 基线结果对齐:已具备初步能力
- 去近似:仍在进行中
所以当前最准确的说法不是“已完成移植”,而是:
Testmodel 已有一版可运行、可导出、可对比的 Python 近似实现。
Testmodel 已知限制
当前最主要的限制可以直接理解成下面几条:
- 介质模型已从常
cp/cv推进到温度相关空气近似,但仍不是Modelica.Media.Air.SimpleAir的严格复刻。 - 系统整体仍是 ODE 化近似,不是原始 Modelica DAE 的直接复现。
mytee1 -> mytank这一段虽然已经去掉早期的“虚拟出口导通系数”,改成了基于压力一致性的下游能量闭合,但本质上仍是工程近似。- 当前
Tee的 stream 语义只覆盖了当前Testmodel需要的最小集合,还不是通用的inStream/actualStream框架。 - 当前一致初值仍是 ODE 入口处的约束投影,不等同于真正的 DAE 初始化求解。
- 当前自动校验主要锁的是 Python 提交基线,还不是稳定的 Modelica 阈值回归。
- 当前闭合器、系统层和 reporting 层虽然已经开始做“双支路结构化”,但对外结果序列、报告字段和部分导出命名仍然保留
Testmodel专名兼容层,还没有完全转成通用表达。 - 当前内核已经足够支撑下一阶段“结构化参数 -> 仿真执行 -> 产物输出”的链路开发,但还没有现成的 Word 参数解析入口和正式报告生成链路。
所以,当前版本适合:
- 架构验证
- 组件接口验证
- 基线工况对比
- 结果导出与误差定位
但当前版本还不适合:
- 直接宣称与 OpenModelica 严格等价
- 作为最终工程结论的唯一依据
- 直接扩展到更复杂拓扑而不补通用连接器语义
Testmodel 文件级现状
按代码现状逐项看:
core/base.py: 正常 只提供最小抽象层,没有明显冗余。core/ports.py: 正常PortState目前只保留p、m_flow、h_outflow三个必要字段。core/state.py: 正常VolumeState只负责[m, U]状态打包。core/network.py: 正常 负责状态向量拼装和连接摘要,不参与物理求解。core/solver.py: 正常 已支持 SciPy、RK4 回退和零时长仿真。components/*.py: 正常 都是当前一版近似模型,没有发现与 README 明显冲突的“未记录能力”。systems/testmodel.py: 是当前最重要的技术债集中区 这里承载了下游流向切换、焓混合、压力投影等近似逻辑,后续演进应主要落在这里。scripts/run_testmodel.py: 正常 已不是“最小打印脚本”,而是当前结果导出和对比入口。baselines/: 是当前稳定基线,不应该随着日常运行频繁改动。runs/: 是当前默认运行产物目录,不是手写源代码,也不应该当作提交基线使用。
Testmodel 当前主技术债
目前最主要的技术债,可以直接理解成下面 4 件事:
- 当前初始化虽然已经引入迭代诊断,但本质上仍是 ODE 入口近似,不是真正的 DAE 初始化器。
systems/testmodel.py还是承载了太多系统级闭合和初始化逻辑,只是主要端口的手写 stream 方向判断已经搬到组件 helper 里了,装配参数本身已经基本收口到配置对象。- 自动校验现在主要锁的是 Python 这一版自己的基线,还不是稳定的 Modelica 阈值回归。
- 当前空气物性已经完成首轮基线校准,但还不是
SimpleAir的严格复刻。以后如果换工况,或者拿到更多 Modelica 原始结果,参数大概率还要继续调。