5.0 KiB
C 求值的依赖排序与局部求解
本文说明当前内置模型的扩展 C 生成路径。Python 仅在编译时整理计算关系,运行时仍由独立 C 程序完成物性、连接量、局部迭代和积分。没有恢复旧 Python 数值内核或旧 IR 包。
执行过程
- 从当前状态、参数和时间信号准备机械位置/速度、容积及储气物性。压力和焓的储气来源在此确定;可变容积仍使用当前机械状态,不把加速度或下一时刻状态当作已知量。
- 将气动连接计算记录为独立条目,每条列明输入、输出和 C 语句。参考口的别名传递与节点能量汇总分开,管路/阀门流量与储气状态导数分开。
- 根据生产者与使用者关系自动排序。没有循环的关系执行一次;互相依赖的关系组成局部块,先完成该块,再计算使用其结果的关系。
- 计算机械力平衡、状态变化率和结果输出,再由原有 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×本块待更新焓值数量)。 - 压力与焓同时互相依赖时,在这个局部块内保留嵌套求解。迭代到上限仍未满足条件,或出现非有限数值,返回求值失败;不把未完成的局部闭合当作成功。
- 条件表达式记录所有可能分支的依赖,覆盖反向与零流量。此做法较为保守:某个特定工况下能进一步简化的关系仍可能留在小循环内。本版没有实现运行时分支特化。
管路自身的摩擦/流量求根属于元件内部计算。本轮没有改其算法或 16/64/48 轮上限,也没有改积分器或雅可比策略。系统循环的来源检查和局部划分不代表任意新非线性方程都已得到数值求解支持。
新模型如何接入
除了模型的物理接口表,还要在 C 生成实现中拆出能独立执行的关系:
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 不会自动产生方程。
新增能力至少检查:来源缺失、多个提供者、长参考链、组件与连接顺序打乱、正反流和零压差、两个独立循环互不重算、耦合压力/混合循环的质量能量守恒,以及独立数值基准。当前自动排序对象是已接入的内置方程,不接受任意外部 C 代码。
对应测试:tests/test_native_schedule.py、tests/test_native_catalog.py。本轮模型对照记录见 计算排序验证记录。