Files
SystemSimulationApp/docs/other/循环UD00事件边界修复与独立验证-2026-09-17.md
T
ljz 7611f13208 修复循环信号与事件采样并接入 LSTP 接触定位,补充八路验证及复用实验
相较上一版 Jacobian 确定性复用更新,本次补齐事件边界一致性、结果两侧采样及接触事件定位;保留已有物性复用和组件力学公式。

- 统一 UD00 信号求值与下一事件查询的绝对时间边界,修复循环边界浮点舍入导致的阶段错位、重复或漏报,并覆盖零时长、多阶段及长周期场景。
- 引入原生输出语义 v2:保留规则网格真实时间,补充内部时间事件和状态事件的左邻及事件后采样,按保存时间、状态和离散模式重放结果。
- 两条代码生成路径均发出 LSTP 接触描述,默认定位间隙过零及非负力模式的力截断;仅在接受事件时更新防重复记录,增加 contactEvents 诊断计数。
- 补充 MASS/LSTP 独立事件实验、八路全曲线与驱动阶段配对评估,以及 Amesim 不连续点输出对照和力差定位报告;MASS 新增释放机制仍保留为独立实验。
- 保存局部 probe、context 访问与回退、shadow replay、R288 real skip/typed replay 及阀门数值尾部诊断工具和报告;未证明净收益的实验不启用为生产默认优化。
- 更新原生运行说明和元件建模规范,补充信号边界、输出语义、接触事件和实验依赖回归测试。

验证:五组专项回归共 34 项全部通过;37 个待提交 Python 文件语法检查通过;git diff --cached --check 通过。
2026-09-17 23:50:13 +08:00

102 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 循环 UD00 事件边界修复与独立验证
日期:2026-09-17。环境:Windows,项目 `.venv-win`,MinGW GCC 8.1.0,原生 C 运行库,CVODE 7.4.0 BDF / RK45。
## 1. 本次完成的第一步
修复 UD00 的信号求值与事件时间计算不一致。实现位于 [signal.c](../../native/components/modules/signal.c),新增回归位于 [test_native_signal_boundaries.py](../../tests/test_native_signal_boundaries.py)。
原来信号值用 `fmod(t - tstart, period)` 判断当前阶段,事件查询另用绝对时间计算。两种浮点运算可能落在边界两侧。例如阶段时长为 0.8 s 和 10 s 时,事件查询已经返回 44 s,而取模仍小于 0.8,信号在该时刻仍输出上一段的 `1e17`。
现在 `native_signal()` 与 `native_signal_break()` 共用 `signal_interval()`,共同确定当前阶段、阶段起点及下一边界。阶段内部仍按线性插值计算;周期结束统一使用下一周期的起点,避免同一事件出现两个相邻浮点时间。
本次没有修改八路工程 JSON、Amesim 文件、积分算法或结果采样协议。已有 UD00 的调用接口保持不变,两条原生生成路径均自动使用修复后的内核。
## 2. 修改后的事件定位及处理
### 时间边界定位
设信号开始时间为 `s`,各段累计时长为 `offset[i]`,周期为 `P`:普通阶段边界按 `(s + offset[i]) + k * P` 计算;周期终点统一采用下一周期起点 `s + (k + 1) * P`。这些表达式的浮点运算顺序也是合同的一部分,求值与事件查询共同使用同一函数。
当前周期由估算值开始,再依据绝对周期起点修正。阶段采用左闭右开区间:严格早于边界时仍在前段,恰好等于边界时进入后段;零时长阶段被跳过。查询返回严格晚于当前时间的最近事件,无后续事件或事件超过仿真终点时返回仿真终点。
### 运行库处理
1. 生成的 `model_next_break(t, end)` 汇总各信号的下一边界,选择最早一个。
2. BDF / RK45 在这个时间边界处分段积分。对仿真内部的边界,旧段积分终点是边界左侧相邻浮点数 `nextafter(boundary, -INFINITY)`。
3. 到达边界后保留连续状态,将时间推进到该边界,并重新启动积分。此时信号求值已切换到新段,导数计算使用新信号。
4. 结果输出仍按保存的实际时间和状态调用 `model_eval()` 重放。因为信号阶段是时间的纯函数,求解试算、回退和重放不会推进或污染一个全局阶段计数器。
第 1~4 项的运行库流程原来已经存在;本次让第 3、4 项使用的信号值与第 1 项报告的边界保持一致。连续状态如位置、速度不会仅因信号切换被重置;代数量如输出信号和力可以立即改变。机械碰撞、摩擦等由状态触发的事件仍使用各自的状态事件处理。
### 与 Amesim 对齐的范围
本次确立平台自身一致的右连续规则。Amesim 的 UD00 还维护当前阶段及 `TNEXT`,通过离散时间事件调用更新阶段;本次没有完整复制这套离散调用和结果保存协议。
尤其是普通浮点数 `32.4` 与本工况计算出的事件时间 `32.400000000000006` 不相等。前者仍属于边界前,测试专门保留这一点,没有使用全局 epsilon 提前切换。后续对照 Amesim 的原始采样时,应继续区分实际时间和事件前/后侧;此次不能直接推出此前八路模型在 32.4 s 的力尖峰差异已经消失。
## 3. 独立信号测试结果
测试网络只有一个循环 UD00:第一段 `1e17`,持续 0.8 s;第二段 `49000`,持续 10 s;从 0 s 开始循环。两种求解器均运行至 60 s,以 0.01 s 保存结果,共 6,001 个采样点。该模型没有物理动态状态,运行库使用一个内部常量状态执行真实求解及输出重放流程。
| 验证项 | 结果 |
| --- | --- |
| BDF 独立仿真 | 成功完成 60 s;6,001 点逐点符合冻结边界期望值 |
| RK45 独立仿真 | 成功完成 60 s;6,001 点逐点符合冻结边界期望值 |
| 两种求解器比较 | 全部采样值相同 |
| 事件分段 | 两种求解器均启动 12 次:初始启动一次,11 个内部时间事件后分别重启 |
| 44 s 信号 | 修改前 `1e17`;修改后 `49000`,符合后一段 |
| 54 s 信号 | 修改前 `49000`;修改后 `1e17`,符合下一周期 |
| 八段信号 1,000 周期 | 连续检查 8,000 个边界,无漏报、重复;边界左邻/本身/右邻均正确 |
| 非零起点与小周期 | 3 组工况,各 900 个边界通过 |
| 含零时长阶段的循环 | 连续 2,000 个边界通过;周期尾部零时长段不泄漏瞬时值 |
| 其他 | 斜坡、非循环终值、零总时长、单段锯齿、乱序重放通过 |
新增 7 个测试方法及相关 3 个既有测试全部通过(最终版本 10 / 10,39.033 s,包含积分启动次数断言)。修改前先运行关键边界测试,已复现 44 s、54 s 及多阶段循环边界失败,详见保留日志。独立测试用于确认信号内核和求解/重放接入,不替代八路气动与机械系统的精度验收。
测试过程中曾遇到测试子进程返回空输出及 GCC 进程启动重试。边界查询已合并为单次批量调用,数值判定保持严格;最终定向测试通过。相关中间日志保留在验证目录,不把进程空输出当成边界数值错误,也不将一次成功重跑解释为环境问题已修复。
### 完整回归的结果与限制
另执行 `python -m unittest discover -s tests -v`,共运行 458 项,耗时 1,534.126 s,报告 7 个错误、25 项跳过,不能记为整库回归通过。新增 7 项信号测试、既有 UD00 XML、阶跃重启、信号驱动力以及当前八路工程保存配置的完整运行测试均通过。
| 错误来源 | 实际失败点与处理 |
| --- | --- |
| 管道多变过程测试 | `model --init` 输出不能解析为 JSON,尚未进入数值断言;单独复核又遇到 GCC 预处理超时 |
| 冻结参考网络 38 | 发布编译缓存时目录重命名报 Windows `WinError 5` |
| 雅可比固定状态夹具 | 夹具保存的输入哈希与当前八路 JSON 不一致,类初始化被主动阻止 |
| 物性警告测试 | GCC 预处理超时;残留预处理进程退出后测试序列继续 |
| 工程输入合同的三项测试 | 默认 GBK 解码 UTF-8 JSON 失败;仅加 `-X utf8` 单独重跑,3 / 3 通过 |
夹具期望输入 SHA-256 为 `670977bef67e62d9c66e8af497bada208bd72a7301be45128d185d47282cf288`;实际工作树及 `HEAD` 中八路 JSON 的 SHA-256 均为 `60ed16c71b72be4ae71ca7980f4f8cdcdf6b4c4c4c8950863c2390960b71ced4`,确认这项输入不匹配不是本次修改引入。没有为使回归通过而更新参考夹具、修改其他内核或放宽断言。本次仅在 Windows 验证,未声明 Linux 验收通过。
验证文件:
- [修改前失败日志](../../test/ud00-boundaries-20260917/before-tests.log)
- [最终定向测试通过日志](../../test/ud00-boundaries-20260917/final-verified-tests.log)
- [边界及左右相邻浮点值](../../test/ud00-boundaries-20260917/after-probe/signal-boundaries.csv)
- [双求解器独立运行摘要](../../test/ud00-boundaries-20260917/isolated-summary.json)
- [6,001 点结果及期望值](../../test/ud00-boundaries-20260917/isolated-signal-samples.csv)
- [完整回归日志](../../test/ud00-boundaries-20260917/regression-tests.log)
- [UTF-8 编码复核日志](../../test/ud00-boundaries-20260917/utf8-recheck.log)
复现定向测试:
```powershell
.\.venv-win\Scripts\python.exe -m unittest tests.test_native_signal_boundaries tests.test_amesim_ud00_xml tests.test_amesim_semantics.NativeComponentSemanticTests.test_signal_endpoints_and_exponential_contact_law_in_c -v
```
## 4. 新注册类似信号组件时需要什么
| 情况 | 事件方面的工作 |
| --- | --- |
| 新增现有 UD00 实例或调整其参数 | 无需额外注册,直接使用已有求值和事件内核 |
| 新型号复用 UD00 分段语义 | 接入已有 `native_signal` 和 `native_signal_break`,并把后者汇入 `model_next_break` |
| 新阶跃、脉冲、分段周期信号 | 除输出公式,还要提供下一时间事件;与输出共享边界计算和相等判定 |
| 无分段的光滑信号,如正弦 | 通常无需专门时间事件 |
| 由压力、位置等状态触发的切换 | 需要相应状态事件定位与切换机制,时间事件表不足以实现 |
仅在 `library.py` 注册类型,或填写 `PORTS` / `PARAMETERS` / `RESULT_VARIABLES`,不会自动实现这些事件。默认在 `native_codegen/extended.py` 接入;若新型号也进入 `compiler.py` 紧凑路径,两条路径都要覆盖。新增 C 函数时同步公共声明与模块导出/依赖。
新组件通常只负责描述事件时刻及切换语义,由运行库统一执行积分分段与重启。相关要求已写入[元件开发规范第 12.1 节](../standard/component-model-authoring-spec-v1.md#121-分段信号的时间事件)。