Files
SystemSimulationApp/docs/other/C语言数值内核实施计划与可行性评估.md
T

40 KiB
Raw Blame History

C 语言数值内核实施计划与可行性评估

文档状态:待评审实施方案 建立日期:2026-09-02 补充日期:2026-09-09;第 10 节结合最新实测,细化 XML → 系统专用 C → 原生 EXE 路线。前九节保留原阶段方案与历史评估;本次仅补充设计,未完成生产内核替换。 适用分支:model-development 关联文档:求解器性能优化任务清单、后端求解逻辑与效率优化调研 范围:后端数值执行内核;不包含前端重写,也不主张把整个 Python 后端改写为 C

1. 结论

这条路线值得实施,但正确的目标不是“把现有 Python 代码逐行翻译成 C”,而是:

  1. 保留 Python 作为模型解析、网络编译、任务管理、结果服务和正确性参考;
  2. 先把完整的一次 RHS 计算编译成无 Python 回调的扁平数值 IR;
  3. 再让 C 内核一次完成整次 RHS、stream、物性、Jacobian 和事件计算;
  4. Python 旧引擎长期保留为不支持模型的兼容路径和故障回退路径。

总体技术可行性为中高。仓库已经具备因果计划、参考 IR、稀疏 Jacobian 结构、性能埋点和 AMESim 回归基础,因此不是从零开始。主要难点不是 C 语法,而是完整冻结当前模型的计算顺序、副作用、事件和回滚语义。

“实现稳定的 C 数值后端”具有较高可能性;“仅靠换成 C 就达到接近 AMESim 的速度”目前不能承诺。高刚度 LSTP 接触会让积分器产生大量纳秒级小步,有限差分 Jacobian 又会重复调用 RHS。C 能降低每次计算的成本,但不会自动减少这些计算次数。真正的大幅收益需要同时完成:

  • 完整扁平 IR;
  • 整体 C RHS;
  • 稀疏解析或半解析 Jacobian;
  • 正确的接触事件和模式切换;
  • 必要时再评估原生积分器。

2. 推荐的最终边界

XML / JSON
    ↓
Python:解析、校验、组件注册、网络编译
    ↓
完整扁平 IR:槽位、阶段、依赖、模式、事件、Jacobian 结构
    ↓ 一次跨语言调用完成整次计算
C 数值内核:RHS / stream / 物性 / Jacobian / Event
    ↓
SciPy BDF(首阶段保留)或后续通过验证的原生积分器
    ↓
Python:采样、结果编码、API、任务状态和诊断

以下边界必须坚持:

  • 不在每个组件上来回调用 Python 和 C;一次 RHS 最多进行一次主要跨语言调用。
  • C 兼容模型的热路径内不得调用 Python callback。
  • C 内核不直接解析 XML,不管理 HTTP,也不承担组件编辑和数据库职责。
  • 自定义 Python 组件不能静默降级为跨语言逐组件调用;只要整模型不满足原生能力合同,就明确走 Python 引擎。
  • 迁移期间始终保留 python、native、shadow 三种模式;通过长时发布门后才能增加 auto 默认选择。

建议统一配置为:

SIMULATION_NUMERIC_ENGINE=python|native|shadow|auto

其中 shadow 由 Python 控制正式积分,C 只接收相同输入并逐阶段比较,不允许影响 Python 工作区。

3. 当前基础与首个阻断

3.1 已有基础

  • causal_ir.py 已有 schema v1、结构签名、预分配工作区、阶段观察和事务回滚原型。
  • 当前因果执行器已经消去一部分重复逻辑坐标,并证明了数组化和预编译执行计划的方向有效。
  • 已有 Jacobian 稀疏图、局部切向原语和显式实验回退路径。
  • 已有 AMESim 结果读取、物理投影、递进时域 runner、性能统计、取消和故障注入测试。
  • 当前主模型约有 156 个运行组件、20 种组件类型和 132 个连续状态,适合用“能力矩阵 + 分阶段覆盖”推进。

3.2 当前 IR 的缺口

现有 schema v1 只覆盖全局因果代数计划,绑定仍依赖 Python 的 reader、writer 和 evaluator 回调。以下内容尚未进入同一个无回调执行计划:

  • 状态写入和信号传播;
  • dynamic volume 和物性状态包;
  • secondary 压力/流量块;
  • stream SCC/DAG 和热流体外层闭合;
  • 状态导数;
  • 离散 mode、事件检测、reset 和重启;
  • Jacobian 数值填充;
  • 结果投影。

因此,当前 IR 不能直接包一层 C 接口后就获得预期收益。

3.3 P0 跨平台换行阻断(已解决)

当前 Windows 工作树中的权威输入按原始字节计算时与 manifest 不一致;进一步核对确认,差异全部来自 Git 检出后的 CRLF 换行,而不是模型内容变化:

文件 Windows 工作树原始字节 将 CRLF 还原为 LF 后 manifest 记录值
tests/data/test-mql-8.xml 106,188 B;2,078 个 CRLF;SHA-256 e7eef641...b801 104,110 B;0a2d9331...7b0b 104,110 B;0a2d9331...7b0b
tests/data/test-mql-8.json 282,796 B;10,204 个 CRLF;SHA-256 51e1acb3...11e2 272,592 B;b44bf540...fbe0 272,592 B;b44bf540...fbe0
AmesimModels/test_mql.ame 21,708,800 B;cbc3aadd...c20fbb 不适用 一致

因此没有证据表明 fixture 语义发生漂移,也不应仅因这一差异重建 golden。真正的问题是:manifest 按原始字节锁定输入,而 .gitattributes 没有固定这两个文本 fixture 的换行;当前加载器又直接校验工作树原始字节,所以同一提交在 Windows 上可能被拒绝、在 Linux 上通过。

该阻断已于 2026-09-02 完成本地修复:.gitattributes 已将两个主模型输入、历史 0.81 s 输入和 tests/baselines/simulation 下的 JSON 证据固定为 LF,当前 Windows 工作树也已从 Git 索引重新检出为 LF。自动测试会同时检查属性规则、禁止原始 CR 字节,并按 manifest 复核输入大小和 SHA-256;CI 已配置为在 Ubuntu 和 Windows 上分别执行这一合同,远端运行证据待提交后取得。

本地验收显示这些文件均为 index=LF / worktree=LF / eol=lf,两个 regression manifest 可在 Windows 正常完整加载,现有 manifest 和 golden 无需更新。未来只有在规范化后的内容确实变化时,才进入“模型变更、重建 manifest/golden”的流程。历史性能数字仍需按其代码版本看待,但不因换行差异失效。

4. 按重要性排序的实施计划

优先级定义:

  • P0:正确性和架构前置,不完成就不能安全编写生产 C 内核;
  • P1:形成可用且有明显收益的 C 后端;
  • P2:P1 证明有效后再实施的增强项。

表中的工期是单名熟悉现有求解器的开发者的粗略有效工作量,不是交付承诺,也不包含长时仿真排队时间。

顺序 ID 优先级 工作项 可行性 粗略工作量 主要依赖
1 C-00 P0 固定权威基准的跨平台合同,建立双引擎与回滚合同 高 1–2 人周 现有 OPT-00
2 C-01 P0 定义完整数值 IR schema v2 高 3–5 人周 C-00、OPT-01/02
3 C-02 P0 建立纯数值组件 kernel 合同和能力矩阵 中高 3–6 人周 C-01
4 C-03 P0 完成 Python 扁平参考执行器与 Shadow 差分 中高 3–5 人周 C-01、C-02
5 C-04 P1 稳定 C ABI、构建链和隔离 worker 高 3–5 人周 C-01,可并行
6 C-05 P1 完成一个闭环代表子系统的 C 纵向切片 高 2–4 人周 C-03、C-04
7 C-06 P1 扩展到完整内置 RHS、stream 和物性 中高 5–9 人周 C-05、OPT-04
8 C-07 P1 稀疏解析/半解析 Jacobian 与局部回退 中 6–12 人周 C-02、C-06、OPT-03
9 C-08 P1 LSTP/MECMAS 事件、模式和默认灰度 中 4–8 人周 C-06、C-07、OPT-05/06
10 C-09 P2 模型专用 C 代码生成和编译缓存 中 4–8 人周 C-06~C-08
11 C-10 P2 评估 CVODE;有真实 DAE 需求时再评估 IDA 中/当前低 4–10 人周 C-06~C-08
12 C-11 P2 输出、部署、并发和发布收口 中高 3–6 人周 原生默认候选

4.1 P0:先固定语义和参考答案

C-00 固定权威基准、双引擎和回滚合同

工作内容:

  • 固定 test-mql-8.xml/json、历史输入和回归证据的跨平台 LF 字节合同,实际重新规范化当前 Windows 工作树,并用 Git EOL 状态、原始 SHA-256 和双平台 CI 保护该合同;未重建语义未变的 golden。
  • 重新加载 manifest 并执行 Python 0.01 s smoke;只有发现规范化后的内容或物理结果确实变化时,才重新生成结构清单和 golden。
  • 冻结当前 Python 引擎为参考实现,定义 python/native/shadow/auto 的启用条件。
  • 原生不支持的模型只允许在编译或加载阶段整模型回退;正式验收时禁止静默回退。
  • 每个阶段使用独立小提交,记录输入哈希、环境、二进制哈希和回滚开关。

完成标准:相同输入能够稳定强制走 Python;关闭原生后行为与当前参考提交一致;模型、环境和结果来源都能追溯。

C-01 完整数值 IR schema v2

完整 IR 必须描述共享数值阶段,以及 RHS、Event、Jacobian 和输出四类独立的按需入口。它们可以复用同一套槽位和依赖信息,但不能被误实现成“每次 RHS 都顺序计算 Event、Jacobian 和输出”。共享 primal 计划为:

state scatter
→ signal
→ mechanical equivalence / dynamic volume
→ property bundle
→ first pressure-flow closure
→ stream SCC/DAG
→ temperature reference update
→ sensitive-island closure / thermofluid fixed point
→ mechanical acceleration

四类入口分别编译自己的依赖切片:

eval_rhs      = required primal stages → derivative
eval_events   = required primal stages → event values
eval_jacobian = RHS primal / local derivative or JVP → CSR values
eval_outputs  = required primal stages → output projection

IR 至少描述:

  • 连续状态、代数坐标、参数、常量、离散模式和工作区槽位;
  • 每个阶段和 opcode 的读写集合、单位、缩放、上下界和错误来源;
  • stream 图、SCC、物性状态包、外层固定点和事务恢复集合;
  • event ID、左/右状态、reset、Jacobian 失效和模式计划;
  • 固定的 CSR Jacobian 结构和 output projection;
  • IR schema、native ABI、组件模型/实现、介质、编译器、dtype、平台和构建选项组成的结构签名。

原生兼容 program 中不得存在 Python callback。schema v1 保留为参考适配器,不直接扩展成生产 ABI。

完成标准:同一模型重复编译得到字节级稳定的结构和签名;所有索引及读写集合可静态校验;缺少能力时明确拒绝编译。

C-02 纯数值组件合同

每个内置组件需声明:

  • kernel_id 和实现版本;
  • 输入、输出、参数、状态和 mode 槽位;
  • primal、局部导数/JVP、event 和 reset 能力;
  • 支持的正反流、接触和物性范围;
  • 类型化错误码和局部数值差分回退边界。

组件 kernel 必须满足“相同输入得到相同输出”,不能依赖隐藏的 Python 对象写入。旧组件类继续作为参考实现。

首批纵向切片建议选择一条完整的 MECMAS21 → PNRP17 → PNCH012 → PNL0001/LSTP00A 支路及其介质/stream 闭合,因为它同时覆盖机械、气动、物性、流量和高刚度接触。

C-03 Python 扁平参考执行器

先用 Python/NumPy 完整执行 schema v2,目标是验证语义,而不是追求最终速度。它应摆脱 PortState 作为主语义载体,能在每个阶段与当前对象引擎比较首个差异。

完成标准:代表模型在普通点、反向流点、事件左右和 Jacobian 扰动点逐槽一致;0.01/0.2/0.81/2.10 s 的状态、事件、守恒和物理投影满足现有合同。

4.2 P1:形成真正有价值的 C 后端

C-04 稳定 C ABI、构建链和隔离 worker

底层使用稳定纯 C ABI,Python 绑定保持很薄。最小接口应包含:

model_create
eval_rhs
eval_jacobian
eval_events
apply_event
eval_outputs
model_destroy

接口使用不透明 ModelContext、固定宽度整数、连续 float64 数组和显式长度;CSR 结构在编译时固定,运行时只填 values。每个任务独占可变 context/workspace/cache/mode,不使用可变全局缓存,热路径预分配且不进行常规堆分配。

首阶段仍由 SciPy BDF 积分,只替换整个 RHS/Jacobian 计算。构建链需覆盖 Windows x64 和 Linux x86_64,固定构建依赖,并禁止 /fp:fast、-ffast-math、-march=native 等会改变事件边界或破坏可移植性的默认选项。

原生访问违规、崩溃或死循环不能在同一 Python 进程中安全恢复。因此原生默认启用前,仿真必须运行在可硬终止的隔离 worker 中;C 同时接收取消标志并在组件阶段、stream SCC、Jacobian 和迭代处有界检查。

C-05 闭环纵向切片和首次 Go/No-Go

一次跨语言调用应完成代表子系统的整次 RHS,禁止按组件往返。先为该闭环子系统建立独立代表 fixture,执行 Shadow 和短时积分,不立即扩大组件覆盖。由于此阶段尚未覆盖主模型的全部组件,test-mql-8 整模型按能力合同回退 Python 是预期行为,不能拿它衡量 C-05 的端到端收益。

继续扩面的最低门槛:

  • RHS 微基准至少达到 Python 的 2×;目标值为 3× 以上;
  • 独立代表 fixture 的短时完整仿真中位时间至少降低 25%;
  • 代表 fixture 的状态、事件、模式和守恒门全部通过;存在对应 AMESim 投影时也必须通过;
  • nfev/njev/nlu 和接受步等工作量没有无法解释的变化;
  • 无 Python callback、无静默回退、无内存错误。

如果 RHS 已快很多而完整仿真几乎不变,应先检查 Jacobian、微步数和跨语言边界,而不是直接继续扩大 C 代码。

C-06 完整内置 RHS、stream 和物性

  • 把所有受支持的内置组件纳入能力矩阵;一个组件不支持时整模型明确走 Python。
  • 将 stream 图编译成 SCC 和缩点 DAG:无环部分一次传播,只在循环 SCC 中迭代。
  • 将 p/T/rho/u/h 及其导数组成同一物性状态包,按精确输入和阶段统一复用。
  • 移除每轮临时字典/列表,使用预分配数组和原地误差统计。
  • 保留试探状态事务回滚、反向流、温度参考更新和类型化可恢复失败语义。

完成标准:主目标使用的全部内置组件可原生执行;stream 迭代、压力流量残差和物性结果不恶化;跨平台字节合同修复后的同一权威输入通过短程与中程回归,且 0.2 s 完整仿真中位时间至少降低 30%。

C-07 稀疏解析/半解析 Jacobian

这是获得大幅端到端收益的关键步骤。当前历史报告中,有限差分 Jacobian 会贡献大量额外 RHS 调用;仅把 primal RHS 改成 C 仍会重复执行它。

  • 组件局部导数沿完整 IR 传播,直接填充固定 CSR values。
  • 不支持或非光滑点只对局部组件、状态列或 SCC 做数值差分,不能让一个局部问题恢复全网有限差分。
  • 每次构建记录解析列、局部差分列、回退位置、JVP 审计和装配时间。
  • LSTP 接触、流向切换、饱和及临界流动使用分段导数,并在边界上明确选择事件分段或局部回退。

完成标准:有限差分附加 RHS 至少减少 50%,相对完整 C RHS 阶段再降低至少 20% 总时间;事件顺序、模式和物理投影不变。

C-08 事件、模式和默认灰度

  • 将 LSTP/MECMAS 的 event、mode、reset、Jacobian 失效和 solver restart 纳入正式 IR/C 合同。
  • 对接触进入、保持、释放,上下端挡、回弹、同时事件、擦边事件和防抖分别测试。
  • 按 shadow → 显式 native → 模型签名白名单 → auto 逐级启用。
  • 只有当前权威 10 s 连续三次通过后,才允许受支持模型默认走 C。

需要特别注意:C-08 不只是把同一接触公式写成 C,还要减少因接触表达方式造成的不必要纳秒级微步。任何软化、容差或事件改动都属于数值算法变化,必须与纯执行优化分开提交和验收。

4.3 P2:证明 P1 有效后再做

C-09 模型专用 C 代码生成

通用 C IR 解释器稳定后,若 opcode 分派仍是明确热点,再为固定模型生成专用 C。XML 文本不得直接拼入源码;生成器只消费校验后的数值 IR。编译缓存键必须包含完整结构签名、编译器和 flags。

只有相对通用 C 执行器额外获得至少约 1.5× 的稳定收益,并且编译成本能在重复运行中摊销时才继续。否则保留通用执行器,放弃这一层复杂度。

C-10 原生积分器

  • 当前系统是“RHS 内完成代数闭合”的半显式 ODE,先评估 CVODE,不直接切换为 DASSL/IDA。
  • 只有 C RHS、Jacobian、事件、可恢复缩步、partial result、activity 和 cancel 合同稳定后,才对 CVODE 做独立 A/B。
  • CVODE 在相同正确性门下额外降低至少 25% 总时间才值得替换 SciPy。
  • IDA/DASSL 需要新的 F(t,y,ydot)=0、代数变量布局、一致初始化、指数和质量矩阵合同。只有真实模型证明无法稳健因果化或 ODE 化时再立项,当前不把它视为提速开关。

C-11 输出、部署和发布收口

吸收按需结果变量、列式输出、结果分块、编译缓存、并发限流、worker 硬终止和原生崩溃恢复。确保 native 崩溃只影响当前任务,服务仍能接收新任务。

5. 分阶段验证与 Go/No-Go 门

验证阶段 主要内容 通过条件 未通过时的处理
V0 fixture、环境、Python 和 AMESim 基线 来源一致;Python 连续 3 次稳定;现有测试通过 基线仍漂移时停止 C 默认路径开发
V1 完整 Python IR 槽位、阶段、读写集合、事件和回滚差分通过 先修隐藏副作用和 IR 合同
V2 C 纵向切片 单组件/逐阶段通过;RHS 至少 2× 未达门槛则检查 IR/FFI,必要时停止扩面
V3 完整 C RHS + SciPy 0.01/0.2 s 通过;总时间至少降低 30%;工作量变化不超过可解释范围 不作为纯执行优化合入默认路径
V4 C Jacobian/Event FD 附加 RHS 至少减少 50%;相对 V3 再降低 20%;事件/模式一致 保留局部差分,禁止整网静默回退
V5 长时与鲁棒性 1/5/10 s 递进;10 s 连续 3 次;无泄漏、无静默回退 任一短时前驱失败即停止后续长跑
V6 默认启用 Windows/Linux 发布通过;支持模型走 C,不支持模型明确走 Python;可一键关闭 保持显式 opt-in

正式性能比较统一要求:预热 1 次、完整仿真至少测量 3 次并比较中位数、固定机器与电源模式,冷编译和热缓存分开报告。至少记录:

  • 总时间、积分、RHS、闭合、stream、物性、Jacobian 和输出时间;
  • nfev/njev/nlu、接受/拒绝步、solver 启动和事件次数;
  • Jacobian 附加 RHS、局部数值差分和回退原因;
  • Python/C 边界调用次数和耗时;
  • 峰值 RSS、C 工作区、IR/ABI 版本、编译器、flags 和二进制哈希。

附加硬门:关闭 C 时 Python 路径额外开销不超过 2%;纯执行层替换若使 nfev/njev/nlu 或接受步数变化超过 2%,必须按数值语义变化单独调查,不能直接归为性能优化;峰值 RSS 不超过 Python 基线的 110%;10 s 中 C 固定工作区不随步数增长,只有结果数组可随采样数增长。

6. 正确性验证范围

6.1 Shadow 阶段观察点

两个引擎必须接收完全相同的 time/state/mode/parameters,按第 4.1 节的 RHS 阶段逐项比较。差异报告至少包含:阶段、槽位、组件、输入、Python 值、C 值、绝对/相对误差和当前模式。

  • 槽位布局、模式码、事件 ID、执行顺序和迭代次数要求一致。
  • 连续量使用“每种物理量绝对容差 + 相对容差”,不能用一个绝对容差覆盖压力、流量、温度和位移。
  • NaN/Inf、压力越界、符号错误和模式不同立即失败。
  • 压力流量最大缩放残差继续使用现有 1e-7 门。
  • 事件点分别比较左极限、事件处理结果和右极限,不扩大容差掩盖不连续。

差分语料至少覆盖:初始状态、接受状态、Jacobian 扰动状态、固定随机种子扰动、压力近似相等、流量换向、接触启停、端挡释放、物性边界,以及历史 0.69/0.81/2.05 s 区域。

6.2 AMESim 物理门

Python golden 只用于发现实现漂移,AMESim 结果仍是外部物理基线。正式默认前,应在现有 physical-state-v2.1 基础上至少覆盖:

  • 代表性气室压力、温度和质量;
  • PNL 管路压力、流量及换向;
  • PNRP 活塞力;
  • MECMAS 位移、速度和模式;
  • LSTP 间隙、接触力和接触切换;
  • 每个主要支路至少一个代表量。

检查点至少包含 0、0.04 s 左右、0.2、0.8 s 左右、1、2、5 和 10 s。AMESim 未保存的内部守恒量继续执行独立绝对残差门。

6.3 错误、取消和并发

必须覆盖 C 返回非有限值、物性域错误、闭合不收敛、未知 opcode、ABI 不匹配、损坏二进制、失败后事务回滚、编译失败、并发 context 隔离、单任务取消、重复创建/销毁无内存增长和 native 崩溃后的服务存活。

回退分为三层:

  1. 启动前自动选择:编译或加载时发现不支持能力,整次任务在开始积分前明确选择 Python;这是正式模式唯一允许的自动换引擎路径。
  2. 受控运行时错误:native 已开始积分后返回错误时,当前 native 任务直接失败并保留诊断。灰度验证可以从不可变原始请求的 tStart 另起一个 Python 重放任务,但必须标为独立重放,不能算作 native 成功。没有完整保存 BDF 历史、事件上下文和已有输出前,禁止从某个 (t,y,mode) 假装无缝续跑。
  3. 进程级故障:访问违规或无法返回时,由父进程硬终止 worker,禁止在受损进程中继续;如需 Python 对照,同样从原始请求重新执行。

正式 C 验收要求异常 fallbackCount=0。声明过的局部 Jacobian 数值差分不是异常回退,但必须单独计数。

7. 对现有优化清单的取舍

现有优化清单不能整体放弃,应按新路线重组:

现有任务 决策 在新路线中的位置
OPT-00 基线与回归 保留并加强 C-00 和全部发布门
OPT-01 因果代数内核 冻结成果 作为 IR 编译输入和 Python 回退,不再继续零散微调
OPT-02 扁平 IR 升为主线 C-01、C-03、C-05
OPT-03 Jacobian 升为主线 C-07
OPT-04 stream/物性 升为主线 C-06
OPT-05 步长、接触和鲁棒性 必须保留 C-08;C 不能消除微步根因
OPT-06 dense output 部分前置、其余后置 事件合同进入 C-01/C-08,插值微调后置
OPT-07 输出与内存 暂缓但不删除 C-11
OPT-08 取消与并发 必须保留并提前 C-04/C-11;原生代码更需要进程隔离
OPT-09 10 s 验收 必须保留 V5/V6 发布门
OPT-10 高指数 DAE 有条件暂缓 只有真实 DAE 需求时进入 C-10

可以停止继续投入的方向:

  • 在现有对象热路径上继续做零散字典和属性访问微优化;
  • 继续叠加缺少统一失效合同的通用缓存;
  • 优化目标模型中未触发的 least_squares 回退;
  • 不断增加少量特例半解析列,而不先建立通用组件导数合同;
  • 直接对当前对象图使用 Numba;
  • 通过修改容差、软化物理或寻找“幸运 maxStep”伪装性能收益。

Numba 可以在完整数组 IR 后用于 1–2 周的架构验证,但不作为长期生产依赖。若完整数组 RHS 在 Numba 原型中仍没有明显改善,应先修正 IR 和算法,而不是立即开始大规模 C 重写。

8. 可行性评估

目标 当前判断 原因
验证框架、双引擎和基准恢复 高 现有 runner、golden、AMESim 读取和埋点可复用
完整 Python 扁平 IR 中高 结构基础已有,主要工作是显式化隐藏副作用和事件语义
代表子系统通用 C RHS 高 数值边界清楚,一次调用可避免 FFI 碎片化
当前全部内置组件的 C RHS 中高 约 20 种类型可逐类迁移,但 stream/物性/模式较复杂
可维护的稀疏解析/半解析 Jacobian 中 收益大,但非光滑接触、流向切换和导数覆盖难度最高
模型专用 C 代码生成 中 收益上限高,但构建、缓存、安全和诊断成本明显增加
CVODE 替换 SciPy 中,且后置 可能减少调度开销,但不会自动解决错误方程或接触微步
IDA/DASSL 直接提速 当前低 目前缺少真正 DAE 的残差、一致初始化和质量矩阵合同
接近 AMESim 的总速度 中低、待实测 缺少同机 AMESim 墙钟基线,且当前主要瓶颈同时包含 Jacobian 和接触微步数

从项目节奏看,建议把目标分成三档;这里与第 4 节一样使用“人周”,多人并行时日历时间可短于人周总量:

  1. 约 15–27 人周:技术决策闭环。 完成 C-00~C-05,回答完整 IR 是否正确、C RHS 是否有足够收益。
  2. 累计约 20–36 人周:可选原生后端。 完成 C-06,使主要内置 RHS、stream/物性、双平台构建和受控回退可由用户显式启用。
  3. 累计约 30–56 人周:默认候选。 完成 C-07/C-08、10 s 长时、并发隔离和发布门。

模型专用代码生成和原生积分器属于额外阶段,不应计入首个可用 C 后端的承诺。多人并行可以缩短日历时间,但 IR、组件合同、Jacobian 和事件语义存在强依赖,不能按人数等比例压缩。

9. 建议立即开展的第一批工作

第一批只做 P0,不直接开始大规模 C 编码:

  1. 已完成: 固定当前权威输入和回归证据的 LF 检出规则,重新规范化 Windows 工作树,并增加 Windows/Linux 字节合同测试;未重建语义未变的 golden。
  2. 生成当前模型的组件类型、槽位、阶段、副作用和事件能力矩阵。
  3. 将 schema v2 写成独立规范,先冻结 RHS 阶段、错误、事务、事件和结构签名。
  4. 建立对象引擎与扁平 IR 的逐阶段 Shadow runner。
  5. 用一条完整机械—气动—管路—接触支路完成 Python 参考闭环。
  6. 评审通过后,再建立最小 C ABI 和纵向切片。

第一批的退出条件不是“已经写了多少 C”,而是:当前基准可信、同一计算能被无回调 IR 完整表达、差异能定位到具体阶段和槽位。只有达到这个条件,后续 C 工作才具有可预测的收益和可控的回滚成本。

10. 2026-09-09 补充:XML 自动生成系统专用 C / EXE

10.1 新证据与本次目标

用户本次希望解析 XML 后,自动生成对应系统的 C 求解程序。建议把“模型专用代码生成 + 原生积分运行”作为目标架构;开发时仍保留“相同输入逐阶段比较”和“相同 SciPy 积分器仅替换完整 RHS/Jacobian”的诊断阶段,用来分别测量执行语言与积分算法的影响。后者是验证手段,不是最终交付边界。

最新证据不能把速度差异全部归因于 Python:

  • 先前特定模型的完整 C EXE 求解仍约 44.82 s;C 剖析中模型求值占 97.07%,雅可比构造占 87.17%。雅可比包含模型求值,两项不能相加。
  • 该 C 版本采用 132 状态的逐列差分,每次雅可比约需 133 次完整 RHS,累计 124,089 次。这个结论针对测试 C 实现;当前 Python 引擎已经有稀疏结构、着色和部分半解析路径,不能把它也描述成始终采用全稠密差分。
  • Amesim 仅关闭 optimized solver,进程 CPU 从 2.890625 s 增至 13.890625 s,结果文件字节、函数/雅可比计数、成功步数均相同。这证明执行优化层本身很重要,但没有揭示其专有优化实现。
  • C 管流中的固定次数二分及频繁未收敛退出是已知热点,不能作为成熟组件内核直接复制到生产生成器。

依据:C 剖析报告、Amesim 优化开关对照。这是特定模型对照,不是受控的生产 Python/C 语言收益测量,也不保证新引擎达到 Amesim 的速度。

10.2 最终执行边界

网页导出 System XML
  → Python:协议校验、组件版本/参数/连接校验
  → ModelIR:方程/可信组件原语、连续状态、代数变量、事件、依赖
  → 结构编译:合并等价变量、确定计算顺序、划分代数环、生成稀疏结构
  → C emitter:model.c + model.h + manifest.json
  → 编译并链接:组件 C 库 + 原生积分运行库
  → 独立 native worker / 模型 EXE:完成初始化、积分、事件、数值采样
  → Python:读取结果、任务状态、现有 API 编码

编译和 EXE 均在后端机器执行。Python 可以继续完成低频管理工作;一次积分中的 RHS、代数闭合、物性、Jacobian、Newton 和事件处理全部留在原生进程,不能在每次求值时回调 Python。

首个交付可生成模型专用 EXE,便于独立运行、复现和隔离崩溃。之后如实测编译或启动成本成为问题,再用通用 worker 加载缓存模型 DLL;DLL 同样加载在隔离进程中。原生运行时与组件库预先编译,每个新拓扑只生成和编译模型装配部分。

10.3 XML 不包含完整方程,必须建立 C 组件库

现行 XML 保存组件类型、版本、参数和连接,不包含各组件的物理公式。自动编译依赖一份可信映射:

(model_type, model_version, 支持的结构选项)
  → 参数校验 + 状态/端口布局 + 方程或 C kernel + 局部导数 + 事件/reset

app/simulation/registry.py 的注册定义可以继续作为编译前端的数据来源,逐个增加原生能力描述。普通公式可用受限表达式 IR 表达,同时生成数值计算和导数;复杂物性与非线性管流使用经过验证的手写 C 原语,并声明完整输入输出及导数合同。不要依赖自动翻译任意 Python 类。

例如“气室 A—管道—气室 B”编译后固定为状态和参数数组下标:求两边物性、算这根管道的流量、按端口方向累加质量/能量导数。运行时不再寻找组件对象、端口名称或连接边。系统专用代码可以直接调用 pipe_kernel(...),无需把物性库和积分器源码重复展开进每一个模型。

现有 EquationResidual 保存变量名称及已经计算出的 value,并没有保存完整公式表达式;causal_ir.py 提供部分结构基础,但其执行绑定仍有 Python 回调。因此需要补齐完整数值 IR。当前 app/simulation/ir/ 仅发现缓存文件,不能视为已有可维护的完整 IR 源码。

test/native-mql8-bdf/generate_model.py 是验证原型:仍限定特定阀动作、机械组件编号、部分参数和连接形式,并拒绝某些循环闭合。正式编译器应由拓扑和能力合同推导这些内容,不能直接把这份脚本改成 XML 读入便宣称通用。

10.4 编译器要提前完成的计算

编译工作 运行时收益或正确性要求
合并等压、等速度等别名变量;组装流量/力平衡 消除多余未知量,统一端口方向与 SI 单位
对方程和未知量做匹配,将相互依赖部分划成小块 能顺序计算的直接计算,仅对真正代数环迭代;无法配平时定位到组件
固定状态、代数变量、参数、工作区和输出下标 每次求值使用连续数组,避免字典、字符串查找和临时分配
编译 stream 图及受温度影响的物理子网 保留现有无环传播和局部闭合能力,避免退回每轮全网重算
计算各入口的依赖集合与所有支持模式的稀疏结构 RHS、事件、Jacobian 和输出按需计算;求 RHS 不自动附带所有输出
提取几何常量、公共表达式和物性状态包 参数不变的部分只初始化一次;相同精确输入下共享结果
声明事件、离散模式、缓存失效和状态复位 阀门跳变、流向切换、接触不依赖碰巧足够小的积分步长

跨积分调用的缓存必须按输入和模式有效性管理,不能仅以时间为键;同一时间可能存在多个 Newton 试探状态或雅可比扰动。试探工作区与已接受状态分开,拒绝步与事件回退不得提交试探产生的模式或缓存。

10.5 积分库与代数环的选择

当前引擎在 RHS 内闭合代数变量,对外给出 ODE。第一版原生全流程建议使用 SUNDIALS CVODE 的 BDF 模式。CVODE 提供 C 接口、1–5 阶变阶变步长 BDF,以及稠密、带状、稀疏线性求解接口。它的 Adams/BDF 由调用方选择,不能将其描述成 Amesim/LSODA 那样自动切换两种方法。CVODE 数学方法

生成器负责模型方程、结构和导数,成熟积分库负责步长/阶数、误差控制和求解器历史。不要为每份 XML 重新生成一套 BDF 算法。先锁定可复现的 SUNDIALS 版本、C 接口、构建工具和依赖;Windows 用原生 C 构建链产出 EXE,Linux 对应 ELF 程序。

稀疏矩阵可配合 KLU,但先提供正确稀疏 Jacobian,再根据模型规模比较稀疏与稠密线性代数;132 状态案例的 LU 仅占约 2.19%,只换 LU 库难以改变总体耗时。CVODE 线性求解选择

如果某类代数环难以可靠消去,可另行建立 F(t, y, ydot) = 0 的 DAE 后端,评估 C 接口的 IDA。此时必须处理代数变量、一致初始化和系统指数;高指数约束不能直接交给 IDA 期待自动解决。IDA 官方介绍

消去代数变量后,Jacobian 不能只拼接相邻组件的直接偏导。对

ydot = f(t, y, z)
0    = g(t, y, z)

在当前光滑分支、g_z 可逆时,需通过线性求解得到 g_z * dz/dy = -g_y,再组装 J = f_y + f_z * dz/dy,无需显式计算逆矩阵。这样才包含“状态变化 → 代数环重新平衡 → 导数变化”的影响。结构编译也要考虑此过程产生的新依赖。

第一版可用保守结构着色差分建立正确基线,再逐类增加局部解析导数或自动微分。旧实测的非零图和 27 色只是某次运行观察,不能直接当作全部流向/接触模式的可靠结构。迭代求解器的导数应针对其收敛方程,不宜把固定次数迭代轨迹直接当作物理方程导数。

10.6 项目接入点和生成产物

位置 具体接入方式
app/system_xml.py 复用现有校验和 SystemXmlDocument;补充明确的误差设置传递合同
app/main.py:compile_system_xml_network 抽取共同的版本、参数、介质与连接规范化前端;避免 Python/C 分别解释模型
app/simulation/solvers/causal_ir.py 与现有分块/stream 计划 复用结构分析结果,转换为无 Python 回调的数值计划
建议新增 app/simulation/native_codegen/ 完整数值 IR、组件原生能力注册、结构编译、C emitter、编译缓存
建议新增 native/runtime/、native/components/、native/include/ CVODE 适配、代数求解、组件与物性原语、版本化 C ABI
建议新增 app/simulation/backends/ Python 与原生 worker 的统一运行接口、结果适配和诊断
app/main.py:_run_system_xml_simulation_profiled 在现有任务/进度/取消接口内选择后端,保持结果变量标识和单位一致

每个模型的缓存目录建议包含:

<build-key>/
  model.c / model.h       # 状态、端口布局和模型专用计算入口
  manifest.json          # 来源、版本、布局、组件映射、支持能力、构建哈希
  model.exe              # 链接原生运行库的可执行模型

每次运行另建目录,保存数值参数、运行选项、结果和诊断。模型 ABI 至少区分 initialize、rhs/residual、jacobian、roots、apply_event、outputs,通用运行库提供 run。RHS 返回导数,事件函数返回零点函数值,输出函数只在需要物理投影时调用。每个运行拥有独立 context,禁止共享可变全局状态。

缓存键包括规范化拓扑、组件/介质实现版本、结构参数、IR/ABI 版本、精度、平台、编译器和编译选项。普通数值参数以数组传入,时长、容差、最大步长与输出间隔由运行选项传入;这些修改通常无需重新编译。改变端口数量、状态数量或方程分支结构的参数必须触发重新编译。若把某个普通参数特化进源码,也必须将其值纳入缓存键。

生成器只输出可信原语与合法数值,用户组件 ID 通过映射表定位,不作为任意 C 源码或编译命令片段。未支持的组件、模式或算法在编译/启动阶段明确报告;强制原生测试不得静默改用 Python。

10.7 精度、采样与性能口径

本次检查发现两个实际差异:

  • 网页入口 app/main.py 的 rtol 当前写死为 1e-6;此前测试 C 为 1e-7。XML v3 尚无 rtol/atol 字段,应通过明确的协议演进同时更新 XSD、解析、导出和执行设置,不可只修改某一后端。
  • 当前 tests/data/test-mql-8.xml 仍为 maxStep=0.001,而此前 JSON 测试副本使用过 1e30 和 0.02。后续应由同一份规范化模型与独立运行配置驱动比较,不能用文件同名推断设置相同。

rtol 相同也不等于总体精度相同:需同时固定每类状态的 atol、单位/缩放、代数闭合容差、初始化及事件处理。首阶段保持状态定义和方程一致;改变质量/内能为压力/温度等状态形式属于单独的数值方案。

sampleStep 与内部积分步长分开:使用积分库的插值能力取得输出采样点,不为每个输出点重新启动求解器。已知不连续时刻仍要显式分段,状态事件要定位零点并执行复位/重启。maxStep=1e30 只放宽上限,不会使求解器忽略精度或事件自动迈大步。

主指标单列求解墙钟与求解 CPU;XML 解析、代码生成、冷编译、初始化、输出投影、文件写入和 HTTP 序列化分别计时。性能模式只计推进所需的求值与事件定位,不计绘图及额外结果投影;正确性模式另行采样曲线验证。积分器要求的初始 RHS/Jacobian 也计入求解工作量,避免从计时中漏掉。

10.8 推荐实施顺序

  1. 冻结一份共同输入和运行配置。 以 test-mql-8.xml 为首个完整目标,列出其全部组件类型/模式、公式与原生能力;对齐当前 Python、旧 C 原型和 Amesim 的参数、单位、状态与事件差异。
  2. 完成一条闭环支路的完整 IR 和 C kernel。 以同一 t/y/mode/parameters 比较初始化、物性、闭合、RHS、Jacobian 与事件左右值;覆盖正常、反向流、近零压差和接触状态。
  3. 生成该支路的 C 并链接 CVODE,跑通独立 EXE。 同时以“相同 SciPy + Python/C RHS”作为执行开销对照,分开判断 C 迁移收益与更换积分器的影响。
  4. 覆盖首个完整模型,并接入现有 API 的显式原生选项。 将当前 Python 的结构消元、stream 局部执行、事务回滚和稀疏能力迁入;优先修复管流收敛与雅可比重复求值,再考虑低占比线性代数优化。
  5. 验证通用性和再逐步默认启用。 用组件重命名、连线变化、不同支路数量、反向流和其他受支持参数模式证明生成器不依赖原测试拓扑;按现有物理验收合同检查 Amesim 投影、事件时刻和守恒残差。

首个交付应能由任意命名、符合已声明支持范围的 XML 稳定生成可运行 EXE,同时报告实际后端、求解器版本、模型/二进制哈希、计时及求值/迭代计数。本次文档没有执行这项迁移,也没有新增性能测试;提速倍率需待上述共同输入对照完成后报告。