95 lines
8.7 KiB
Markdown
95 lines
8.7 KiB
Markdown
# 求解器外层提前终止修复与验收
|
||
|
||
日期:2026-09-14。分支:`system-optimization`。
|
||
|
||
## 结论
|
||
|
||
原先在 10.8 秒退出的八路模型,使用新封装完成 50 秒,纯求解墙钟时间 9.082 秒。旧封装在同一输入及当前组件实现下仍于 10.8 秒退出,确认本次修复针对外层判定,而非通过改模型参数避开问题。
|
||
|
||
新旧结果在前 10.8 秒的 1,082 个共同采样时刻、1,785 条序列(包括时间)上逐项完全一致。新封装共遇到 12 次成功返回但时间未变,最长连续 6 次,均自行恢复。当前四路、八路 corrected JSON 也均完成 50 秒。22 项相关自动测试通过。
|
||
|
||
## 处理规则
|
||
|
||
旧代码将 `flag < 0` 和 `next <= t` 合并为失败。两者含义不同:负返回码表示 CVODE 报告错误;成功的内部步可能小于当前时间的浮点分辨率,因此外部读取的时间暂时不变。
|
||
|
||
| 情况 | 新行为 |
|
||
| --- | --- |
|
||
| CVODE 成功返回且时间增加 | 正常处理接受步、采样和事件 |
|
||
| CVODE 成功返回但时间相同 | 保存 CVODE 当前状态与历史,继续调用;记录次数,不做零长度采样/事件处理 |
|
||
| 连续时间相同 | 不设置次数门槛;继续检查取消与当前整次求解的实际墙钟时限 |
|
||
| 超时发生且仍在上述停滞阶段 | 失败原因 `time-stagnation` |
|
||
| 其他实际求解超时 | 失败原因 `timeout` |
|
||
| CVODE 返回负错误码 | 立即报告 `solver-error`,保存失败操作、原始返回码和最后接受时间 |
|
||
| 时间倒退、越过请求边界或非有限时间/状态 | 分别报告 `time-regression`、`time-overshoot`、`nonfinite-time`、`nonfinite-state` |
|
||
| 接受步或状态切换超过既有保护上限 | 报告 `resource-limit`,明确具体上限类型 |
|
||
| 用户取消 | 保持取消状态;不会被后续 CVODE 回调错误覆盖为求解失败 |
|
||
| 积分器声称完成但未到请求终点 | 报告 `incomplete-result` |
|
||
|
||
时间相同的恢复路径不增加容差、不设置最小步长、不人为令时间增加,也不调用 `CVodeReInit` 跳过困难区间。既有信号分段边界和物理事件重启流程保留。
|
||
|
||
默认求解时限仍是 300 秒,Python 进程监控保留 5 秒退出宽限;这不是“某次停滞开始后再计 300 秒”。测试中的原模型使用 180 秒时限,实际远未触及。独立运行可通过已有 `--timeout` 设置时限。
|
||
|
||
CVODE 初始化、资源分配、配置 API,以及 BDF 步内采样与事件处理失败有独立操作/原因信息。CVODE 的正常停止返回码 `1` 不视为失败。返回码语义参见 [SUNDIALS 官方 CVODE 文档](https://sundials.readthedocs.io/en/latest/cvode/Usage/index.html)。
|
||
|
||
## Python 进程封装
|
||
|
||
同时修正进程监控中的分类问题:此前取消后进程不响应,被强制结束时也抛出“超过时间限制”的异常。
|
||
|
||
现在,取消后不响应且达到退出宽限的进程仍返回 `cancelled`,网页任务映射为 `stopped`。如果强制结束导致没有完整结果文件,则明确返回空轨迹和空最终量,只保留最后收到的时间及进度计数,标记 `resultAvailable=false`、`statisticsComplete=false`;不读取可能写了一半的结果,也不伪造未知的最终统计。
|
||
|
||
真正超出进程时限且没有取消请求,仍报告进程超时;自行崩溃则报告退出码。已经退出的进程不会仅因监控线程尚在读取日志而被误判超时。
|
||
|
||
正常结果新增 `solverControl` 字段,网页后端保存在 `diagnostics.native.solverControl`。包含原因、操作、返回码、同时间返回次数、最长连续次数及结束时 CVODE 内部时间与步长;这些 CVODE 数值字段只对 BDF 有意义。非有限诊断数字使用 JSON `null`,受控失败输出保留最后有效的接受状态。
|
||
|
||
## 模型验收
|
||
|
||
共同设置:BDF/CVODE 7.4.0,时长 50 秒,最大步长 `1e30` 秒,`rtol=1e-8`,采样间隔 0.01 秒,状态绝对误差限沿用生成器。原输入文件未修改;当前 corrected 文件仅在运行配置中延长时长。
|
||
|
||
旧封装对照使用临时 native 目录,仅将 `cvode_solver.c` 恢复为本轮修改前的 HEAD 内容;组件、生成器和其他当前运行库一致。因此该对照不是拿不同历史组件实现作比较。
|
||
|
||
| 输入与入口 | 结果 | 最终仿真时间 | 纯求解墙钟时间 | 同时间返回总数 / 最长连续数 |
|
||
| --- | --- | ---: | ---: | ---: |
|
||
| 原触发模型,旧 CVODE 封装 | 提前失败 | 10.8 s | 4.805 s | 旧封装不记录 |
|
||
| 原触发模型,新封装独立进程 | 完成 | 50 s | 9.082 s | 12 / 6 |
|
||
| 原触发模型,新封装网页后端入口 | 完成 | 50 s | 9.006 s | 12 / 6 |
|
||
| 当前八路 corrected,新封装独立进程 | 完成 | 50 s | 6.792 s | 0 / 0 |
|
||
| 当前四路 corrected,新封装独立进程 | 完成 | 50 s | 1.011 s | 0 / 0 |
|
||
|
||
上述时间是各次单次验收测量,不是速度基准测试;尤其旧封装只完成 10.8 秒,不能据此计算新旧加速比。纯求解计时包括积分、采样和事件处理,不含编译、进程启动及结果投影/文件写入。
|
||
|
||
原触发输入为 `test/mql8-50s-20260913/model-50s.json`,SHA-256:
|
||
|
||
`7c48a380e465ba5ee8a15610586325d14ac36a8cda5b3caa02053a8efc27fe47`
|
||
|
||
当前输入为 `tests/data/test-mql-8-corrected.json`、`tests/data/test-mql-4-corrected.json`。原触发模型保留历史循环信号,以覆盖旧故障;它与当前 corrected 模型用途不同,不拿它直接判定与 Amesim 的一致性。
|
||
|
||
独立进程三组完整输出均为有限数,采样时间严格递增。按输出气体质量相加核对闭合系统总质量漂移:原触发模型 `4.53e-14 kg`,当前八路 `2.58e-14 kg`,当前四路 `2.75e-14 kg`。网页后端入口走 JSON→XML→模型生成→原生进程→原始序列传输,返回 5,010 个采样点,接受步统计与独立进程一致。
|
||
|
||
本轮没有重新启动 Amesim 或新增组件语义对照;摩擦及管路的既有 Amesim 数值基准回归已通过。本次验收目标是封装正确终止及不扰动已有轨迹。
|
||
|
||
## 自动测试与故障注入
|
||
|
||
| 测试模块 | 方法数 | 验证范围 |
|
||
| --- | ---: | --- |
|
||
| `tests.test_native_solver_control` | 4 | 实际 CVODE + 临时注入:连续 1,000 次同时间成功返回后完整轨迹不变;负错误码、倒退、非有限时间/状态;持续停滞实际超时与取消;普通超时及既有资源保护 |
|
||
| `tests.test_native_worker_control` | 3 | 实际子进程拒绝退出时的强制取消、普通/原始序列两种传输、网页 stopped 合同、超时与崩溃分类;半写入文件不作为结果 |
|
||
| `tests.test_native_codegen` | 8 | RK45/BDF 完成与最大步长、取消、独立进程、缓存及默认 C 后端 XML 接口等 |
|
||
| `tests.test_native_result_transport` | 6 | 实际 HTTP/流式结果合同、取消、序列与索引完整性等 |
|
||
| `tests.test_native_friction` | 1 | 既有 8 组 Amesim 冻结数值基准,共 10 次 RK45/BDF 积分 |
|
||
| 合计 | 22 | 全部通过 |
|
||
|
||
注入代码只存在于测试创建的临时 C 源码副本,生产运行库没有注入开关。连续 1,000 次恢复测试同时核对轨迹、接受步、事件和重启次数,防止以隐蔽重启或丢失事件的方式“跑完”。
|
||
|
||
实测平台为 Windows、GCC 8.1、SUNDIALS 7.4.0。本轮遇到既有 GCC 子进程启动偶发故障,重试构建后完成测试,没有改编译器路径或工具链配置。新增测试采用公共 SUNDIALS API、Python 可移植子进程接口和路径处理;已加入 Linux 原生 CI,Windows CI 的完整发现也会包含它们。本机没有执行 Linux 实测,Linux 通过情况待 CI 验证。
|
||
|
||
## 交付与复现
|
||
|
||
- 运行库修改:`native/runtime/cvode_solver.c`、`common.c`、`main.c` 和 `native/include/runtime.h`。
|
||
- 进程封装:`app/simulation/native_codegen/runner.py`。
|
||
- 新增自动测试:`tests/test_native_solver_control.py`、`tests/test_native_worker_control.py`。
|
||
- 本机验收脚本:`test/verify_solver_wrapper_20260914.py`、`test/verify_solver_wrapper_backend_20260914.py`。
|
||
- 模型结果与摘要:`test/solver-wrapper-20260914/`;前缀逐项对照见 `prefix-comparison.json`。
|
||
- 测试日志:`test/solver-wrapper-control-20260914.log`、`solver-wrapper-worker-20260914.log`、`solver-wrapper-regression-20260914.log`、`solver-wrapper-friction-20260914.log`、`solver-wrapper-backend-20260914.log`。
|
||
|
||
`test/` 是本机忽略目录,包含大体积运行结果,不进入 Git;正式自动测试与本报告进入源码交付范围。重启后端后,新请求会使用更新的 Python 封装;C 运行库内容变化参与构建缓存键,正常构建会自动生成对应的新程序,无需手动清空缓存。此次未提交或推送远端,也未处理工作区已有的 Amesim 文件变更。
|