From b6c22a54f8dc8d27efe2c561c7c47934d5c20b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A2=E4=BA=AC=E6=B3=BD?= Date: Sat, 12 Sep 2026 15:49:09 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B5=8B=E8=AF=95=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E8=B7=AF=E5=BE=84=E5=B9=B6=E5=AE=8C=E5=96=84=E5=9B=9B?= =?UTF-8?q?=E8=B7=AF=E5=85=AB=E8=B7=AF=E5=9B=9E=E5=BD=92=E4=B8=8E=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 按文件哈希区分当前八路工程和历史 Amesim 基准,修复测试与清单中的旧路径。 - 四路审计从 JSON 按需生成 XML,八路新增只核对模式,并显式使用 UTF-8 与 LF。 - 将缺失高刚度夹具的测试替换为当前八路完整 BDF 运行,明确未恢复原 RK45 专项覆盖。 - 更新前端大型工程测试、历史活动回放入口、数据说明、现行规范和相关报告。 验证:全量后台 374 项通过、1 项条件跳过;前端 11 项通过、历史独立服务用例 1 项跳过;四路八路 AME 审计通过。 环境、缓存和运行产物保持忽略;本次未实施工程 JSON 字段精简。 --- .gitattributes | 3 - app/simulation/paths.py | 10 ++- .../2026-09-12-project-contract-acceptance.md | 6 +- .../2026-09-12-test-input-path-refresh.md | 77 +++++++++++++++++++ .../Amesim与C计算顺序差异核查-2026-09-10.md | 2 + docs/other/C计算依赖排序验证-2026-09-10.md | 2 + docs/other/Python数值实现退役记录.md | 2 + .../amesim-component-migration-matrix.md | 2 +- docs/other/amesim-helium-peng-robinson.md | 6 +- ...test-mql-4模型复核与输入修正-2026-09-11.md | 2 + ...l-8当前AME归档与完整曲线核查-2026-09-11.md | 2 + .../test-mql-8模型对照与修正-2026-09-11.md | 6 +- .../牛顿管流求根与四路网页验证-2026-09-11.md | 4 +- docs/standard/README.md | 4 +- docs/standard/optimization-benchmark-model.md | 11 ++- docs/update-log/更新日志-2026-08-17.md | 2 + docs/update-log/更新日志-2026-08-18.md | 2 + docs/update-log/更新日志-2026-09-12.md | 7 ++ frontend/tests/e2e/fit-view.spec.ts | 10 ++- .../tests/e2e/simulation-live-mql8.spec.ts | 17 +++- native/README.md | 2 +- .../baselines/simulation/test_mql_8/README.md | 9 +++ .../simulation/test_mql_8/manifest.json | 2 +- tests/data/AmesimModels/test_mql/README.md | 2 +- tests/data/README.md | 34 ++++---- tests/model_audit/audit_mql4_model.py | 55 ++++++++----- tests/test_amesim_results.py | 5 +- tests/test_current_mql8_simulation.py | 59 ++++++++++++++ tests/test_high_stiffness_explicit_rk45.py | 74 ------------------ tests/test_regression_fixture_line_endings.py | 4 +- tests/test_test_mql_ame_contract.py | 9 ++- tools/audit_test_mql8_model.py | 39 +++++++--- 32 files changed, 318 insertions(+), 153 deletions(-) create mode 100644 docs/other/2026-09-12-test-input-path-refresh.md create mode 100644 tests/baselines/simulation/test_mql_8/README.md create mode 100644 tests/test_current_mql8_simulation.py delete mode 100644 tests/test_high_stiffness_explicit_rk45.py diff --git a/.gitattributes b/.gitattributes index bb0bedb..2392688 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,8 +4,6 @@ # Regression manifests hash these files as raw bytes. Keep their checkout # representation identical on Windows and Linux so hashes remain portable. -tests/fixtures/legacy/test-mql-8.xml text eol=lf -tests/fixtures/legacy/test-mql-8.json text eol=lf tests/baselines/simulation/test_mql_full_branches/sources/test_mql-full-branches-01-04.xml text eol=lf tests/baselines/simulation/**/*.json text eol=lf @@ -14,4 +12,3 @@ tests/baselines/simulation/**/sources/* text eol=lf # Browser projects and relocated native references. tests/data/*.json text eol=lf tests/baselines/native/*.json text eol=lf -tests/fixtures/amesim/*.xml text eol=lf diff --git a/app/simulation/paths.py b/app/simulation/paths.py index ba8d444..e4f9eed 100644 --- a/app/simulation/paths.py +++ b/app/simulation/paths.py @@ -16,7 +16,15 @@ DATA_DIR = ( SIMULATION_RUNS_DIR = DATA_DIR / "simulation-runs" SIMULATION_BASELINES_DIR = PROJECT_ROOT / "tests" / "baselines" -AMESIM_TEST_MQL_ARCHIVE_PATH = PROJECT_ROOT / "AmesimModels" / "test_mql.ame" +# Current browser/solver input and the matching AME drawing (September 2026). +CURRENT_MQL8_PROJECT_PATH = PROJECT_ROOT / "tests" / "data" / "test-mql-8-corrected.json" +CURRENT_MQL8_AME_PATH = PROJECT_ROOT / "tests" / "data" / "test_mql.ame" + +# Frozen August reference: its AME bytes differ from the current drawing. +HISTORICAL_MQL8_AME_PATH = PROJECT_ROOT / "tests" / "data" / "AmesimModels" / "test_mql.ame" +HISTORICAL_MQL8_PROJECT_PATH = SIMULATION_BASELINES_DIR / "simulation" / "test_mql_8" / "sources" / "test-mql-8.json" +HISTORICAL_MQL8_XML_PATH = HISTORICAL_MQL8_PROJECT_PATH.with_suffix(".xml") +AMESIM_TEST_MQL_ARCHIVE_PATH = HISTORICAL_MQL8_AME_PATH MODELICA_TESTMODEL_RESULT_PATH = ( PROJECT_ROOT / "ModelicaModels" / "Simulation" / "Testmodel_res.csv" ) diff --git a/docs/other/2026-09-12-project-contract-acceptance.md b/docs/other/2026-09-12-project-contract-acceptance.md index 79cf8b8..fd90ef0 100644 --- a/docs/other/2026-09-12-project-contract-acceptance.md +++ b/docs/other/2026-09-12-project-contract-acceptance.md @@ -1,6 +1,6 @@ # 工程版本警告与统一参数输入验收 -报告版本:1.0.0;日期:2026-09-12;基线:`22579e5` 加本次工作区改动。 +报告版本:1.0.1;日期:2026-09-12;基线:`22579e5` 加本次工作区改动。 本次落实旧组件版本警告和参数输入语义统一。验收案例为 `tests/data/test-mql-8-corrected.json`,157 个组件、178 条连接、1092 个已保存参数。测试没有改动四路/八路基准文件、C 方程、雅可比算法或求解精度。 @@ -38,6 +38,10 @@ 全量检查的缺失项为两组测试仍指向不存在的 `AmesimModels/test_mql.ame`,以及缺少 `tests/fixtures/high_stiffness_explicit_rk45.xml`。这些测试/夹具不在本次实现改动内,没有伪造数据或改成通过。原始日志保留。没有重新执行 Amesim 对照。Linux 实测完成;Windows 使用通用 Python/浏览器实现,没有新增平台专用调用,但本次没有 Windows 实机验收。 +## 缺失输入后续修复 / 2026-09-12 + +用户确认移动/删除了夹具后,已按实际文件哈希修复历史 AME 路径;高刚度 XML 未找到,按用户授权改为当前八路完整 BDF 测试。全量后台重新执行 375 项,374 通过、1 条原条件跳过,0 失败/错误。原 RK45 专项未恢复,不将本次替代用例算成原覆盖。另修复前端/审计对已删除 JSON/XML 的依赖,画布复测 11 项通过。上述表格保留前次原始执行结果;文件映射、报告依据及本次测试日志见 [路径修复报告](2026-09-12-test-input-path-refresh.md)。 + ## 版本检查的独立耗时 在 Chromium 内执行正式 `projectCompatibility.ts` 中的目录索引、版本比较及提示文字生成函数。目录仅建立一次内存 Map,通过 WeakMap 按目录数组复用;每次扫描节点,无逐组件网络请求或文件扫描。预热后每组 200 个样本、每样本 10 次调用;时间分辨率约 0.1 ms,通过批量计时减小量化误差。首次索引建立约 0.10 ms。 diff --git a/docs/other/2026-09-12-test-input-path-refresh.md b/docs/other/2026-09-12-test-input-path-refresh.md new file mode 100644 index 0000000..8992f46 --- /dev/null +++ b/docs/other/2026-09-12-test-input-path-refresh.md @@ -0,0 +1,77 @@ +# 测试输入路径修复与八路替代验证 + +报告版本:1.0.0;日期:2026-09-12;基线:`44b6ea7` 加本次改动。 + +用户确认曾移动 AME/JSON、删除 fixture,并要求找回实际路径;找不到专项 XML 时先改用当前八路系统。此次修复了测试入口和文档引用,四路/八路工程及 AME 内容均未修改。全量后台检查恢复为 OK;原高刚度 RK45 专项没有被伪装成已恢复。 + +## 当前文件与身份 + +后续浏览器建模和优化仍用 [八路 corrected JSON](../../tests/data/test-mql-8-corrected.json) 与 [当前八路 AME](../../tests/data/test_mql.ame)。备用为 [四路 corrected JSON](../../tests/data/test-mql-4-corrected.json) 与 [四路 AME](../../tests/data/test_mql_4.ame)。方便后续删改的目录索引见 [tests/data/README.md](../../tests/data/README.md)。 + +| 文件(仓库相对路径) | 字节数 | SHA-256 | +| --- | ---: | --- | +| `tests/data/test-mql-8-corrected.json` | 271259 | `670977bef67e62d9c66e8af497bada208bd72a7301be45128d185d47282cf288` | +| `tests/data/test_mql.ame` | 21698560 | `1ff0ea4284b9248260eeceb8b27cd0bc14dbccb43554ab8c0b49900d0b944c3d` | +| `tests/data/test-mql-4-corrected.json` | 457639 | `542494eed8e235cb36eae5f80a0776b812e547d9215fa370949b0882834ebc8b` | +| `tests/data/test_mql_4.ame` | 13578240 | `99a071896671d634d651a7b21f487662dd7c122af8fd3d60ead404a5afa53ed4` | +| `tests/data/AmesimModels/test_mql.ame` | 21708800 | `cbc3aadd4569a49b3a63e5d66d4143ec16126c0f950df73fb637e07673c20fbb` | +| `tests/baselines/simulation/test_mql_8/sources/test-mql-8.json` | 272592 | `b44bf540ccd1c293fe2af2b9b9052b540abf83961ad955a0f6a4ab40fbe0bb18` | +| `tests/baselines/simulation/test_mql_8/sources/test-mql-8.xml` | 104110 | `0a2d9331df9eb5974daec25a61c1238ba32b1742d933ffc8b16ce316c5627b0b` | + +历史 AME 原路径为 `AmesimModels/test_mql.ame`,现为 `tests/data/AmesimModels/test_mql.ame`,其身份与原测试要求完全一致。对应历史 JSON/XML 已在 `tests/baselines/simulation/test_mql_8/sources/`。本次只修复 manifest 中的 AME 路径,没有改动历史哈希、golden、采样曲线或耗时。 + +被删除的 legacy 八路 JSON 的历史 SHA 为 `4f1bf698519ba21429a11a83653adc7e52191f2d2b51fca943ee0b84f62feebd`;现存 sources JSON 是 `b44bf540…`。两者不是同一字节文件。普通画布测试因此转到当前 corrected 工程,历史基准读取则保持自己的 sources/AME 配对。 + +## 测试及报告来源 + +| 测试 / 工具 | 原报告或规范中的要求、用途 | 本次处理 | +| --- | --- | --- | +| `tests/test_test_mql_ame_contract.py` | [2026-08-17 日志 22:35](../update-log/更新日志-2026-08-17.md):锁定 XML/JSON 双哈希和配置;[2026-08-18 日志 07:58、23:10](../update-log/更新日志-2026-08-18.md):独立 AME 防漂移、单位/参数/精确端口、manifest 来源绑定 | 指向历史 AME 和历史 sources,保留原物理、哈希和结果断言 | +| `tests/test_amesim_results.py` | [2026-08-18 日志](../update-log/更新日志-2026-08-18.md):AME 二进制结果及变量、时间轴读取,支撑外部物理基准 | 只修复历史 AME 路径,已保存结果继续原样核对 | +| 八路/四路 AME 审计脚本 | [八路模型修正](test-mql-8模型对照与修正-2026-09-11.md)、[四路模型复核](test-mql-4模型复核与输入修正-2026-09-11.md)、[管流求根与四路网页验证](牛顿管流求根与四路网页验证-2026-09-11.md):逐组件、参数和参考端口核对 | 当前 JSON 作为工程输入,AME 图纸独立作为参照;不再依赖已删除 legacy JSON 或独立四路 XML | +| `frontend/tests/e2e/fit-view.spec.ts` | [2026-08-17 日志 20:01、22:48](../update-log/更新日志-2026-08-17.md):大型工程导入、视口、端点、结果系统图线桥 | 改用当前 corrected 八路;按其 P4 布局选取实际交叉边;兼容新版本提示,仍检查展开日志里的加载/恢复成功 | +| `frontend/tests/e2e/simulation-live-mql8.spec.ts` | [八路修正报告的测试范围说明](test-mql-8模型对照与修正-2026-09-11.md):这是 August Python 内部活动心跳流程,不是当前原生验收入口 | 改为读取历史 sources JSON;新证据写入忽略目录,避免覆盖历史报告。普通 4173 配置明确跳过;独立 live 配置依然保留,未重新验收旧遥测流程 | +| 原 `tests/test_high_stiffness_explicit_rk45.py` | [前次输入合同验收](2026-09-12-project-contract-acceptance.md) 披露该夹具缺失;旧测试自身要求刚度 XML 的短时 RK45 事件/重启。未找到另一个单独规定该文件的现行报告;历史运行源码清单出现其测试名不等于恢复了夹具 | XML 未找到,按本次用户授权改为 `tests/test_current_mql8_simulation.py`,用当前八路完整 BDF 运行验证 | +| 后续优化案例 | [现行优化验证约定](../standard/optimization-benchmark-model.md) | 维持 corrected 八路优先;四路只作明确的退路,历史冻结基准不能混入当前性能比较 | + +前次所谓“三处旧夹具缺失”是三个报错位置,并不代表三个不同文件。两处依赖同一份被移动的历史 AME,另一处依赖已删除的高刚度 XML。补查还发现前端/审计依赖的 legacy JSON 和独立四路 XML 已删除,一并消除了活动路径依赖。 + +相关历史日志、模型报告、物性资料路径、native README、数据索引和现行规范已加当前位置或迁移说明。历史 `goldens/`、`runs/`、已归档审计 JSON 中的旧路径和源码状态保留为当时证据,当前位置由 manifest、数据索引和新增历史基准 README 说明。 + +## 实现及验收边界 + +- 共享路径集中在 `app/simulation/paths.py`,区分当前八路与 August 历史八路;前端用相对模块 URL 定位工程;审计脚本显式使用 UTF-8 读写文本,并固定写出 LF,避免 Windows 系统默认编码影响中文 JSON。 +- 八路 `--check` 逐项检查 AME 的 157 个元件、1092 个公开参数、178 条连接以及时间配置,参数/端口差异均为 0。证据写入 `test/model-audit/mql8/`,不改工程文件。 +- 四路 `--check` 从 JSON 经正式 CLI 输入转换生成内存 XML,再对照独立 AME 图纸,81 个元件、582 个参数、90 条连接通过;原 XML 缺失不再阻塞审计。无需另保存一份需同步维护的 fixture XML。 +- 新八路运行测试沿正式 JSON → SI XML → 原生 C 路径执行完整 0~10 s,使用工程保存的 BDF、step 0.01、max_step 1e30 和后端 rtol 1e-8。检查终态、事件/重启、时间单调、规则采样点完整性、各列长度和所有值有限,并检查源文件未变。这不等于原 RK45 高刚度覆盖,也不单凭运行成功判定 Amesim 曲线一致。 +- 更新 `.gitattributes` 与配套测试,使用现有历史 sources 和当前 JSON 的 LF 规则,避免 Windows checkout 改变受哈希保护的文本字节。 + +| 验证 | 结果 | +| --- | --- | +| AME 读取、历史契约、当前八路定向运行 | 11 项通过,4.988 s | +| 全量后台 `unittest discover` | 375 项:374 通过、1 条原条件跳过,57.360 s;0 失败/错误 | +| 跳过的后台项目 | Linux 完整发布锁版本实机对照,按原有 `SYSTEM_SIMULATION_VERIFY_LOCKED_ENV=1` 开关控制;不是夹具缺失 | +| 当前四路/八路 AME 审计 | 均通过;另外在临时副本故意修改参数、端口,四个错误输入均被拒绝 | +| 前端定向复测 | 11 项通过,31.4 s;历史真实后端活动回放 1 项因普通配置跳过 | +| 文件身份 | 上表 7 个输入均逐字节等于本次改动前 HEAD,模型未被审计覆盖 | + +前端首次扩展检查中,两项仍期待加载信息占据最后一条摘要;新合同会追加旧工程单位提示。现在同时检查最新提示和展开日志中的加载/恢复成功。另一个线桥断言使用旧 P4 布局中已经不相交的边,已换成 corrected 工程实际相交的 P4 #5 边,仍要求生成线桥。第一次还误用普通无后端配置运行了历史 live 测试;独立服务要求现已显式标记,不能将该跳过记为真实浏览器求解通过。 + +本次没有重跑 Amesim 官方程序,没有新增八路网页求解耗时结论;前端是模拟 API 的画布回归,实际八路完整求解由后台测试完成。全量后台包含现有四路冻结 Amesim 曲线比较。Linux 实测完成,Windows 路径/换行兼容保持,但没有 Windows 实机环境验收。 + +## 复现与产物 + +从仓库根执行(Windows 将解释器替换为 `.venv-win\Scripts\python.exe`): + +```bash +.venv/bin/python tools/audit_test_mql8_model.py --check +.venv/bin/python tests/model_audit/audit_mql4_model.py --check +.venv/bin/python -m unittest tests.test_current_mql8_simulation -v +.venv/bin/python -m unittest discover -s tests -v +``` + +在 `frontend/` 中用项目 Node 环境执行 `npm run test:e2e -- fit-view.spec.ts simulation-live-mql8.spec.ts`;Linux 浏览器依赖沿用 `.venv/native/browser-libs/`。需要当前真实网页/HTTP/C 验收时,使用 `tests/manual/browser_native_simulation.mjs` 和 corrected JSON;不以历史 Python 心跳脚本代替。 + +本次原始日志和身份摘要位于被 Git 忽略的 `test/model-path-refresh-20260912/`:`summary.json`、`targeted.log`、`backend-all.log`、`audit-mql4.log`、`audit-mql8.log`、`audit-negative.log`、`frontend.log`(首次诊断)、`frontend-final.log`(最终复测)。临时 XML/AME 审计证据位于 `test/model-audit/`。环境、缓存、测试产物均未纳入 Git;本次未提交或推送。 + +临时前端服务已退出;收尾检查 4173、5173、8000 均无监听。`git diff --check` 与本轮新增文档链接检查通过。 diff --git a/docs/other/Amesim与C计算顺序差异核查-2026-09-10.md b/docs/other/Amesim与C计算顺序差异核查-2026-09-10.md index b027362..610f233 100644 --- a/docs/other/Amesim与C计算顺序差异核查-2026-09-10.md +++ b/docs/other/Amesim与C计算顺序差异核查-2026-09-10.md @@ -1,5 +1,7 @@ # Amesim 与 C 计算顺序差异核查 +> 路径复核 / 2026-09-12:本文引用的旧 `tests/data/test-mql-8.json` 已删除,保留当时实验描述;后续实验输入统一为 `tests/data/test-mql-8-corrected.json`,对应 `tests/data/test_mql.ame`。 详见 [路径修复记录](2026-09-12-test-input-path-refresh.md)。 + 日期:2026-09-10。分支:`system-optimization`;核查时提交:`0dcb465`。 本次对照当前 `tests/data/test-mql-8.json`、Amesim 2404 的生成代码和本机组件源码,并在 `test/amesim-order-study/` 中进行独立实验。正式模型、C 生成器、数值内核、积分器和雅可比策略均未修改。 diff --git a/docs/other/C计算依赖排序验证-2026-09-10.md b/docs/other/C计算依赖排序验证-2026-09-10.md index a3d05da..b71033a 100644 --- a/docs/other/C计算依赖排序验证-2026-09-10.md +++ b/docs/other/C计算依赖排序验证-2026-09-10.md @@ -1,5 +1,7 @@ # C 计算依赖排序验证(2026-09-10) +> 路径复核 / 2026-09-12:本文引用的旧 `tests/data/test-mql-8.json` 已删除,保留当时实验与耗时;后续验证使用 `tests/data/test-mql-8-corrected.json`,不得把换了输入后的耗时直接与本文计算优化比例。 详见 [路径修复记录](2026-09-12-test-input-path-refresh.md)。 + 在 `system-optimization` 分支完成已知来源识别、依赖排序和局部循环划分,接入默认 C 编译入口使用的扩展生成路径。未修改管路内部求根、积分器、雅可比策略或用户模型接线。 ## 本轮实现 diff --git a/docs/other/Python数值实现退役记录.md b/docs/other/Python数值实现退役记录.md index 2f9d367..e4dda42 100644 --- a/docs/other/Python数值实现退役记录.md +++ b/docs/other/Python数值实现退役记录.md @@ -1,5 +1,7 @@ # Python 数值实现退役记录(2026-09-10) +> 路径复核 / 2026-09-12:本文当时使用的 `tests/data/test-mql-8.json` 已删除;当前活动输入为 `tests/data/test-mql-8-corrected.json`,按其实际保存配置运行。August 冻结基准仍在 `tests/baselines/simulation/test_mql_8/sources/`,对应 AME 为 `tests/data/AmesimModels/test_mql.ame`。 详见 [路径修复记录](2026-09-12-test-input-path-refresh.md)。 + 网页与 CLI 统一采用 C 数值内核。删除旧 Python 积分器及模型数值实现;Python 保留输入校验、模型元数据、C 生成、构建缓存、任务与结果管理。 ## 删除与保留 diff --git a/docs/other/amesim-component-migration-matrix.md b/docs/other/amesim-component-migration-matrix.md index d29e4cd..2c3213c 100644 --- a/docs/other/amesim-component-migration-matrix.md +++ b/docs/other/amesim-component-migration-matrix.md @@ -2,7 +2,7 @@ 状态:22 个模型已在临时 AMESim 库完成第一版公开,持续校准中 -适用模型:`AmesimModels/test_mql.ame` / `app.simulation.examples.test_mql.system` +适用模型:`tests/data/AmesimModels/test_mql.ame` / `app.simulation.examples.test_mql.system` 配套规范:[`component-model-authoring-spec-v1.md`](../standard/component-model-authoring-spec-v1.md) diff --git a/docs/other/amesim-helium-peng-robinson.md b/docs/other/amesim-helium-peng-robinson.md index ae46029..aadc6c1 100644 --- a/docs/other/amesim-helium-peng-robinson.md +++ b/docs/other/amesim-helium-peng-robinson.md @@ -4,11 +4,11 @@ 仓库中已经包含该模型所需的说明和算例数据: -- `AmesimModels/help/pneumatic_library_manual/03_gas_properties.pdf` 第 3.3 节 +- `tests/data/AmesimModels/help/pneumatic_library_manual/03_gas_properties.pdf` 第 3.3 节 给出 AMESim Pneumatic Library 的气体物性层级和 Peng-Robinson 状态方程。 -- `AmesimModels/help/pneumatic_library_manual/lib_pneumatic.pdf` 第 87--88 页 +- `tests/data/AmesimModels/help/pneumatic_library_manual/lib_pneumatic.pdf` 第 87--88 页 给出 293.15 K、1 barA 下的氦气参考物性。 -- `AmesimModels/test_mql.ame` 中的 `PNGD00` 实例明确选择 +- `tests/data/AmesimModels/test_mql.ame` 中的 `PNGD00` 实例明确选择 `fluidType=12`(helium)和 `eosType=6`(Peng-Robinson)。 - `app/simulation/core/peng_robinson.py` 已实现纯物质 Peng-Robinson 方程, 并定义了 `HELIUM_PR`。 diff --git a/docs/other/test-mql-4模型复核与输入修正-2026-09-11.md b/docs/other/test-mql-4模型复核与输入修正-2026-09-11.md index 30cd050..f831f69 100644 --- a/docs/other/test-mql-4模型复核与输入修正-2026-09-11.md +++ b/docs/other/test-mql-4模型复核与输入修正-2026-09-11.md @@ -1,5 +1,7 @@ # test-mql-4 模型复核与输入修正(2026-09-11) +> 路径复核 / 2026-09-12:下文独立四路 XML 已删除。当前工程仍为 `tests/data/test-mql-4-corrected.json`;审计脚本从该 JSON 临时生成 XML,并在 `--check` 中独立核对 AME 参数与端口。历史 XML 哈希和生成记录保留为当时证据。 详见 [路径修复记录](2026-09-12-test-input-path-refresh.md)。 + 目录整理补充:`tests/data` 的 JSON/XML 仅保留浏览器可导入的四路、八路 corrected JSON;参考曲线、旧工程和后端 XML 已迁到 `tests/baselines` / `tests/fixtures`,本文当前路径同步更新,文件内容及来源哈希不变。详见 [数据目录说明](../../tests/data/README.md)。 本次从 `tests/data/test_mql_4.ame` 的原始 `.cir` 图纸重新核对元件、参数和端口连接,生成了可导入网页的 `tests/data/test-mql-4-corrected.json`,并同步 `tests/fixtures/amesim/test-mql-4-corrected.xml`。两份输入均有 81 个元件、90 条连接、582 个公开参数,对应 64 个动态状态;生成结果的每个公开参数和连接端口均已按同一元件映射与 AME 图纸逐项比较。生成的 XML 通过后端语法和语义校验,无错误、无警告。 diff --git a/docs/other/test-mql-8当前AME归档与完整曲线核查-2026-09-11.md b/docs/other/test-mql-8当前AME归档与完整曲线核查-2026-09-11.md index 0c85e5f..2c43493 100644 --- a/docs/other/test-mql-8当前AME归档与完整曲线核查-2026-09-11.md +++ b/docs/other/test-mql-8当前AME归档与完整曲线核查-2026-09-11.md @@ -1,5 +1,7 @@ # test-mql-8 当前 AME 归档与完整曲线核查 +> 路径复核 / 2026-09-12:本报告当前 AME/JSON 路径仍有效;文中 SHA 开头 `cbc3aadd` 的另一个历史 AME 位于 `tests/data/AmesimModels/test_mql.ame`,只与 August 冻结基准配套。 详见 [路径修复记录](2026-09-12-test-input-path-refresh.md)。 + 2026-09-11。只读解析当前输入、AME 归档和已完成的原生运行,不重新求解、编译或修改模型。 **当前八路归档的数据身份、公开参数和初始状态相互对应,可以作为已保存曲线的观察参考。完整曲线仍有差异,不能判为一致;Amesim 没有可信 CPU/墙钟耗时,速度比较明确 `skip`。** 归档名义容差为 1e-7,本轮原生为 1e-8,亦不构成同精度的性能比较。 diff --git a/docs/other/test-mql-8模型对照与修正-2026-09-11.md b/docs/other/test-mql-8模型对照与修正-2026-09-11.md index 8f1bef0..c697017 100644 --- a/docs/other/test-mql-8模型对照与修正-2026-09-11.md +++ b/docs/other/test-mql-8模型对照与修正-2026-09-11.md @@ -1,8 +1,10 @@ # test-mql-8 模型对照与修正(2026-09-11) -目录整理补充:`tests/data` 的 JSON/XML 仅保留浏览器可导入的四路、八路 corrected JSON;参考曲线、旧工程和后端 XML 已迁到 `tests/baselines` / `tests/fixtures`,本文当前路径同步更新,文件内容及来源哈希不变。详见 [数据目录说明](../../tests/data/README.md)。 +> 路径复核 / 2026-09-12:下文修正前 legacy JSON 已删除;现存历史基准 JSON 的哈希不同,不能代替那份原输入。当前审计改读 `tests/data/test-mql-8-corrected.json`,使用 `--check` 与 `tests/data/test_mql.ame` 复核而不覆盖工程。 详见 [路径修复记录](2026-09-12-test-input-path-refresh.md)。 -已依据 `tests/data/test_mql.ame` 的真实图纸生成 `tests/data/test-mql-8-corrected.json`,保留原 `test-mql-8.json` 供审计。修正版包含参数、逐端口连接和仿真最大步长的对齐。静态校验通过。rtol=1e-7 的首次求解在 120 s 限额内推进到 5.144858167 s 后超时;后续按用户确认的全局 rtol=1e-8,以同一执行模型和内核完成了完整 10 s 单次运行。本轮未运行 Amesim 官方程序,也未进行八路 Amesim 曲线对比。 +2026-09-11 目录整理记录(后续位置以上方复核说明为准):`tests/data` 的 JSON/XML 仅保留浏览器可导入的四路、八路 corrected JSON;参考曲线、旧工程和后端 XML 已迁到 `tests/baselines` / `tests/fixtures`,本文当前路径同步更新,文件内容及来源哈希不变。详见 [数据目录说明](../../tests/data/README.md)。 + +已依据 `tests/data/test_mql.ame` 的真实图纸生成 `tests/data/test-mql-8-corrected.json`,当时保留原 `test-mql-8.json` 供审计(现已删除)。修正版包含参数、逐端口连接和仿真最大步长的对齐。静态校验通过。rtol=1e-7 的首次求解在 120 s 限额内推进到 5.144858167 s 后超时;后续按用户确认的全局 rtol=1e-8,以同一执行模型和内核完成了完整 10 s 单次运行。本轮未运行 Amesim 官方程序,也未进行八路 Amesim 曲线对比。 ## 来源与核查方法 diff --git a/docs/other/牛顿管流求根与四路网页验证-2026-09-11.md b/docs/other/牛顿管流求根与四路网页验证-2026-09-11.md index 633fc12..00e3dc4 100644 --- a/docs/other/牛顿管流求根与四路网页验证-2026-09-11.md +++ b/docs/other/牛顿管流求根与四路网页验证-2026-09-11.md @@ -1,5 +1,7 @@ # 牛顿管流求根、AME 输入复核与网页验证(2026-09-11) +> 路径复核 / 2026-09-12:当前交付工程仍为四路/八路 corrected JSON;独立四路 XML 夹具已删除,改为从 JSON 按需导出。下文速度和曲线记录是原验收结果。 详见 [路径修复记录](2026-09-12-test-input-path-refresh.md)。 + 目录整理补充:`tests/data` 的 JSON/XML 仅保留浏览器可导入的四路、八路 corrected JSON;参考曲线、旧工程和后端 XML 已迁到 `tests/baselines` / `tests/fixtures`,本文当前路径同步更新,文件内容及来源哈希不变。详见 [数据目录说明](../../tests/data/README.md)。 本轮在 `5d5a2e1843bc10cf7764c77cc2c1182c21a68df5` 的物性复用实现上,补全局部管流牛顿求根的区间保护和二分回退;按用户确认,将网页/API 与 Python CLI 默认相对误差限改为 `1e-8`。四路输入先按 AME 图纸核对,再做实际原生计算和浏览器验证。环境依赖、生成程序和运行数据均留在 Git 忽略目录,没有提交或推送 Git。 @@ -26,7 +28,7 @@ PNL00R 原有直接层流分支继续保留。物性状态复用、物理方程 ## 先核对模型,再比较数值 -交付输入为 [四路 JSON](../../tests/data/test-mql-4-corrected.json)、[同步 XML](../../tests/fixtures/amesim/test-mql-4-corrected.xml) 和 [八路修正版 JSON](../../tests/data/test-mql-8-corrected.json)。完整差异分别见 [四路复核](test-mql-4模型复核与输入修正-2026-09-11.md) 与 [八路修正](test-mql-8模型对照与修正-2026-09-11.md)。 +交付输入为 [四路 JSON](../../tests/data/test-mql-4-corrected.json)、当时同步的 XML(现从四路 JSON 按需导出,见 [当前输入说明](../../tests/data/README.md)) 和 [八路修正版 JSON](../../tests/data/test-mql-8-corrected.json)。完整差异分别见 [四路复核](test-mql-4模型复核与输入修正-2026-09-11.md) 与 [八路修正](test-mql-8模型对照与修正-2026-09-11.md)。 四路 AME 为 `tests/data/test_mql_4.ame`。其 `.cir` 包含 63 个组件和 18 条建模管线,展开为 81 个公开元件、90 条连接、582 项公开参数、64 个动态状态。修正遗留管阻 `amesim_pnl00r_4.rr` 为 `1e-5`,并将 25 条涉及等价端口编号/阻力方向的连线与 AME 编号逐项对齐。 diff --git a/docs/standard/README.md b/docs/standard/README.md index 3bbaf0e..e3b2e9b 100644 --- a/docs/standard/README.md +++ b/docs/standard/README.md @@ -1,6 +1,6 @@ # 现行规范索引 -索引版本:1.1.0;整理/复核日期:2026-09-12。 +索引版本:1.1.1;整理/复核日期:2026-09-12。 新增组件从[注册流程](component-registration-workflow-v1.md)开始,再按涉及的能力读取专项规范。[注册示例](component-registration-example-v1.md)包含实际失败阶段、修订对照和复现方法。本目录保存现行版本,修订时更新正文版本、日期及变更说明;历史实现报告仍在 `docs/other/`,不覆盖其历史结论。 @@ -22,7 +22,7 @@ | 文件 | 文档版本 / 核对日期 | 本轮调整 | | --- | --- | --- | | [System XML v3](system-xml-v3.md) | 1.1.0 / 2026-09-12 | 求解方法、示例可执行边界、参数输入及连接检查层次 | -| [优化基准模型](optimization-benchmark-model.md) | 1.0.0 / 2026-09-12 | 真实计时字段、预热、网页保存/CSV 与原始数据口径 | +| [优化基准模型](optimization-benchmark-model.md) | 1.1.0 / 2026-09-12 | 计时口径、当前/历史输入隔离、按需 XML 与可执行测试入口 | 以上版本仅表示文档,XML Schema 和用户指定的八路基准未改变。 diff --git a/docs/standard/optimization-benchmark-model.md b/docs/standard/optimization-benchmark-model.md index 16d3d26..cf65cea 100644 --- a/docs/standard/optimization-benchmark-model.md +++ b/docs/standard/optimization-benchmark-model.md @@ -1,6 +1,6 @@ # 优化验证的模型、数据和计时口径 -文档版本:1.0.0;核对日期:2026-09-12;代码基线:`22579e5`。本轮补充实际计时字段和运行入口,保留用户指定的八路优先及精度要求。 +文档版本:1.1.0;核对日期:2026-09-12;本次路径修订基线:`44b6ea7`。本轮补充实际计时字段和运行入口,保留用户指定的八路优先及精度要求。 本约定记录用户于 2026-09-11 明确的长期偏好,适用于后续仿真正确性检查与性能优化。 @@ -10,6 +10,15 @@ 只有八路模型确实跑不通,而且短期内没有可行修复时,才退回 `tests/data/test-mql-4-corrected.json` 与 `tests/data/test_mql_4.ame`。报告必须记录八路失败的实际终点、诊断、已排查内容及退回原因;四路结果不能代替八路验收。 +## 当前入口与历史基准隔离(2026-09-12 补充) + +- 当前八路核对:`.venv/bin/python tools/audit_test_mql8_model.py --check`。读取 corrected JSON 与当前 AME,证据写入 `test/model-audit/mql8/`,不修改工程。 +- 当前四路核对:`.venv/bin/python tests/model_audit/audit_mql4_model.py --check`。XML 从四路 JSON 在内存中生成,再独立对照 AME 的参数与精确端口连接,不依赖已删除的 XML 夹具。 +- 当前八路运行:`.venv/bin/python -m unittest tests.test_current_mql8_simulation -v`。完整使用工程所存区间和配置,验证原生执行、事件、采样完整性与数值有限。该项代替已删除的高刚度 XML 测试,不宣称覆盖原 RK45 专项或证明与 Amesim 曲线一致。 +- 2026 年 8 月冻结输入在 `tests/baselines/simulation/test_mql_8/sources/`,对应 `tests/data/AmesimModels/test_mql.ame`。其字节数、SHA 和数值配置仍按 manifest 校验;历史记录中的旧路径不作为活动入口。普通前端大型画布测试使用当前 corrected JSON,历史 Python 活动遥测回放保持显式启用。 + +Windows 对应使用 `.venv-win\Scripts\python.exe` 调用相同脚本;路径由仓库根解析。详细文件映射、报告来源及本次实际平台见 [路径修复记录](../other/2026-09-12-test-input-path-refresh.md)。 + ## 先核对实际数据,再计时 逐元件、逐端口、逐参数检查拓扑和物理量,尤其是参考口、表压与绝对压力、SI 换算、力和流量方向、初始化及信号时刻。完整区间实际结果、守恒、事件和所需曲线按明确口径核查。存在数值差异时仍可按用户要求测量并记录运行成本,但必须将其标为性能观察,不能据此宣布正确性验收通过。完成运行和数值有限只证明可运行,不能直接判定曲线一致。 diff --git a/docs/update-log/更新日志-2026-08-17.md b/docs/update-log/更新日志-2026-08-17.md index eebcac4..5d5930e 100644 --- a/docs/update-log/更新日志-2026-08-17.md +++ b/docs/update-log/更新日志-2026-08-17.md @@ -1,5 +1,7 @@ # 更新日志 2026-08-17 +> 路径复核 / 2026-09-12:本文八路冻结基准的 AME 现位于 `tests/data/AmesimModels/test_mql.ame`,对应 JSON/XML 位于 `tests/baselines/simulation/test_mql_8/sources/`;保留原哈希、数值结果和当时测试结论。后续性能测试另用 corrected 八路工程,见 [文件映射与复测记录](../other/2026-09-12-test-input-path-refresh.md)。 + ## 11:15 - 复测复杂系统 XML:磁盘文件的终止时间为 `0.81 s`,原样运行正常完成;仅在内存中延长至 `2.10 s` 后也正常越过此前报告的 `2.05 s` 慢区并结束,没有发生重试、回退或无进度卡死。 diff --git a/docs/update-log/更新日志-2026-08-18.md b/docs/update-log/更新日志-2026-08-18.md index 9ea56c7..5534c98 100644 --- a/docs/update-log/更新日志-2026-08-18.md +++ b/docs/update-log/更新日志-2026-08-18.md @@ -1,5 +1,7 @@ # 更新日志 2026-08-18 +> 路径复核 / 2026-09-12:本文八路冻结基准的 AME 现位于 `tests/data/AmesimModels/test_mql.ame`,对应 JSON/XML 位于 `tests/baselines/simulation/test_mql_8/sources/`;保留原哈希、数值结果和当时测试结论。后续性能测试另用 corrected 八路工程,见 [文件映射与复测记录](../other/2026-09-12-test-input-path-refresh.md)。 + ## 02:28 - 完成 P0 基础回归闭环,并推进 OPT-01:将 760 个兼容代数槽压缩为 440 个逻辑坐标,逻辑消去 320 个 effort 别名;全局 RHS 微基准约提升 12.9%,Python 调用数约下降 36.8%。 diff --git a/docs/update-log/更新日志-2026-09-12.md b/docs/update-log/更新日志-2026-09-12.md index 3892a12..cfd2ef0 100644 --- a/docs/update-log/更新日志-2026-09-12.md +++ b/docs/update-log/更新日志-2026-09-12.md @@ -10,3 +10,10 @@ 93 项后端回归、收尾 37 项缓存专项(部分重叠)、前端构建和 12 项超时/看门狗测试通过。Windows 换行做了模拟回归,未实机验证 Windows 锁实现。正常 5173/8000 服务保持停止,临时验证服务已停止;传输压缩仍仅归档备份。本轮未安装环境、提交或推送 Git。 实现、缓存目录、容量边界、阶段成本和原始记录见 [完整报告](../other/按需编译与分层缓存优化-2026-09-12.md)。 + +## 22:41(北京时间):测试输入路径修复 + +- 找到历史 AME 于 `tests/data/AmesimModels/test_mql.ame`,与 August 清单哈希一致;历史 JSON/XML 位于 `tests/baselines/simulation/test_mql_8/sources/`。两组历史测试和 manifest 已更新,未混用当前 AME 或重写历史数值证据。 +- 高刚度 XML 未找到,按用户授权改用当前 corrected 八路完整 10 s BDF 测试;独立四路 XML 改为从 JSON 临时生成。四路/八路 AME 参数和端口复核通过,两个工程内容未改。 +- 全量后台 375 项中 374 通过、1 条原条件跳过(57.360 s),原三处缺失错误消除。前端画布 11 项通过(31.4 s),独立服务的历史活动回放在普通配置显式跳过;未宣称重跑了当前网页求解或 Amesim。 +- 已更新现行规范、数据说明、审计入口及相关历史报告的迁移说明。文件身份、报告依据、替代覆盖限制与复现命令见 [完整记录](../other/2026-09-12-test-input-path-refresh.md)。未安装环境、提交或推送 Git。 diff --git a/frontend/tests/e2e/fit-view.spec.ts b/frontend/tests/e2e/fit-view.spec.ts index 6b3edb8..75e9678 100644 --- a/frontend/tests/e2e/fit-view.spec.ts +++ b/frontend/tests/e2e/fit-view.spec.ts @@ -23,7 +23,7 @@ type ImportedProject = { }; const MQL_8_PROJECT_PATH = fileURLToPath( - new URL("../../../tests/fixtures/legacy/test-mql-8.json", import.meta.url), + new URL("../../../tests/data/test-mql-8-corrected.json", import.meta.url), ); const mql8Project = JSON.parse( readFileSync(MQL_8_PROJECT_PATH, "utf8"), @@ -337,7 +337,7 @@ test("加载本地工程后自动适配建模画布", async ({ page }) => { exact: true, }); await expect(consolePanel.locator(".simulation-console-dock-summary")).toContainText( - "工程已加载", + "已按旧工程规则读取参数", ); await expect(consolePanel.locator(".simulation-console-dock-log")).toHaveCount(0); await page.getByRole("button", { name: "展开仿真控制台" }).click(); @@ -480,7 +480,7 @@ test("恢复自动保存工程后自动适配建模画布", async ({ page }) => exact: true, }); await expect(consolePanel.locator(".simulation-console-dock-summary")).toContainText( - "已恢复上次自动保存的工程", + "已按旧工程规则读取参数", ); await expect(consolePanel.locator(".simulation-console-dock-log")).toHaveCount(0); await page.getByRole("button", { name: "展开仿真控制台" }).click(); @@ -687,9 +687,11 @@ test("结果页系统图为真实大型工程的非连接交叉线显示线桥", await expect( page.locator(".results-system-canvas .react-flow__node"), ).toHaveCount(mql8Project.nodes.length); + // In the AME-corrected layout P4 #5 crosses the non-connected vertical line. + // P4 #1 used by the old fixture no longer has that crossing after rotation. await expect( page.locator( - '.results-system-canvas .react-flow__edge[data-id="edge-amesim_p4node2_1-port_2-amesim_pnl0001_13-port_2-1786972847258"] .react-flow__edge-path', + '.results-system-canvas .react-flow__edge[data-id="edge-amesim_p4node2_5-port_3-amesim_pnl0001_17-port_2-1786949374542"] .react-flow__edge-path', ), ).toHaveAttribute("d", / Q /); }); diff --git a/frontend/tests/e2e/simulation-live-mql8.spec.ts b/frontend/tests/e2e/simulation-live-mql8.spec.ts index e9af5a9..564e1dd 100644 --- a/frontend/tests/e2e/simulation-live-mql8.spec.ts +++ b/frontend/tests/e2e/simulation-live-mql8.spec.ts @@ -1,6 +1,7 @@ import { expect, test, type Page } from "@playwright/test"; import { createHash } from "node:crypto"; -import { readFileSync, statSync, writeFileSync } from "node:fs"; +import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; +import { dirname } from "node:path"; import { fileURLToPath } from "node:url"; type CompactProgressEvent = { @@ -42,12 +43,14 @@ type BrowserStreamEvidence = { parseErrors: string[]; }; +// Historical Python activity replay, opt-in only. Current native acceptance uses +// tests/manual/browser_native_simulation.mjs with the corrected eight-branch JSON. const PROJECT_PATH = fileURLToPath( - new URL("../../../tests/fixtures/legacy/test-mql-8.json", import.meta.url), + new URL("../../../tests/baselines/simulation/test_mql_8/sources/test-mql-8.json", import.meta.url), ); const EVIDENCE_PATH = fileURLToPath( new URL( - "../../../tests/baselines/simulation/test_mql_8/runs/2026-08-18-production-browser-live-activity-v1-0.2.json", + "../../../test/live-browser-mql8/latest-evidence.json", import.meta.url, ), ); @@ -206,6 +209,11 @@ function retainMoreCompleteEvidence( return retained; } +test.skip( + ({ baseURL }) => baseURL !== "http://127.0.0.1:14173", + "Historical activity replay requires --config playwright.live.config.ts and its separate backend; current native acceptance has a separate entry.", +); + test("test-mql-8 在真实浏览器流式链路完成 0.2 s BDF 仿真", async ({ page, }, testInfo) => { @@ -514,7 +522,7 @@ test("test-mql-8 在真实浏览器流式链路完成 0.2 s BDF 仿真", async ( servingMode: "isolated-vite-dev-proxy", }, source: { - path: "tests/fixtures/legacy/test-mql-8.json", + path: "tests/baselines/simulation/test_mql_8/sources/test-mql-8.json", bytes: statSync(PROJECT_PATH).size, sha256: createHash("sha256").update(projectBytes).digest("hex"), }, @@ -562,6 +570,7 @@ test("test-mql-8 在真实浏览器流式链路完成 0.2 s BDF 仿真", async ( }, }; const serializedEvidence = `${JSON.stringify(evidence, null, 2)}\n`; + mkdirSync(dirname(EVIDENCE_PATH), { recursive: true }); writeFileSync(EVIDENCE_PATH, serializedEvidence, "utf8"); await testInfo.attach("mql8-live-browser-evidence", { body: Buffer.from(serializedEvidence), diff --git a/native/README.md b/native/README.md index 7a1ed4d..989f54a 100644 --- a/native/README.md +++ b/native/README.md @@ -99,7 +99,7 @@ BDF 默认使用结构着色差分,无需环境变量或网页选项。修正 ## AME 对齐后的工程输入 -当前用户运行请使用 `tests/data/test-mql-4-corrected.json` 或 `tests/data/test-mql-8-corrected.json`。四路执行 XML 位于 `tests/fixtures/amesim/`,与其 JSON 同步;原八路 JSON 已移入 `tests/fixtures/legacy/` 供差异审计。`test-mql-4-amesim-reference.json` 是曲线基准,不是工程输入。模型复核、P4 端口图形修正、默认 `rtol=1e-8` 的验收和速度记录见 [本轮验证报告](../docs/other/牛顿管流求根与四路网页验证-2026-09-11.md)。 +当前用户运行请使用 `tests/data/test-mql-4-corrected.json` 或 `tests/data/test-mql-8-corrected.json`。执行 XML 从当前 JSON 按需生成;原独立四路 XML 和 legacy 八路夹具已删除。历史八路 JSON/XML 在 `tests/baselines/simulation/test_mql_8/sources/`,其 AME 在 `tests/data/AmesimModels/test_mql.ame`,仅用于冻结基准核对,不能与当前 corrected 输入混用。路径、生成方式和测试入口见 [测试文件说明](../tests/data/README.md)。`test-mql-4-amesim-reference.json` 是曲线基准,不是工程输入。模型复核、P4 端口图形修正、默认 `rtol=1e-8` 的验收和速度记录见 [本轮验证报告](../docs/other/牛顿管流求根与四路网页验证-2026-09-11.md)。 后续优化和网页计时默认使用修正后的八路工程;仅当八路无法运行且短期不能解决时退回四路。选择规则见 [优化验证约定](../docs/standard/optimization-benchmark-model.md),当前效率、迭代触顶与网页分阶段记录见 [八路评估报告](../docs/other/八路模型计算效率与网页阶段计时-2026-09-11.md)。 diff --git a/tests/baselines/simulation/test_mql_8/README.md b/tests/baselines/simulation/test_mql_8/README.md new file mode 100644 index 0000000..56b88cf --- /dev/null +++ b/tests/baselines/simulation/test_mql_8/README.md @@ -0,0 +1,9 @@ +# 八路历史冻结基准 + +路径复核:2026-09-12,版本 1.0.0。 + +本目录保存 2026 年 8 月回归输入和结果。当前可读取的来源为 [JSON](sources/test-mql-8.json)、[XML](sources/test-mql-8.xml) 及 [历史 AME](../../../data/AmesimModels/test_mql.ame),以 [manifest](manifest.json) 中的字节数和 SHA 为准。 + +`goldens/`、`runs/` 中的路径与源码状态是历史运行记录,不重新计算或覆盖;其中旧 `AmesimModels/test_mql.ame` 对应上述历史 AME。`tests/data/test_mql.ame` 是另一份当前归档,不能仅凭同名代入历史比较。显式启用的前端历史活动回放现在把新证据写到被忽略的 `test/live-browser-mql8/`,不会覆盖冻结运行报告。 + +普通功能/性能测试使用 [当前八路 corrected JSON](../../../data/test-mql-8-corrected.json)。相关测试与原报告的关系见 [路径修复记录](../../../../docs/other/2026-09-12-test-input-path-refresh.md)。 diff --git a/tests/baselines/simulation/test_mql_8/manifest.json b/tests/baselines/simulation/test_mql_8/manifest.json index 8e0832c..0f6dec0 100644 --- a/tests/baselines/simulation/test_mql_8/manifest.json +++ b/tests/baselines/simulation/test_mql_8/manifest.json @@ -8,7 +8,7 @@ "bytes": 104110, "xmlIsAuthoritative": true, "referenceArchive": { - "path": "AmesimModels/test_mql.ame", + "path": "tests/data/AmesimModels/test_mql.ame", "sha256": "cbc3aadd4569a49b3a63e5d66d4143ec16126c0f950df73fb637e07673c20fbb", "bytes": 21708800, "role": "authoritativePhysicalBaseline", diff --git a/tests/data/AmesimModels/test_mql/README.md b/tests/data/AmesimModels/test_mql/README.md index fef95e1..92c4700 100644 --- a/tests/data/AmesimModels/test_mql/README.md +++ b/tests/data/AmesimModels/test_mql/README.md @@ -12,7 +12,7 @@ Python 输出只有通过 AMESim baseline 对比后才能作为数值一致性 ## 模型与源数据 -- AMESim 源模型:`AmesimModels/test_mql.ame` +- AMESim 源模型:`tests/data/AmesimModels/test_mql.ame` - Python 系统类:`app.simulation.examples.test_mql.system.TestMqlSystem` - 结构运行入口:`app.simulation.examples.test_mql.run` - 132 状态比较入口:`app.simulation.examples.test_mql.run_full_state_comparison` diff --git a/tests/data/README.md b/tests/data/README.md index 53de4ff..c6f6695 100644 --- a/tests/data/README.md +++ b/tests/data/README.md @@ -1,26 +1,30 @@ # 浏览器四路、八路测试工程 -本目录中的 JSON/XML 仅保留以下两个可由网页“导入工程 JSON”识别的工程文件。已逐项核对对应 AME 图纸的元件、公开物理参数及端口连接;AME 单位与工程 SI 单位之间的换算已纳入检查。 +版本:1.1.0;路径复核日期:2026-09-12。 -| 浏览器工程 | Amesim 原文件 | 元件 / 参数 / 连接 | +本目录直接保留两个可由网页“导入工程 JSON”识别的工程。已重新逐项核对对应 AME 图纸的元件、公开物理参数及端口连接,包含 AME 单位到 SI 单位的换算。 + +| 浏览器工程(后续删改入口) | 对应 Amesim 原文件 | 元件 / 参数 / 连接 | |---|---|---| | [test-mql-4-corrected.json](test-mql-4-corrected.json) | [test_mql_4.ame](test_mql_4.ame) | 81 / 582 / 90 | | [test-mql-8-corrected.json](test-mql-8-corrected.json) | [test_mql.ame](test_mql.ame) | 157 / 1092 / 178 | -网页当前只支持导入工程 JSON;XML 可从网页下载,用作后端输入。网页使用原生 BDF 和当前默认 `rtol=1e-8`,与 AME 内部积分器及历史数值设置不能视为相同。这里的参数对齐指模型物理参数与初值,不声称不同仿真器所有数值输出完全一致。 +后续优化优先使用八路 corrected 工程;只有八路无法运行且短期不能修复时,再退回四路。具体要求见 [优化验证约定](../../docs/standard/optimization-benchmark-model.md)。网页使用原生 BDF、当前默认 `rtol=1e-8`;物理参数对齐不意味着与 AME 积分算法、历史数值设置和输出完全相同。 -后续优化测试优先使用八路 corrected 工程;仅当八路跑不通且短期无法解决时,再使用四路。具体记录要求见 [优化验证约定](../../docs/standard/optimization-benchmark-model.md)。 +XML 不作为另一份手工维护的工程。需要时从网页下载,或用原生输入转换入口从 JSON 生成。四路审计 `--check` 在内存中生成 XML;八路审计的 XML 证据放在被 Git 忽略的 `test/model-audit/mql8/corrected.xml`。 -2026-09-11 整理时,两份工程内容保持不变。其他 JSON/XML 已按用途移出本目录,移动前后字节数及 SHA256 相同: +## 历史基准与辅助文件 -| 原文件 | 当前路径 | 用途 | -|---|---|---| -| native-python-reference.json | [原生数值基准](../baselines/native/native-python-reference.json) | 50 网络冻结参考 | -| test-mql-4-amesim-reference.json | [四路 Amesim 曲线基准](../baselines/simulation/test_mql_4/test-mql-4-amesim-reference.json) | 72 曲线、57 时刻 | -| test-mql-4-corrected.xml | [四路执行 XML](../fixtures/amesim/test-mql-4-corrected.xml) | 与四路 JSON 同步的后端输入 | -| test-mql-8.json / test-mql-8.xml | [旧 JSON](../fixtures/legacy/test-mql-8.json) / [旧 XML](../fixtures/legacy/test-mql-8.xml) | 修正前输入及布局来源 | -| test_mql-full-branches-01-04.xml | [历史分支基准输入](../baselines/simulation/test_mql_full_branches/sources/test_mql-full-branches-01-04.xml) | 历史回归清单绑定的输入 | -| native-skill-test.xml | [原生通用测试输入](../fixtures/native-skill-test.xml) | 原生编译、执行与 CI 检查 | -| fixtures/high_stiffness_explicit_rk45.xml | [高刚度 RK45 输入](../fixtures/high_stiffness_explicit_rk45.xml) | 独立刚度回归用例 | +| 当前路径 | 用途与限制 | +|---|---| +| [AmesimModels/test_mql.ame](AmesimModels/test_mql.ame) | 2026-08-18 冻结基准的 AME,SHA 开头 `cbc3aadd`;与本目录当前 AME 的 `1ff0ea42` 不同 | +| [历史八路 JSON](../baselines/simulation/test_mql_8/sources/test-mql-8.json) / [XML](../baselines/simulation/test_mql_8/sources/test-mql-8.xml) | 与上述历史 AME 对应;由 [manifest](../baselines/simulation/test_mql_8/manifest.json) 锁定身份,不能替换为当前 corrected 工程 | +| [原生数值基准](../baselines/native/native-python-reference.json) | 50 个网络的冻结参考,不是浏览器工程 | +| [四路 Amesim 曲线基准](../baselines/simulation/test_mql_4/test-mql-4-amesim-reference.json) | 72 条曲线、57 个时刻,不是浏览器工程 | +| [历史分支 XML](../baselines/simulation/test_mql_full_branches/sources/test_mql-full-branches-01-04.xml) | 另一份历史回归清单绑定的输入 | +| [原生通用 XML](../fixtures/native-skill-test.xml) | 原生编译、执行与 CI 检查 | +| [参数表达式用例](../fixtures/parameter-expressions.json) | 前后端共享的参数语义测试数据,不是浏览器工程 | -AME 原文件及本目录中其他类型的资料保持原位。历史运行记录保留当时路径;活动测试、审计脚本、CI 和当前交付说明已更新到新路径。 +2026-09-11 整理后,用户又移动/删除了部分文件。原 `tests/fixtures/amesim/test-mql-4-corrected.xml`、`tests/fixtures/legacy/test-mql-8.json/.xml`、`tests/fixtures/high_stiffness_explicit_rk45.xml` 当前均不存在。历史八路 sources 中的 JSON 也不是被删除 legacy JSON 的逐字节副本,不能冒充恢复了原文件。 + +高刚度 RK45 专项按用户决定改用 [当前八路完整运行测试](../test_current_mql8_simulation.py)。它验证当前 BDF 路径,不代表恢复了原 RK45 高刚度覆盖。文件身份、相关报告来源及验证结果见 [路径修复报告](../../docs/other/2026-09-12-test-input-path-refresh.md)。冻结运行记录中的旧路径是当时来源记录,当前位置以本说明和 manifest 为准。 diff --git a/tests/model_audit/audit_mql4_model.py b/tests/model_audit/audit_mql4_model.py index 7b1f902..41ba239 100644 --- a/tests/model_audit/audit_mql4_model.py +++ b/tests/model_audit/audit_mql4_model.py @@ -1,4 +1,4 @@ -"""Audit the AME diagram against the corrected XML; emit an aligned browser project. +"""Audit the AME diagram against the current browser project; generate XML on demand. Run from the repository root with .venv/bin/python. Does not run Amesim or use the stale eight-branch compiled/result cache inside the four-branch archive. @@ -27,9 +27,9 @@ from tests.test_test_mql_ame_contract import ( _public_model_type, _expected_parameters, _value_fields, ) -OUT = ROOT / 'test/solver-newton-20260911/mql4' +OUT = ROOT / 'test/model-audit/mql4' AME = ROOT / 'tests/data/test_mql_4.ame' -XML = ROOT / 'tests/fixtures/amesim/test-mql-4-corrected.xml' +XML = OUT / 'aligned-input.xml' REFERENCE = ROOT / 'tests/baselines/simulation/test_mql_4/test-mql-4-amesim-reference.json' PROJECT = ROOT / 'tests/data/test-mql-4-corrected.json' @@ -147,9 +147,17 @@ def audit_source(): } +def project_xml(): + # The separately maintained XML fixture was deleted. Use the same input + # conversion as the native CLI; the AME remains the independent authority. + from app.simulation.native_codegen.input import load_input + xml, _ = load_input(PROJECT) + return xml + + def xml_model(root=None): if root is None: - root = ET.parse(XML).getroot() + root = ET.fromstring(project_xml()) nodes = {item.attrib['id']: { 'modelType': item.attrib['type'], 'modelVersion': item.attrib['modelVersion'], @@ -257,8 +265,9 @@ def check_project_contract(project, xml_root): def generate_project(source, source_edges, target, target_edges, xml_root, mapping): - layout_source = ROOT / 'tests/fixtures/legacy/test-mql-8.json' - layout = json.loads(layout_source.read_text()) + layout_source = PROJECT + layout = json.loads(layout_source.read_text(encoding='utf-8')) + layout_sha = sha(layout_source) layout_nodes = {node['id']: node for node in layout['nodes']} reverse_mapping = {value: key for key, value in mapping.items()} nodes = [] @@ -331,14 +340,14 @@ def generate_project(source, source_edges, target, target_edges, xml_root, mappi project = {'projectSchemaVersion': 1, 'name': 'test-mql-4-AME-aligned', 'nodes': nodes, 'edges': edges, 'simulation': simulation} check_project_contract(project, xml_root) - PROJECT.write_text(json.dumps(project, indent=2, ensure_ascii=False) + '\n') + PROJECT.write_text(json.dumps(project, indent=2, ensure_ascii=False) + '\n', encoding='utf-8', newline='\n') generated_xml = OUT / 'aligned-input.xml' xml_root.attrib['name'] = project['name'] ET.indent(xml_root) ET.ElementTree(xml_root).write(generated_xml, encoding='utf-8', xml_declaration=True) XML.write_bytes(generated_xml.read_bytes()) # Check the delivered JSON itself, including every exact endpoint and number. - json_nodes = {node['id']: node['data'] for node in json.loads(PROJECT.read_text())['nodes']} + json_nodes = {node['id']: node['data'] for node in json.loads(PROJECT.read_text(encoding='utf-8'))['nodes']} for source_id, project_id in mapping.items(): assert json_nodes[project_id]['parameters'] == source[source_id]['parameters'] expected_edges = {tuple(sorted(((mapping[a], ap), (mapping[b], bp)))) for ((a, ap), (b, bp)) in source_edges} @@ -354,34 +363,44 @@ def generate_project(source, source_edges, target, target_edges, xml_root, mappi 'nodeCount': len(nodes), 'edgeCount': len(edges), 'parameterCount': sum(len(n['data']['parameters']) for n in nodes), 'allMappedParametersExactlyMatchAme': True, 'allMappedConnectionPortsExactlyMatchAme': True, 'parameterChangesFromLegacyXml': parameter_changes, 'connectionPortChangesFromLegacyXml': changed_connections, - 'layoutMetadataSource': {'path': str(layout_source.relative_to(ROOT)), 'sha256': sha(layout_source)}, + 'layoutMetadataSource': {'path': str(layout_source.relative_to(ROOT)), 'sha256': layout_sha}, 'simulation': simulation, 'simulationSettingsNote': 'AME archive stores historical fixed-step settings. This JSON uses native BDF; rtol is supplied by the backend and is not stored in the project. Initial validation used 1e-7; the backend default was subsequently changed to 1e-8. The earlier official Standard variable-step reference remains tolerance 1e-7. Inspect actual run diagnostics; solver algorithms are not identical.'} def main(): parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('--check', action='store_true', help='Validate delivered JSON/XML only; do not generate or write files.') + parser.add_argument('--check', action='store_true', help='Check current JSON parameters and exact connection ports against AME; generate XML in memory, without file writes.') arguments = parser.parse_args() if arguments.check: - print(json.dumps(check_project_contract(json.loads(PROJECT.read_text()), ET.parse(XML).getroot()), indent=2)) + target, target_edges, xml_root = xml_model() + result = check_project_contract(json.loads(PROJECT.read_text(encoding='utf-8')), xml_root) + source, source_edges, _ = audit_source() + mapping = find_mapping(source, source_edges, target, target_edges, ports=True) + if mapping is None or len(source) != len(target) or len(source_edges) != len(target_edges): + raise ValueError('Current JSON differs from AME in components, parameters or connection ports.') + for source_id, target_id in mapping.items(): + if source[source_id]['parameters'] != target[target_id]['parameters']: + raise ValueError(f'{target_id}: parameters do not exactly match AME.') + result['ameParametersAndConnectionPortsMatch'] = True + result['executionXmlSource'] = str(PROJECT.relative_to(ROOT)) + print(json.dumps(result, indent=2)) return OUT.mkdir(parents=True, exist_ok=True) source, source_edges, audit = audit_source() - legacy_xml = OUT / 'legacy-corrected-input.xml' - if not legacy_xml.exists(): - legacy_xml.write_bytes(XML.read_bytes()) + XML.write_bytes(project_xml()) + (OUT / 'input-before-audit.xml').write_bytes(XML.read_bytes()) target, target_edges, xml_root = xml_model() mapping = find_mapping(source, source_edges, target, target_edges) audit['correctedXml'] = {'path': str(XML.relative_to(ROOT)), 'sha256': sha(XML), 'parameterAndComponentTopologyIsomorphic': mapping is not None} - ref = json.loads(REFERENCE.read_text()) + ref = json.loads(REFERENCE.read_text(encoding='utf-8')) audit['reference'] = {'path': str(REFERENCE.relative_to(ROOT)), 'sha256': sha(REFERENCE), 'source': ref['source'], 'timeCount': len(ref['times']), 'seriesCount': len(ref['series']), 'ameHashMatches': ref['originalAmeSha256'] == sha(AME), 'xmlHashMatches': ref['xmlSha256'] == sha(XML), 'newAmesimRunPerformed': False} - (OUT / 'source-model.json').write_text(json.dumps({'nodes': source, 'edges': source_edges}, indent=2, ensure_ascii=False) + '\n') + (OUT / 'source-model.json').write_text(json.dumps({'nodes': source, 'edges': source_edges}, indent=2, ensure_ascii=False) + '\n', encoding='utf-8', newline='\n') if not mapping: mapping = find_mapping(source, source_edges, target, target_edges, parameters=False) if mapping: @@ -394,8 +413,8 @@ def main(): audit['generatedProject'] = generate_project(source, source_edges, target, target_edges, xml_root, mapping) audit['correctedXml']['scope'] = 'Input XML snapshot before this generation; generatedProject describes the delivered corrected files.' audit_text = json.dumps(audit, indent=2, ensure_ascii=False) + '\n' - (OUT / 'model-audit.json').write_text(audit_text) - (ROOT / 'tests/model_audit/test-mql-4-audit.json').write_text(audit_text) + (OUT / 'model-audit.json').write_text(audit_text, encoding='utf-8', newline='\n') + (ROOT / 'tests/model_audit/test-mql-4-audit.json').write_text(audit_text, encoding='utf-8', newline='\n') print(json.dumps({key: value for key, value in audit.items() if key not in {'componentMapping', 'members', 'globals'}}, indent=2)) diff --git a/tests/test_amesim_results.py b/tests/test_amesim_results.py index 0ee5102..6ed9fd3 100644 --- a/tests/test_amesim_results.py +++ b/tests/test_amesim_results.py @@ -1,17 +1,16 @@ from __future__ import annotations import unittest -from pathlib import Path from unittest.mock import Mock +from app.simulation.paths import HISTORICAL_MQL8_AME_PATH from app.simulation.reporting.amesim_results import ( _resolve_result_members, load_test_mql_amesim_results, ) -REPO_ROOT = Path(__file__).resolve().parents[1] -TEST_MQL_AME = REPO_ROOT / "AmesimModels" / "test_mql.ame" +TEST_MQL_AME = HISTORICAL_MQL8_AME_PATH class AmesimResultsTests(unittest.TestCase): diff --git a/tests/test_current_mql8_simulation.py b/tests/test_current_mql8_simulation.py new file mode 100644 index 0000000..c794df3 --- /dev/null +++ b/tests/test_current_mql8_simulation.py @@ -0,0 +1,59 @@ +"""Current eight-branch execution regression. + +The deleted high_stiffness_explicit_rk45.xml is not reconstructed or renamed. +Per the user decision on 2026-09-12, exercise the corrected eight-branch JSON +through the real XML/native path. This does not claim the former RK45 coverage. +""" +from __future__ import annotations + +import json +import math +import unittest +from xml.etree import ElementTree as ET + +from app.main import run_system_xml_simulation +from app.simulation.native_codegen.input import load_input +from app.simulation.paths import CURRENT_MQL8_PROJECT_PATH + + +class CurrentMql8SimulationTests(unittest.TestCase): + def test_corrected_eight_branch_runs_its_complete_saved_configuration(self): + original = CURRENT_MQL8_PROJECT_PATH.read_bytes() + project = json.loads(original) + settings = project["simulation"] + xml, document = load_input(CURRENT_MQL8_PROJECT_PATH) + self.assertEqual(len(document.components), len(project["nodes"])) + self.assertEqual(len(document.connections), len(project["edges"])) + self.assertEqual(ET.fromstring(xml).get("unitSystem"), "SI") + result = run_system_xml_simulation(xml, cancel_check=lambda: False) + + self.assertTrue(result["success"], result["message"]) + self.assertEqual(result["status"], "completed") + self.assertFalse(result["partial"]) + self.assertEqual(result["simulatedUntil"], settings["t_stop"]) + integration = result["diagnostics"]["integration"] + self.assertEqual(integration["method"], settings["method"]) + self.assertEqual(integration["rtol"], 1e-8) + self.assertEqual(result["diagnostics"]["native"]["backend"], "native-c") + # The current case includes a mechanical transition over the complete 10 s. + self.assertGreaterEqual(integration["totals"]["stateTransitionCount"], 1) + self.assertGreaterEqual(integration["totals"]["solverStartCount"], 2) + + series = result["series"] + times = series["time"] + self.assertEqual(times[0], settings["t_start"]) + self.assertEqual(times[-1], settings["t_stop"]) + self.assertTrue(all(a < b for a, b in zip(times, times[1:]))) + # Keep every requested output point, in addition to inserted event points. + step = settings["step"] + actual_grid = {round(t / step) for t in times if abs(t / step - round(t / step)) < 1e-8} + expected_grid = set(range(round(settings["t_start"] / step), round(settings["t_stop"] / step) + 1)) + self.assertTrue(expected_grid.issubset(actual_grid)) + for key, values in series.items(): + self.assertEqual(len(values), len(times), key) + self.assertTrue(all(math.isfinite(value) for value in values), key) + self.assertEqual(CURRENT_MQL8_PROJECT_PATH.read_bytes(), original) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_high_stiffness_explicit_rk45.py b/tests/test_high_stiffness_explicit_rk45.py deleted file mode 100644 index fb8011c..0000000 --- a/tests/test_high_stiffness_explicit_rk45.py +++ /dev/null @@ -1,74 +0,0 @@ -from __future__ import annotations - -import hashlib -import math -from pathlib import Path -import unittest - -from app.main import run_system_xml_simulation - - -FIXTURE_PATH = ( - Path(__file__).resolve().parent - / "fixtures" - / "high_stiffness_explicit_rk45.xml" -) -ORIGINAL_XML_SHA256 = ( - "27048a99da0a21922d75785b760c3b5d04be3349b8aef6fbfedfd811d87ef1d5" -) - - -def short_explicit_rk45_xml() -> bytes: - original = FIXTURE_PATH.read_bytes() - canonical = original.replace(b"\r\n", b"\n").rstrip(b"\n") - if hashlib.sha256(canonical).hexdigest() != ORIGINAL_XML_SHA256: - raise AssertionError("The high-stiffness RK45 fixture differs from the user XML.") - if original.count(b'tStop="10"') != 1: - raise AssertionError("Expected one original simulation stop time.") - if original.count(b'sampleStep="0.02"') != 1: - raise AssertionError("Expected one original simulation sample step.") - return original.replace( - b'tStop="10"', - b'tStop="0.005"', - 1, - ).replace( - b'sampleStep="0.02"', - b'sampleStep="0.001"', - 1, - ) - - -class HighStiffnessExplicitRk45RegressionTests(unittest.TestCase): - def test_short_user_model_crosses_the_early_stiff_limit_event(self) -> None: - result = run_system_xml_simulation( - short_explicit_rk45_xml(), - cancel_check=lambda: False, - ) - - self.assertTrue(result["success"], result["message"]) - self.assertEqual(result["status"], "completed") - self.assertFalse(result["partial"]) - self.assertEqual(result["simulatedUntil"], 0.005) - self.assertNotIn("provided bounds", result["message"]) - - integration = result["diagnostics"]["integration"] - totals = integration["totals"] - self.assertEqual(integration["method"], "RK45") - self.assertGreaterEqual(totals["stateTransitionCount"], 1) - self.assertGreaterEqual(totals["solverStartCount"], 2) - - series = result["series"] - times = series["time"] - self.assertEqual(times[0], 0.0) - self.assertEqual(times[-1], 0.005) - self.assertTrue( - all(first < second for first, second in zip(times, times[1:])) - ) - self.assertTrue(any(0.0 < time < 0.001 for time in times)) - for values in series.values(): - self.assertEqual(len(values), len(times)) - self.assertTrue(all(math.isfinite(value) for value in values)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_regression_fixture_line_endings.py b/tests/test_regression_fixture_line_endings.py index ea18e14..bcfbb03 100644 --- a/tests/test_regression_fixture_line_endings.py +++ b/tests/test_regression_fixture_line_endings.py @@ -11,8 +11,8 @@ BASELINE_ROOT = REPOSITORY_ROOT / "tests" / "baselines" / "simulation" GIT_ATTRIBUTES_PATH = REPOSITORY_ROOT / ".gitattributes" EXPECTED_ATTRIBUTE_RULES = { - ("tests/fixtures/legacy/test-mql-8.xml", "text", "eol=lf"), - ("tests/fixtures/legacy/test-mql-8.json", "text", "eol=lf"), + ("tests/baselines/simulation/**/sources/*", "text", "eol=lf"), + ("tests/data/*.json", "text", "eol=lf"), ("tests/baselines/simulation/test_mql_full_branches/sources/test_mql-full-branches-01-04.xml", "text", "eol=lf"), ("tests/baselines/simulation/**/*.json", "text", "eol=lf"), } diff --git a/tests/test_test_mql_ame_contract.py b/tests/test_test_mql_ame_contract.py index 6f081f8..6850d50 100644 --- a/tests/test_test_mql_ame_contract.py +++ b/tests/test_test_mql_ame_contract.py @@ -14,14 +14,15 @@ import tarfile import unittest import xml.etree.ElementTree as ET +from app.simulation.paths import HISTORICAL_MQL8_AME_PATH, HISTORICAL_MQL8_PROJECT_PATH, HISTORICAL_MQL8_XML_PATH from app.simulation.registry import get_component_model_spec from app.simulation.reporting.amesim_results import load_test_mql_amesim_results REPOSITORY_ROOT = Path(__file__).resolve().parents[1] -AME_PATH = REPOSITORY_ROOT / "AmesimModels" / "test_mql.ame" -JSON_PATH = REPOSITORY_ROOT / "tests/baselines/simulation/test_mql_8/sources/test-mql-8.json" -XML_PATH = REPOSITORY_ROOT / "tests/baselines/simulation/test_mql_8/sources/test-mql-8.xml" +AME_PATH = HISTORICAL_MQL8_AME_PATH +JSON_PATH = HISTORICAL_MQL8_PROJECT_PATH +XML_PATH = HISTORICAL_MQL8_XML_PATH MANIFEST_PATH = ( REPOSITORY_ROOT / "tests/baselines/simulation/test_mql_8/manifest.json" @@ -512,7 +513,7 @@ class TestMqlAmeAuthorityContractTests(unittest.TestCase): self.assertEqual( reference, { - "path": "AmesimModels/test_mql.ame", + "path": "tests/data/AmesimModels/test_mql.ame", "sha256": EXPECTED_AME_SHA256, "bytes": EXPECTED_AME_BYTES, "role": "authoritativePhysicalBaseline", diff --git a/tools/audit_test_mql8_model.py b/tools/audit_test_mql8_model.py index c5c4844..b03ab13 100644 --- a/tools/audit_test_mql8_model.py +++ b/tools/audit_test_mql8_model.py @@ -1,11 +1,14 @@ """Reproduce the eight-branch AME drawing -> editor model audit. -Run from the repository root with .venv/bin/python. The original inputs are -read only. Mapping uses typed graph isomorphism and endpoint edit cost, never -component display names; names are emitted only as evidence for review. +Run from the repository root with .venv/bin/python and --check to preserve +the current project; omit --check to regenerate it. Audit evidence goes to +the ignored test/model-audit/mql8 directory. Mapping uses typed graph +isomorphism and endpoint edit cost, never component display names; names +are emitted only as evidence for review. """ from __future__ import annotations +import argparse from collections import Counter, defaultdict from copy import deepcopy import hashlib @@ -24,11 +27,12 @@ from tests.test_test_mql_ame_contract import ( _SOURCE_FIELD_BY_PARAMETER, _numeric_project_value, ) from app.simulation.registry import get_component_model_spec +from app.simulation.paths import CURRENT_MQL8_AME_PATH, CURRENT_MQL8_PROJECT_PATH -HERE = ROOT / 'test/solver-newton-20260911/mql8' -AME = ROOT / 'tests/data/test_mql.ame' -INPUT = ROOT / 'tests/fixtures/legacy/test-mql-8.json' -OUTPUT = ROOT / 'tests/data/test-mql-8-corrected.json' +HERE = ROOT / 'test/model-audit/mql8' +AME = CURRENT_MQL8_AME_PATH +INPUT = CURRENT_MQL8_PROJECT_PATH +OUTPUT = CURRENT_MQL8_PROJECT_PATH def read_drawing(): @@ -179,8 +183,14 @@ def match_graph(entities, ame_edges, project): def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--check', action='store_true', + help='Audit current JSON against AME without changing the project; evidence goes to test/model-audit/mql8/.') + arguments = parser.parse_args() HERE.mkdir(parents=True, exist_ok=True) - original = json.loads(INPUT.read_text()) + sources = {str(p.relative_to(ROOT)): {'sha256': hashlib.sha256(p.read_bytes()).hexdigest(), + 'bytes': p.stat().st_size} for p in (AME, INPUT)} + original = json.loads(INPUT.read_text(encoding='utf-8')) entities, edges, globals_, sim, model_info = read_drawing() mapping, matching = match_graph(entities, edges, original) corrected = deepcopy(original) @@ -266,7 +276,8 @@ def main(): payload = ReactFlowProjectPayload.model_validate(executable) network = compile_reactflow_network(payload) program = compile_native_program(network) - OUTPUT.write_text(json.dumps(corrected, ensure_ascii=False, indent=2) + '\n') + if not arguments.check: + OUTPUT.write_text(json.dumps(corrected, ensure_ascii=False, indent=2) + '\n', encoding='utf-8', newline='\n') xml = build_reactflow_system_xml(payload) (HERE / 'corrected.xml').write_bytes(xml if isinstance(xml, bytes) else xml.encode()) # The archive's parameter table and run input have matching row order. @@ -296,7 +307,8 @@ def main(): cache_checks.append({'dataPath': path, 'parameterDataLine': line, 'drawingValue': drawing_value, 'runValue': run_value}) audit = { - 'sources': {str(p.relative_to(ROOT)): {'sha256': hashlib.sha256(p.read_bytes()).hexdigest(), 'bytes': p.stat().st_size} for p in (AME, INPUT)}, + 'sources': sources, + 'checkOnly': arguments.check, 'output': str(OUTPUT.relative_to(ROOT)), 'globals': globals_, 'matching': matching, 'counts': {'components': len(entities), 'connections': len(edges), 'parameters': len(parameter_audit), 'changedParameters': len(parameter_changes), 'changedConnections': len(connection_changes), @@ -315,11 +327,16 @@ def main(): 'parameters': parameter_audit, 'connections': connection_audit, 'evaluationSchedule': program.evaluation_schedule, } - (HERE / 'audit.json').write_text(json.dumps(audit, ensure_ascii=False, indent=2) + '\n') + (HERE / 'audit.json').write_text(json.dumps(audit, ensure_ascii=False, indent=2) + '\n', encoding='utf-8', newline='\n') print(json.dumps({'counts': audit['counts'], 'matching': matching, 'parameters': parameter_changes, 'connections': connection_changes, 'simulation': audit['simulation']}, ensure_ascii=False, indent=2)) + if arguments.check: + assert not parameter_changes, 'Current JSON parameters differ from AME; see audit.json.' + assert not connection_changes, 'Current JSON connection ports differ from AME; see audit.json.' + assert original['simulation'] == corrected['simulation'], 'Current JSON time settings differ from AME.' + if __name__ == '__main__': main()