修复测试模型路径并完善四路八路回归与文档
- 按文件哈希区分当前八路工程和历史 Amesim 基准,修复测试与清单中的旧路径。 - 四路审计从 JSON 按需生成 XML,八路新增只核对模式,并显式使用 UTF-8 与 LF。 - 将缺失高刚度夹具的测试替换为当前八路完整 BDF 运行,明确未恢复原 RK45 专项覆盖。 - 更新前端大型工程测试、历史活动回放入口、数据说明、现行规范和相关报告。 验证:全量后台 374 项通过、1 项条件跳过;前端 11 项通过、历史独立服务用例 1 项跳过;四路八路 AME 审计通过。 环境、缓存和运行产物保持忽略;本次未实施工程 JSON 字段精简。
This commit is contained in:
1 parent
44b6ea74ab
commit
b6c22a54f8
32 files changed
+318
-153
No files matched your search
@@ -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)。
|
||||
@@ -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",
|
||||
|
||||
@@ -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`
|
||||
|
||||
+19
-15
@@ -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 为准。
|
||||
@@ -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))
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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"),
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user