# 物性与管流分段性能分析(2026-09-11) 后续实现已完成:见 [物性复用与管流求根实现及验证](物性复用与管流求根实现及验证-2026-09-11.md)。本文保留为实施前的剖析与隔离实验记录;最新生产实现、计时和回归结论以实现报告为准。 本次沿用网页端已验证的 `test-mql-4-corrected.json`,直接测试网页后端使用的 C 求解程序。结论是:两个方向都值得优化,但物性应优先减少重复反算,管流则应改进收敛方法并检查残差。把迭代次数直接调小,不能可靠地解决问题。 隔离试验中,完整 10 秒仿真的纯求解耗时从 **3.119 秒降至 2.102 秒,中位耗时缩短 32.6%**。这些是 `/test` 内的实验结果;本次没有更改网页后端的生产内核,也没有修改系统雅各比矩阵。 **测试对象与测量口径** - 输入:`test/mql4-20260910/test-mql-4-corrected.json`,与上一轮网页端对照使用同一模型。 - 配置:真实氦气 Peng–Robinson 物性;CVODE/BDF;仿真 0~10 s;相对误差限 `1e-7`;最大积分步长 `1e30`;状态绝对误差限沿用生产配置。 - 规模:64 个状态,882 个输出;本模型的编译计算顺序已经形成,无代数环。当前检查的是这个顺序内部的成本。 - 代码:`system-optimization` 分支,构建标识 `a23de3b825eaacc2df5b61f524df66ec3f92da943acb5a9a98aaf4ba479fb02f`。试验脚本首先核验生产 C 源文件与构建清单的哈希一致。 - 性能比较:每个方案独立进程运行三轮,交错执行,报告中位数。只统计 C 求解阶段,使用 `--solve-only`,不含 Python 前处理、编译、进程启动、曲线输出、网页交互和绘图。 - 正确性比较另跑完整输出,保存 0.01 s 网格及事件点;对组合方案另外记录碰撞附近内部积分点。 - 分段测试是在一次连续求解中计数,按方程试算的时间归入区间,**没有在分段边界重启求解器**。试算可能回退、拒绝或越过区间边界,因此这些数据表示“为该区间试算花了多少成本”,不是各段独立启动的时间。 - 计时探针和详细计数、输入采集分两次执行;二者的全部输出序列均与未插桩基准逐点完全一致。插桩运行总耗时为 3.600 s,同批完整输出基准为 3.211 s,探针约增加 12% 耗时。因此下面函数时间用于定位成本,最终加速比使用无探针程序测量。 本次没有重跑 Amesim;数值对照读取此前官方重新编译运行的 `amesim-fresh/test_mql_4.ame`。不将历史 Amesim 耗时与本次机器负载不同的测量混合计算加速比。 **1.时间花在哪个阶段** 基准共执行 24,941 次系统方程计算,接受 5,137 个积分步,发生 2 次状态切换。下表中的时间是探针测得的方程计算时间,不包括方程外的求解器工作。 | 仿真时间区间 / s | 方程计算次数 | 方程计算耗时 / s | 占全部方程计算时间 | |---|---:|---:|---:| | 0~0.01 | 760 | 0.068 | 1.9% | | 0.01~0.8 | 12,042 | 1.757 | 49.9% | | 0.8~1.20 | 2,165 | 0.272 | 7.7% | | 1.20~1.202,包含首次碰撞 | 1,186 | 0.146 | 4.1% | | 1.202~1.23,包含第二次限位事件 | 2,451 | 0.313 | 8.9% | | 1.23~2 | 4,333 | 0.668 | 19.0% | | 2~10 | 2,004 | 0.299 | 8.5% | | 合计 | 24,941 | 3.522 | 100% | 接近一半的工作发生在 0.01~0.8 s。最后 8 秒仿真只占约 8.5% 的方程计算时间。碰撞附近确实需要处理很快的变化,但本次测量不支持“主要时间都花在碰撞瞬间”的判断。 另有 16,768 次方程计算来自线性求解器估算数值导数,占全部方程调用的 67.2%。这部分已经包含在表内,不能另加一次。它意味着物性和管流计算会随系统试算反复执行。本轮保留现有雅各比矩阵处理方式,只降低每次试算成本。 **2.把物性和管流迭代分开后,结论发生了什么变化** 下表按互不重复的函数耗时分类:例如管路内部调用的可压缩流动计算,记在“可压缩流动系数”中,不再记入“管流摩擦迭代”。 | 计算内容 | 插桩耗时 / s | 占方程计算时间 | 调用次数 | |---|---:|---:|---:| | 可压缩流动系数:等熵修正、密度、临界/亚音速关系等 | 1.009 | 28.6% | 728,296 | | 管流摩擦迭代 | 0.945 | 26.8% | 483,678 次进入循环 | | 用压力、焓反求温度 | 0.728 | 20.7% | 646,040 | | 从质量、内能恢复气体状态 | 0.328 | 9.3% | 648,466 | | 管路诊断量:雷诺数、速度、摩擦系数等 | 0.252 | 7.2% | 548,702 | | 管路其余准备工作 | 0.093 | 2.6% | 548,702 次管流调用 | | 系统组装、阀口剩余计算、接触力等 | 0.166 | 4.7% | — | 因此,不能把整个 `native_pipe_flow` 函数的时间都解释成“内部摩擦迭代太慢”。它首先还要计算黏度、真实气体修正以及可压缩流动系数。表中 28.6% 的项目也包含流量公式中的幂函数等,不是全部都属于物性查询。 本次计数还发现:一次完整求解累计调用约 **620 万次气体状态方程求根**、348 万次焓偏差计算和829万次摩擦系数计算。现在这些均运行在 C 内核中,当前问题已经是物理计算量和重复调用量,不能继续归因于 Python 语言开销。 **3.物性为什么贵,应该怎样优化** 当前真实氦气的 `p,h → T` 过程大致如下:先用理想气体关系猜温度,再代入真实气体关系计算焓,看是否吻合,然后更新温度。每次更新又要解一次气体状态方程,并计算对数等函数。 | 反算对象 | 次数 | 平均迭代次数 | 观察到的最多次数 | 达到16次上限且未收敛 | |---|---:|---:|---:|---:| | 质量、内能恢复温度 | 648,466 | 4.41 | 5 | 0 | | 压力、焓恢复温度 | 646,040 | 4.38 | 5 | 0 | 物性反算在这个工况下并不存在迭代失控;主要问题是一次略贵的计算被调用了很多遍。C 输入重放试验中,`p,h → T` 平均约 **1.065 μs/次**,几十万次累加后就成为明显成本。 更重要的是,这 646,040 次调用,在同一次方程计算中,都能找到压力、焓完全相同的上游气体状态。温度此前已经算出来了。其中 369,553 次还是对相同压力、焓的重复请求,占 57.2%。 通俗地说,目前有些路径在做:“先算出温度,把它换成焓传给下一个部件,下一个部件又把同一压力下的焓反算成温度。”这个模型中,优先消除这条往返路径,比单独把反算公式加快更有价值。 已验证的两个层次: 1. **同一次试算中,相同输入只反算一次。** 完全相同的介质、压力、焓使用同一个结果。纯求解耗时减少 11.0%,全部882个输出及时间序列逐点完全一致,方程调用数也不变。 2. **传递与压力、焓对应的已知温度。** 如果气体状态已经提供了同一组压力、焓,直接使用其温度;找不到时仍调用原来的反算。单独使用这一方案,纯求解耗时减少 16.7%。采集的17,527个气体状态中,源温度与反算温度最大只差 `1.08e-10 K`,但微小数值变化仍会改变 BDF 的后续试算路径,所以不能承诺逐位相同。 通用实现建议是建立一个“本次方程计算的物性状态”结构,保存介质身份、压力、焓、温度及已经算出的密度、黏度等。编译器知道量来自哪个状态时,直接传递状态引用;不知道时再查询或反算。缓存由求解上下文持有,不依赖整个进程中的可变全局状态。 必须遵守物理含义:**相同焓并不代表相同温度**。节流后的压力改变了,或者支路发生焓混合,就不能直接沿用原来的温度。缓存必须核对介质与完整输入,未来两相介质还需要区分物性区域。每次试算更新或失效缓存;本轮实验不采用“数值差不多就复用”,以免把求解器正在估算的细小变化抹掉。 还有两个次级机会: - `valve → isentropic → local_isentropic` 已经求过上游密度,返回阀口后又按相同 `p,T` 求了一遍。可以让一次物性更新同时提供密度、热容和所需导数,把公共中间量复用。该方向还未纳入本轮完整系统加速结果。 - 已知内能、压力、密度时,可以利用 `h = u + p/ρ` 得到焓,避免再从 `p,T` 求一遍真实气体焓。对采集状态的微基准,气体状态恢复从 **0.484 降至 0.247 μs/次**,约减少49%;与原式的最大焓差为 `1.67e-7 J/kg`。这是物性定义层面的通用机会,但必须先核对各介质的能量参考点与实现一致性。本轮只完成了单项验证,没有把该收益算进完整系统的32.6%。 对于确实需要反算的状态,后续再考虑使用上一次温度作初始猜测、利用局部热容改善更新、加区间保护与失败回退。初始猜测只能帮助收敛,不能绕过结果残差检查。查表、插值等近似方案暂不作为第一步,因为本轮已经找到无需近似的重复计算。 **4.管流为什么反复迭代,而且到上限仍没有收敛** 当前过程是:猜流量 → 算雷诺数 → 算摩擦系数 → 用新摩擦系数重新算流量 → 新旧流量各取一半 → 重复。每次调用又从“摩擦系数等于0.02”的固定猜测开始。 | 管路类别 | 调用次数 | 未进入循环的次数 | 达到上限且未收敛 | 占全部调用 | |---|---:|---:|---:|---:| | PNL00R(kind 0) | 49,882 | 49,644 | 0 | 0% | | PNL0001/2(kind 1) | 399,056 | 8,028 | 390,268 | 97.8% | | PNL0003(kind 3) | 99,764 | 7,352 | 92,412 | 92.6% | PNL00R 大多走层流公式,确实进入迭代的少量调用最多需要62次,但都在其64次上限内收敛。PNL0003 的92,412次循环全部走满16次;PNL0001/2 的非零流动调用也几乎如此。 代码达到上限后直接返回当前流量,没有要求最终残差合格。这不只是速度问题,还意味着当前局部流量并未都达到代码宣称的收敛精度。 “新旧各取一半”虽然温和,却会限制收敛速度。例如层流关系下,在解附近一次更新大约只把误差缩至原来的75%;16次后仍可能剩下初始误差的约1%。充分粗糙的湍流区,即使摩擦系数已经近似恒定,仍然反复取平均,也会继续消耗循环。真实过渡区更不能依靠固定次数保证精度。 对实际仿真每37次调用抽取一组输入,重放得到14,830组管流输入,覆盖正流7,362组、反流6,951组、等压517组。压力约0.1~15.3 MPa,温度约173~526 K。 本次把相同的管流关系写成 `Re² × f(Re) = K`,以左右两边的相对差衡量残差。`K` 来自原来的物性、压力、温度及几何关系,没有换用新的物理流量公式。 | 类别 | 旧方法残差中位数 | 旧方法残差90分位 | 旧方法残差99分位 | 新方法最大残差 | |---|---:|---:|---:|---:| | PNL0001/2 | `7.51e-6` | 3.07% | 15.9% | `9.59e-10` | | PNL0003 | `2.82e-6` | `1.85e-5` | 2.19% | `6.38e-11` | 相对残差在流量接近零时会很大,不能直接当成系统流量误差。即使只看新流量绝对值大于 `1e-8 kg/s` 的输入,旧方法的最大方程残差仍分别为21.6%和12.3%。在全部重放输入上,新旧流量的最大绝对差分别为 **`2.91e-6 kg/s`、`1.93e-6 kg/s`**。因此既要看残差,也要看流量的绝对变化和系统曲线。 针对这一点,实验采用带区间保护的局部 Newton 求根:根据当前偏差决定下一次改多少;如果下一次估计越界,则用区间二分回退。保留原有摩擦系数公式、层流分支、流向、可压缩修正及近平衡平滑。这里计算的是管路内部一个量的斜率,没有修改系统雅各比矩阵。 | 同一批输入的 C 微基准 | 旧方法 / μs每次 | 实验方法 / μs每次 | 时间减少 | |---|---:|---:|---:| | 只计算摩擦迭代,物性和几何系数提前准备 | 1.619 | 0.555 | 65.7% | | 整个管流计算,包含物性准备 | 3.369 | 2.043 | 39.4% | PNL0001/2 真正进入新迭代时平均4.40次,PNL0003平均4.08次,最多6次。用独立的100次二分求解同一方程作参照,新流量最大差约 `1.1e-13 kg/s`。 另测906组独立工况,覆盖雷诺数 `1e-6~1e9`、相对粗糙度 `0~0.1`:无非有限结果,最多7次迭代,与二分结果最大相对差 `3.62e-11`。部分工况确实触发了区间回退,最多2次。这个标量求根部分通过 `K` 与介质物性分离,因此可以复用于采用同一阻力方程的不同气体与几何参数。其他阻力模型应提供自己的残差函数和物理适用区间,不能直接套用氦气物性公式。 正式实现还应返回 `converged / iterations / residual / fallback` 等状态;超过上限时继续可靠回退或报告失败,不能静默返回未经检查的数值。 **5.完整系统能节省多少时间** 下表全部使用无性能探针的可执行程序,三轮交错运行。每个方案都运行到10 s。 | 方案 | 求解墙钟中位数 / s | 三轮范围 / s | CPU中位数 / s | 比基准减少 | 方程计算次数 | |---|---:|---|---:|---:|---:| | 当前生产算法基准 | 3.119 | 3.111~3.165 | 3.172 | — | 24,941 | | 只复用完全相同输入的物性反算结果 | 2.775 | 2.772~2.807 | 2.844 | 11.0% | 24,941 | | 直接使用对应气体状态的已知温度 | 2.599 | 2.573~2.607 | 2.672 | 16.7% | 25,493 | | 只替换管流内部求根方法 | 2.500 | 2.496~2.508 | 2.578 | 19.8% | 24,626 | | 已知温度复用+管流求根 | 2.102 | 2.091~2.108 | 2.172 | 32.6% | 28,164 | 组合方案虽然单次计算明显加快,方程调用次数却增加约12.9%。它改变了局部流量精度与微小舍入,BDF在这个敏感模型上的步长选择随之变化。因此,“单次管流快39%”不等于“整个系统快39%”,两个单项收益也不能直接相加。真实的完整系统收益应以上表为准。 相同输入复用方案和直接传递温度的方案是两个层次,不应叠加计算11.0%与16.7%。组合实验包含的是后一种物性方案。 **6.是否牺牲了仿真结果** 只复用相同反算输入的方案,包含事件点在内的全部输出完全一致。其余方案保持同一物理方程,但不会逐位相同,必须检查结果差异。 组合方案与基准在0.01 s公共网格上的比较,针对此前对照的72条压力、温度、位移、速度和阀口流量曲线: | 量 | 最大绝对差 | |---|---:| | 压力 | 19.3 Pa | | 温度 | 0.00586 K | | 位移 | `2.06e-7 m` | | 速度 | `1.78e-6 m/s` | | 阀口质量流量 | `9.36e-5 kg/s` | 流量最大差可能发生在短暂变化附近,不能只拿最后时刻或少量输出点作结论。上述72条量的完整公共网格已经比较;与存档Amesim的回归对照另外采用原有57个指定时间点,不能将其称为每个内部积分点均一致。 与Amesim在这57个指定时间点的对照中,组合方案最大压力差约199.8 Pa、最大温度差0.01154 K;基准对应约198.3 Pa、0.00963 K。位移、速度、阀口流量差仍在同一数量级。新方案没有消除原有模型/数值差异,也没有显示出数量级上的恶化。详情按曲线存入 `accuracy.json`。 此前关注的第一个 LSTP 接口力单独比较: - 1001个公共网格点,与Amesim存档力曲线的最大差:基准0.5770 N,组合方案0.5766 N。 - 第一次碰撞时间相对基准变化约 `−4.32e-8 s`,第二次约 `−1.52e-7 s`。 - 第一次事件的瞬时峰值从 `1.498588675e11 N` 变为 `1.498588441e11 N`,相对变化约 `−1.56e-7`。 - 另行记录内部接受的积分点后,峰值降至一半的时间均约0.348 ns,降至1%的时间均约2.336 ns。记录探针也未改变组合方案原有输出。 所以本轮加速没有跳过或删除此前发现的纳秒级力脉冲。Amesim存档在这附近没有相同密度的内部采样,仍不能据此判断其内部是否存在同样脉冲。这项既有对照限制继续保留。 这些验证支持方案继续进入正式开发,并不等价于所有模型、所有介质均已通过生产回归。 **7.建议的通用实施顺序** 1. **先补可观测性和收敛判据。** 把本轮的调用次数、迭代分布、未收敛次数、最大残差做成可选诊断,默认关闭详细计时。每条管路分别累计,便于新增模型后直接定位。改变流量算法前保存基准。 2. **将物性复用做进编译与运行上下文。** 第一步采用完整输入一致的缓存,获得本轮已证明的低风险收益;随后让编译器沿供需关系传递物性状态引用,跳过已有温度的反算。焓混合、压力改变、介质改变时重新构造状态。未来介质各自实现 `pT / ph / rho-u` 更新及可复用的派生量。 3. **统一管路标量求根器。** 将介质与几何计算形成的系数交给局部求根器,保留各管路已有的物理分支;以流量绝对误差和相对残差共同验收。区间保护、失败回退和统计接口作为公共能力,不按具体实例编号写特例。局部斜率可由阻力模型提供,缺少时仍有不依赖斜率的区间方法。 4. **合并物性中间量,再分离仅用于显示的诊断量。** 复用上游密度、热容和等熵计算中间量;验证 `h=u+p/ρ` 快路径。对雷诺数、显示速度等,编译器检查是否被其他模型或控制器使用;只有确认为纯输出时才移到采样阶段。不能一律关闭,因为新增模型可能把这些量接入反馈。 5. **逐项做系统回归,再合并收益。** 检查不同气体、正反流、等压、层流、过渡区、粗糙管、限位事件和小体积储气模型;同时保存稳态输出和事件附近内部点。每个方案单独测,再测组合,接受与拒绝步数也要记录。保持本轮所用系统雅各比矩阵策略,避免混入其他变化。 优先顺序上,建议尽快处理管流“达到上限仍返回”的行为,同时先落地完全一致输入的物性复用。前者关系到局部方程是否真的解好,后者已有逐点一致和约11%完整系统收益的证据。更进一步的状态传递与管流求根组合,已有约32.6%的隔离试验收益,可以作为第一轮正式优化的目标;不把这一百分比保证给其他模型。 **复现与文件** 所有原始记录与实验程序位于:`F:/Master/SystemSimulationApp/test/property-pipe-profile-20260911/`。 | 文件 | 内容 | |---|---| | `profile.py` | 核验源码哈希,构建基准、分段计时、独立计数和输入采集程序 | | `timed/profile.json` | 各阶段独占耗时、调用次数、迭代直方图 | | `counters/profile.json`、`counters/replay.txt` | 缓存命中机会及49,818条气体/反算/管流采集输入 | | `experiments.py` | 完全相同输入缓存、已知温度复用、管流求根及组合方案生成器 | | `microbench.py`、`micro-summary.json` | C重放微基准、残差、906组独立工况检查 | | `benchmark.py`、`benchmark.json` | 无探针的三轮交错纯求解测量及逐次结果 | | `analyze.py`、`accuracy.json` | 公共网格、Amesim存档及事件点的数值比较 | | `trace_combined.py`、`event-validation.json` | 组合方案碰撞附近内部积分点检查 | | `baseline/`、`cache_exact/`、`known_temperature/`、`pipe_root/`、`combined/` | 各自的C源文件、可执行程序、完整结果及日志 | 在仓库根目录,用 `.venv-win/Scripts/python.exe` 依次运行上述 `profile.py`、`experiments.py`、`microbench.py`、`benchmark.py`、`analyze.py` 可复现主结果。需要本机现有GCC及SUNDIALS开发环境。事件记录脚本会拒绝向既有记录追加,避免混合两次结果;重跑时应指定新的实验输出目录。 `/test` 按现有仓库规则被Git忽略;本报告进入文档目录。正式合入时需把通用实现和必要回归测试迁入对应源码目录,本轮实验代码不作为已经上线的功能。