相较上一版 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 通过。
102 lines
8.9 KiB
Markdown
102 lines
8.9 KiB
Markdown
# 循环 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-分段信号的时间事件)。
|