修复测试模型路径并完善四路八路回归与文档

- 按文件哈希区分当前八路工程和历史 Amesim 基准,修复测试与清单中的旧路径。
- 四路审计从 JSON 按需生成 XML,八路新增只核对模式,并显式使用 UTF-8 与 LF。
- 将缺失高刚度夹具的测试替换为当前八路完整 BDF 运行,明确未恢复原 RK45 专项覆盖。
- 更新前端大型工程测试、历史活动回放入口、数据说明、现行规范和相关报告。

验证:全量后台 374 项通过、1 项条件跳过;前端 11 项通过、历史独立服务用例 1 项跳过;四路八路 AME 审计通过。
环境、缓存和运行产物保持忽略;本次未实施工程 JSON 字段精简。
This commit is contained in:
lujingze committed 2026-09-12 15:49:09 +00:00
1 parent 44b6ea74ab
commit b6c22a54f8
32 files changed
+318 -153

No files matched your search

+37 -18
View File
@@ -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))