96 lines
12 KiB
Markdown
96 lines
12 KiB
Markdown
# C 求值的依赖排序与局部求解
|
||
|
||
文档版本:1.1.1
|
||
修订日期:2026-09-12
|
||
核对代码基线:`22579e5`;本次版本号仅标注文档,不改变模型版本或协议版本。
|
||
|
||
本文说明当前内置模型的扩展 C 生成路径。Python 仅在编译时整理计算关系,运行时仍由独立 C 程序完成物性、连接量、局部迭代和积分。没有恢复旧 Python 数值内核或旧 IR 包。
|
||
|
||
## 执行过程
|
||
|
||
1. 从当前状态、参数和时间信号准备机械位置/速度、容积及储气物性。压力和焓的储气来源在此确定;可变容积仍使用当前机械状态,不把加速度或下一时刻状态当作已知量。
|
||
2. 将气动连接计算记录为独立条目,每条列明输入、输出和 C 语句。参考口的别名传递与节点能量汇总分开,管路/阀门流量与储气状态导数分开。
|
||
3. 根据生产者与使用者关系自动排序。没有循环的关系执行一次;互相依赖的关系组成局部块,先完成该块,再计算使用其结果的关系。
|
||
4. 计算机械力平衡、状态变化率和结果输出,再由原有 RK45/BDF 推进时间。
|
||
|
||
这里的“一次”指一次 `model_eval` 系统状态求值。积分器为误差控制、数值差分或步长重试而进行的多次求值仍然必要,不能把它们当作重复调用删掉。
|
||
|
||
紧凑的储气锚定生成路径已经直接按依赖执行,继续保留。扩展路径使用同一套图算法处理不同内置组件组合,不依据模型名称、实例名称、画布位置或固定流向写特例。
|
||
|
||
## 编译结构
|
||
|
||
`app/simulation/native_codegen/schedule.py` 提供:
|
||
|
||
- `Computation`:稳定标识、输出、输入、C 语句、计算类别,以及压力平衡残差(适用时)。
|
||
- `EvaluationSchedule`:检查来源与重复提供者,划分相互依赖的块,并按依赖排序。
|
||
- `emit()`:生成直接计算语句和局部 C 求解函数。
|
||
- `report()`:输出来源、输入输出关系及循环块清单,保存到构建清单的 `evaluationSchedule`。
|
||
|
||
连接流量的常系数线性消元仍在编译阶段完成;消元得到的中间表达式也参加排序,不把整组连接方程作为不可拆分的大步骤。图遍历不用 Python 递归,长参考链不会受递归深度限制。
|
||
|
||
`knownSources` 记录进入该阶段前已准备好的来源;`blocks` 给出执行顺序、输入、输出、来源和未知压力;`operations` 给出逐条关系。循环初值不算作已知来源。缺少输入来源、重复输出提供者、没有热力来源的纯别名环在编译时拒绝。
|
||
|
||
## 局部循环规则
|
||
|
||
- 未知压力仅使用该块已有的压力边界确定试算范围。单个压力试算只刷新其质量平衡所需的流量关系;不调用全网流量函数。
|
||
- 一个块内存在多个相互影响的压力时,保留现有逐压力二分与扫掠方法,最多 256 轮扫掠,每次二分 48 轮,质量平衡阈值仍为 1e-11 kg/s。
|
||
- 焓传播确有循环时,从该块外已经提供的焓值初始化该块;只检查本块输出变化。相对变化阈值仍为 1e-12,轮数上限为 `max(64, 4×本块待更新焓值数量)`。
|
||
- 压力与焓同时互相依赖时,在这个局部块内保留嵌套求解。迭代到上限仍未满足条件,或出现非有限数值,返回求值失败;不把未完成的局部闭合当作成功。
|
||
- 条件表达式记录所有可能分支的依赖,覆盖反向与零流量。此做法较为保守:某个特定工况下能进一步简化的关系仍可能留在小循环内。本版没有实现运行时分支特化。
|
||
|
||
管路自身的摩擦/流量求根属于元件内部计算。PNL管流将既有阻力关系写成 `Re² × f(Re) = K`,使用带区间保护的局部Newton求根,保留可压缩流动、摩擦系数混合、流向和近平衡平滑公式。求根器遵守:
|
||
|
||
- 先检查有限输入,并通过最多128次上界试算显式确认夹根;无法得到有限且满足上界符号的值时失败,不进入未受保护的迭代。
|
||
- Newton试点越界、斜率非正或非有限时二分回退。一次Newton试点若未将方程绝对残差至少减半,下一步强制二分,使区间内的无效小步也能触发保护。
|
||
- 在实际返回候选点检查方程相对残差不超过 `1e-9`,同时保留流量变化的绝对 `1e-13 kg/s` 或相对 `1e-10` 判据;比较过程避免流量尺度溢出造成误判。解析低雷诺数分支也核对残差及返回流量的有限性。
|
||
- 求根最多128轮。区间已缩至相邻浮点值时核验两端候选,仍不满足上述条件则失败;非有限方程、无法夹根、迭代耗尽均返回失败值,不将最后一次试算当成解。
|
||
|
||
PNL00R原有的直接层流分支(对应 `Re ≤ 1000`)继续按其解析式计算;该保留分支不经过上述混合摩擦关系的求根检查。区间法的收敛仍以区间内方程连续等条件为前提,不代表任意经验公式、任意有限输入或整套耦合系统都能成功求解。上述压力/焓闭合与管路求根是不同层次;当前系统 BDF 的雅可比已采用下节说明的有条件着色差分。系统循环的来源检查和局部划分不代表任意新非线性方程都已得到数值求解支持。
|
||
|
||
每次 `model_eval` 还建立调用者持有的 `NativePropertyCache`,并显式传给局部辅助函数。气体状态准备后登记已有温度、焓和密度;阀口、管路及诊断计算共享相同状态中的有效物性。压力、焓或介质参数改变时按完整输入重新查询,缓存不跨系统试算复用。记录容量满时使用正常计算回退。
|
||
|
||
## 新模型如何接入
|
||
|
||
除了模型的物理接口表,还要在 C 生成实现中拆出能独立执行的关系:
|
||
|
||
```python
|
||
operations.append(Computation.assignment(
|
||
f'alias:{component.name}.port_1', target_h, reference_h, 'alias'))
|
||
```
|
||
|
||
单个赋值的输入从编译器生成的受控表达式中提取。提取器只识别本生成器的 `p/h/q/w/fb` 数组与气体物性字段,不解析用户输入的 C 代码。调用写出多个结果的 C 函数时,必须显式列出实际读取的参数和写出的结果;把输出指针误当作输入,会制造虚假循环。
|
||
|
||
没有依赖流量的参考值复制必须单独列出,不能与需要流量的能量计算合并为一个条目。共享状态、机械/信号准备和气体物性阶段仍需要在现有 C 接入中实现;仅注册 `PORTS/PARAMETERS` 不会自动产生方程。
|
||
|
||
新增气动部件应传递现有 `properties` 上下文,使用 `native_medium_gas_context`、`native_temperature_ph_context`、`native_density_context` 及相应阀口/管路的 `_context` 接口。记录和有效标志由内核维护,不能由部件绕过检查直接改写。新增物性字段时应在内核中定义其输入条件与有效标志;不能因焓相同就沿用不同压力下的温度,也不能在多次试算之间建立未核对输入的全局缓存。旧无上下文接口用于兼容或独立计算,不作为新生成代码的默认路径。
|
||
|
||
新增能力至少检查:来源缺失、多个提供者、长参考链、组件与连接顺序打乱、正反流和零压差、两个独立循环互不重算、耦合压力/混合循环的质量能量守恒,以及独立数值基准。当前自动排序对象是已接入的内置方程,不接受任意外部 C 代码。
|
||
|
||
对应测试:`tests/test_native_schedule.py`、`tests/test_native_catalog.py`。本轮模型对照记录见 [计算排序验证记录](../other/C计算依赖排序验证-2026-09-10.md)。
|
||
|
||
物性复用和管流求根还由 `tests/test_native_properties.py`、`tests/test_native_pipe_physics.py` 覆盖。`tests/test_native_pipe_solver.py` 仅依赖Python标准库和C编译器,通过独立二分、切换邻点及临时测试副本中的斜率/方程故障注入,验证区间保护、残差、回退与明确失败。实现边界与实测结果见 [物性复用与管流求根实现及验证](../other/物性复用与管流求根实现及验证-2026-09-11.md)。
|
||
|
||
## 新注册模型的构建与数值检查(2026-09-12 修订)
|
||
|
||
公共数值函数位于 `native/components/modules/`,导出与依赖由 `native_codegen/modules.py` 管理;`kernels.c` 仅为诊断聚合入口。增加 C 调用后检查函数所属模块实际进入链接。
|
||
|
||
求值排序的输入输出声明与 BDF 雅可比的状态依赖分析需要分别核对:`jacobian.py` 识别新的受控函数时必须知道全部状态输入,分支及投影取保守依赖;不能因调度无环就断言着色正确。无法证明结构时保守回退,动态模型适用时对生成的 `model`/`model.exe` 执行 `--verify-jacobian`;它不是 Python 包装 CLI 的参数。新增状态还须审查 `tolerances.py` 的量纲尺度。完整步骤见[注册流程](component-registration-workflow-v1.md)。本次无状态斜坡源演练未覆盖新的非线性环、动态端口或非平凡的雅可比结构。
|
||
|
||
## 当前 BDF 雅可比与误差尺度
|
||
|
||
`jacobian.py` 在编译期追踪状态依赖。仅当依赖可证明且 `0 < colorCount < stateCount` 时,CVODE 安装着色前向差分;未知依赖或无分组收益时保留默认稠密差分。紧凑路径当前明确使用稠密回退。着色减少 RHS 评估次数,底层仍是 `SUNMatrix_Dense` 与 `SUNLinSol_Dense`,不是稀疏矩阵分解器。
|
||
|
||
扩展路径为雅可比生成 `model_eval_jacobian()`:差分基点和扰动点统一采用 canonical 物性求值方式,避免初始缓存填充与后续查询的细小舍入差影响导数;普通 `model_eval()` 的物性复用行为不因此更改。运行策略以构建清单 `jacobianStructure.defaultRuntimePolicy/runtimeEligible/runtimeFallbackReason` 为准。
|
||
|
||
网页、XML API 和原生 Python CLI 默认经 `backends.simulation_config()` 取得 `rtol=1e-8`;CLI 可显式覆盖。单独构造 `SolveIVPConfig()` 或直接运行未指定容差的生成 EXE,其默认 `rtol` 仍是 `1e-6`;直接 EXE 默认方法还是 RK45,不能混作网页的 BDF 默认。原生 runner 不支持自定义 `atol/first_step`,保留的配置 `atol=1e-8` 只是入口约定;实际 `model_atol` 为质量字段 `m/m1/m2:1e-14`、位移/速度 `x/v:1e-12`,其余 `1e-8`。
|
||
|
||
## 构建缓存的实际边界
|
||
|
||
默认根目录为 `app/data/native-builds/`。`objects/<hash>` 保存可复用编译单元(cacheVersion 1),`models/<hash>` 保存完整模型程序及合同(cacheVersion 2);它们与工程/结果存储分开,当前不会随 `SIMULATIONAPP_DATA_DIR` 自动迁移。
|
||
|
||
按需选择只作用于 `modules.py` 列出的组件功能模块,粒度是 C 模块文件,不是某实例或单个函数;运行库的六个 C 单元总会参与构建身份计算。即使完整模型命中,每次仍检查工具链、预处理所选源码、计算内容/依赖哈希并核验产物,再跳过编译和链接。因此缓存命中不是零构建成本。参数或元件组合改变通常改变生成的 `model.c`,公共单元仍可复用。
|
||
|
||
默认模型预算 256 MiB、对象预算 128 MiB,分别由 `SIMULATION_NATIVE_MODEL_CACHE_MB`、`SIMULATION_NATIVE_OBJECT_CACHE_MB` 设置非负整数。清理按目录最近使用时间进行,并保护正在使用、并发变化或非受管条目;单个剩余超大条目会保留,因此是可报告超限的预算,不是硬磁盘配额。设 0 也不是关闭缓存开关。启动预热(默认启用,`SIMULATIONAPP_WARMUP=off` 可禁用)只检查工具链和 XML Schema,不预先编译全部组件;`build.py` 仍会验证命中缓存的工具链条件。
|
||
|
||
Windows/Linux 编译、依赖布局和实际验证范围见[跨平台约定](跨平台交付约定.md)。
|