存档求解器回归基线与当前改动

纳管 AMESim 对齐基线、发布锁、回归测试及当前物理门禁调整。

更新日志仅记录已完成成果,并注明当前 HEAD 尚待真实 production 复跑与远端 workflow 验证。
This commit is contained in:
lujingze committed 2026-08-18 15:20:42 +00:00
1 parent 53f8601fec
commit a8c733883c
32 files changed
+47239 -106

No files matched your search

+15 -10
View File
@@ -76,11 +76,12 @@ jobs:
cache-dependency-path: |
requirements.txt
constraints/python312-direct.txt
- name: Install reference dependencies
constraints/python312-linux-x86_64.lock
- name: Install hashed Linux release lock
run: |
python -m pip install \
-r requirements.txt \
-c constraints/python312-direct.txt
--force-reinstall \
-r constraints/python312-linux-x86_64.lock
python -m pip check
- name: Run solver foundation tests
env:
@@ -89,6 +90,8 @@ jobs:
python -W error::ResourceWarning -m unittest \
tests.test_dependency_constraints \
tests.test_benchmark_regression \
tests.test_physical_state_v21 \
tests.test_test_mql_ame_contract \
tests.test_test_mql_8_regression \
tests.test_mql_full_branches_regression \
tests.test_pressure_flow_causal_execution \
@@ -115,18 +118,19 @@ jobs:
cache-dependency-path: |
requirements.txt
constraints/python312-direct.txt
- name: Install reference dependencies
constraints/python312-linux-x86_64.lock
- name: Install hashed Linux release lock
run: |
python -m pip install \
-r requirements.txt \
-c constraints/python312-direct.txt
--force-reinstall \
-r constraints/python312-linux-x86_64.lock
python -m pip check
- name: Run 0.81 and 2.10 second historical regression
run: |
mkdir -p artifacts
python -m app.simulation.benchmark_regression \
--manifest tests/baselines/simulation/test_mql_full_branches/manifest.json \
--lane solver-only \
--lane production \
--output artifacts/test-mql-full-branches.json
- if: always()
uses: actions/upload-artifact@v4
@@ -150,11 +154,12 @@ jobs:
cache-dependency-path: |
requirements.txt
constraints/python312-direct.txt
- name: Install reference dependencies
constraints/python312-linux-x86_64.lock
- name: Install hashed Linux release lock
run: |
python -m pip install \
-r requirements.txt \
-c constraints/python312-direct.txt
--force-reinstall \
-r constraints/python312-linux-x86_64.lock
python -m pip check
- name: Run bounded progressive main-model regression
env:
+14 -13
View File
@@ -6,9 +6,9 @@ ReactFlow 系统建模与 `app.simulation` 仿真后端。
后端统一使用 Python 3.12;仓库根目录的 `.python-version` 记录本轮参考补丁版本
`3.12.3`。`requirements.txt` 保留支持范围,
`constraints/python312-direct.txt` 固定应用直接依赖的参考版本。普通开发、性能复测和
CI 应同时使用两者,以避免同一代码在不同时间解析到不同的 NumPy、SciPy 或 Web
框架版本。
`constraints/python312-direct.txt` 固定跨平台开发环境的直接依赖参考版本;
`constraints/python312-linux-x86_64.lock` 则完整固定发布与 CI 所用的 Linux x86_64
wheel、全部传递依赖及其 SHA-256。
Windows:
@@ -25,20 +25,21 @@ Linux:
```bash
python3.12 -m venv .venv
./.venv/bin/python -m pip install \
-r requirements.txt \
-c constraints/python312-direct.txt
-r constraints/python312-linux-x86_64.lock
./.venv/bin/python -m pip check
```
约束文件只固定代码直接导入或启动的 `fastapi`、`lxml`、`numpy`、`pydantic`、
`scipy` 和 `uvicorn`。`uvicorn[standard]` 的可选传递依赖包含平台差异,因此仍由 pip
按目标平台解析;这套方案固定求解器和接口层的主要版本,但不是带 wheel 哈希的逐位
相同发布锁。若要测试 `requirements.txt` 声明的兼容范围,可显式省略 `-c`,但这类
结果不应与受约束环境的性能数据直接比较。
Linux 发布锁仅适用于兼容 manylinux_2_28 的 Linux x86_64 和 CPython 3.12。它启用
`--only-binary=:all:` 与 `--require-hashes`,因此不会静默改用源码包或未审计 wheel;
CI 和正式性能复测必须直接以 `-r` 安装该文件。Windows 或其他平台的开发环境继续
使用 `requirements.txt` 加 `constraints/python312-direct.txt`。若要测试
`requirements.txt` 声明的兼容范围,可显式省略约束,但这类结果不应与锁定环境的
性能数据直接比较。
升级参考版本时,应在干净的 Python 3.12 虚拟环境中同时安装范围文件和约束文件,
运行 `pip check` 与后端测试,再更新约束;不要从单个平台的 `pip freeze` 直接复制
所有传递依赖。
升级参考版本时,应在干净的 Python 3.12 Linux x86_64 虚拟环境中解析范围文件,
仅下载兼容 wheel,逐个记录 wheel 的 SHA-256,再从空环境安装发布锁并运行
`pip check`、依赖契约测试与后端测试。不能只复制 `pip freeze`,因为它既不证明
依赖来源,也不校验安装产物。
前端使用 Vite 8,需要 Node.js `20.19+` 或 `22.12+`。首次启动前安装前端依赖。
+329 -2
View File
@@ -31,6 +31,14 @@ from time import monotonic, perf_counter, process_time
from typing import Any
import xml.etree.ElementTree as ET
from app.simulation.physical_state_v21 import (
PhysicalStateV21Error,
evaluate_physical_state_v21,
load_approved_golden as load_physical_state_v21_golden,
physical_state_v21_applicable,
project_physical_state_v21,
)
try: # resource is unavailable on native Windows Python.
import resource
except ImportError: # pragma: no cover - Windows regression job
@@ -456,6 +464,50 @@ def load_regression_manifest(path: Path | str) -> dict[str, object]:
"Authoritative XML byte count does not match manifest source.bytes."
)
reference_archive = source.get("referenceArchive")
reference_archive_path: Path | None = None
reference_archive_sha256: str | None = None
if reference_archive is not None:
if not isinstance(reference_archive, Mapping):
raise RegressionManifestError(
"Manifest source.referenceArchive must be an object when declared."
)
archive_path_value = reference_archive.get("path")
archive_sha256 = reference_archive.get("sha256")
archive_bytes = reference_archive.get("bytes")
if (
not isinstance(archive_path_value, str)
or not archive_path_value
or not _valid_sha256(archive_sha256)
or not isinstance(archive_bytes, int)
or archive_bytes <= 0
or reference_archive.get("role") != "authoritativePhysicalBaseline"
):
raise RegressionManifestError(
"Manifest AMESim reference archive identity or role is incomplete."
)
reference_archive_path = (repository_root / archive_path_value).resolve()
if not reference_archive_path.is_relative_to(repository_root):
raise RegressionManifestError(
"AMESim reference archive must remain inside the repository."
)
try:
archive_payload = reference_archive_path.read_bytes()
except OSError as exc:
raise RegressionManifestError(
f"Could not read AMESim reference archive {reference_archive_path}: {exc}"
) from exc
if len(archive_payload) != archive_bytes:
raise RegressionManifestError(
"AMESim reference archive byte count mismatch."
)
actual_archive_sha256 = _sha256(archive_payload)
if actual_archive_sha256 != archive_sha256:
raise RegressionManifestError(
"AMESim reference archive hash mismatch."
)
reference_archive_sha256 = str(archive_sha256)
companion = source.get("companionProject")
companion_path: Path | None = None
if companion is not None:
@@ -627,6 +679,28 @@ def load_regression_manifest(path: Path | str) -> dict[str, object]:
correctness = manifest.get("correctness")
if not isinstance(correctness, dict):
raise RegressionManifestError("Manifest correctness must be an object.")
python_golden_role = correctness.get("pythonGoldenRole", "acceptanceGate")
if python_golden_role not in {
"acceptanceGate",
"determinismDiagnosticOnly",
}:
raise RegressionManifestError(
"correctness.pythonGoldenRole must be acceptanceGate or "
"determinismDiagnosticOnly."
)
physical_baseline_authority = correctness.get("physicalBaselineAuthority")
compare_amesim_on_every_run = correctness.get(
"compareAmesimOnEveryRun", False
)
if physical_baseline_authority is not None and (
physical_baseline_authority != "amesim"
or compare_amesim_on_every_run is not True
or reference_archive_path is None
):
raise RegressionManifestError(
"AMESim physical authority requires a validated reference archive "
"and compareAmesimOnEveryRun=true."
)
state_relative_tolerance = _finite_nonnegative(
correctness.get("stateRelativeTolerance"),
field="correctness.stateRelativeTolerance",
@@ -641,6 +715,9 @@ def load_regression_manifest(path: Path | str) -> dict[str, object]:
)
loaded_goldens: dict[str, dict[str, dict[str, object]]] = {}
loaded_physical_state_v21_goldens: dict[
str, dict[str, dict[str, object]]
] = {}
for case_id in sequence:
variant = variants[case_id]
assert isinstance(variant, dict)
@@ -654,6 +731,14 @@ def load_regression_manifest(path: Path | str) -> dict[str, object]:
raise RegressionManifestError(
f"Variant {case_id!r} has an invalid golden lane reference."
)
if python_golden_role == "determinismDiagnosticOnly" and (
reference.get("role") != "pythonDeterminismRegression"
or reference.get("affectsPhysicalCorrectness") is not False
):
raise RegressionManifestError(
f"Variant {case_id!r} Python golden must be marked as a "
"non-physical determinism regression."
)
golden_path_value = reference.get("path")
golden_sha256 = reference.get("sha256")
if (
@@ -704,6 +789,95 @@ def load_regression_manifest(path: Path | str) -> dict[str, object]:
)
loaded_goldens.setdefault(case_id, {})[lane_name] = golden
raw_physical_state_v21_goldens = variant.get(
"physicalStateV21Goldens", {}
)
if not isinstance(raw_physical_state_v21_goldens, dict):
raise RegressionManifestError(
f"Variant {case_id!r} physicalStateV21Goldens must be an object "
"keyed by lane."
)
for lane_name, reference in raw_physical_state_v21_goldens.items():
if lane_name not in lanes or not isinstance(reference, Mapping):
raise RegressionManifestError(
f"Variant {case_id!r} has an invalid physical-state-v2.1 "
"golden lane reference."
)
if physical_baseline_authority == "amesim" and (
reference.get("role") != "amesimPhysicalBaseline"
or reference.get("compareOnEveryRun") is not True
):
raise RegressionManifestError(
f"Variant {case_id!r} physical-state reference must be marked "
"as the per-run AMESim physical baseline."
)
golden_path_value = reference.get("path")
golden_sha256 = reference.get("sha256")
if (
not isinstance(golden_path_value, str)
or not golden_path_value
or not _valid_sha256(golden_sha256)
):
raise RegressionManifestError(
f"Variant {case_id!r} physical-state-v2.1 golden reference "
"is incomplete."
)
golden_path = (repository_root / golden_path_value).resolve()
if not golden_path.is_relative_to(repository_root):
raise RegressionManifestError(
"Physical-state-v2.1 golden must remain inside the repository."
)
try:
physical_state_golden = load_physical_state_v21_golden(golden_path)
except (OSError, PhysicalStateV21Error) as exc:
raise RegressionManifestError(
f"Could not load physical-state-v2.1 golden {golden_path}: {exc}"
) from exc
if physical_state_golden.get("_sha256") != golden_sha256:
raise RegressionManifestError(
f"Variant {case_id!r} physical-state-v2.1 golden hash mismatch."
)
if (
physical_state_golden.get("caseId") != case_id
or physical_state_golden.get("lane") != lane_name
or physical_state_golden.get("sourceXmlSha256") != expected_sha256
):
raise RegressionManifestError(
f"Variant {case_id!r} physical-state-v2.1 golden identity "
"does not match the manifest."
)
physical_provenance = physical_state_golden.get("provenance")
physical_amesim = (
physical_provenance.get("amesim")
if isinstance(physical_provenance, Mapping)
else None
)
if physical_baseline_authority == "amesim" and (
not isinstance(physical_amesim, Mapping)
or physical_amesim.get("archiveSha256")
!= reference_archive_sha256
):
raise RegressionManifestError(
f"Variant {case_id!r} AMESim baseline provenance does not "
"match source.referenceArchive."
)
expected_times = [float(value) for value in variant["checkpointTimes"]]
physical_state_checkpoints = physical_state_golden.get("checkpoints")
assert isinstance(physical_state_checkpoints, list)
golden_times = [
float(checkpoint["requestedTime"])
for checkpoint in physical_state_checkpoints
if isinstance(checkpoint, Mapping)
]
if expected_times != golden_times:
raise RegressionManifestError(
f"Variant {case_id!r} physical-state-v2.1 golden checkpoint "
"times differ from manifest."
)
loaded_physical_state_v21_goldens.setdefault(case_id, {})[
lane_name
] = physical_state_golden
_finite_positive(
execution.get("predictionSafetyFactor", 1.0),
field="execution.predictionSafetyFactor",
@@ -720,10 +894,14 @@ def load_regression_manifest(path: Path | str) -> dict[str, object]:
manifest["_manifestPath"] = str(manifest_path)
manifest["_repositoryRoot"] = str(repository_root)
manifest["_sourcePath"] = str(source_path)
manifest["_referenceArchivePath"] = (
str(reference_archive_path) if reference_archive_path is not None else None
)
manifest["_companionPath"] = (
str(companion_path) if companion_path is not None else None
)
manifest["_goldens"] = loaded_goldens
manifest["_physicalStateV21Goldens"] = loaded_physical_state_v21_goldens
return manifest
@@ -1105,7 +1283,7 @@ def summarize_simulation_result(
"eventTrace": event_trace,
"comparisonMode": "numericTolerance",
}
return {
summary = {
"success": bool(result.get("success")),
"status": result.get("status"),
"partial": bool(result.get("partial")),
@@ -1126,6 +1304,13 @@ def summarize_simulation_result(
"diagnostics": dict(diagnostic_mapping),
"eventTrace": event_trace,
}
if physical_state_v21_applicable(result):
summary["physicalStateV21"] = project_physical_state_v21(
result,
checkpoint_times=checkpoint_times,
sample_step=sample_step,
)
return summary
def _worker_control_listener(cancel_event: threading.Event) -> None:
@@ -1666,6 +1851,77 @@ def evaluate_regression_golden(
return audit
def evaluate_physical_state_v21_golden(
summary: Mapping[str, object] | None,
golden: Mapping[str, object] | None,
) -> dict[str, object]:
"""Evaluate the algebraic/discrete v2.1 contract when a manifest pins it."""
if golden is None:
return {
"configured": False,
"evaluated": False,
"passed": None,
"issues": [],
}
provenance = golden.get("provenance")
amesim_provenance = (
provenance.get("amesim") if isinstance(provenance, Mapping) else None
)
alignment = golden.get("amesimAlignmentAtGeneration")
audit: dict[str, object] = {
"configured": True,
"evaluated": False,
"passed": False,
"goldenId": golden.get("id"),
"goldenPath": golden.get("_path"),
"goldenSha256": golden.get("_sha256"),
"amesimArchiveSha256": (
amesim_provenance.get("archiveSha256")
if isinstance(amesim_provenance, Mapping)
else None
),
"amesimAlignmentAtGenerationPassed": (
alignment.get("passed") if isinstance(alignment, Mapping) else None
),
"issues": [],
"metrics": [],
}
if summary is None:
audit["issues"] = ["missingSummaryForPhysicalStateV21Golden"]
return audit
contract = summary.get("physicalStateV21")
if not isinstance(contract, Mapping):
audit["issues"] = ["missingPhysicalStateV21Contract"]
return audit
audit["evaluated"] = True
try:
evaluation = evaluate_physical_state_v21(contract, golden)
except PhysicalStateV21Error as exc:
audit["issues"] = ["invalidPhysicalStateV21Contract"]
audit["error"] = str(exc)
return audit
audit.update(evaluation)
audit.update(
{
"configured": True,
"evaluated": True,
"goldenId": golden.get("id"),
"goldenPath": golden.get("_path"),
"goldenSha256": golden.get("_sha256"),
"amesimArchiveSha256": (
amesim_provenance.get("archiveSha256")
if isinstance(amesim_provenance, Mapping)
else None
),
"amesimAlignmentAtGenerationPassed": (
alignment.get("passed") if isinstance(alignment, Mapping) else None
),
}
)
return audit
def _case_correctness_issues(
result: Mapping[str, object],
*,
@@ -1827,8 +2083,20 @@ def _case_correctness_issues(
if isinstance(golden_evaluation, Mapping):
raw_golden_issues = golden_evaluation.get("issues")
if isinstance(raw_golden_issues, list):
python_golden_role = correctness.get(
"pythonGoldenRole", "acceptanceGate"
)
accepted_issues = (
raw_golden_issues
if python_golden_role == "acceptanceGate"
else [
issue
for issue in raw_golden_issues
if issue in {"missingOutputContract", "outputContractMismatch"}
]
)
issues.extend(
str(issue) for issue in raw_golden_issues if isinstance(issue, str)
str(issue) for issue in accepted_issues if isinstance(issue, str)
)
return tuple(dict.fromkeys(issues))
@@ -1912,6 +2180,10 @@ def run_regression_suite(
assert isinstance(correctness, dict)
loaded_goldens = manifest.get("_goldens", {})
assert isinstance(loaded_goldens, dict)
loaded_physical_state_v21_goldens = manifest.get(
"_physicalStateV21Goldens", {}
)
assert isinstance(loaded_physical_state_v21_goldens, dict)
case_reports: list[dict[str, object]] = []
predecessor_completed = True
@@ -1988,6 +2260,45 @@ def run_regression_suite(
summary if isinstance(summary, Mapping) else None,
golden if isinstance(golden, Mapping) else None,
)
golden_evaluation["role"] = correctness.get(
"pythonGoldenRole", "acceptanceGate"
)
golden_evaluation["affectsPhysicalCorrectness"] = (
correctness.get("pythonGoldenRole", "acceptanceGate")
== "acceptanceGate"
)
raw_python_golden_issues = golden_evaluation.get("issues")
golden_evaluation["acceptanceIssues"] = (
list(raw_python_golden_issues)
if correctness.get("pythonGoldenRole", "acceptanceGate")
== "acceptanceGate"
and isinstance(raw_python_golden_issues, list)
else [
issue
for issue in (
raw_python_golden_issues
if isinstance(raw_python_golden_issues, list)
else []
)
if issue in {"missingOutputContract", "outputContractMismatch"}
]
)
case_physical_state_v21_goldens = loaded_physical_state_v21_goldens.get(
case_id, {}
)
physical_state_v21_golden = (
case_physical_state_v21_goldens.get(lane)
if isinstance(case_physical_state_v21_goldens, Mapping)
else None
)
physical_state_v21_evaluation = evaluate_physical_state_v21_golden(
summary if isinstance(summary, Mapping) else None,
(
physical_state_v21_golden
if isinstance(physical_state_v21_golden, Mapping)
else None
),
)
correctness_issues = (
_case_correctness_issues(
result,
@@ -1998,6 +2309,21 @@ def run_regression_suite(
if solver_completed
else ()
)
if solver_completed:
raw_v21_issues = physical_state_v21_evaluation.get("issues")
if isinstance(raw_v21_issues, list):
correctness_issues = tuple(
dict.fromkeys(
(
*correctness_issues,
*(
str(issue)
for issue in raw_v21_issues
if isinstance(issue, str)
),
)
)
)
report = {
"caseId": case_id,
"stopTime": stop_time,
@@ -2016,6 +2342,7 @@ def run_regression_suite(
"passed": solver_completed and not correctness_issues,
"issues": list(correctness_issues),
"regressionGolden": golden_evaluation,
"physicalStateV21Golden": physical_state_v21_evaluation,
},
}
if solver_completed and correctness_issues:
File diff suppressed because it is too large. Load diff
@@ -23,6 +23,8 @@ class TestMqlComparisonMetric:
max_abs_error: float
mean_abs_error: float
max_rel_error: float
undefined_rel_error_count: int
near_zero_baseline_count: int
final_abs_error: float
@@ -44,6 +46,10 @@ class TestMqlComparisonResult:
def max_rel_error(self) -> float:
return max((metric.max_rel_error for metric in self.metrics), default=0.0)
@property
def undefined_rel_error_count(self) -> int:
return sum(metric.undefined_rel_error_count for metric in self.metrics)
class TestMqlComparisonError(ValueError):
"""Raised when Python and AMESim series cannot be aligned."""
@@ -57,6 +63,16 @@ def compare_test_mql_series(
data_paths: tuple[str, ...] | list[str] | None = None,
relative_floor: float = 1.0e-12,
) -> TestMqlComparisonResult:
"""Compare current values directly with AMESim simulation values.
``relative_floor`` only identifies near-zero baselines for reporting. It is
never substituted into the relative-error denominator. An exact zero
AMESim baseline has undefined relative error and is counted separately;
absolute error remains available for judgement.
"""
if relative_floor < 0.0:
raise TestMqlComparisonError("relative_floor cannot be negative.")
_validate_time_axis(python_times)
selected_paths = _select_data_paths(python_series_by_data_path, amesim_results, data_paths)
metrics = []
@@ -70,11 +86,18 @@ def compare_test_mql_series(
amesim_values = amesim_results.series(data_path)
abs_errors = []
rel_errors = []
undefined_rel_error_count = 0
near_zero_baseline_count = 0
for time_value, python_value in zip(python_times, python_values):
amesim_value = interpolate_series_value(amesim_results.times, amesim_values, time_value)
abs_error = abs(python_value - amesim_value)
abs_errors.append(abs_error)
rel_errors.append(abs_error / max(abs(amesim_value), relative_floor))
if abs(amesim_value) <= relative_floor:
near_zero_baseline_count += 1
if amesim_value == 0.0:
undefined_rel_error_count += 1
else:
rel_errors.append(abs_error / abs(amesim_value))
final_amesim_value = interpolate_series_value(
amesim_results.times,
amesim_values,
@@ -87,6 +110,8 @@ def compare_test_mql_series(
max_abs_error=max(abs_errors, default=0.0),
mean_abs_error=sum(abs_errors) / max(len(abs_errors), 1),
max_rel_error=max(rel_errors, default=0.0),
undefined_rel_error_count=undefined_rel_error_count,
near_zero_baseline_count=near_zero_baseline_count,
final_abs_error=abs(python_values[-1] - final_amesim_value),
)
)
@@ -153,8 +178,19 @@ def write_test_mql_comparison_csv(
float(time_value),
)
abs_error = abs(python_value - amesim_value)
rel_error = abs_error / max(abs(amesim_value), 1.0e-12)
row.extend([python_value, amesim_value, abs_error, rel_error])
rel_error = (
None
if amesim_value == 0.0
else abs_error / abs(amesim_value)
)
row.extend(
[
python_value,
amesim_value,
abs_error,
"" if rel_error is None else rel_error,
]
)
writer.writerow(row)
summary_lines = [
@@ -163,6 +199,7 @@ def write_test_mql_comparison_csv(
f"max_abs_error={metric.max_abs_error:.12g}, "
f"mean_abs_error={metric.mean_abs_error:.12g}, "
f"max_rel_error={metric.max_rel_error:.12%}, "
f"undefined_rel_error_count={metric.undefined_rel_error_count}, "
f"final_abs_error={metric.final_abs_error:.12g}"
)
for metric in comparison.metrics
+31
View File
@@ -0,0 +1,31 @@
# Fully resolved release lock for CPython 3.12 on manylinux_2_28-compatible
# Linux x86_64. Resolved and wheel-verified on 2026-08-18.
#
# Install this file as a requirements file (with -r), not as a constraint
# (with -c). Each package is pinned to the wheel selected for the reference
# platform, and pip verifies that wheel against its SHA-256 digest.
--only-binary=:all:
--require-hashes
annotated-doc==0.0.5 --hash=sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101
annotated-types==0.8.0 --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0
anyio==4.14.2 --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494
click==8.4.2 --hash=sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76
fastapi==0.141.1 --hash=sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3
h11==0.16.0 --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
httptools==0.8.0 --hash=sha256:b15fc622b0f869d19207c4089a501d9bcc63ca5e071ffdd2f03f922df882dcb2
idna==3.18 --hash=sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2
lxml==6.1.1 --hash=sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818
numpy==2.5.2 --hash=sha256:3cdec01fa790a186d430433fdd4d4ffb70eed6f0eeb4bf05c8dbe2dce0a9bcb8
pydantic==2.13.4 --hash=sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba
pydantic-core==2.46.4 --hash=sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce
python-dotenv==1.2.3 --hash=sha256:904552145e8bfed22162c09dab1c2b9b54fefa7b23ba780f4f26ca0316b0f0d9
PyYAML==6.0.3 --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc
scipy==1.18.0 --hash=sha256:1f55797419e16e7f30cf88ffb3113ce0467f00cfe3f70d5c281730b21769bfc2
starlette==1.6.0 --hash=sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c
typing-extensions==4.16.0 --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8
typing-inspection==0.4.4 --hash=sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147
uvicorn[standard]==0.52.3 --hash=sha256:116af2710dbf47c80f463cd20ee4884b6662f4c9f227d797ddc7279d2fcc2c7c
uvloop==0.22.1 --hash=sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4
watchfiles==1.2.0 --hash=sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5
websockets==17.0.1 --hash=sha256:f47b0815af3948ec6a440b3afa02f05b18cc0939549e91b5c677b5d9c2c8472a
+102 -34
View File
@@ -6,8 +6,9 @@
> 基线模型:`tests/data/test_mql-full-branches-01-04.xml`
> 模型 SHA-256:`2fb95e65f5de0c85a6a17802aef74ea004087323fd00fd8d01acf0184ff71d48`
> 当前主固化目标:`tests/data/test-mql-8.xml`
> 主目标 XML SHA-256:`170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3`
> 配套项目 JSON:`tests/data/test-mql-8.json`,SHA-256 `258c50ee4850baa72fb7c2cc24536d0a631fc6a7f1fa6cedb7b6eea7c857cbaa`
> 主目标 XML SHA-256:`0a2d9331df9eb5974daec25a61c1238ba32b1742d933ffc8b16ce316c5627b0b`
> 配套项目 JSON:`tests/data/test-mql-8.json`,SHA-256 `b44bf540ccd1c293fe2af2b9b9052b540abf83961ad955a0f6a4ab40fbe0bb18`
> AMESim 权威物理基线归档:`AmesimModels/test_mql.ame`,SHA-256 `cbc3aadd4569a49b3a63e5d66d4143ec16126c0f950df73fb637e07673c20fbb`
## 1. 使用规则
@@ -17,6 +18,9 @@
- 只有同时完成代码、自动测试、基准复测和本文档更新后,任务才可标记为“已完成”。
- 每次性能对比必须记录代码提交、工作树状态、输入哈希、解释器与依赖版本、硬件和运行参数。
- 正确性门槛先于速度收益。若结果越过误差契约,即使运行更快也不能合入默认路径。
- AMESim 归档中的仿真结果是物理数值正确性的唯一基线;每次正式回归都必须按投影逐项计算并保存当前值、AMESim 基线值、绝对误差和相对误差。
- Python exact/state golden 仅用于检测确定性、实现漂移和输出契约变化,不得单独或与本地 physical golden 一起批准物理正确性。
- AMESim 基线为 0 时相对误差在数学上未定义,报告写为 `null` 并用绝对误差判定;AMESim 未保存的内部守恒量必须明确标记为不可外部比较,并继续执行独立绝对残差门。
- 容差、模型方程或输出字段发生变化时,必须单独说明;不得将其伪装成纯性能优化。
- 墙钟时间只在同一台机器、相同负载和相同环境下直接比较;跨环境以工作量计数和正确性指标为主。
- 每项优化都应保留明确的关闭开关或旧路径,直到新路径经过复杂模型和通用回归验证。
@@ -101,17 +105,17 @@
| 项目 | 主目标值 |
| --- | ---: |
| 运行组件 / 连接 | 152 / 174 |
| 动态组件 / 连续状态 | 54 / 124 |
| 代数未知量 / 方程 | 760 / 760 |
| ODE Jacobian 结构 | 3296 nnz / 52 色 |
| 因果 effort / flow 赋值 | 432 / 328 |
| secondary 代数块 / 未知量 | 12 / 352 |
| 结果变量 | 1,716 |
| 原始 `tStop / sampleStep / maxStep` | 0.2 / 0.01 / 0.01 s |
| 运行组件 / 连接 | 156 / 178 |
| 动态组件 / 连续状态 | 58 / 132 |
| 代数未知量 / 方程 | 776 / 776 |
| ODE Jacobian 结构 | 3280 nnz / 52 色 |
| 因果 effort / flow 赋值 | 440 / 336 |
| secondary 代数块 / 未知量 | 12 / 368 |
| 结果变量 | 1,784 |
| 原始 `tStop / sampleStep / maxStep` | 10 / 0.01 / 0.001 s |
| 信号断点 | 0.04、0.8 s |
本轮参考环境使用仓库 `.venv`:Python 3.12.3、NumPy 2.5.2、SciPy 1.18.0。`.python-version` 与 `constraints/python312-direct.txt` 已固定 Python 和六个直接依赖,README、CI 与依赖契约测试使用同一安装口径;它有意不锁平台相关传递依赖与 wheel 哈希,因此是可审计的参考约束,不是发布级逐位锁。机器可读 manifest 与 runner 分别位于 `tests/baselines/simulation/test_mql_8/manifest.json` 和 `app/simulation/benchmark_regression.py`;默认顺序为 `0.01 smoke → 0.2 → 1 → 5 → 10 s`,每档均有合作取消、硬终止、资源记录与后续档延迟门,且 `sampleStep` 与 `maxStep` 可按 lane 独立覆盖。
本轮正式环境使用仓库 `.venv`:Python 3.12.3、NumPy 2.5.2、SciPy 1.18.0。`.python-version` 与 `constraints/python312-direct.txt` 固定跨平台开发参考;`constraints/python312-linux-x86_64.lock` 则固定 Linux x86_64 发布环境的 22 个直接/传递包、wheel SHA-256,并强制 binary-only 与 hash 校验。README、CI 与依赖契约测试使用同一安装口径。机器可读 manifest 与 runner 分别位于 `tests/baselines/simulation/test_mql_8/manifest.json` 和 `app/simulation/benchmark_regression.py`;默认顺序为 `0.01 smoke → 0.2 → 1 → 5 → 10 s`,每档均有合作取消、硬终止、资源记录与后续档延迟门,且 `sampleStep` 与 `maxStep` 可按 lane 独立覆盖。
## 3. 总体验收协议
@@ -146,25 +150,51 @@
- stream/热流体迭代次数、物性缓存命中率、事件候选与定位次数。
- 输出标量数、编码字节数、传输字节数和后处理峰值内存。
### 3.4 P0 最大积分步长路径鲁棒性门
权威工程场景固定为 `test-mql-8.json` 经浏览器导出并由服务执行的同一系统语义,方法为 BDF。短基线使用
`tStop=0.2 s`、`sampleStep=0.01 s`、`maxStep=0.001 s`;长基线只将 `tStop` 延长到
`10 s`。`sampleStep` 是输出网格,`maxStep` 是积分步长上限,报告与诊断不得混用两者。
- **时域延长不变量**:固定模型、容差、方法和 `maxStep` 时,如果较短的 `tStop=T1` 能完成,则
`T2>T1` 的运行不得因数值错误在 `T1` 之前提前结束。只比较严格位于 `T1` 之前的公共检查点;
短任务终点的 accepted endpoint 与长任务内部插值单独标记,不作位级误判。
- **步长细化可解性不变量**:在约定工程区间内,如果较大的 `maxStep` 能完成同一时域,则更小
`maxStep` 不得反而出现不可恢复数值失败。更小上限可以更慢;若仅因工作量增加超过预算,必须归类为
`budget_limited` 并证明仍持续推进,不能记为 solver failure 或借此选择一个“幸运步长”。
- [ ] 首先对账 JSON、浏览器生成的 System XML、服务请求和 worker 最终生效参数;保存输入哈希,禁止用 runner 的内存覆盖掩盖浏览器路径问题。
- [ ] 浏览器当前工程验收明确为失败:`0.2 s / 0.001 s` 在 `t≈0.0489 s` 被判定计算超时;离线 production runner 的完成记录只作对照,不能写成浏览器正常完成。必须用同一 JSON 复现并分类为内部慢步、求解器真停滞、后处理/传输或服务假超时。
- [ ] 第一阶段在其余参数不变时串行运行 `tStop={0.2,1} s × maxStep={0.001,0.002,0.005,0.01,0.02} s` 的短时二维矩阵;最终签收要求所有单元到达终点,不得出现 NaN/Inf、不可恢复数值错误或无解释回退。定位阶段允许记录有持续进展证据的 `budget_limited`,但它既不算数值失败,也不算通过。
- [ ] 短时二维矩阵通过后,再按相同契约依次延长到 `2 s → 5 s → 10 s`;每一档先运行权威 `0.001 s`,再运行代表性的 `0.005/0.02 s`,最后补齐其余步长。每一档先确认同 `maxStep` 公共前缀,再进入下一档,不因较长 `tStop` 改变已覆盖区间的成败结论。
- [ ] 跨 `maxStep` 不要求逐位相同;按积分状态、派生量、关键压力/流量、守恒量、事件和离散模式分别验收,近零派生加速度不得使用统一相对误差误判真实积分状态。
- [ ] 每个 `0.01 s` 模拟区间记录墙钟、实际 `h_abs`/BDF 阶次、接受/拒绝步、重启、`nfev/njev/nlu`、Jacobian 构建、stream/热流体闭合与恢复轨迹;性能悬崖必须能定位到具体阶段和组件。
- [ ] 内部 RHS、Jacobian、stream/热流体闭合或恢复循环仍有活动时,服务持续发送活动心跳,浏览器不得因 60 s 内没有接受步而误判卡死;真正无活动必须有界终止并报告最后阶段、时刻、步长和计数。
- [ ] 将终止结果明确分类为 `numerical_failure`、`service_timeout_worker_active`、`active_slow_trial`、`true_stall` 或 `budget_limited`;浏览器超时始终属于工程路径未通过,但在证据不足时不得冒充数值失败。
- [ ] 任一会改变数值路径、事件语义、容差或默认求解策略的修复,必须先形成“复现证据 → 首个异常阶段 → 根因假设 → 最小方案 → A/B 判据 → 回退方式”,提交审阅后再实施;每轮只修改一个概念并先复跑原失败单元。
- [ ] 在 `0.2/1 s` 短时门通过后,依次取得 `2/5/10 s` 数据;在对本次长跑做任何性能调优前,先完整取得一次 `10 s / 0.001 s` 原始基线,完成后才按时间线选择最小优化并复跑完整阶梯。
该门的目标不是寻找一个“碰巧能跑”的固定 `maxStep`,也不是要求所有步长得到位级相同轨迹,而是让合理工程区间内的
`maxStep` 只影响可解释的误差与成本,不决定仿真能否完成。
## 4. 优化任务总览
优先级定义:`P0` 为基线或正确性前置,`P1` 为主要性能收益,`P2` 为第二阶段,`P3` 为战略性或条件性工作。
| ID | 优先级 | 任务 | 当前状态 | 难度 | 预期价值 | 主要依赖 |
| --- | --- | --- | --- | --- | --- | --- |
| OPT-00 | P0 | 固化复现、环境和回归基线 | 部分实现(基础闭环) | 中 | 很高 | 无 |
| OPT-00 | P0 | 固化复现、环境和回归基线 | 重新打开(离线基线完成;浏览器路径失败待定位) | 中 | 很高 | 无 |
| OPT-01 | P1 | 完成因果代数内核与坐标消元 | 基本完成(主要矛盾闭环) | 中高 | 中高 | OPT-00 |
| OPT-02 | P1 | 建立扁平数值 IR 和数组执行内核 | 部分实现(参考 IR) | 很高 | 很高 | OPT-01 |
| OPT-03 | P1 | 稀疏 Jacobian 数值层与解析/半解析演进 | 部分实现 | 很高 | 很高 | OPT-00;解析链可与 OPT-02 分阶段 |
| OPT-04 | P1 | stream 拓扑传播与物性成组复用 | 部分实现 | 中高 | 中高 | OPT-00 |
| OPT-05 | P1 | 状态缩放、分量容差和步长策略 | 部分实现(可恢复试探步) | 中高 | 中高 | OPT-00 |
| OPT-05 | P0/P1 | 最大积分步长路径鲁棒性、状态缩放和步长策略 | 进行中(P0 阻断) | 中高 | 很高 | OPT-00 |
| OPT-06 | P2 | 事件检测与 dense output 按需化 | 部分实现 | 中 | 中 | OPT-00 |
| OPT-07 | P2 | 输出、后处理和传输内存优化 | 未开始 | 中 | 中高(长仿真) | OPT-00 |
| OPT-08 | P2 | 进度、取消和服务并发鲁棒性 | 部分实现 | 中 | 中 | OPT-00 |
| OPT-09 | P0/P2 | 建立 10 s 长时验证与模式覆盖 | 进行中 | 中高 | 很高 | OPT-00 |
| OPT-08 | P0/P2 | 进度、取消和服务并发鲁棒性 | 进行中(浏览器 `0.0489 s` 失败待定位) | 中 | 很高 | OPT-00、OPT-05 P0 门 |
| OPT-09 | P0/P2 | 建立 10 s 长时验证与模式覆盖 | 进行中(权威 `0.001 s` 基线未完成) | 中高 | 很高 | OPT-00、OPT-05 P0 门、OPT-08 服务门 |
| OPT-10 | P3 | 明确高指数 DAE/强非光滑系统边界 | 未开始 | 很高 | 条件性 | OPT-09 |
推荐实施顺序:`OPT-00 → OPT-03/OPT-01 → OPT-04/OPT-05 → OPT-02 → OPT-06/OPT-07/OPT-08 → OPT-09`。其中 OPT-02 与 OPT-03 可先做最小原型,再根据端到端数据调整顺序。
当前推荐实施顺序:`OPT-00 浏览器失败复现 → OPT-05/OPT-08 定位并解除 0.2 s 阻断 → 0.2/1 s × 五档 maxStep 短时鲁棒性门 → 2/5/10 s 逐级延长 → OPT-09 首次 10 s / 0.001 s 完整基线 → 基于时间线提案、审阅、单项优化与完整复验`。OPT-01/02/03/04 的既有成果保留,但在该 P0 工程门通过前不以单点性能收益替代鲁棒性验收。
## 5. 详细任务
@@ -172,42 +202,56 @@
**目标**:先让“是否更快、是否仍正确、是否又卡住”可以稳定复现和自动判断。
**当前状态**:P0 基础设施与新主目标的有界 `0.2 s` 基线已经闭环:权威 XML/JSON、参考依赖约束、仓库内 runner、状态 golden、输出形状契约、三层 CI 和机器可读报告均已建立。完整 OPT-00 仍缺发布级依赖锁、远端 CI 首次实跑、历史 `2.10 s` 三次复测以及更长时域的批准 golden。
**当前状态**:重新打开工程端到端复核。2026-08-18 的离线 production runner、发布锁、golden、physical-state-v2.1、历史 `2.10 s` 三次复测和本地自动测试证据继续有效;但浏览器使用权威 JSON、BDF、`tStop=0.2 s`、`sampleStep=0.01 s`、`maxStep=0.001 s` 时当前会在 `t≈0.0489 s` 被判定计算超时,工程验收结果为失败。离线 runner 的完成记录不能替代浏览器结果;同 `maxStep` 的时域延长不变量和减小 `maxStep` 的可解性不变量也尚未验收。
**工作项**:
- [x] 将新主目标 XML/JSON 放入固定 fixture 路径,并在 manifest/测试中校验双哈希、字节数和配对配置;提交本轮工作时必须一并纳入版本控制。
- [x] 建立 Python 3.12.3 与六个直接依赖的跨平台参考约束,并在 CI 中校验;发布级传递依赖/wheel 哈希锁仍待后续。
- [x] 建立 Python 3.12.3 与六个直接依赖的跨平台参考约束,并建立 Linux x86_64 的 22 个直接/传递包、binary-only wheel SHA-256 发布锁;空 venv 离线安装、`pip check` 与依赖契约均通过。
- [x] 将临时探针整理为仓库内可重复运行的 benchmark,不依赖 `/tmp` 文件。
- [x] 添加 `0.81 s` 和仅改 `tStop=2.10 s` 的历史标准运行入口。
- [x] 添加模型结构快照断言;结构有意变化时显式更新原因。
- [x] 建立 `physical-state-v2` 的首批 state/checkpoint/event 投影,并批准 production `0.2 s` golden。
- [ ] 将关键压力、流量、守恒量和离散模式加入 `physical-state-v2.1` 数值投影。
- [x] 建立 `physical-state-v2` 的首批 state/checkpoint/event 投影;其 Python golden 现仅作为 production `0.2 s` 的确定性与实现回归诊断。
- [x] 将关键压力、质量流量、三类质量守恒、总储气质量和离散模式加入 `physical-state-v2.1`,绑定 AMESim 单位/符号变换,并在每次运行时以 AMESim reference values 执行物理门。
- [x] 将无数值的完整输出形状契约与物理状态 golden 分开;完整 API 序列化契约若需逐字段稳定性,后续另行定义。
- [x] 建立短 CI、夜间 `0.81/2.10 s`、定期递进至 `10 s` 的三层 workflow;远端首次执行待提交后确认。
- [x] 保存带环境、仓库、输入、运行统计和验收结果的机器可读 JSON 报告。
- [ ] 以权威 JSON 经浏览器生成 XML 并走流式 API 的真实路径完成 `0.2 s / 0.001 s`,与 production worker 对账输入哈希、生效参数、事件、物理量和最终结果;定位并消除 `t≈0.0489 s` 的计算超时或假超时。
- [ ] 通过第 3.4 节的 `0.2/1 s × 五档 maxStep` P0 短时矩阵与两条不变量;既有 `0.01/0.02/0.05/0.10 s` 历史运行不能替代缺失的 `0.001 s` 工程验收。
**验收条件**:
- [ ] 干净环境一条命令可复现;当前参考约束、`pip check`、soft/hard timeout、worker error 与 correctness failure 分类已完成,独立无进度看门狗、服务级故障分类和发布级空环境重建尚未验收。
- [ ] 正式环境连续 3 次完成 `2.10 s`,结果满足数值契约且无非预期回退。
- [x] 干净环境可按发布锁一条安装命令复现:全新空 venv 使用 22 个锁定 wheel 与 SHA-256 完成安装,`pip check`、锁定环境契约和最终 quick workflow 同口径测试通过。
- [x] 正式 production 环境严格串行 3 次完成 `0.81/2.10 s`;两档检查点、状态、事件、输出契约和除计时外的诊断逐值一致,无非有限值或非预期回退。
- [x] 新主目标 `0.2 s` 性能报告完整记录环境、提交、工作树、输入哈希和统计口径。
- [ ] 浏览器/服务/worker 三条路径对权威 `0.2 s / 0.001 s` 均能完成且不会发生无证据的 60 s 假停滞;真实无活动仍能有界退出并提供诊断。
**前后对比**:
| 指标 | 当前 | 完成后 |
| --- | --- | --- |
| 正式锁定环境 | 无 | Python 3.12.3 + 直接依赖参考约束;发布锁待补 |
| 复杂模型自动回归 | 部分 | 新主目标 0.01/0.2/1/5/10 递进 + 历史 0.81/2.10 入口 |
| 物理解哈希 | 环境相关 v1 | production 0.2 state golden + 独立 output shape contract |
| `2.10 s` 连续成功率 | 单次证据 | 待填 |
| 正式锁定环境 | Python 3.12.3 + 22 包 hash lock | 空 venv 安装、`pip check`、依赖契约通过 |
| 复杂模型自动回归 | 新主目标 0.01/0.2 分层门禁 | 1/5/10 递进入口 + 历史 0.81/2.10 production 入口 |
| 物理解哈希 | 142 个状态键 × 3 检查点 | Python state golden 作确定性诊断 + output shape + AMESim 当次相对误差物理门 |
| `2.10 s` 连续成功率 | 3/3 | worker 墙钟 113.497–115.868 s,逐值一致且 0 回退 |
#### 2026-08-18 / AMESim 权威基线对齐与 OPT-00 收口
- 状态:部分实现 → 已完成(本地验收)。AME 归档固定 SHA-256 `cbc3aadd...`;XML/JSON 修正后的 SHA-256 分别为 `0a2d9331...`、`b44bf540...`,仿真配置统一为 `0→10 s / sampleStep=0.01 s / maxStep=0.001 s`。
- 权威契约:直接解析 AME 的 117 个 COMP 与 40 个建模 LINE,推导出 157 个项目节点、178 条连接、20 类组件和 1092 个参数;单位转 SI、表压转绝压、公式等价、DIRECT/接触/线模型拓扑以及 XML↔JSON 逐 ID/端口均有自动测试。
- 权威物理门:production `0.2 s` 每次都把当前 physical-state-v2.1 投影直接与 AMESim reference values 比较并保存相对误差;25 项参与判定,2 个 `t=0.04 s` 跳变流量保留误差但因左右极限语义不参与判定,另有 6 项无 AMESim 数据的内部守恒量单独执行绝对残差门。Python 142 个状态键 × 3 个检查点的 426 值 golden 仅作确定性诊断,不再批准物理正确性。
- 历史最终报告:`runs/2026-08-18-production-opt00-approved-replay-0.2.json`,SHA-256 `2e27cd54...`;worker/orchestration 墙钟 `159.607/160.473 s`。其中 Python 值零重放误差是确定性证据;物理结论以该次结果对 AMESim 的最差相对误差 `0.1393485%` 为准,最大质量守恒残差为 `1.82146e-17 kg/s`。
- 历史稳定性:production `0.81/2.10 s` 严格串行运行三次,所有 case 通过;`2.10 s` worker 墙钟分别为 `114.075/113.497/115.868 s`,机械事件时刻、检查点、1200 个状态值和除性能计时外的诊断逐值一致。
- 环境与自动化:新增 22 包 Linux x86_64 hash lock,并在全新空 venv 完成离线安装和 `pip check`;最终 quick workflow 同口径为 179 项通过(2 项预期跳过),完整后端为 849 项通过(3 项预期跳过)。
- 递进边界:由最终 `0.2 s` worker 时间按 `×5×1.5` 外推,`1 s` 为 `1197.053 s`,略低于 1200 s soft budget,因此记录为下一阶段 `eligible`;本次 OPT-00 不启动 1/5/10 s,后续长时递进仍归 OPT-09。
- 远端说明:workflow 已使用相同 hash lock 与测试命令;本轮未获授权提交/推送,因此没有声称远端 CI 已运行,提交后的首次托管运行作为运营证据补充。
#### 2026-08-17 / `test-mql-8` 固化 runner v2
- 状态:进行中 → 部分实现(P0 基础闭环)。新权威 XML/JSON、双哈希、结构快照、参考环境约束、分层 manifest、机器可读报告、批准的 production `0.2 s` golden 和仓库内 runner 已建立;发布级依赖锁、关键代数量投影以及该里程碑时尚未运行的 1/5/10 s 结果仍待后续。
- runner 行为:默认严格按 `0.01 smoke → 0.2 → 1 → 5 → 10 s` 递进;smoke 不参与耗时外推。soft deadline 先经 stdin 合作取消,hard deadline 再 terminate/kill;失败、超时、物理验收失败或下一档预测超过预算时,剩余档位统一标记 `deferred`。
- 已执行正确性门:完成并到达终点、非空且全有限的输出序列、采样时间严格递增、检查点及状态值、最大缩放残差、预期信号事件及其实际积分分段、机械切换次数/时刻、golden 来源报告与布局哈希、逐状态容差比较和独立 output-shape contract。
- 两条 lane:manifest 中 `solver-only` 在内存把 `sampleStep` 改为 0.02 s,并把 `maxStep` 固定为 0.05 s,用于算法迭代;`production` 的 `sampleStep/maxStep` 均使用权威 XML 源值,当前为 0.01/0.01 s。max-step 矩阵可再显式覆盖单次运行的 `maxStep`;所有覆盖都只发生在内存,不改写 XML。
- 两条 lane:该 2026-08-17 里程碑的 manifest 中,`solver-only` 在内存把 `sampleStep` 改为 0.02 s,并把 `maxStep` 固定为 0.05 s,用于算法迭代;当时 SHA `170463d6...` 的 `production` 源值为 `sampleStep/maxStep=0.01/0.01 s`。当前 2026-08-18 权威 AME/XML/JSON 已统一为 `sampleStep/maxStep=0.01/0.001 s`;旧报告仅作历史证据。
- 进程鲁棒性:软取消、硬终止、子进程提前关闭 stdin 的 BrokenPipe 和 stdout/stderr 资源清理均有自动测试。
- 备份:`backup/general-solver-v1-before-20260817-16a7eb2` 精确指向进入本轮前的 `16a7eb2d6c2f01b23e3bdc7781a6cf6cc3fbe369`。
- P0 证据:`tests/baselines/simulation/test_mql_8/runs/2026-08-17-production-v2-0.2.json`、`goldens/production-0.2s-v1.json` 与 `runs/2026-08-17-production-v2-extension-decision.json`。
@@ -222,7 +266,7 @@ PYTHONPATH=. .venv/bin/python -m app.simulation.benchmark_regression \
--output tests/baselines/simulation/test_mql_8/runs/latest-production.json
```
正式验收默认使用 `production`,从而执行已批准的 0.2 s 数值 golden;算法迭代若需降低输出成本,可显式改为 `--lane solver-only`。仅重跑首个正式基线档可加 `--case 0.2s`。
正式验收默认使用 `production`,从而对 `0.2 s` 当前结果执行 AMESim 权威物理基线比较;Python 数值 golden 同时输出确定性诊断但不作为物理通过依据。算法迭代若需降低输出成本,可显式改为 `--lane solver-only`。仅重跑首个正式基线档可加 `--case 0.2s`。
命令退出码约定:`0` 表示所有选定档完成,`2` 表示依据预算安全暂缓后续档,`1` 表示运行失败或正确性验收失败。显式选择 `1s/5s/10s` 时,runner 仍会自动补齐并先执行所有较短前置档。
### OPT-01 完成因果代数内核与坐标消元
@@ -502,11 +546,11 @@ PYTHONPATH=. .venv/bin/python -m app.simulation.benchmark_regression \
| `2.10 s` 压力闭合 | 57,601 | 待填 |
| 物性调用 / 缓存命中率 | 待测 | 待填 |
### OPT-05 状态缩放、分量容差和步长策略
### OPT-05 最大积分步长路径鲁棒性、状态缩放和步长策略
**目标**:减少量纲差异造成的不必要小步和 Jacobian 重建,同时维持事件与守恒精度。
**目标**:首先保证在合理 `maxStep` 工程区间内,步长上限只影响可解释的误差和成本,而不决定仿真能否完成;随后再减少量纲差异造成的不必要小步和 Jacobian 重建,同时维持事件与守恒精度。
**当前状态**:模型中不同物理量的量级差异大。历史试验显示机械绝对容差放宽可能带来约 16% 收益,但属于精度策略变化;热流体固定点容差的简单放宽曾使表现变差,不能直接采用。当前已先完成不改变容差契约的可恢复试探步:按积分器实际 `h_abs` 对半退避,并在首次接受后恢复分段步长上限。Generic 显式 opt-in,即使模型无状态事件、断点或取消回调,也会进入支持重建的 stepwise 路径;普通 `integrate_ode` 调用者的默认路径不变。分量 `atol`、缩放和标准/快速配置仍未开始。
**当前状态**:P0 阻断、先定位。模型中不同物理量的量级差异大。历史试验显示机械绝对容差放宽可能带来约 16% 收益,但属于精度策略变化;热流体固定点容差的简单放宽曾使表现变差,不能直接采用。当前已先完成不改变容差契约的可恢复试探步:按积分器实际 `h_abs` 对半退避,并在首次接受后恢复分段步长上限。Generic 显式 opt-in,即使模型无状态事件、断点或取消回调,也会进入支持重建的 stepwise 路径;普通 `integrate_ode` 调用者的默认路径不变。权威浏览器场景 `BDF / 0.2 s / sampleStep=0.01 s / maxStep=0.001 s` 当前在 `t≈0.0489 s` 计算超时,已构成工程失败;尚须用同 JSON 的 browser/API/worker A/B 定位其属于数值热点、服务假超时或二者叠加。既有 `2 s` 矩阵没有覆盖 `0.001 s`,也未系统验证时域延长与步长细化不变量,不能替代本次 P0 门。
**工作项**:
@@ -516,6 +560,12 @@ PYTHONPATH=. .venv/bin/python -m app.simulation.benchmark_regression \
- [ ] 统计限制步长的状态分量、误差拒步和 Jacobian 重建原因。
- [ ] 对事件前后、接触临界区和稳态区分别评估步长上限策略。
- [ ] 建立严格/标准/快速配置,但默认配置必须有明确精度契约。
- [ ] 用完全相同的 JSON 生成请求,对浏览器流式路径与 production worker 做同参 A/B,逐层核对 requested/effective `tStop/sampleStep/maxStep/method`。
- [ ] 围绕 `t=0.04–0.05 s` 按模拟区间记录 BDF 阶次、实际 `h_abs`、接受/拒绝步、重启、Jacobian/RHS/闭合耗时及最坏组件,定位 `0.0489 s` 的首次性能悬崖。
- [ ] 在改变算法前提交定位报告与候选方案供审阅,明确证据、影响面、正确性风险、预期计数变化和回退方式;获准后只实施一个概念,并以原失败单元及同一行/列相邻配置做 A/B。
- [ ] 审计失败试探的事务恢复、缓存、端口和离散模式,证明失败点不会污染下一条缩步路径。
- [ ] 只在证据支持时对事件、接触、流向或闭合边界使用局部步长上限、有界缩步或模式感知策略;禁止靠全程硬编码某个“幸运” `maxStep` 收口。
- [ ] 将第 3.4 节的 `0.2/1 s × 五档` 二维矩阵设为每次步长/缩放改动的 P0 回归;短时通过后再按 `2/5/10 s` 逐级验证公共前缀。
- [x] 对可恢复的热流体闭合失败使用积分器实际试探步 `h_abs` 对半回退;最多 16 次且不低于 64 ULP,恢复步仅设置 `first_step`,首次接受后恢复分段 `maxStep` 上限并记录 attempted/next step。
- [x] 为 eventless Generic 显式启用 `recoverable_trial_retries`,使没有状态事件、断点或取消回调的通用模型也能选择 stepwise 恢复;该参数默认关闭,避免改变其他调用者的直接 `solve_ivp` 语义。
@@ -524,6 +574,7 @@ PYTHONPATH=. .venv/bin/python -m app.simulation.benchmark_regression \
- [ ] 每个配置都有状态、事件、残差和守恒误差界限。
- [ ] 标准配置在复杂模型上减少拒步或分解工作,不引入模式遗漏。
- [ ] 所有收益报告同时给出误差变化,禁止只报告墙钟。
- [ ] 满足第 3.4 节 P0 门:短时二维矩阵满足时域延长与步长细化不变量,事件/模式/守恒满足分类契约,任何性能悬崖都有可复现的阶段与组件归因。
### OPT-06 事件检测与 dense output 按需化
@@ -570,7 +621,7 @@ PYTHONPATH=. .venv/bin/python -m app.simulation.benchmark_regression \
**目标**:区分“内部慢步”和“真正无进度”,并让长任务可取消、可限流、不会拖垮服务进程。
**当前状态**:已有 stream 进度和取消检查;前端无进度阈值约 60 s。历史 2.05 s 附近可见最大间隔约 7.5 s,且中间有接受步与 CPU 活动,因此没有触发真实无进度条件。当前又补充了热流体失败位置、迭代尾部、最差端口及求解器恢复轨迹;`5 s / maxStep=0.05 s` 在 1200 s soft budget 后合作取消并保留 `t=4.2523535 s` 的部分诊断,属于预算终止而非 solver failure。单格 max-step 矩阵不再把“没有跨步长比较对”误判为失败。
**当前状态**:P0 服务门进行中。已有 stream 进度和取消检查;前端无接受步进度阈值约 60 s。历史 2.05 s 附近可见最大间隔约 7.5 s,且中间有接受步与 CPU 活动,因此没有触发真实无进度条件。当前又补充了热流体失败位置、迭代尾部、最差端口及求解器恢复轨迹;`5 s / maxStep=0.05 s` 在 1200 s soft budget 后合作取消并保留 `t=4.2523535 s` 的部分诊断,属于预算终止而非 solver failure。单格 max-step 矩阵不再把“没有跨步长比较对”误判为失败。权威 `0.2 s / 0.001 s` 浏览器运行当前在 `t≈0.0489 s` 计算超时,浏览器工程门已经失败;但根因尚未确定,必须追踪前端 60 s 计时器、NDJSON 心跳、API worker 与内部求解活动,不能直接等同于求解器死锁。
**工作项**:
@@ -580,6 +631,8 @@ PYTHONPATH=. .venv/bin/python -m app.simulation.benchmark_regression \
- [ ] 限制并发仿真 worker、队列长度和单任务 CPU/内存预算。
- [ ] 超时报告最后活动阶段、模拟时刻、步长和关键计数,而非只返回通用错误。
- [ ] 添加故意慢 RHS、死循环防护、客户端断连和多任务竞争测试。
- [ ] 将活动心跳从“接受新积分步”扩展到 RHS、Jacobian 构建、stream/热流体闭合和恢复循环;至少携带 phase、wall time、last accepted time、current RHS time、`h_abs`、`nfev` 与闭合迭代。
- [ ] 验证客户端流断开、浏览器本地 watchdog 和显式取消的语义不同;客户端误判或断连不得在无用户授权时静默丢失仍健康运行的 worker 结果。
- [x] 热流体失败记录 RHS 时刻、最近迭代尾部、最大增量/尺度/容差、最差端口及带符号差值,并保留求解器逐次恢复的 attempted/next step 与原因。
- [x] 矩阵报告分别记录外层 `soft_timeout` 和 worker 的合作 `cancelled`,避免把预算取消误记为求解器数值失败。
- [x] 单格 max-step 矩阵将空的跨步长比较集合视为“不适用”而非失败;最终 `2 s / 0.02 s` 单格复验整体通过且 `comparisonFailureCount=0`。
@@ -589,12 +642,13 @@ PYTHONPATH=. .venv/bin/python -m app.simulation.benchmark_regression \
- [ ] 正常慢步不会被误判为死锁,真实无活动能在约定时间内终止并给出诊断。
- [ ] 取消请求在每个主要阶段都能在有界时间内生效。
- [ ] 并发压力下服务仍能响应健康检查和新请求拒绝/排队逻辑。
- [ ] 权威 `0.2 s / 0.001 s` 浏览器路径完成且不发生假超时;只要内部活动持续,60 s 无接受步不得触发 `SOLVER_STALLED`,真正无活动仍能按约定上限停止并返回最后阶段与计数。
### OPT-09 建立 10 s 长时验证与模式覆盖
**目标**:用实测替代“0.81 s 或 2.10 s 可以外推到 10 s”的假设。
**目标**:用权威 JSON 的 `BDF / tStop=10 s / sampleStep=0.01 s / maxStep=0.001 s` 完整实测,替代“短仿真或较大 `maxStep` 可以外推到最终工程场景”的假设。
**当前状态**:新主目标的 solver-only `1 s / maxStep=0.05 s` 已完成,worker 墙钟 `182.111 s`。修复前,`tStop=2 s` 与 `tStop=5 s` 在同一 `maxStep=0.05 s` 下具有相同的首次失败时刻和求解统计,均在 `t=1.859512845 s` 耗尽热流体外层 25 次;四档 `maxStep` 的失败时刻集中在 `1.8595–1.8603 s`。这说明远端 `tStop` 不是直接失败原因,它只决定运行是否到达该局部数值困难区。
**当前状态**:权威 `0.001 s` 长基线尚未完成。既有 `10 s / maxStep=0.02 s` 只作为算法可行性、恢复机制和历史性能证据,不能签收当前 JSON 的工程基线。此前新主目标的 solver-only `1 s / maxStep=0.05 s` 已完成,worker 墙钟 `182.111 s`。修复前,`tStop=2 s` 与 `tStop=5 s` 在同一 `maxStep=0.05 s` 下具有相同的首次失败时刻和求解统计,均在 `t=1.859512845 s` 耗尽热流体外层 25 次;四档 `maxStep` 的失败时刻集中在 `1.8595–1.8603 s`。这说明远端 `tStop` 不是直接失败原因,它只决定运行是否到达该局部数值困难区。
PNL00R stream 语义、单次 RHS 事务回滚和基于实际试探步的恢复完成后,production `2 s` 的 `maxStep=0.01/0.02/0.05/0.10 s` 四个单元均到达 `2.0 s`,`caseFailureCount=0`。矩阵命令整体退出码仍为 1,原因是跨 `maxStep` 的严格状态一致性门未通过,而不是任何单元运行失败:差异集中在事件后的 8 个 MECMAS21 速度和 8 个加速度;在差异最大的一组跨 `maxStep` 终点比较中,绝对差约 `1.01e-6–1.12e-6`。`0.05/0.10 s` 两档则逐位一致。因此当前结论是“2 s 运行失败已解决”,但“跨步长数值等价”尚未签收,不能据此批准长时 golden。
@@ -633,18 +687,24 @@ PNL00R stream 语义、单次 RHS 事务回滚和基于实际试探步的恢复
**工作项**:
- [ ] 在 OPT-00 的 `0.2/1 s` 短时二维门通过后,使用同一 JSON 和 `maxStep=0.001 s` 依次延长到 `2/5/10 s`;在任何针对本次长跑的性能调优前先完整运行一次权威 `10 s / 0.001 s` 原始基线。
- [ ] 首次长基线不得因总墙钟较长而提前当作性能失败;只有 worker、CPU 和内部活动心跳均停止并满足真停滞条件时才有界终止。若发现致命正确性问题,只做使基线可完成的最小修复,然后从 `t=0` 重新运行。
- [ ] 在正式锁定环境运行未优化基线 `10 s`,设置心跳、资源上限和可恢复日志。
- [ ] 保存事件、模式、步长、拒步、Jacobian、闭合和内存随模拟时间的时间线。
- [ ] 为长跑设置阶段性检查点,支持定位首次偏差而非只比较终点。
- [ ] 将每项 P1 优化分别加入 `10 s` A/B,不把多个改动混成一个结果。
- [ ] 根据首次基线制定合理的 CI 频率和资源门槛。
- [x] 最终通用接线后的当前工作树完成首次 `10 s / maxStep=0.02 s` 单次运行并保存完整统计;连续 3 次验收仍待后续。
- [ ] 首次 `10 s / maxStep=0.001 s` 完整报告生成后,才根据各阶段墙钟与内部计数决定性能优化目标;旧 `0.02 s` 报告不得用于跳过该顺序。
**验收条件**:
- [ ] 权威 `10 s / 0.001 s` 首次基线到达 `t=10`,输出 `0..10 s` 共 1001 个采样时刻且全部有限;事件、模式、关键状态、压力/流量和守恒量满足契约。
- [ ] 连续 3 次完成 `10 s`,没有无解释回退、NaN/Inf 或资源失控。
- [ ] 全程模式、事件、关键状态和守恒量满足契约。
- [ ] 可从日志快速判断任何慢区属于积分、Jacobian、闭合、事件还是输出。
- [ ] 同一 `maxStep=0.001 s` 下,`0.2/1/2/5/10 s` 的严格公共前缀按分类契约一致;短任务终点单独标记 terminal,不与长任务内部插值作位级误判。
- [ ] 最终 `10 s` 至少完成 `maxStep=0.001/0.005/0.02 s` 三个代表档,并逐步补齐 `0.002/0.01 s`;较小步长不得出现较大步长没有的可复现数值失败或更早真停滞。
### OPT-10 明确高指数 DAE 和强非光滑系统边界
@@ -678,6 +738,12 @@ PNL00R stream 语义、单次 RHS 事务回滚和基于实际试探步的恢复
| 本文复杂 XML `10 s` | 必测 | 必测 | 必测 | 必测 | 必测 | 必测 |
| 主目标 `test-mql-8` `0.2 s` | 必测 | 必测 | 必测 | 必测 | 必测 | 必测 |
| 主目标 `test-mql-8` `1/5/10 s` | 必测 | 必测 | 必测 | 必测 | 必测 | 必测 |
| 权威 JSON 浏览器/流式 API `0.2 s / maxStep=0.001 s` | 必测 | 必测 | 必测 | 必测 | 必测(含内部活动心跳) | 可选 |
| 权威 worker `0.2/1 s × maxStep={0.001,0.002,0.005,0.01,0.02} s` | 必测 | 必测(分类容差) | 必测 | 必测 | 必测(串行矩阵) | 可选 |
| 权威 JSON `10 s / maxStep=0.001 s` | 必测 | 必测 | 必测 | 必测 | 必测(完整时间线) | 必测 |
2026-08-17 的 `maxStep=0.01/0.02/0.05/0.10 s` 延长结果继续作为恢复机制与历史路径证据,但不替代
2026-08-18 权威 `maxStep=0.001 s` 的 browser/API/worker P0 门和 10 s 最终基线。
当前相关回归套件包括:
@@ -728,6 +794,8 @@ PNL00R stream 语义、单次 RHS 事务回滚和基于实际试探步的恢复
| 2026-08-17 | 同一工作树 | OPT-01/02 因果坐标与参考 IR | `760` 个兼容槽压缩为 `440` 个逻辑坐标;独立 schema v1 参考 IR 覆盖 `112+328` 坐标和 320 个逻辑别名 | kernel on/off、兼容槽、状态导数、结构签名和逐阶段差分通过;审计/验证/回退失败均为 0 | Python 调用 `-36.8%`,RHS 微基准 `-12.9%`,production 0.2 s 单次 `-3.68%` | OPT-01 基本完成;IR 暂不接管默认热路径 |
| 2026-08-17 | 同一工作树 | OPT-00/04/05/08/09 热流体恢复与延长矩阵 | 修正 PNL00R 温度 stream 参考;加入 RHS 事务、类型化闭合失败、基于 `h_abs` 的对半重试和完整诊断 | production 0.2 s golden 通过;2 s 四档 `maxStep` 均完成且 `caseFailureCount=0`,但跨步长严格门因近零机械 `a/v` 差异未过;5 s 的 0.02 s 档完成,0.05 s 档为预算取消而非 solver failure | 2 s worker 墙钟为 324.727/292.035/450.425/448.033 s;5 s 的 0.02 s 档为 696.418 s、0 retry、`18736/1347/4988`,0.05 s 档在 1200 s 预算停止于 4.2523535 s | 原 1.86 s 致命失败已恢复;暂以 0.02 s 作为延长测试首选但不修改正式默认值或批准 golden;10 s 的 0.02 s 档进行中 |
| 2026-08-17 | 同一工作树;最终通用接线与 10 s repeat | OPT-04/05/08/09 `10 s` 最终收口 | eventless Generic opt-in stepwise recovery;StreamResolver 刷新全部温度参考 override;修复单格矩阵空比较器 | 0.01 s 接线前后逐值一致;两次 0.2 s final candidate 彼此逐值相同且均为旧 golden 398/402,同样 4 个终点派生 MECMAS21 `a` 超差、最大容差比 1.373,未覆盖 golden;最终 2 s 单格通过;真实 SciPy direct/stepwise A/B 等价;完整 unittest 828 项 OK(3 项跳过) | 最终接线后 10 s worker/orchestration 1602.733/1604.152 s,`45455/3075/15282`,接受步 9569、启动 6、事件 2;`t=6.9640458 s` 的 1 次热流体失败经 1 次重试恢复,最大迭代 23、残差 `1.082e-16`,1717 序列/1,722,151 标量全有限;最终 2 s worker 301.782 s | 最终通用接线后的 10 s 已完成;803.622 s 旧报告只作接线前历史证据、不作最终性能;旧 exit 1 仅为空比较器缺陷;旧 golden 保留,连续 3 次 10 s 仍待后续 |
| 2026-08-18 | 工作树基于 `684d287`;AME SHA `cbc3aadd...` | OPT-00 完成 | AME→XML/JSON 权威契约、22 包发布锁、双 golden、最终 replay 与历史 2.10 s 三次复测 | AME 25 项外部评估通过;状态 426/426、物理 33/33 本地重放零误差;quick 179、全量 849 项通过 | 0.2 s worker 159.607 s;2.10 s 三次 113.497–115.868 s | OPT-00 本地验收完成;1 s 预算内 eligible,长时递进转 OPT-09;远端 CI 待提交触发 |
| 2026-08-18 | 同一权威 AME/XML/JSON 工作树 | OPT-00/05/08/09 步长鲁棒性重新打开 | 浏览器在 `BDF / 0.2 s / sampleStep=0.01 s / maxStep=0.001 s` 下于 `t≈0.0489 s` 计算超时,当前工程路径判定失败;新增 browser/API/worker 对账、`0.2/1 s × 五档` 短时矩阵、内部活动心跳和权威 10 s 门 | 失败事实已确认,具体根因尚未区分为数值真停滞、内部慢步、后处理/传输或 60 s 服务假超时;离线 OPT-00 证据保留但不足以签收浏览器工程路径 | 暂不使用旧 `0.02 s` 长跑推断 `0.001 s`;先定位并提交方案审阅,短时门通过后再完整取得未经本次性能优化的 `10 s / 0.001 s` 基线 | OPT-00 工程端到端门重新打开;OPT-05 提升为 P0/P1,OPT-08 为 P0 服务门,OPT-09 只认 `0.001 s` 权威长基线 |
## 9. 相关文档
@@ -10,3 +10,15 @@
- 相同 `maxStep=0.02` 的 `2 s` 与 `10 s` 运行在公共严格前缀检查点逐值一致,确认此前约 `1.86 s` 的失败来自不可恢复的试探态闭合处理,而不是远端 `tStop` 直接改变物理方程。
- 全量后端共运行 `828` 项测试,结果为 OK,其中 `3` 项按条件跳过;差异检查通过且测试后无遗留仿真进程。
- 当前仍有明确限制:跨最大步长严格比较尚未全部通过;最终 `0.2 s` 候选相对旧 golden 为 `398/402`,4 个超差项均是终点派生加速度,旧 golden 未被覆盖;最终 `10 s` 仅完成一次,仍需三次中位数、资源稳定性和新 golden 决策。
## 07:58
- 以 `AmesimModels/test_mql.ame` 为权威重新核对 `test-mql-8.xml/json`:20 类、157 个项目组件、178 条连接和 1092 个参数一致;唯一确定差异是仿真配置,已按 AME 与其结果网格修正为 `0→10 s`、`sampleStep=0.01 s`、`maxStep=0.001 s`。
- 新增 AME 防漂移契约,直接验证归档 SHA/字节、`.sim/.results` 时间轴、单位与表压转换、公式等价、DIRECT/接触/建模线拓扑,以及 XML↔JSON 的逐 ID、参数和端口一致性。
- 建立并批准 production `0.2 s` 的通用状态 golden 与 physical-state-v2.1 golden:前者覆盖 142 个状态键、3 个检查点和 426 个值;后者覆盖压力、质量流量、质量守恒、总储气质量和离散模式,并保留 AMESim 外部校准 provenance。
- AMESim 外部评估 25 项通过;2 个 `t=0.04 s` 跳变流量因左右极限语义显式不参与外部比较。最差相对误差为 `0.1393485%`,未超过 0.2% 审阅容差;本地物理 golden 33/33 重放误差为 0。
- 最终 production replay 的 worker/orchestration 墙钟为 `159.607/160.473 s`,状态与物理门禁均通过;按既定安全公式外推 1 s 为 `1197.053 s`,略低于 1200 s soft budget,记录为后续阶段可运行但本次 OPT-00 未启动。
- 历史 production `0.81/2.10 s` 严格串行复测三次并批准两档 golden;三次检查点、状态、事件、输出契约及除计时外诊断逐值一致,无非有限值或非预期回退。
- 新增 Linux x86_64 的 22 包 binary-only SHA-256 发布锁;全新空 venv 离线安装、`pip check` 和依赖契约通过,CI 的 quick/historical/main-long 三层任务统一使用该锁。
- 最终 quick workflow 同口径 179 项通过(2 项预期跳过),完整后端 849 项通过(3 项预期跳过),`git diff --check` 与残留进程检查进入最终收尾。
- OPT-00 已完成当时工作树的本地验收。仍存在的问题:批准报告来自 `684d287` 的脏工作树;后续求解器提交合入后尚未重新运行真实 production 基线和全量回归,远端 workflow 也待提交后首次托管验证。
+2 -2
View File
@@ -1,5 +1,5 @@
# Supported dependency ranges. Reproducible Python 3.12 installs should also
# apply constraints/python312-direct.txt; see README.md.
# Supported dependency ranges. Linux CPython 3.12 x86_64 release installs
# should use constraints/python312-linux-x86_64.lock directly; see README.md.
fastapi
lxml>=5,<7
numpy>=1.26,<3
@@ -0,0 +1,952 @@
{
"schemaVersion": 1,
"id": "test_mql_8-production-0.2s-physical-state-v2.1",
"contractId": "physical-state-v2.1",
"caseId": "0.2s",
"lane": "production",
"sourceXmlSha256": "0a2d9331df9eb5974daec25a61c1238ba32b1742d933ffc8b16ce316c5627b0b",
"approval": {
"status": "candidate",
"generatedAt": "2026-08-18T07:35:03.491746+00:00",
"warning": "The correctness gate rejects this file until explicit approval."
},
"provenance": {
"localSourceReport": {
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-18-production-physical-state-v2.1-0.2.json",
"bytes": 380076,
"sha256": "c647fc7dab6c5cdaea188789dd8e0192d9caf374873afa1f5f5db0da9d69c49b",
"generatedAt": "2026-08-18T07:34:00.055573+00:00"
},
"amesim": {
"archivePath": "AmesimModels/test_mql.ame",
"archiveBytes": 21708800,
"archiveSha256": "cbc3aadd4569a49b3a63e5d66d4143ec16126c0f950df73fb637e07673c20fbb",
"members": [
{
"name": "test_mql_.var",
"bytes": 95371,
"sha256": "175010af62d254b34b1f876ff652feb903fe2ce88bee80d4dc0ef1d31f04067b"
},
{
"name": "test_mql_.results",
"bytes": 8968576,
"sha256": "434a2cdff1322565957934354154425868fa03737558636927cb0f3ec51f1760"
}
],
"referencePressurePa": 101300.0,
"role": "external calibration reference; not an equality claim"
},
"amesimStoredMassDataPaths": [
"mgas1@pn_c1_8",
"mgas1@pn_c1_9",
"mgas1@pn_c1_10",
"mgas1@pn_c1_11",
"mgas1@pn_c1_12",
"mgas1@pn_c1_13",
"mgas1@pn_c1_14",
"mgas1@pn_c1_15",
"mgas@pn_general_chamber",
"mgas@pn_general_chamber_2",
"mgas@pn_general_chamber_4",
"mgas@pn_general_chamber_5",
"mgas@pneumatic_65",
"mgas@pneumatic_66",
"mgas@pneumatic_68",
"mgas@pneumatic_69",
"mgas@pneumatic_70",
"mgas@pneumatic_71",
"mgas@pneumatic_72",
"mgas@pneumatic_73",
"mgas@pneumatic_74",
"mgas@pneumatic_75",
"mgas@pneumatic_76",
"mgas@pneumatic_77",
"mgas@pneumatic_78",
"mgas@pneumatic_79",
"mgas@pneumatic_80",
"mgas@pneumatic_81",
"mgas@pneumatic_82",
"mgas@pneumatic_83",
"mgas@pneumatic_84",
"mgas@pneumatic_85",
"mgas@pneumatic_86",
"mgas@pneumatic_87",
"mgas@pneumatic_88",
"mgas@pneumatic_91",
"mgas@pneumatic_92",
"mgas@pneumatic_93",
"mgas@pneumatic_94",
"mgas@pneumatic_95",
"mgas@pneumatic_96",
"mgas@pneumatic_97",
"mgas@pneumatic_98",
"mgas@pneumatic_99",
"mgas@pneumatic_101",
"mgas@pneumatic_102",
"mgas@pneumatic_103",
"mgas@pneumatic_104"
],
"comparisonPolicy": "Local values are the regression target; transformed AMESim values are retained visibly as an external calibration reference."
},
"layout": {
"projectionKeys": [
"pressure.pnch012_8.absolute",
"pressure.pnl0001_20.absolute",
"massFlow.pnl0001_20.port_1.intoComponent",
"massFlow.pnvo001_5.port_2.intoComponent",
"conservation.p4node2_8.massBalance",
"conservation.pnvo001_5.massBalance",
"conservation.pnl0001_20_pnch012_8.connectionMassBalance",
"conservation.totalStoredGasMass",
"discrete.pnvo001_5.openMode",
"discrete.mecmas21_9.endstopMode",
"discrete.mecmas21_10.endstopMode"
],
"projections": [
{
"key": "pressure.pnch012_8.absolute",
"category": "pressure",
"unit": "Pa",
"python": {
"sourceKeys": [
"amesim_pnch012_8.p"
],
"formula": "identity"
},
"amesim": {
"dataPaths": [
"press@pn_c1_8"
],
"sourceUnit": "Pa (gauge)",
"targetUnit": "Pa (absolute)",
"formula": "target = source + 101300",
"scale": 1.0,
"offset": 101300.0
},
"tolerance": {
"relative": 0.0002,
"absolute": 0.001
}
},
{
"key": "pressure.pnl0001_20.absolute",
"category": "pressure",
"unit": "Pa",
"python": {
"sourceKeys": [
"amesim_pnl0001_20.p"
],
"formula": "identity"
},
"amesim": {
"dataPaths": [
"p2@pneumatic_69"
],
"sourceUnit": "Pa (gauge)",
"targetUnit": "Pa (absolute)",
"formula": "target = source + 101300",
"scale": 1.0,
"offset": 101300.0
},
"tolerance": {
"relative": 0.0002,
"absolute": 0.001
}
},
{
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_pnl0001_20.port_1.m_flow"
],
"formula": "identity"
},
"amesim": {
"dataPaths": [
"dm1@pneumatic_69"
],
"sourceUnit": "g/s (AMESim component orientation)",
"targetUnit": "kg/s (positive into generic component)",
"formula": "target = -source * 1e-3",
"scale": -0.001,
"offset": 0.0
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09
}
},
{
"key": "massFlow.pnvo001_5.port_2.intoComponent",
"category": "massFlow",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_pnvo001_5.port_2.m_flow"
],
"formula": "identity"
},
"amesim": {
"dataPaths": [
"dm2@pn_morifice_1"
],
"sourceUnit": "g/s (AMESim component orientation)",
"targetUnit": "kg/s (positive into generic component)",
"formula": "target = -source * 1e-3",
"scale": -0.001,
"offset": 0.0
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09
}
},
{
"key": "conservation.p4node2_8.massBalance",
"category": "conservation",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_p4node2_8.port_1.m_flow",
"amesim_p4node2_8.port_2.m_flow",
"amesim_p4node2_8.port_3.m_flow",
"amesim_p4node2_8.port_4.m_flow"
],
"formula": "sum"
},
"amesim": {
"dataPaths": [],
"note": "AMESim archive has no complete saved four-port tuple."
},
"tolerance": {
"relative": 0.0,
"absolute": 1e-09
}
},
{
"key": "conservation.pnvo001_5.massBalance",
"category": "conservation",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_pnvo001_5.port_2.m_flow",
"amesim_pnvo001_5.port_3.m_flow"
],
"formula": "sum"
},
"amesim": {
"dataPaths": [
"dm2@pn_morifice_1",
"dm3@pn_morifice_1"
],
"sourceUnit": "g/s",
"targetUnit": "kg/s",
"formula": "target = -(source1 + source2) * 1e-3"
},
"tolerance": {
"relative": 0.0,
"absolute": 1e-09
}
},
{
"key": "conservation.pnl0001_20_pnch012_8.connectionMassBalance",
"category": "conservation",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_pnl0001_20.port_1.m_flow",
"amesim_pnch012_8.port_1.m_flow"
],
"formula": "sum"
},
"amesim": {
"dataPaths": [],
"note": "PNCH012 port flow is not saved; local connector contract is authoritative."
},
"tolerance": {
"relative": 0.0,
"absolute": 1e-09
}
},
{
"key": "conservation.totalStoredGasMass",
"category": "conservation",
"unit": "kg",
"python": {
"sourceKeys": [
"amesim_pnch012_10.m",
"amesim_pnch012_11.m",
"amesim_pnch012_12.m",
"amesim_pnch012_13.m",
"amesim_pnch012_14.m",
"amesim_pnch012_15.m",
"amesim_pnch012_8.m",
"amesim_pnch012_9.m",
"amesim_pnch023_1.m",
"amesim_pnch023_2.m",
"amesim_pnch023_3.m",
"amesim_pnch023_4.m",
"amesim_pnl0001_1.m",
"amesim_pnl0001_10.m",
"amesim_pnl0001_11.m",
"amesim_pnl0001_13.m",
"amesim_pnl0001_14.m",
"amesim_pnl0001_15.m",
"amesim_pnl0001_16.m",
"amesim_pnl0001_17.m",
"amesim_pnl0001_18.m",
"amesim_pnl0001_19.m",
"amesim_pnl0001_2.m",
"amesim_pnl0001_20.m",
"amesim_pnl0001_21.m",
"amesim_pnl0001_25.m",
"amesim_pnl0001_26.m",
"amesim_pnl0001_27.m",
"amesim_pnl0001_4.m",
"amesim_pnl0001_5.m",
"amesim_pnl0001_7.m",
"amesim_pnl0001_9.m",
"amesim_pnl0002_1.m",
"amesim_pnl0002_2.m",
"amesim_pnl0002_3.m",
"amesim_pnl0002_4.m",
"amesim_pnl0002_5.m",
"amesim_pnl0002_6.m",
"amesim_pnl0002_7.m",
"amesim_pnl0002_8.m",
"amesim_pnl0003_1.m1",
"amesim_pnl0003_1.m2",
"amesim_pnl0003_2.m1",
"amesim_pnl0003_2.m2",
"amesim_pnl0003_3.m1",
"amesim_pnl0003_3.m2",
"amesim_pnl0003_4.m1",
"amesim_pnl0003_4.m2",
"amesim_pnl0003_5.m1",
"amesim_pnl0003_5.m2",
"amesim_pnl0003_6.m1",
"amesim_pnl0003_6.m2",
"amesim_pnl0003_7.m1",
"amesim_pnl0003_7.m2",
"amesim_pnl0003_8.m1",
"amesim_pnl0003_8.m2"
],
"formula": "sum"
},
"amesim": {
"dataPathSelection": "units == 'g' and signal starts with 'mgas'",
"sourceUnit": "g",
"targetUnit": "kg",
"formula": "target = sum(sources) * 1e-3"
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09
}
},
{
"key": "discrete.pnvo001_5.openMode",
"category": "discreteMode",
"unit": "1",
"python": {
"sourceKeys": [
"amesim_pnvo001_5.xv"
],
"formula": "1 if opening >= 0.5 else 0"
},
"amesim": {
"dataPaths": [
"xv@pn_morifice_1"
],
"sourceUnit": "1",
"targetUnit": "mode code {0,1}",
"formula": "1 if source >= 0.5 else 0"
},
"tolerance": {
"relative": 0.0,
"absolute": 0.0
}
},
{
"key": "discrete.mecmas21_9.endstopMode",
"category": "discreteMode",
"unit": "1",
"python": {
"sourceKeys": [
"amesim_mecmas21_9.x",
"amesim_mecmas21_9.v",
"amesim_mecmas21_9.port_1.f",
"amesim_mecmas21_9.port_2.f"
],
"formula": "-1 lower, 0 free, +1 upper using boundary and net-force direction"
},
"amesim": {
"dataPaths": [
"x1@mass_friction_endstops_19",
"v1@mass_friction_endstops_19"
],
"lowerBound": -0.72,
"upperBound": 0.0,
"note": "No saved AMESim endstop code; reference is boundary occupancy."
},
"tolerance": {
"relative": 0.0,
"absolute": 0.0
}
},
{
"key": "discrete.mecmas21_10.endstopMode",
"category": "discreteMode",
"unit": "1",
"python": {
"sourceKeys": [
"amesim_mecmas21_10.x",
"amesim_mecmas21_10.v",
"amesim_mecmas21_10.port_1.f",
"amesim_mecmas21_10.port_2.f"
],
"formula": "-1 lower, 0 free, +1 upper using boundary and net-force direction"
},
"amesim": {
"dataPaths": [
"x1@mass_friction_endstops_18",
"v1@mass_friction_endstops_18"
],
"lowerBound": 0.0,
"upperBound": 0.37,
"note": "No saved AMESim endstop code; reference is boundary occupancy."
},
"tolerance": {
"relative": 0.0,
"absolute": 0.0
}
}
],
"projectionLayoutSha256": "55cc8f71ea72fd46f71f1ede2587aeb9f6d590bde8e97e881648f88a4a90dca6"
},
"checkpoints": [
{
"requestedTime": 0.0,
"values": [
100000.0,
100000.0,
0.0,
0.0,
0.0,
0.0,
0.0,
5.5668930151375235,
0.0,
1.0,
-1.0
],
"amesimReferenceValues": [
100000.00000000001,
100000.00000000001,
-0.0,
-0.0,
null,
-0.0,
null,
5.5668930151375235,
0.0,
1.0,
-1.0
]
},
{
"requestedTime": 0.04,
"values": [
100153.57210434608,
100152.45747547514,
6.682314111565264e-06,
-0.4978238221497115,
0.0,
0.0,
0.0,
5.5668930151375235,
1.0,
1.0,
-1.0
],
"amesimReferenceValues": [
100153.61882579065,
100152.49205385521,
6.672550142634319e-06,
-0.4978238226601424,
null,
-0.0,
null,
5.566893015142308,
1.0,
1.0,
-1.0
]
},
{
"requestedTime": 0.2,
"values": [
3620589.728099082,
3756379.1344475006,
-0.3723610392591412,
-0.3962730176377355,
1.8214596497756474e-17,
0.0,
0.0,
5.566893015137554,
1.0,
1.0,
-1.0
],
"amesimReferenceValues": [
3625053.2009413484,
3760735.7825351665,
-0.3728806429214546,
-0.3961933197727504,
null,
-0.0,
null,
5.566892994134813,
1.0,
1.0,
-1.0
]
}
],
"amesimAlignmentAtGeneration": {
"passed": true,
"issues": [],
"comparedValueCount": 25,
"excludedValueCount": 2,
"maxToleranceRatio": 0.6967416724836332,
"maxToleranceRatioUnbounded": false,
"worstValue": {
"requestedTime": 0.2,
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"python": -0.3723610392591412,
"amesim": -0.3728806429214546,
"absoluteError": 0.0005196036623134059,
"relativeError": 0.0013934852135053246,
"allowedError": 0.0007457622858429093,
"toleranceRatio": 0.6967416724836332,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
"metrics": [
{
"requestedTime": 0.0,
"key": "pressure.pnch012_8.absolute",
"category": "pressure",
"python": 100000.0,
"amesim": 100000.00000000001,
"absoluteError": 1.4551915228366852e-11,
"relativeError": 1.455191522836685e-16,
"allowedError": 200.00100000000003,
"toleranceRatio": 7.275921234577252e-14,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "pressure.pnl0001_20.absolute",
"category": "pressure",
"python": 100000.0,
"amesim": 100000.00000000001,
"absoluteError": 1.4551915228366852e-11,
"relativeError": 1.455191522836685e-16,
"allowedError": 200.00100000000003,
"toleranceRatio": 7.275921234577252e-14,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "massFlow.pnvo001_5.port_2.intoComponent",
"category": "massFlow",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "conservation.pnvo001_5.massBalance",
"category": "conservation",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "conservation.totalStoredGasMass",
"category": "conservation",
"python": 5.5668930151375235,
"amesim": 5.5668930151375235,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.011133787030275047,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "discrete.pnvo001_5.openMode",
"category": "discreteMode",
"python": 0.0,
"amesim": 0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "discrete.mecmas21_9.endstopMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "discrete.mecmas21_10.endstopMode",
"category": "discreteMode",
"python": -1.0,
"amesim": -1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "pressure.pnch012_8.absolute",
"category": "pressure",
"python": 100153.57210434608,
"amesim": 100153.61882579065,
"absoluteError": 0.04672144456708338,
"relativeError": 4.6649781720170954e-07,
"allowedError": 200.3082376515813,
"toleranceRatio": 0.00023324774415094828,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "pressure.pnl0001_20.absolute",
"category": "pressure",
"python": 100152.45747547514,
"amesim": 100152.49205385521,
"absoluteError": 0.03457838007307146,
"relativeError": 3.4525731076644157e-07,
"allowedError": 200.30598410771043,
"toleranceRatio": 0.00017262779355846724,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"python": 6.682314111565264e-06,
"amesim": 6.672550142634319e-06,
"absoluteError": 9.763968930944316e-09,
"relativeError": 0.0014633039426046946,
"allowedError": 1.4345100285268639e-08,
"toleranceRatio": 0.6806483563569921,
"unboundedToleranceRatio": false,
"evaluated": false,
"passed": null,
"exclusionReason": "signalDiscontinuityLeftRightLimitSemanticsNotComparableAt0.04s"
},
{
"requestedTime": 0.04,
"key": "massFlow.pnvo001_5.port_2.intoComponent",
"category": "massFlow",
"python": -0.4978238221497115,
"amesim": -0.4978238226601424,
"absoluteError": 5.104309197534462e-10,
"relativeError": 1.0253244150228433e-09,
"allowedError": 0.0009956486453202849,
"toleranceRatio": 5.126616926086898e-07,
"unboundedToleranceRatio": false,
"evaluated": false,
"passed": null,
"exclusionReason": "signalDiscontinuityLeftRightLimitSemanticsNotComparableAt0.04s"
},
{
"requestedTime": 0.04,
"key": "conservation.pnvo001_5.massBalance",
"category": "conservation",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "conservation.totalStoredGasMass",
"category": "conservation",
"python": 5.5668930151375235,
"amesim": 5.566893015142308,
"absoluteError": 4.784617146924575e-12,
"relativeError": 8.594771147766101e-13,
"allowedError": 0.011133787030284616,
"toleranceRatio": 4.2973851879060635e-10,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "discrete.pnvo001_5.openMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "discrete.mecmas21_9.endstopMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "discrete.mecmas21_10.endstopMode",
"category": "discreteMode",
"python": -1.0,
"amesim": -1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "pressure.pnch012_8.absolute",
"category": "pressure",
"python": 3620589.728099082,
"amesim": 3625053.2009413484,
"absoluteError": 4463.4728422663175,
"relativeError": 0.0012312847825536048,
"allowedError": 7250.107401882698,
"toleranceRatio": 0.6156423063618685,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "pressure.pnl0001_20.absolute",
"category": "pressure",
"python": 3756379.1344475006,
"amesim": 3760735.7825351665,
"absoluteError": 4356.648087665904,
"relativeError": 0.0011584563073795695,
"allowedError": 7521.472565070333,
"toleranceRatio": 0.5792280766798443,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"python": -0.3723610392591412,
"amesim": -0.3728806429214546,
"absoluteError": 0.0005196036623134059,
"relativeError": 0.0013934852135053246,
"allowedError": 0.0007457622858429093,
"toleranceRatio": 0.6967416724836332,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "massFlow.pnvo001_5.port_2.intoComponent",
"category": "massFlow",
"python": -0.3962730176377355,
"amesim": -0.3961933197727504,
"absoluteError": 7.969786498507325e-05,
"relativeError": 0.00020115903274388008,
"allowedError": 0.0007923876395455008,
"toleranceRatio": 0.10057938943972738,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "conservation.pnvo001_5.massBalance",
"category": "conservation",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "conservation.totalStoredGasMass",
"category": "conservation",
"python": 5.566893015137554,
"amesim": 5.566892994134813,
"absoluteError": 2.1002740879794146e-08,
"relativeError": 3.7727940705744284e-09,
"allowedError": 0.011133786988269626,
"toleranceRatio": 1.8863968658572583e-06,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "discrete.pnvo001_5.openMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "discrete.mecmas21_9.endstopMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "discrete.mecmas21_10.endstopMode",
"category": "discreteMode",
"python": -1.0,
"amesim": -1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
}
],
"toleranceProfile": {
"id": "amesim-alignment-reviewed-v1",
"byCategory": {
"pressure": {
"relative": 0.002,
"absolute": 0.001
},
"massFlow": {
"relative": 0.002,
"absolute": 1e-09
},
"conservation": {
"relative": 0.002,
"absolute": 1e-09
},
"discreteMode": {
"relative": 0.0,
"absolute": 0.0
}
},
"basis": "0.2% relative envelope for AMESim pressure/flow/inventory alignment; conservation retains a 1e-9 absolute floor and discrete modes are exact.",
"exclusions": [
{
"requestedTime": 0.04,
"category": "massFlow",
"reason": "The Python and AMESim samples at the signal discontinuity may represent different left/right limits. Raw errors remain recorded."
}
]
},
"policy": "diagnostic external-reference comparison; local golden is the regression gate"
}
}
@@ -0,0 +1,955 @@
{
"schemaVersion": 1,
"id": "test_mql_8-production-0.2s-physical-state-v2.1",
"contractId": "physical-state-v2.1",
"caseId": "0.2s",
"lane": "production",
"sourceXmlSha256": "0a2d9331df9eb5974daec25a61c1238ba32b1742d933ffc8b16ce316c5627b0b",
"approval": {
"status": "approved",
"approvedAt": "2026-08-18T07:35:25.264794+00:00",
"reviewedBy": "Codex OPT-00 physical-state-v2.1 gate",
"note": "Reviewed full 0/0.04/0.2 checkpoints: AMESim external alignment passed the declared 0.2% profile; t=0.04 mass-flow values retain errors but are excluded for explicit left/right-limit ambiguity; local p4node, PNVO, and connection mass-balance residuals pass the strict 1e-9 kg/s gate; discrete modes are exact.",
"candidatePath": "tests/baselines/simulation/test_mql_8/goldens/candidates/production-0.2s-physical-state-v2.1-candidate.json",
"candidateSha256": "699033337c30e6aed97538e886b81ff897fe2c455860b4ad1ed3293f580fb53a"
},
"provenance": {
"localSourceReport": {
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-18-production-physical-state-v2.1-0.2.json",
"bytes": 380076,
"sha256": "c647fc7dab6c5cdaea188789dd8e0192d9caf374873afa1f5f5db0da9d69c49b",
"generatedAt": "2026-08-18T07:34:00.055573+00:00"
},
"amesim": {
"archivePath": "AmesimModels/test_mql.ame",
"archiveBytes": 21708800,
"archiveSha256": "cbc3aadd4569a49b3a63e5d66d4143ec16126c0f950df73fb637e07673c20fbb",
"members": [
{
"name": "test_mql_.var",
"bytes": 95371,
"sha256": "175010af62d254b34b1f876ff652feb903fe2ce88bee80d4dc0ef1d31f04067b"
},
{
"name": "test_mql_.results",
"bytes": 8968576,
"sha256": "434a2cdff1322565957934354154425868fa03737558636927cb0f3ec51f1760"
}
],
"referencePressurePa": 101300.0,
"role": "external calibration reference; not an equality claim"
},
"amesimStoredMassDataPaths": [
"mgas1@pn_c1_8",
"mgas1@pn_c1_9",
"mgas1@pn_c1_10",
"mgas1@pn_c1_11",
"mgas1@pn_c1_12",
"mgas1@pn_c1_13",
"mgas1@pn_c1_14",
"mgas1@pn_c1_15",
"mgas@pn_general_chamber",
"mgas@pn_general_chamber_2",
"mgas@pn_general_chamber_4",
"mgas@pn_general_chamber_5",
"mgas@pneumatic_65",
"mgas@pneumatic_66",
"mgas@pneumatic_68",
"mgas@pneumatic_69",
"mgas@pneumatic_70",
"mgas@pneumatic_71",
"mgas@pneumatic_72",
"mgas@pneumatic_73",
"mgas@pneumatic_74",
"mgas@pneumatic_75",
"mgas@pneumatic_76",
"mgas@pneumatic_77",
"mgas@pneumatic_78",
"mgas@pneumatic_79",
"mgas@pneumatic_80",
"mgas@pneumatic_81",
"mgas@pneumatic_82",
"mgas@pneumatic_83",
"mgas@pneumatic_84",
"mgas@pneumatic_85",
"mgas@pneumatic_86",
"mgas@pneumatic_87",
"mgas@pneumatic_88",
"mgas@pneumatic_91",
"mgas@pneumatic_92",
"mgas@pneumatic_93",
"mgas@pneumatic_94",
"mgas@pneumatic_95",
"mgas@pneumatic_96",
"mgas@pneumatic_97",
"mgas@pneumatic_98",
"mgas@pneumatic_99",
"mgas@pneumatic_101",
"mgas@pneumatic_102",
"mgas@pneumatic_103",
"mgas@pneumatic_104"
],
"comparisonPolicy": "Local values are the regression target; transformed AMESim values are retained visibly as an external calibration reference."
},
"layout": {
"projectionKeys": [
"pressure.pnch012_8.absolute",
"pressure.pnl0001_20.absolute",
"massFlow.pnl0001_20.port_1.intoComponent",
"massFlow.pnvo001_5.port_2.intoComponent",
"conservation.p4node2_8.massBalance",
"conservation.pnvo001_5.massBalance",
"conservation.pnl0001_20_pnch012_8.connectionMassBalance",
"conservation.totalStoredGasMass",
"discrete.pnvo001_5.openMode",
"discrete.mecmas21_9.endstopMode",
"discrete.mecmas21_10.endstopMode"
],
"projections": [
{
"key": "pressure.pnch012_8.absolute",
"category": "pressure",
"unit": "Pa",
"python": {
"sourceKeys": [
"amesim_pnch012_8.p"
],
"formula": "identity"
},
"amesim": {
"dataPaths": [
"press@pn_c1_8"
],
"sourceUnit": "Pa (gauge)",
"targetUnit": "Pa (absolute)",
"formula": "target = source + 101300",
"scale": 1.0,
"offset": 101300.0
},
"tolerance": {
"relative": 0.0002,
"absolute": 0.001
}
},
{
"key": "pressure.pnl0001_20.absolute",
"category": "pressure",
"unit": "Pa",
"python": {
"sourceKeys": [
"amesim_pnl0001_20.p"
],
"formula": "identity"
},
"amesim": {
"dataPaths": [
"p2@pneumatic_69"
],
"sourceUnit": "Pa (gauge)",
"targetUnit": "Pa (absolute)",
"formula": "target = source + 101300",
"scale": 1.0,
"offset": 101300.0
},
"tolerance": {
"relative": 0.0002,
"absolute": 0.001
}
},
{
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_pnl0001_20.port_1.m_flow"
],
"formula": "identity"
},
"amesim": {
"dataPaths": [
"dm1@pneumatic_69"
],
"sourceUnit": "g/s (AMESim component orientation)",
"targetUnit": "kg/s (positive into generic component)",
"formula": "target = -source * 1e-3",
"scale": -0.001,
"offset": 0.0
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09
}
},
{
"key": "massFlow.pnvo001_5.port_2.intoComponent",
"category": "massFlow",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_pnvo001_5.port_2.m_flow"
],
"formula": "identity"
},
"amesim": {
"dataPaths": [
"dm2@pn_morifice_1"
],
"sourceUnit": "g/s (AMESim component orientation)",
"targetUnit": "kg/s (positive into generic component)",
"formula": "target = -source * 1e-3",
"scale": -0.001,
"offset": 0.0
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09
}
},
{
"key": "conservation.p4node2_8.massBalance",
"category": "conservation",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_p4node2_8.port_1.m_flow",
"amesim_p4node2_8.port_2.m_flow",
"amesim_p4node2_8.port_3.m_flow",
"amesim_p4node2_8.port_4.m_flow"
],
"formula": "sum"
},
"amesim": {
"dataPaths": [],
"note": "AMESim archive has no complete saved four-port tuple."
},
"tolerance": {
"relative": 0.0,
"absolute": 1e-09
}
},
{
"key": "conservation.pnvo001_5.massBalance",
"category": "conservation",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_pnvo001_5.port_2.m_flow",
"amesim_pnvo001_5.port_3.m_flow"
],
"formula": "sum"
},
"amesim": {
"dataPaths": [
"dm2@pn_morifice_1",
"dm3@pn_morifice_1"
],
"sourceUnit": "g/s",
"targetUnit": "kg/s",
"formula": "target = -(source1 + source2) * 1e-3"
},
"tolerance": {
"relative": 0.0,
"absolute": 1e-09
}
},
{
"key": "conservation.pnl0001_20_pnch012_8.connectionMassBalance",
"category": "conservation",
"unit": "kg/s",
"python": {
"sourceKeys": [
"amesim_pnl0001_20.port_1.m_flow",
"amesim_pnch012_8.port_1.m_flow"
],
"formula": "sum"
},
"amesim": {
"dataPaths": [],
"note": "PNCH012 port flow is not saved; local connector contract is authoritative."
},
"tolerance": {
"relative": 0.0,
"absolute": 1e-09
}
},
{
"key": "conservation.totalStoredGasMass",
"category": "conservation",
"unit": "kg",
"python": {
"sourceKeys": [
"amesim_pnch012_10.m",
"amesim_pnch012_11.m",
"amesim_pnch012_12.m",
"amesim_pnch012_13.m",
"amesim_pnch012_14.m",
"amesim_pnch012_15.m",
"amesim_pnch012_8.m",
"amesim_pnch012_9.m",
"amesim_pnch023_1.m",
"amesim_pnch023_2.m",
"amesim_pnch023_3.m",
"amesim_pnch023_4.m",
"amesim_pnl0001_1.m",
"amesim_pnl0001_10.m",
"amesim_pnl0001_11.m",
"amesim_pnl0001_13.m",
"amesim_pnl0001_14.m",
"amesim_pnl0001_15.m",
"amesim_pnl0001_16.m",
"amesim_pnl0001_17.m",
"amesim_pnl0001_18.m",
"amesim_pnl0001_19.m",
"amesim_pnl0001_2.m",
"amesim_pnl0001_20.m",
"amesim_pnl0001_21.m",
"amesim_pnl0001_25.m",
"amesim_pnl0001_26.m",
"amesim_pnl0001_27.m",
"amesim_pnl0001_4.m",
"amesim_pnl0001_5.m",
"amesim_pnl0001_7.m",
"amesim_pnl0001_9.m",
"amesim_pnl0002_1.m",
"amesim_pnl0002_2.m",
"amesim_pnl0002_3.m",
"amesim_pnl0002_4.m",
"amesim_pnl0002_5.m",
"amesim_pnl0002_6.m",
"amesim_pnl0002_7.m",
"amesim_pnl0002_8.m",
"amesim_pnl0003_1.m1",
"amesim_pnl0003_1.m2",
"amesim_pnl0003_2.m1",
"amesim_pnl0003_2.m2",
"amesim_pnl0003_3.m1",
"amesim_pnl0003_3.m2",
"amesim_pnl0003_4.m1",
"amesim_pnl0003_4.m2",
"amesim_pnl0003_5.m1",
"amesim_pnl0003_5.m2",
"amesim_pnl0003_6.m1",
"amesim_pnl0003_6.m2",
"amesim_pnl0003_7.m1",
"amesim_pnl0003_7.m2",
"amesim_pnl0003_8.m1",
"amesim_pnl0003_8.m2"
],
"formula": "sum"
},
"amesim": {
"dataPathSelection": "units == 'g' and signal starts with 'mgas'",
"sourceUnit": "g",
"targetUnit": "kg",
"formula": "target = sum(sources) * 1e-3"
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09
}
},
{
"key": "discrete.pnvo001_5.openMode",
"category": "discreteMode",
"unit": "1",
"python": {
"sourceKeys": [
"amesim_pnvo001_5.xv"
],
"formula": "1 if opening >= 0.5 else 0"
},
"amesim": {
"dataPaths": [
"xv@pn_morifice_1"
],
"sourceUnit": "1",
"targetUnit": "mode code {0,1}",
"formula": "1 if source >= 0.5 else 0"
},
"tolerance": {
"relative": 0.0,
"absolute": 0.0
}
},
{
"key": "discrete.mecmas21_9.endstopMode",
"category": "discreteMode",
"unit": "1",
"python": {
"sourceKeys": [
"amesim_mecmas21_9.x",
"amesim_mecmas21_9.v",
"amesim_mecmas21_9.port_1.f",
"amesim_mecmas21_9.port_2.f"
],
"formula": "-1 lower, 0 free, +1 upper using boundary and net-force direction"
},
"amesim": {
"dataPaths": [
"x1@mass_friction_endstops_19",
"v1@mass_friction_endstops_19"
],
"lowerBound": -0.72,
"upperBound": 0.0,
"note": "No saved AMESim endstop code; reference is boundary occupancy."
},
"tolerance": {
"relative": 0.0,
"absolute": 0.0
}
},
{
"key": "discrete.mecmas21_10.endstopMode",
"category": "discreteMode",
"unit": "1",
"python": {
"sourceKeys": [
"amesim_mecmas21_10.x",
"amesim_mecmas21_10.v",
"amesim_mecmas21_10.port_1.f",
"amesim_mecmas21_10.port_2.f"
],
"formula": "-1 lower, 0 free, +1 upper using boundary and net-force direction"
},
"amesim": {
"dataPaths": [
"x1@mass_friction_endstops_18",
"v1@mass_friction_endstops_18"
],
"lowerBound": 0.0,
"upperBound": 0.37,
"note": "No saved AMESim endstop code; reference is boundary occupancy."
},
"tolerance": {
"relative": 0.0,
"absolute": 0.0
}
}
],
"projectionLayoutSha256": "55cc8f71ea72fd46f71f1ede2587aeb9f6d590bde8e97e881648f88a4a90dca6"
},
"checkpoints": [
{
"requestedTime": 0.0,
"values": [
100000.0,
100000.0,
0.0,
0.0,
0.0,
0.0,
0.0,
5.5668930151375235,
0.0,
1.0,
-1.0
],
"amesimReferenceValues": [
100000.00000000001,
100000.00000000001,
-0.0,
-0.0,
null,
-0.0,
null,
5.5668930151375235,
0.0,
1.0,
-1.0
]
},
{
"requestedTime": 0.04,
"values": [
100153.57210434608,
100152.45747547514,
6.682314111565264e-06,
-0.4978238221497115,
0.0,
0.0,
0.0,
5.5668930151375235,
1.0,
1.0,
-1.0
],
"amesimReferenceValues": [
100153.61882579065,
100152.49205385521,
6.672550142634319e-06,
-0.4978238226601424,
null,
-0.0,
null,
5.566893015142308,
1.0,
1.0,
-1.0
]
},
{
"requestedTime": 0.2,
"values": [
3620589.728099082,
3756379.1344475006,
-0.3723610392591412,
-0.3962730176377355,
1.8214596497756474e-17,
0.0,
0.0,
5.566893015137554,
1.0,
1.0,
-1.0
],
"amesimReferenceValues": [
3625053.2009413484,
3760735.7825351665,
-0.3728806429214546,
-0.3961933197727504,
null,
-0.0,
null,
5.566892994134813,
1.0,
1.0,
-1.0
]
}
],
"amesimAlignmentAtGeneration": {
"passed": true,
"issues": [],
"comparedValueCount": 25,
"excludedValueCount": 2,
"maxToleranceRatio": 0.6967416724836332,
"maxToleranceRatioUnbounded": false,
"worstValue": {
"requestedTime": 0.2,
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"python": -0.3723610392591412,
"amesim": -0.3728806429214546,
"absoluteError": 0.0005196036623134059,
"relativeError": 0.0013934852135053246,
"allowedError": 0.0007457622858429093,
"toleranceRatio": 0.6967416724836332,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
"metrics": [
{
"requestedTime": 0.0,
"key": "pressure.pnch012_8.absolute",
"category": "pressure",
"python": 100000.0,
"amesim": 100000.00000000001,
"absoluteError": 1.4551915228366852e-11,
"relativeError": 1.455191522836685e-16,
"allowedError": 200.00100000000003,
"toleranceRatio": 7.275921234577252e-14,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "pressure.pnl0001_20.absolute",
"category": "pressure",
"python": 100000.0,
"amesim": 100000.00000000001,
"absoluteError": 1.4551915228366852e-11,
"relativeError": 1.455191522836685e-16,
"allowedError": 200.00100000000003,
"toleranceRatio": 7.275921234577252e-14,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "massFlow.pnvo001_5.port_2.intoComponent",
"category": "massFlow",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "conservation.pnvo001_5.massBalance",
"category": "conservation",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "conservation.totalStoredGasMass",
"category": "conservation",
"python": 5.5668930151375235,
"amesim": 5.5668930151375235,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.011133787030275047,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "discrete.pnvo001_5.openMode",
"category": "discreteMode",
"python": 0.0,
"amesim": 0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "discrete.mecmas21_9.endstopMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.0,
"key": "discrete.mecmas21_10.endstopMode",
"category": "discreteMode",
"python": -1.0,
"amesim": -1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "pressure.pnch012_8.absolute",
"category": "pressure",
"python": 100153.57210434608,
"amesim": 100153.61882579065,
"absoluteError": 0.04672144456708338,
"relativeError": 4.6649781720170954e-07,
"allowedError": 200.3082376515813,
"toleranceRatio": 0.00023324774415094828,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "pressure.pnl0001_20.absolute",
"category": "pressure",
"python": 100152.45747547514,
"amesim": 100152.49205385521,
"absoluteError": 0.03457838007307146,
"relativeError": 3.4525731076644157e-07,
"allowedError": 200.30598410771043,
"toleranceRatio": 0.00017262779355846724,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"python": 6.682314111565264e-06,
"amesim": 6.672550142634319e-06,
"absoluteError": 9.763968930944316e-09,
"relativeError": 0.0014633039426046946,
"allowedError": 1.4345100285268639e-08,
"toleranceRatio": 0.6806483563569921,
"unboundedToleranceRatio": false,
"evaluated": false,
"passed": null,
"exclusionReason": "signalDiscontinuityLeftRightLimitSemanticsNotComparableAt0.04s"
},
{
"requestedTime": 0.04,
"key": "massFlow.pnvo001_5.port_2.intoComponent",
"category": "massFlow",
"python": -0.4978238221497115,
"amesim": -0.4978238226601424,
"absoluteError": 5.104309197534462e-10,
"relativeError": 1.0253244150228433e-09,
"allowedError": 0.0009956486453202849,
"toleranceRatio": 5.126616926086898e-07,
"unboundedToleranceRatio": false,
"evaluated": false,
"passed": null,
"exclusionReason": "signalDiscontinuityLeftRightLimitSemanticsNotComparableAt0.04s"
},
{
"requestedTime": 0.04,
"key": "conservation.pnvo001_5.massBalance",
"category": "conservation",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "conservation.totalStoredGasMass",
"category": "conservation",
"python": 5.5668930151375235,
"amesim": 5.566893015142308,
"absoluteError": 4.784617146924575e-12,
"relativeError": 8.594771147766101e-13,
"allowedError": 0.011133787030284616,
"toleranceRatio": 4.2973851879060635e-10,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "discrete.pnvo001_5.openMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "discrete.mecmas21_9.endstopMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.04,
"key": "discrete.mecmas21_10.endstopMode",
"category": "discreteMode",
"python": -1.0,
"amesim": -1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "pressure.pnch012_8.absolute",
"category": "pressure",
"python": 3620589.728099082,
"amesim": 3625053.2009413484,
"absoluteError": 4463.4728422663175,
"relativeError": 0.0012312847825536048,
"allowedError": 7250.107401882698,
"toleranceRatio": 0.6156423063618685,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "pressure.pnl0001_20.absolute",
"category": "pressure",
"python": 3756379.1344475006,
"amesim": 3760735.7825351665,
"absoluteError": 4356.648087665904,
"relativeError": 0.0011584563073795695,
"allowedError": 7521.472565070333,
"toleranceRatio": 0.5792280766798443,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "massFlow.pnl0001_20.port_1.intoComponent",
"category": "massFlow",
"python": -0.3723610392591412,
"amesim": -0.3728806429214546,
"absoluteError": 0.0005196036623134059,
"relativeError": 0.0013934852135053246,
"allowedError": 0.0007457622858429093,
"toleranceRatio": 0.6967416724836332,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "massFlow.pnvo001_5.port_2.intoComponent",
"category": "massFlow",
"python": -0.3962730176377355,
"amesim": -0.3961933197727504,
"absoluteError": 7.969786498507325e-05,
"relativeError": 0.00020115903274388008,
"allowedError": 0.0007923876395455008,
"toleranceRatio": 0.10057938943972738,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "conservation.pnvo001_5.massBalance",
"category": "conservation",
"python": 0.0,
"amesim": -0.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 1e-09,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "conservation.totalStoredGasMass",
"category": "conservation",
"python": 5.566893015137554,
"amesim": 5.566892994134813,
"absoluteError": 2.1002740879794146e-08,
"relativeError": 3.7727940705744284e-09,
"allowedError": 0.011133786988269626,
"toleranceRatio": 1.8863968658572583e-06,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "discrete.pnvo001_5.openMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "discrete.mecmas21_9.endstopMode",
"category": "discreteMode",
"python": 1.0,
"amesim": 1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
},
{
"requestedTime": 0.2,
"key": "discrete.mecmas21_10.endstopMode",
"category": "discreteMode",
"python": -1.0,
"amesim": -1.0,
"absoluteError": 0.0,
"relativeError": 0.0,
"allowedError": 0.0,
"toleranceRatio": 0.0,
"unboundedToleranceRatio": false,
"evaluated": true,
"passed": true
}
],
"toleranceProfile": {
"id": "amesim-alignment-reviewed-v1",
"byCategory": {
"pressure": {
"relative": 0.002,
"absolute": 0.001
},
"massFlow": {
"relative": 0.002,
"absolute": 1e-09
},
"conservation": {
"relative": 0.002,
"absolute": 1e-09
},
"discreteMode": {
"relative": 0.0,
"absolute": 0.0
}
},
"basis": "0.2% relative envelope for AMESim pressure/flow/inventory alignment; conservation retains a 1e-9 absolute floor and discrete modes are exact.",
"exclusions": [
{
"requestedTime": 0.04,
"category": "massFlow",
"reason": "The Python and AMESim samples at the signal discontinuity may represent different left/right limits. Raw errors remain recorded."
}
]
},
"policy": "diagnostic external-reference comparison; local golden is the regression gate"
}
}
@@ -0,0 +1,629 @@
{
"schemaVersion": 1,
"id": "test_mql_8-production-0.2s-physical-state-v2-amesim-aligned",
"caseId": "0.2s",
"lane": "production",
"sourceXmlSha256": "0a2d9331df9eb5974daec25a61c1238ba32b1742d933ffc8b16ce316c5627b0b",
"approval": {
"status": "approved",
"approvedAt": "2026-08-18T07:35:01+00:00",
"basis": "AME-aligned source, full physical-state-v2.1 external-reference assessment, finite-series, checkpoint, signal-segment, mechanical-event, residual, audit, no-fallback, and output-shape gates passed in the locked Python 3.12 production environment."
},
"provenance": {
"sourceReport": {
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-18-production-physical-state-v2.1-0.2.json",
"sha256": "c647fc7dab6c5cdaea188789dd8e0192d9caf374873afa1f5f5db0da9d69c49b",
"generatedAt": "2026-08-18T07:34:00.055573+00:00",
"metadataCompatibility": {
"status": "current",
"differences": []
}
},
"amesimReference": {
"path": "AmesimModels/test_mql.ame",
"sha256": "cbc3aadd4569a49b3a63e5d66d4143ec16126c0f950df73fb637e07673c20fbb",
"role": "external physical-state-v2.1 calibration reference"
}
},
"physicalLayout": {
"projectionCategories": [
"state"
],
"stateKeys": [
"amesim_mecmas21_1.a",
"amesim_mecmas21_1.v",
"amesim_mecmas21_1.x",
"amesim_mecmas21_10.a",
"amesim_mecmas21_10.v",
"amesim_mecmas21_10.x",
"amesim_mecmas21_2.a",
"amesim_mecmas21_2.v",
"amesim_mecmas21_2.x",
"amesim_mecmas21_3.a",
"amesim_mecmas21_3.v",
"amesim_mecmas21_3.x",
"amesim_mecmas21_4.a",
"amesim_mecmas21_4.v",
"amesim_mecmas21_4.x",
"amesim_mecmas21_5.a",
"amesim_mecmas21_5.v",
"amesim_mecmas21_5.x",
"amesim_mecmas21_6.a",
"amesim_mecmas21_6.v",
"amesim_mecmas21_6.x",
"amesim_mecmas21_7.a",
"amesim_mecmas21_7.v",
"amesim_mecmas21_7.x",
"amesim_mecmas21_8.a",
"amesim_mecmas21_8.v",
"amesim_mecmas21_8.x",
"amesim_mecmas21_9.a",
"amesim_mecmas21_9.v",
"amesim_mecmas21_9.x",
"amesim_pnch012_10.U",
"amesim_pnch012_10.m",
"amesim_pnch012_11.U",
"amesim_pnch012_11.m",
"amesim_pnch012_12.U",
"amesim_pnch012_12.m",
"amesim_pnch012_13.U",
"amesim_pnch012_13.m",
"amesim_pnch012_14.U",
"amesim_pnch012_14.m",
"amesim_pnch012_15.U",
"amesim_pnch012_15.m",
"amesim_pnch012_8.U",
"amesim_pnch012_8.m",
"amesim_pnch012_9.U",
"amesim_pnch012_9.m",
"amesim_pnch023_1.U",
"amesim_pnch023_1.m",
"amesim_pnch023_2.U",
"amesim_pnch023_2.m",
"amesim_pnch023_3.U",
"amesim_pnch023_3.m",
"amesim_pnch023_4.U",
"amesim_pnch023_4.m",
"amesim_pnl0001_1.U",
"amesim_pnl0001_1.m",
"amesim_pnl0001_10.U",
"amesim_pnl0001_10.m",
"amesim_pnl0001_11.U",
"amesim_pnl0001_11.m",
"amesim_pnl0001_13.U",
"amesim_pnl0001_13.m",
"amesim_pnl0001_14.U",
"amesim_pnl0001_14.m",
"amesim_pnl0001_15.U",
"amesim_pnl0001_15.m",
"amesim_pnl0001_16.U",
"amesim_pnl0001_16.m",
"amesim_pnl0001_17.U",
"amesim_pnl0001_17.m",
"amesim_pnl0001_18.U",
"amesim_pnl0001_18.m",
"amesim_pnl0001_19.U",
"amesim_pnl0001_19.m",
"amesim_pnl0001_2.U",
"amesim_pnl0001_2.m",
"amesim_pnl0001_20.U",
"amesim_pnl0001_20.m",
"amesim_pnl0001_21.U",
"amesim_pnl0001_21.m",
"amesim_pnl0001_25.U",
"amesim_pnl0001_25.m",
"amesim_pnl0001_26.U",
"amesim_pnl0001_26.m",
"amesim_pnl0001_27.U",
"amesim_pnl0001_27.m",
"amesim_pnl0001_4.U",
"amesim_pnl0001_4.m",
"amesim_pnl0001_5.U",
"amesim_pnl0001_5.m",
"amesim_pnl0001_7.U",
"amesim_pnl0001_7.m",
"amesim_pnl0001_9.U",
"amesim_pnl0001_9.m",
"amesim_pnl0002_1.U",
"amesim_pnl0002_1.m",
"amesim_pnl0002_2.U",
"amesim_pnl0002_2.m",
"amesim_pnl0002_3.U",
"amesim_pnl0002_3.m",
"amesim_pnl0002_4.U",
"amesim_pnl0002_4.m",
"amesim_pnl0002_5.U",
"amesim_pnl0002_5.m",
"amesim_pnl0002_6.U",
"amesim_pnl0002_6.m",
"amesim_pnl0002_7.U",
"amesim_pnl0002_7.m",
"amesim_pnl0002_8.U",
"amesim_pnl0002_8.m",
"amesim_pnl0003_1.U1",
"amesim_pnl0003_1.U2",
"amesim_pnl0003_1.m1",
"amesim_pnl0003_1.m2",
"amesim_pnl0003_2.U1",
"amesim_pnl0003_2.U2",
"amesim_pnl0003_2.m1",
"amesim_pnl0003_2.m2",
"amesim_pnl0003_3.U1",
"amesim_pnl0003_3.U2",
"amesim_pnl0003_3.m1",
"amesim_pnl0003_3.m2",
"amesim_pnl0003_4.U1",
"amesim_pnl0003_4.U2",
"amesim_pnl0003_4.m1",
"amesim_pnl0003_4.m2",
"amesim_pnl0003_5.U1",
"amesim_pnl0003_5.U2",
"amesim_pnl0003_5.m1",
"amesim_pnl0003_5.m2",
"amesim_pnl0003_6.U1",
"amesim_pnl0003_6.U2",
"amesim_pnl0003_6.m1",
"amesim_pnl0003_6.m2",
"amesim_pnl0003_7.U1",
"amesim_pnl0003_7.U2",
"amesim_pnl0003_7.m1",
"amesim_pnl0003_7.m2",
"amesim_pnl0003_8.U1",
"amesim_pnl0003_8.U2",
"amesim_pnl0003_8.m1",
"amesim_pnl0003_8.m2"
],
"stateKeyLayoutSha256": "9c5276b19aebf5648aa0175a8b30eeb090ae48a645e00af5efe400679c2a19c0"
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09,
"checkpointTimeAbsoluteSeconds": 1e-12
},
"physicalCheckpoints": [
{
"requestedTime": 0.0,
"values": [
-0.8167936695810981,
0.0,
0.0,
0.0,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
0.0,
0.0,
0.0,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-2395.136531414716,
0.0037120966016654976,
-32.754109884455104,
5.158193227968668e-05,
-4.814854153014898,
7.582544045113939e-06,
-4.814854153014898,
7.582544045113939e-06,
-4.814854153014898,
7.582544045113939e-06,
-4.814854153014898,
7.582544045113939e-06,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627
]
},
{
"requestedTime": 0.04,
"values": [
-0.7202663767630412,
-0.031313670277005726,
-0.0006393262654652846,
0.0,
0.0,
0.0,
-0.7202445746758257,
-0.03131326532214226,
-0.0006393222149083683,
-0.7202431079507902,
-0.031313245044468446,
-0.000639322126344006,
-0.7202608891564758,
-0.03131357572935321,
-0.000639325500334864,
-0.7203004770720824,
-0.03131467563203143,
-0.0006393436082476693,
-0.7203200385813013,
-0.031314937758515383,
-0.0006393445984135971,
-0.7203210417978083,
-0.03131494127751647,
-0.0006393443430309219,
-0.7203038829240781,
-0.031314872463294875,
-0.0006393483841598907,
0.0,
-1.3196376986328492e-22,
-2.1367171061617978e-27,
-1563.2317229265764,
0.0024627103498254554,
-1563.2320575702738,
0.002462711020582906,
-1563.2329311978795,
0.002462712553941299,
-1563.233271708201,
0.0024627131946489966,
-1563.2332458437982,
0.0024627131438128085,
-1563.2328271988004,
0.0024627123577517533,
-1563.2319625907526,
0.0024627108660579895,
-1563.2317089857306,
0.0024627103184389166,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-32.75534627762145,
5.162913025809795e-05,
-32.755348137443136,
5.1629142243738343e-05,
-32.75534804136382,
5.162914291472205e-05,
-32.7553506164913,
5.162913577247215e-05,
-32.755345646102356,
5.1629112834267025e-05,
-32.75534577906393,
5.162910342320198e-05,
-32.75534577952895,
5.162910292904694e-05,
-2395.136531414716,
0.0037120966016654976,
-32.75534579846172,
5.1629111455848846e-05,
-4.815030419476938,
7.589448594417277e-06,
-4.815030011609514,
7.589450747154388e-06,
-4.815030010504332,
7.589450673612998e-06,
-4.815030207302603,
7.589452052046662e-06,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-65.50987529417476,
0.00010325724257177476,
-65.50922049387084,
0.00010325683053111428,
-65.50922023152388,
0.00010325682249857915,
-65.50983596710446,
0.00010325719283273935,
-65.51060953242626,
0.00010325766285951179,
-65.51060943575153,
0.00010325765252819858,
-65.51060962617947,
0.0001032576608609912,
-65.51061020790279,
0.00010325768895577629,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627
]
},
{
"requestedTime": 0.2,
"values": [
2.633213298395276e-06,
1.1892894705560516e-06,
1.5894798508064198e-07,
0.0,
-2.578751264156614e-22,
-4.4071381894537746e-27,
2.632795658428222e-06,
1.189314089655013e-06,
1.589407569517069e-07,
2.632610849104822e-06,
1.1893270837855721e-06,
1.5893755046259315e-07,
2.6318631717003883e-06,
1.1893767434265974e-06,
1.5892477796136446e-07,
2.627345675136894e-06,
1.1896818589834777e-06,
1.588580216987135e-07,
2.626597124617547e-06,
1.189729910245627e-06,
1.5884573170762932e-07,
2.626716741360724e-06,
1.1897230638966853e-06,
1.5884742627218636e-07,
2.627662615850568e-06,
1.189655709206818e-06,
1.588644534572078e-07,
0.0,
-1.3196897803884347e-20,
2.911421882340363e-22,
-21722.04652916195,
0.06623994849325848,
-21723.45572154048,
0.06624103226621368,
-21805.8029650575,
0.06629330102524067,
-21807.348632461802,
0.06629448766538328,
-21807.718245902277,
0.06629475455162867,
-21841.35926610171,
0.06631660356825392,
-21724.166913573034,
0.06624154605298845,
-21722.236505250527,
0.06624008714645983,
-877966.3231213323,
1.2399467552157493,
-877966.3231959188,
1.2399467558278492,
-877963.9668549704,
1.2399274199318358,
-877963.9667803708,
1.2399274193197396,
-2232.5019275800073,
0.00315798322918964,
-2232.4899974231744,
0.003157926865311079,
-2232.5374050849664,
0.003157991047678687,
-1390.8326096413955,
0.0020292934150847593,
-1389.5677102640286,
0.0020284826306108832,
-1389.567755519397,
0.0020284825046839408,
-1389.5640026929677,
0.002028477899231957,
-1384.2773409257754,
0.0020250769587299407,
-1384.2734539740877,
0.002025072398131466,
-1384.273453362348,
0.002025072497347585,
-2232.5493408893735,
0.0031580474195833626,
-1384.2772469562626,
0.0020250771935230094,
-225.385588248691,
0.00032428657025173376,
-225.38393721998736,
0.0003242844761714517,
-225.38395476780747,
0.00032428450098976854,
-225.3856398271819,
0.0003242866434989134,
-2232.549339734738,
0.0031580474193159055,
-2232.501928009357,
0.003157983231060593,
-2232.489996268724,
0.0031579268650437325,
-2232.537405514378,
0.0031579910495496645,
-152.21172950802372,
0.002369452972089371,
-233.94138935309604,
0.0024219418441874473,
-234.03246203501217,
0.0024219979480173534,
-213.0564611194868,
0.002408520518742427,
-185.7911471147455,
0.0023910044024748737,
-185.83507440684312,
0.0023910306600708267,
-185.52866330836179,
0.0023908362272859114,
-113.23743631814425,
0.0023444176118442837,
-665.6994611241944,
-664.8834960853846,
0.0009422838620001763,
0.0009411594503803834,
-665.7201688784651,
-664.9043863133073,
0.0009423119505012294,
0.0009411876374833056,
-665.6994612846539,
-664.8834962525066,
0.0009422838626008165,
0.0009411594509858708,
-665.715814148825,
-664.8994727705187,
0.0009422941728241457,
0.000941169278566084,
-665.6951112104504,
-664.8785874929312,
0.0009422660888104697,
0.0009411410960587667,
-665.695111741859,
-664.8785880272087,
0.0009422660891455687,
0.0009411410963954202,
-665.7158139885557,
-664.8994726048427,
0.0009422941722236292,
0.0009411692779619336,
-665.7201683466378,
-664.9043857816836,
0.0009423119501658692,
0.0009411876371485153
]
}
],
"outputContract": {
"sha256": "ae78c3bc5c60768e1cfc9542267d89922ef10673c5da216f257323c4d94b7499"
}
}
@@ -1,26 +1,55 @@
{
"schemaVersion": 1,
"id": "test_mql_8-progressive-v3",
"description": "Primary generic-solver regression target for the restored eight-branch 0.005 s-grid authoritative inputs, with a short smoke gate before staged extension.",
"description": "Primary generic-solver regression target for the restored eight-branch inputs aligned to the test_mql AMESim archive, with a short smoke gate before staged extension.",
"source": {
"path": "tests/data/test-mql-8.xml",
"sha256": "6eb2753a15af66fd8e91c169c4587e66b0ace2164c08c2cb23a67aab5fd09164",
"bytes": 104109,
"sha256": "0a2d9331df9eb5974daec25a61c1238ba32b1742d933ffc8b16ce316c5627b0b",
"bytes": 104110,
"xmlIsAuthoritative": true,
"referenceArchive": {
"path": "AmesimModels/test_mql.ame",
"sha256": "cbc3aadd4569a49b3a63e5d66d4143ec16126c0f950df73fb637e07673c20fbb",
"bytes": 21708800,
"role": "authoritativePhysicalBaseline",
"resultStartTime": 0.0,
"resultStopTime": 10.0,
"resultSampleStep": 0.01,
"resultPointCount": 1002
},
"companionProject": {
"path": "tests/data/test-mql-8.json",
"sha256": "8d7a263691a9f09f39c92aba19ae1350911dc66b4904e98bae5f20592c5a3fcd",
"bytes": 272591,
"sha256": "b44bf540ccd1c293fe2af2b9b9052b540abf83961ad955a0f6a4ab40fbe0bb18",
"bytes": 272592,
"executionInput": false
},
"simulation": {
"tStart": 0.0,
"tStop": 5.0,
"sampleStep": 0.005,
"maxStep": 0.005,
"tStop": 10.0,
"sampleStep": 0.01,
"maxStep": 0.001,
"method": "BDF"
}
},
"acceptanceEvidence": {
"production0.2ApprovedReplay": {
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-18-production-opt00-approved-replay-0.2.json",
"sha256": "2e27cd54026a2612df88d1903431f3968e03599b2ca6eb83d9b98638cc051502",
"bytes": 398198,
"workerWallSeconds": 159.60707960650325,
"orchestrationWallSeconds": 160.47319143638015,
"acceptancePassed": true,
"regressionGoldenPassed": true,
"physicalStateV21GoldenPassed": true
},
"extensionDecision": {
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-18-production-opt00-extension-decision.json",
"sha256": "7d34aea736863788d9efa72c46910afffd543ddc46eacdf1c6aa9caeab202022",
"bytes": 1313,
"oneSecondOutcome": "eligible",
"extensionWasNotStarted": true
}
},
"historicalReports": [
{
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-17-solver-only-v1.json",
@@ -44,7 +73,7 @@
"sourceXmlSha256": "170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3",
"status": "historicalOnly",
"compatibleWithCurrentSource": false,
"reason": "This accepted 0.2 s report predates the four restored PNL0001 branches and cannot validate the v3 source."
"reason": "This accepted 0.2 s report predates the four restored PNL0001 branches and the AMESim-aligned 10 s simulation settings; it remains historical evidence only."
},
{
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-17-production-v2-extension-decision.json",
@@ -52,7 +81,17 @@
"sourceXmlSha256": "170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3",
"status": "historicalOnly",
"compatibleWithCurrentSource": false,
"reason": "This runtime extension decision was derived from the v2 source and is retained only as historical evidence."
"reason": "This runtime extension decision was derived from the superseded v2 source and must not control the AMESim-aligned v3 source."
}
],
"historicalGoldens": [
{
"path": "tests/baselines/simulation/test_mql_8/goldens/production-0.2s-v1.json",
"sha256": "99fa7b3631a89f59f86551847175f3701a1567d8d0bd10d87545734f17515d72",
"sourceXmlSha256": "170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3",
"status": "historicalOnly",
"compatibleWithCurrentSource": false,
"reason": "This Python-state golden predates the four restored PNL0001 branches and the AMESim-aligned simulation settings; it must not approve the current v3 source."
}
],
"structure": {
@@ -90,7 +129,7 @@
"productionEquivalentOutput": false
},
"production": {
"description": "Acceptance lane: changes only tStop and preserves the source 0.005 s output and internal-step grid.",
"description": "Acceptance lane: changes only tStop and preserves the AMESim-aligned source 0.01 s output grid and 0.001 s internal-step cap.",
"samplingMode": "source",
"maxStepMode": "source",
"instrumentationMode": "standard",
@@ -121,7 +160,23 @@
"expectedSignalEventTimes": [
0.04
],
"expectedMechanicalTransitionTimes": []
"expectedMechanicalTransitionTimes": [],
"goldens": {
"production": {
"path": "tests/baselines/simulation/test_mql_8/goldens/production-0.2s-v2.json",
"sha256": "7ca8e859d7d2889d6608726eba0faf3441f0f2ad579799c2abdfe571fd49fd0e",
"role": "pythonDeterminismRegression",
"affectsPhysicalCorrectness": false
}
},
"physicalStateV21Goldens": {
"production": {
"path": "tests/baselines/simulation/test_mql_8/goldens/production-0.2s-physical-state-v2.1.json",
"sha256": "6f0752afe8c1f7690599c2709a76d9a1342f99f726c6c8593321dc6267e175b2",
"role": "amesimPhysicalBaseline",
"compareOnEveryRun": true
}
}
},
"1s": {
"stopTime": 1.0,
@@ -175,12 +230,16 @@
}
},
"correctness": {
"status": "partial",
"status": "complete",
"maximumScaledResidual": 1e-7,
"requireFiniteSeries": true,
"requireStrictlyIncreasingTimes": true,
"physicalProjectionCategories": [
"state"
"state",
"pressure",
"massFlow",
"conservation",
"discreteMode"
],
"stateRelativeTolerance": 0.0002,
"stateAbsoluteTolerance": 1e-9,
@@ -188,7 +247,10 @@
"eventTimeAbsoluteToleranceSeconds": 0.00002,
"signalEventTimeAbsoluteToleranceSeconds": 1e-12,
"mechanicalTransitionTimesAvailable": true,
"note": "The restored v3 source currently enforces structure, finite series, residuals, checkpoints, and events. Its v2 state golden is historical-only; a new reviewed v3 state golden remains pending."
"physicalBaselineAuthority": "amesim",
"compareAmesimOnEveryRun": true,
"pythonGoldenRole": "determinismDiagnosticOnly",
"note": "AMESim simulation results are the sole physical baseline and every production run reports current-to-AMESim relative errors. The exact Python-state golden detects determinism and implementation drift only; it cannot approve physical correctness. Longer progressive horizons remain independently staged work."
},
"execution": {
"causalExecutorV2Default": true,
@@ -0,0 +1,40 @@
{
"schemaVersion": 1,
"kind": "progressive-extension-decision",
"manifestId": "test_mql_8-progressive-v2",
"sourceXmlSha256": "0a2d9331df9eb5974daec25a61c1238ba32b1742d933ffc8b16ce316c5627b0b",
"sourceReport": "2026-08-18-production-opt00-approved-replay-0.2.json",
"sourceReportSha256": "2e27cd54026a2612df88d1903431f3968e03599b2ca6eb83d9b98638cc051502",
"lane": "production",
"observedCase": {
"caseId": "0.2s",
"outcome": "completed",
"workerWallSeconds": 159.60707960650325,
"orchestrationWallSeconds": 160.47319143638015,
"acceptancePassed": true,
"regressionGoldenPassed": true,
"physicalStateV21GoldenPassed": true
},
"predictionSafetyFactor": 1.5,
"decisions": [
{
"caseId": "1s",
"outcome": "eligible",
"predictedWallSeconds": 1197.0530970487744,
"softTimeoutSeconds": 1200.0,
"reason": "predictedWallWithinSoftBudget"
},
{
"caseId": "5s",
"outcome": "notEvaluated",
"reason": "predecessorNotRunInOpt00"
},
{
"caseId": "10s",
"outcome": "notEvaluated",
"reason": "predecessorNotRunInOpt00"
}
],
"extensionWasNotStarted": true,
"scopeReason": "OPT-00 closes at the approved production 0.2 s gate; longer progressive horizons remain separately staged work."
}
@@ -0,0 +1,631 @@
{
"schemaVersion": 1,
"id": "test_mql_full_branches-production-0.81s-physical-state-v2",
"caseId": "0.81s",
"lane": "production",
"sourceXmlSha256": "2fb95e65f5de0c85a6a17802aef74ea004087323fd00fd8d01acf0184ff71d48",
"approval": {
"status": "approved",
"approvedAt": "2026-08-18T07:25:28+00:00",
"basis": "OPT-00 locked Python 3.12 production repeat 1 passed finite-series, checkpoint, event, residual, audit, and no-fallback gates; repeats 2 and 3 replayed every projected value exactly."
},
"provenance": {
"sourceReport": {
"path": "tests/baselines/simulation/test_mql_full_branches/runs/2026-08-18-opt00-historical-repeat-1.json",
"sha256": "4659ae850eef4f1541b3172f783b01a1859744a3f0a50030e094c2c25f93e195",
"generatedAt": "2026-08-18T07:12:43.593117+00:00",
"metadataCompatibility": {
"status": "current",
"differences": []
}
}
},
"physicalLayout": {
"projectionCategories": [
"state"
],
"stateKeys": [
"branch_boundary_pnl0002_b_chamber.U",
"branch_boundary_pnl0002_b_chamber.m",
"branch_boundary_pnl0002_branch05_chamber.U",
"branch_boundary_pnl0002_branch05_chamber.m",
"branch_boundary_pnl00r_branch05_chamber.U",
"branch_boundary_pnl00r_branch05_chamber.m",
"branch_boundary_pnl00r_chamber.U",
"branch_boundary_pnl00r_chamber.m",
"mass_friction_endstops_10.a",
"mass_friction_endstops_10.v",
"mass_friction_endstops_10.x",
"mass_friction_endstops_11.a",
"mass_friction_endstops_11.v",
"mass_friction_endstops_11.x",
"mass_friction_endstops_12.a",
"mass_friction_endstops_12.v",
"mass_friction_endstops_12.x",
"mass_friction_endstops_13.a",
"mass_friction_endstops_13.v",
"mass_friction_endstops_13.x",
"mass_friction_endstops_18.a",
"mass_friction_endstops_18.v",
"mass_friction_endstops_18.x",
"mass_friction_endstops_19.a",
"mass_friction_endstops_19.v",
"mass_friction_endstops_19.x",
"pn_c1_10.U",
"pn_c1_10.m",
"pn_c1_11.U",
"pn_c1_11.m",
"pn_c1_8.U",
"pn_c1_8.m",
"pn_c1_9.U",
"pn_c1_9.m",
"pn_general_chamber.U",
"pn_general_chamber.m",
"pn_general_chamber_2.U",
"pn_general_chamber_2.m",
"pneumatic_65.U",
"pneumatic_65.m",
"pneumatic_66.U",
"pneumatic_66.m",
"pneumatic_68.U",
"pneumatic_68.m",
"pneumatic_69.U",
"pneumatic_69.m",
"pneumatic_75.U1",
"pneumatic_75.U2",
"pneumatic_75.m1",
"pneumatic_75.m2",
"pneumatic_76.U1",
"pneumatic_76.U2",
"pneumatic_76.m1",
"pneumatic_76.m2",
"pneumatic_80.U",
"pneumatic_80.m",
"pneumatic_83.U",
"pneumatic_83.m",
"pneumatic_85.U",
"pneumatic_85.m",
"pneumatic_86.U",
"pneumatic_86.m",
"pneumatic_87.U1",
"pneumatic_87.U2",
"pneumatic_87.m1",
"pneumatic_87.m2",
"pneumatic_88.U1",
"pneumatic_88.U2",
"pneumatic_88.m1",
"pneumatic_88.m2",
"pneumatic_95.U",
"pneumatic_95.m",
"pneumatic_96.U",
"pneumatic_96.m",
"pneumatic_97.U",
"pneumatic_97.m",
"pneumatic_98.U",
"pneumatic_98.m",
"pneumatic_99.U",
"pneumatic_99.m"
],
"stateKeyLayoutSha256": "65824cf9793e935481c106c9e673bd2034a439c6dc3d74997ae2b8149ced4fbe"
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09,
"checkpointTimeAbsoluteSeconds": 1e-12
},
"physicalCheckpoints": [
{
"requestedTime": 0.0,
"values": [
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-65.50821976891021,
0.00010316386455937335,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976
]
},
{
"requestedTime": 0.04,
"values": [
-879404.9359576713,
1.2522714879032233,
-879404.9358312349,
1.2522714867708487,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
9.734498453326524e-06,
2.2165787255696986e-06,
7.615138175359735e-08,
6.899700674694032e-06,
1.6791980885681643e-06,
5.607885010135549e-08,
6.900102307554334e-06,
1.6791970458948259e-06,
5.6078837470433516e-08,
9.7344332607463e-06,
2.2165785543742696e-06,
7.615138012421167e-08,
0.0,
-1.3786916256412132e-25,
-5.521417937686967e-24,
0.0,
1.7204535623474844e-21,
-1.2449238864173693e-21,
-3836.4295505841374,
0.03971246371985885,
-8215.697802972754,
0.06449828662707775,
-8215.698180211439,
0.06449829186881316,
-3836.3905996761514,
0.03971246529726471,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-2323.937662655075,
0.003365680267480819,
-1558.6812613835866,
0.00229549447954163,
-1558.6815406132575,
0.0022954946593348505,
-2323.9377625288566,
0.0033656803769320197,
-733.2050606371582,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506629,
0.001136356102550663,
-3931.718242540372,
0.005752354208859085,
-3931.719290698283,
0.005752354971956523,
-7784.908056387039,
0.011153351022249161,
1704.5017150238582,
0.001497027829154341,
-733.205060637158,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506629,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-7784.908156509664,
0.011153351115632995,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976
]
},
{
"requestedTime": 0.69,
"values": [
-860379.0245019491,
1.1985497181126685,
-860379.0247699278,
1.1985497182559621,
-874070.5350132573,
1.2108685641280736,
-874070.5351659104,
1.2108685651503308,
6.302155088633299e-07,
2.0477201022089e-06,
1.739862170731644e-06,
6.885582115501165e-07,
2.072356484372671e-06,
1.7151425366011e-06,
6.88283471390605e-07,
2.0723565171342975e-06,
1.7151425037886711e-06,
6.301060784608125e-07,
2.0477201243571633e-06,
1.739862148436311e-06,
0.0,
2.0520928869397134e-22,
4.9098612203217504e-20,
0.0,
4.7142848729216316e-20,
-2.0780593120000206e-21,
-95711.13598161584,
0.2330223756901088,
-99484.30868025024,
0.23540556157200768,
-99484.30933223445,
0.23540556198958848,
-95711.12399082902,
0.23302236813021637,
-874021.6128809263,
1.2105342428506563,
-874021.6127639221,
1.2105342420477019,
-4699.544021984157,
0.006579168843426967,
-4720.142085434138,
0.006592090149576305,
-4720.142089865395,
0.006592090152448902,
-4699.544238887633,
0.006579168980161733,
-720.548977698558,
-720.2062838344375,
0.000998791691638376,
0.00099856697258834,
-722.1637803310383,
-722.0477500247466,
0.0010005181981378461,
0.0010004330928084816,
-8639.330806039252,
0.012679745952732602,
-8639.330537692149,
0.012679745783851644,
-8909.106011748408,
0.012849685563490313,
1389.1739707221775,
0.006354956657485442,
-722.1637801799975,
-722.0477531282031,
0.001000518198664749,
0.0010004330953830574,
-720.5489778046637,
-720.2062839511643,
0.0009987916917054815,
0.0009985669726623582,
-8909.106148651528,
0.012849685649995602,
-2357.5431522121776,
0.00326741144588273,
-2355.7331452897465,
0.0032646046108505246,
-2355.733147442795,
0.0032646046116421414,
-2357.543151890905,
0.0032674114436482805
]
},
{
"requestedTime": 0.7,
"values": [
-860379.8819972485,
1.1985586239832504,
-860379.8822663656,
1.1985586241379014,
-873989.7214316746,
1.210316350104016,
-873989.7215632556,
1.2103163509793102,
-2.1975173149257897e-06,
2.02734183270263e-06,
1.7602360080939606e-06,
-2.245873911306262e-06,
2.0517263968615454e-06,
1.7357614512099663e-06,
-2.2355280816555024e-06,
2.051726429425243e-06,
1.7357614187243355e-06,
-2.1975464187562464e-06,
2.0273418547326992e-06,
1.7602359860195857e-06,
0.0,
2.013739831250115e-21,
4.909861220321753e-20,
0.0,
4.665200046136569e-20,
-1.9566383001493946e-21,
-96560.09354570496,
0.2335572995463288,
-100288.67701814357,
0.23591263994887818,
-100288.677678112,
0.23591264036701443,
-96560.0816929563,
0.23355729206770598,
-873947.8864856226,
1.2100313452817673,
-873947.8863858595,
1.2100313445985562,
-4706.906282326286,
0.006583785685946545,
-4721.89741006748,
0.006593168289865688,
-4721.897404439745,
0.006593168286323642,
-4706.906483656017,
0.006583785812787189,
-720.6545512340357,
-720.3217292925623,
0.0009985764249686698,
0.0009983591472543481,
-722.1508419312411,
-722.0421232665894,
0.0010001323660439989,
0.00100005352811943,
-8642.043554742524,
0.012681407602717975,
-8642.043293601184,
0.012681407438297841,
-8909.49862797875,
0.012849959429845414,
1389.0998959067165,
0.006354964303801297,
-722.1508452424541,
-722.0421258113258,
0.0010001323686673997,
0.001000053530255132,
-720.6545510297586,
-720.3217293326904,
0.0009985764248405691,
0.0009983591472800236,
-8909.498764806547,
0.012849959516004638,
-2357.35674156337,
0.0032660606424093387,
-2355.921025330427,
0.0032637130423187337,
-2355.921027270413,
0.0032637130430599385,
-2357.3567413654787,
0.003266060640560602
]
},
{
"requestedTime": 0.8,
"values": [
-860175.1606710927,
1.1970987817186032,
-860175.1609442824,
1.1970987819042118,
-873416.0554547055,
1.206445289457375,
-873416.0554726648,
1.2064452895529436,
-1.949198776856065e-06,
1.826519161772371e-06,
1.9528536810215616e-06,
-1.971406163647771e-06,
1.848554964674094e-06,
1.930697446050263e-06,
-1.9711186178028582e-06,
1.8485549941949607e-06,
1.930697416668372e-06,
-1.94919528439641e-06,
1.826519181824015e-06,
1.9528536610522474e-06,
8.892500282325972,
2.5187368473853223e-21,
4.909861220321753e-20,
-16.252500528926326,
4.576919129896975e-20,
-1.9530653259948938e-21,
-104471.75361732666,
0.23818832921650948,
-107803.92242364622,
0.24029497741554184,
-107803.92309836508,
0.2402949778374652,
-104471.7429655161,
0.2381883224928943,
-873404.5104492744,
1.2063684021036665,
-873404.5104388825,
1.2063684020307441,
-4704.300638717692,
0.006574703133691949,
-4693.326778314112,
0.006567696268767441,
-4693.326526389924,
0.006567696110012524,
-4704.3007263131885,
0.006574703188787359,
-721.1607141734798,
-720.9310979144485,
0.0009965662267659527,
0.0009964191642398892,
-721.9230383692676,
-721.8772183273541,
0.0009972429132872915,
0.0009972108290529252,
-8703.980749781746,
0.012705472199729761,
-8703.980555484635,
0.01270547207717158,
-8967.989302068207,
0.012872002894864527,
1377.5687495145785,
0.006346840211910472,
-721.9230326537768,
-721.8772199435517,
0.0009972429097436842,
0.0009972108301285444,
-721.1607123097651,
-720.9310970023907,
0.000996566225592321,
0.0009964191636655784,
-8967.989425383095,
0.0128720029723915,
-2356.03147928222,
0.0032562562535671563,
-2356.668143012874,
0.0032561870649935125,
-2356.668143725967,
0.0032561870653915977,
-2356.031477688963,
0.0032562562523787346
]
},
{
"requestedTime": 0.81,
"values": [
-860055.7816079621,
1.1962534043760158,
-860055.7818796586,
1.1962534045522308,
-873367.9088398638,
1.206124190328161,
-873367.9088547124,
1.206124190402843,
8.857774649477797,
0.08873345197507769,
0.00044583262150923715,
8.857774480804801,
0.08873347304797707,
0.0004458106820297355,
8.857774445663672,
0.08873347304800631,
0.0004458106820006473,
8.857774661221775,
0.08873345197509747,
0.00044583262148946694,
8.857778206680395,
0.08873165793329868,
0.00044386167425480166,
-16.206598333861397,
-0.16235697968747592,
-0.0008121695210045893,
-105735.3088635258,
0.23897151058696434,
-109037.397892104,
0.24109191168512564,
-109037.39857007933,
0.2410919121122127,
-105735.29829660244,
0.23897150391654698,
-873350.1784581648,
1.206006248314118,
-873350.1784501448,
1.2060062482571396,
-4682.546337026962,
0.006553210204193263,
-4667.866605288969,
0.006543088564608954,
-4667.866320678238,
0.006543088385317721,
-4682.546417371199,
0.006553210254794004,
-721.1212567696082,
-720.9136270898001,
0.0009960146033417782,
0.0009958781343227421,
-721.8635886622386,
-721.8407704168852,
0.0009969210574287178,
0.0009968996150299173,
-8724.337750229228,
0.012701926255908602,
-8724.337567825336,
0.012701926140963282,
-8998.36421558522,
0.012879100492327268,
1364.3747046167766,
0.00633752606051716,
-721.8635847156165,
-721.8407700947419,
0.000996921054988454,
0.0009968996148721218,
-721.121255394678,
-720.9136259824896,
0.0009960146024759352,
0.0009958781336253564,
-8998.364330621043,
0.012879100564768854,
-2355.9148119653287,
0.0032552690027748852,
-2356.531146237746,
0.0032546207052266537,
-2356.5311468883583,
0.0032546207055964556,
-2355.914810407958,
0.0032552690016484026
]
}
],
"outputContract": {
"sha256": "e7596daa27144f14d5210c100126fa0c413862ed8a8e4bf525195b2e6070e0f9"
}
}
@@ -0,0 +1,886 @@
{
"schemaVersion": 1,
"id": "test_mql_full_branches-production-2.10s-physical-state-v2",
"caseId": "2.10s",
"lane": "production",
"sourceXmlSha256": "2fb95e65f5de0c85a6a17802aef74ea004087323fd00fd8d01acf0184ff71d48",
"approval": {
"status": "approved",
"approvedAt": "2026-08-18T07:25:28+00:00",
"basis": "OPT-00 locked Python 3.12 production repeat 1 passed finite-series, checkpoint, event, residual, audit, and no-fallback gates; repeats 2 and 3 replayed every projected value exactly."
},
"provenance": {
"sourceReport": {
"path": "tests/baselines/simulation/test_mql_full_branches/runs/2026-08-18-opt00-historical-repeat-1.json",
"sha256": "4659ae850eef4f1541b3172f783b01a1859744a3f0a50030e094c2c25f93e195",
"generatedAt": "2026-08-18T07:12:43.593117+00:00",
"metadataCompatibility": {
"status": "current",
"differences": []
}
}
},
"physicalLayout": {
"projectionCategories": [
"state"
],
"stateKeys": [
"branch_boundary_pnl0002_b_chamber.U",
"branch_boundary_pnl0002_b_chamber.m",
"branch_boundary_pnl0002_branch05_chamber.U",
"branch_boundary_pnl0002_branch05_chamber.m",
"branch_boundary_pnl00r_branch05_chamber.U",
"branch_boundary_pnl00r_branch05_chamber.m",
"branch_boundary_pnl00r_chamber.U",
"branch_boundary_pnl00r_chamber.m",
"mass_friction_endstops_10.a",
"mass_friction_endstops_10.v",
"mass_friction_endstops_10.x",
"mass_friction_endstops_11.a",
"mass_friction_endstops_11.v",
"mass_friction_endstops_11.x",
"mass_friction_endstops_12.a",
"mass_friction_endstops_12.v",
"mass_friction_endstops_12.x",
"mass_friction_endstops_13.a",
"mass_friction_endstops_13.v",
"mass_friction_endstops_13.x",
"mass_friction_endstops_18.a",
"mass_friction_endstops_18.v",
"mass_friction_endstops_18.x",
"mass_friction_endstops_19.a",
"mass_friction_endstops_19.v",
"mass_friction_endstops_19.x",
"pn_c1_10.U",
"pn_c1_10.m",
"pn_c1_11.U",
"pn_c1_11.m",
"pn_c1_8.U",
"pn_c1_8.m",
"pn_c1_9.U",
"pn_c1_9.m",
"pn_general_chamber.U",
"pn_general_chamber.m",
"pn_general_chamber_2.U",
"pn_general_chamber_2.m",
"pneumatic_65.U",
"pneumatic_65.m",
"pneumatic_66.U",
"pneumatic_66.m",
"pneumatic_68.U",
"pneumatic_68.m",
"pneumatic_69.U",
"pneumatic_69.m",
"pneumatic_75.U1",
"pneumatic_75.U2",
"pneumatic_75.m1",
"pneumatic_75.m2",
"pneumatic_76.U1",
"pneumatic_76.U2",
"pneumatic_76.m1",
"pneumatic_76.m2",
"pneumatic_80.U",
"pneumatic_80.m",
"pneumatic_83.U",
"pneumatic_83.m",
"pneumatic_85.U",
"pneumatic_85.m",
"pneumatic_86.U",
"pneumatic_86.m",
"pneumatic_87.U1",
"pneumatic_87.U2",
"pneumatic_87.m1",
"pneumatic_87.m2",
"pneumatic_88.U1",
"pneumatic_88.U2",
"pneumatic_88.m1",
"pneumatic_88.m2",
"pneumatic_95.U",
"pneumatic_95.m",
"pneumatic_96.U",
"pneumatic_96.m",
"pneumatic_97.U",
"pneumatic_97.m",
"pneumatic_98.U",
"pneumatic_98.m",
"pneumatic_99.U",
"pneumatic_99.m"
],
"stateKeyLayoutSha256": "65824cf9793e935481c106c9e673bd2034a439c6dc3d74997ae2b8149ced4fbe"
},
"tolerance": {
"relative": 0.0002,
"absolute": 1e-09,
"checkpointTimeAbsoluteSeconds": 1e-12
},
"physicalCheckpoints": [
{
"requestedTime": 0.0,
"values": [
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
-0.8167936695810981,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-1563.893548405841,
0.002462855848963059,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-32.754109884455104,
5.158193227968668e-05,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-65.50821976891021,
0.00010316386455937335,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-65.50821976891021,
0.00010316386455937335,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976
]
},
{
"requestedTime": 0.04,
"values": [
-879404.9359576713,
1.2522714879032233,
-879404.9358312349,
1.2522714867708487,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
9.734498453326524e-06,
2.2165787255696986e-06,
7.615138175359735e-08,
6.899700674694032e-06,
1.6791980885681643e-06,
5.607885010135549e-08,
6.900102307554334e-06,
1.6791970458948259e-06,
5.6078837470433516e-08,
9.7344332607463e-06,
2.2165785543742696e-06,
7.615138012421167e-08,
0.0,
-1.3786916256412132e-25,
-5.521417937686967e-24,
0.0,
1.7204535623474844e-21,
-1.2449238864173693e-21,
-3836.4295505841374,
0.03971246371985885,
-8215.697802972754,
0.06449828662707775,
-8215.698180211439,
0.06449829186881316,
-3836.3905996761514,
0.03971246529726471,
-886868.3937230138,
1.374510850335198,
-886868.3937230138,
1.374510850335198,
-2323.937662655075,
0.003365680267480819,
-1558.6812613835866,
0.00229549447954163,
-1558.6815406132575,
0.0022954946593348505,
-2323.9377625288566,
0.0033656803769320197,
-733.2050606371582,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506629,
0.001136356102550663,
-3931.718242540372,
0.005752354208859085,
-3931.719290698283,
0.005752354971956523,
-7784.908056387039,
0.011153351022249161,
1704.5017150238582,
0.001497027829154341,
-733.205060637158,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506629,
-733.2050606371581,
-733.2050606371581,
0.0011363561025506627,
0.0011363561025506627,
-7784.908156509664,
0.011153351115632995,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976,
-2395.136531414716,
0.0037120966016654976
]
},
{
"requestedTime": 0.69,
"values": [
-860379.0245019491,
1.1985497181126685,
-860379.0247699278,
1.1985497182559621,
-874070.5350132573,
1.2108685641280736,
-874070.5351659104,
1.2108685651503308,
6.302155088633299e-07,
2.0477201022089e-06,
1.739862170731644e-06,
6.885582115501165e-07,
2.072356484372671e-06,
1.7151425366011e-06,
6.88283471390605e-07,
2.0723565171342975e-06,
1.7151425037886711e-06,
6.301060784608125e-07,
2.0477201243571633e-06,
1.739862148436311e-06,
0.0,
2.0520928869397134e-22,
4.9098612203217504e-20,
0.0,
4.7142848729216316e-20,
-2.0780593120000206e-21,
-95711.13598161584,
0.2330223756901088,
-99484.30868025024,
0.23540556157200768,
-99484.30933223445,
0.23540556198958848,
-95711.12399082902,
0.23302236813021637,
-874021.6128809263,
1.2105342428506563,
-874021.6127639221,
1.2105342420477019,
-4699.544021984157,
0.006579168843426967,
-4720.142085434138,
0.006592090149576305,
-4720.142089865395,
0.006592090152448902,
-4699.544238887633,
0.006579168980161733,
-720.548977698558,
-720.2062838344375,
0.000998791691638376,
0.00099856697258834,
-722.1637803310383,
-722.0477500247466,
0.0010005181981378461,
0.0010004330928084816,
-8639.330806039252,
0.012679745952732602,
-8639.330537692149,
0.012679745783851644,
-8909.106011748408,
0.012849685563490313,
1389.1739707221775,
0.006354956657485442,
-722.1637801799975,
-722.0477531282031,
0.001000518198664749,
0.0010004330953830574,
-720.5489778046637,
-720.2062839511643,
0.0009987916917054815,
0.0009985669726623582,
-8909.106148651528,
0.012849685649995602,
-2357.5431522121776,
0.00326741144588273,
-2355.7331452897465,
0.0032646046108505246,
-2355.733147442795,
0.0032646046116421414,
-2357.543151890905,
0.0032674114436482805
]
},
{
"requestedTime": 0.7,
"values": [
-860379.8819972485,
1.1985586239832504,
-860379.8822663656,
1.1985586241379014,
-873989.7214316746,
1.210316350104016,
-873989.7215632556,
1.2103163509793102,
-2.1975173149257897e-06,
2.02734183270263e-06,
1.7602360080939606e-06,
-2.245873911306262e-06,
2.0517263968615454e-06,
1.7357614512099663e-06,
-2.2355280816555024e-06,
2.051726429425243e-06,
1.7357614187243355e-06,
-2.1975464187562464e-06,
2.0273418547326992e-06,
1.7602359860195857e-06,
0.0,
2.013739831250115e-21,
4.909861220321753e-20,
0.0,
4.665200046136569e-20,
-1.9566383001493946e-21,
-96560.09354570496,
0.2335572995463288,
-100288.67701814357,
0.23591263994887818,
-100288.677678112,
0.23591264036701443,
-96560.0816929563,
0.23355729206770598,
-873947.8864856226,
1.2100313452817673,
-873947.8863858595,
1.2100313445985562,
-4706.906282326286,
0.006583785685946545,
-4721.89741006748,
0.006593168289865688,
-4721.897404439745,
0.006593168286323642,
-4706.906483656017,
0.006583785812787189,
-720.6545512340357,
-720.3217292925623,
0.0009985764249686698,
0.0009983591472543481,
-722.1508419312411,
-722.0421232665894,
0.0010001323660439989,
0.00100005352811943,
-8642.043554742524,
0.012681407602717975,
-8642.043293601184,
0.012681407438297841,
-8909.49862797875,
0.012849959429845414,
1389.0998959067165,
0.006354964303801297,
-722.1508452424541,
-722.0421258113258,
0.0010001323686673997,
0.001000053530255132,
-720.6545510297586,
-720.3217293326904,
0.0009985764248405691,
0.0009983591472800236,
-8909.498764806547,
0.012849959516004638,
-2357.35674156337,
0.0032660606424093387,
-2355.921025330427,
0.0032637130423187337,
-2355.921027270413,
0.0032637130430599385,
-2357.3567413654787,
0.003266060640560602
]
},
{
"requestedTime": 0.8,
"values": [
-860175.1606710927,
1.1970987817186032,
-860175.1609442824,
1.1970987819042118,
-873416.0554547055,
1.206445289457375,
-873416.0554726648,
1.2064452895529436,
-1.949198776856065e-06,
1.826519161772371e-06,
1.9528536810215616e-06,
-1.971406163647771e-06,
1.848554964674094e-06,
1.930697446050263e-06,
-1.9711186178028582e-06,
1.8485549941949607e-06,
1.930697416668372e-06,
-1.94919528439641e-06,
1.826519181824015e-06,
1.9528536610522474e-06,
8.892500282325972,
2.5187368473853223e-21,
4.909861220321753e-20,
-16.252500528926326,
4.576919129896975e-20,
-1.9530653259948938e-21,
-104471.75361732666,
0.23818832921650948,
-107803.92242364622,
0.24029497741554184,
-107803.92309836508,
0.2402949778374652,
-104471.7429655161,
0.2381883224928943,
-873404.5104492744,
1.2063684021036665,
-873404.5104388825,
1.2063684020307441,
-4704.300638717692,
0.006574703133691949,
-4693.326778314112,
0.006567696268767441,
-4693.326526389924,
0.006567696110012524,
-4704.3007263131885,
0.006574703188787359,
-721.1607141734798,
-720.9310979144485,
0.0009965662267659527,
0.0009964191642398892,
-721.9230383692676,
-721.8772183273541,
0.0009972429132872915,
0.0009972108290529252,
-8703.980749781746,
0.012705472199729761,
-8703.980555484635,
0.01270547207717158,
-8967.989302068207,
0.012872002894864527,
1377.5687495145785,
0.006346840211910472,
-721.9230326537768,
-721.8772199435517,
0.0009972429097436842,
0.0009972108301285444,
-721.1607123097651,
-720.9310970023907,
0.000996566225592321,
0.0009964191636655784,
-8967.989425383095,
0.0128720029723915,
-2356.03147928222,
0.0032562562535671563,
-2356.668143012874,
0.0032561870649935125,
-2356.668143725967,
0.0032561870653915977,
-2356.031477688963,
0.0032562562523787346
]
},
{
"requestedTime": 1.0,
"values": [
-821832.18723991,
1.0200651174775017,
-821832.18747001,
1.0200651176202795,
-867004.8037748901,
1.1678210257789237,
-867004.8037842558,
1.1678210258114112,
5.337506036671112,
1.4692397997585764,
0.1599133361094105,
5.337515379170655,
1.4692397472726986,
0.1599133103986336,
5.337515143153142,
1.4692397472727232,
0.15991331039860968,
5.337506972429692,
1.4692397997585922,
0.15991333610939415,
5.337478209825592,
1.4692396558157577,
0.1599111725256627,
-9.54931997607691,
-2.6696065452676345,
-0.2915209059087121,
-265725.0471201981,
0.37065108819994863,
-274733.0912934626,
0.38314931696882704,
-274733.09196309303,
0.38314931740574626,
-265725.0371165716,
0.3706510817652048,
-865080.8367860075,
1.1575072978479832,
-865080.836781886,
1.157507297823738,
-3796.0300072134555,
0.004795940055001584,
-3536.527034352294,
0.004541242546837507,
-3536.5270264260203,
0.004541242541783211,
-3796.030006397544,
0.004795940054530253,
-669.6911044196434,
-669.5192067053738,
0.000896344956139504,
0.0008958075886883069,
-706.3131229180824,
-705.7453980192186,
0.0009508631922671181,
0.0009500270072581424,
-7576.544518407595,
0.009496349448619141,
-7576.544516641329,
0.009496349447543061,
-8495.442747354022,
0.010545461510647114,
-396.7407438874708,
0.004826437204332604,
-706.3131229284975,
-705.7453980286242,
0.0009508631922926857,
0.0009500270072830445,
-669.6911041651043,
-669.5192060345452,
0.0008963449559782043,
0.0008958075882644087,
-8495.442745022583,
0.010545461509227754,
-2334.4069259408425,
0.003123729270270666,
-2231.2112738065657,
0.002986126319734133,
-2231.211273855305,
0.0029861263197482296,
-2334.4069257629353,
0.0031237292700972397
]
},
{
"requestedTime": 2.0,
"values": [
-772286.4682624673,
0.9179360053806332,
-772286.4692545742,
0.9179360060143595,
-803031.5764482999,
0.9390697516259071,
-803031.5764679932,
0.9390697516680178,
-5.5966665968298915e-09,
6.713197714589101e-08,
0.3700023585738787,
-4.6964269131422045e-08,
8.296314048540649e-08,
0.3700023428086111,
-2.4841283448040484e-08,
8.29631490450475e-08,
0.37000234280860256,
4.3330946937203405e-08,
6.713198290867542e-08,
0.3700023585738729,
0.0,
-1.1654046974175209e-19,
0.37,
0.0,
-3.1507290824741586e-13,
-0.72,
-437317.9315829484,
0.6466111001653223,
-453928.6983570724,
0.6571847803274807,
-453928.69829577766,
0.6571847802863814,
-437317.92241094704,
0.6466110943182897,
-802960.6941217952,
0.9388828023997754,
-802960.694111656,
0.938882802373437,
-4284.7599403173035,
0.005077450257846326,
-4343.976212113444,
0.005115234066714636,
-4343.976212068701,
0.0051152340666703175,
-4284.7599132929745,
0.005077450240619749,
-662.1434159057197,
-661.7970678665274,
0.0007748418062540021,
0.0007746177710019782,
-663.5584177841083,
-663.4278440347166,
0.000776034735673773,
0.0007759468226959079,
-8120.656733496484,
0.009869069686396056,
-8120.656590103802,
0.009869069595030996,
-8328.371203371178,
0.010001111074316178,
-1647.6116574692712,
0.005744784430325623,
-663.5584178697704,
-663.4278441405601,
0.0007760347357471679,
0.000775946822781371,
-662.1434159537747,
-661.7970678998525,
0.0007748418062849091,
0.0007746177710225603,
-8328.371132243179,
0.010001111028980096,
-2168.153541946679,
0.002535378014133708,
-2164.6176191131335,
0.002532440207666662,
-2164.6176190758297,
0.002532440207628468,
-2168.153486402468,
0.0025353779787030337
]
},
{
"requestedTime": 2.05,
"values": [
-772640.2579114933,
0.9189711020154747,
-772640.2589106868,
0.9189711026533357,
-802668.8163858769,
0.9381125189656889,
-802668.8163913072,
0.9381125189701648,
-1.1839671060442924e-07,
6.848947280509e-08,
0.37000236198352005,
-9.816722013056278e-08,
8.350267455159574e-08,
0.3700023469893441,
-9.857350960373879e-08,
8.350268244045684e-08,
0.3700023469893362,
-9.438255801796913e-08,
6.84894782408154e-08,
0.3700023619835146,
0.0,
-1.1644108142482222e-19,
0.37,
0.0,
-3.150728667970711e-13,
-0.72,
-436877.0913448419,
0.6470168220031571,
-453182.789784899,
0.6574025955544724,
-453182.78972087207,
0.6574025955125712,
-436877.08234619617,
0.6470168162690852,
-802668.9179584626,
0.9381130502939634,
-802668.9179569854,
0.93811305029142,
-4285.669856693608,
0.0050823610773067076,
-4354.119008990314,
0.005125983105252238,
-4354.11901311425,
0.005125983107881825,
-4285.669816785954,
0.00508236105187094,
-662.3977781264183,
-662.0512100662392,
0.0007748078072798002,
0.0007745870872944216,
-663.4643136874881,
-663.3368456649802,
0.0007754873920209047,
0.0007754062014915902,
-8144.606717144718,
0.009892956807728209,
-8144.606576808627,
0.009892956718340342,
-8356.938599998502,
0.010028108806575306,
-1651.8254437402938,
0.005756202827816742,
-663.4643140644157,
-663.336846204431,
0.0007754873922663931,
0.000775406201901327,
-662.3977787097264,
-662.0512095396308,
0.0007748078076617882,
0.0007745870869169965,
-8356.938530080884,
0.010028108762001683,
-2167.3598235897343,
0.002533287559515925,
-2164.9943210453084,
0.002531780415394501,
-2164.9943209833646,
0.002531780415326603,
-2167.3597687183774,
0.0025332875249184767
]
},
{
"requestedTime": 2.1,
"values": [
-772729.849215003,
0.919232833810773,
-772729.8502213629,
0.9192328344649761,
-802748.5417139007,
0.9383229687007891,
-802748.5416937041,
0.938322968645941,
-1.5475961845368147e-07,
6.633051283469949e-08,
0.3700023653554917,
-1.6945006791502238e-07,
8.0606090679709e-08,
0.3700023510933796,
-1.6974110621958972e-07,
8.060609824063203e-08,
0.3700023510933721,
-1.5545636415481566e-07,
6.633051807899984e-08,
0.37000236535548653,
0.0,
-1.164558438137342e-19,
0.37,
0.0,
-3.1507286064810157e-13,
-0.72,
-436285.01209659013,
0.646815227151306,
-452333.92653835827,
0.6570381274463638,
-452333.9264734677,
0.6570381273931875,
-436285.00323808845,
0.6468152214977394,
-802747.7609731702,
0.9383213576466783,
-802747.7609716613,
0.9383213576459569,
-4169.822958852925,
0.005009690287404794,
-4279.837336800422,
0.005079777604819243,
-4279.83737473691,
0.0050797776289221675,
-4169.822929680966,
0.005009690268749923,
-662.0363168630721,
-658.7502160682232,
0.0007747218486607169,
0.0007726291344808584,
-662.5313787250212,
-655.702207731343,
0.0007750276677438138,
0.0007706784944489166,
-8148.173203124546,
0.00989744668242247,
-8148.173078272971,
0.009897446602767727,
-8360.714796856231,
0.010032755342377334,
-1652.8900562718827,
0.005759128268286419,
-662.5313698561031,
-655.7022069331298,
0.000775027662068701,
0.0007706784939136677,
-662.0363583491554,
-658.7501715937773,
0.0007747218750818736,
0.0007726291061561864,
-8360.714717131163,
0.010032755291451692,
-2167.569222453132,
0.002533854003606723,
-2165.094351906718,
0.0025322965276950117,
-2165.0943434975566,
0.0025322965223399637,
-2167.569167946825,
0.002533853968955365
]
}
],
"outputContract": {
"sha256": "5ed1efeba81d0f0a47bd1e0513f5503fc507cac8ae8c08f96602f10c1266eb86"
}
}
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"id": "test_mql_full_branches-historical-v1",
"description": "Historical slow-region and 2.05 s regression target for the generic solver.",
"description": "Historical slow-region and 2.05 s production regression target with three locked-environment repeats and approved state goldens.",
"source": {
"path": "tests/data/test_mql-full-branches-01-04.xml",
"sha256": "2fb95e65f5de0c85a6a17802aef74ea004087323fd00fd8d01acf0184ff71d48",
@@ -60,7 +60,7 @@
0.0,
0.04,
0.69,
0.70,
0.7,
0.8,
0.81
],
@@ -68,7 +68,13 @@
0.04,
0.8
],
"expectedMechanicalTransitionTimes": []
"expectedMechanicalTransitionTimes": [],
"goldens": {
"production": {
"path": "tests/baselines/simulation/test_mql_full_branches/goldens/production-0.81s-v1.json",
"sha256": "72c4ccf995c7904f80598934bf59fe88cff2f9b85e003396befc4ae3f37a4e53"
}
}
},
"2.10s": {
"stopTime": 2.1,
@@ -78,7 +84,7 @@
0.0,
0.04,
0.69,
0.70,
0.7,
0.8,
1.0,
2.0,
@@ -88,19 +94,35 @@
"expectedSignalEventTimes": [
0.04,
0.8
]
],
"expectedMechanicalTransitionTimes": [
1.1199696877773673,
1.1331667954945566,
1.2023983086085228
],
"goldens": {
"production": {
"path": "tests/baselines/simulation/test_mql_full_branches/goldens/production-2.10s-v1.json",
"sha256": "5fced8349404c9dd1f6284b76af3f1e24ed0933df76fb4b5d2a419b719f64d4c"
}
}
}
},
"correctness": {
"status": "partial",
"maximumScaledResidual": 1e-7,
"status": "complete",
"maximumScaledResidual": 1e-07,
"requireFiniteSeries": true,
"requireStrictlyIncreasingTimes": true,
"stateRelativeTolerance": 0.0002,
"eventTimeAbsoluteToleranceSeconds": 0.00002,
"eventTimeAbsoluteToleranceSeconds": 2e-05,
"signalEventTimeAbsoluteToleranceSeconds": 1e-12,
"mechanicalTransitionTimesAvailable": true,
"note": "Historical checkpoints are recorded; approved physical-state-v2 golden values remain pending."
"note": "OPT-00 production 0.81/2.10 s state, event, output-shape, finite-series, residual, audit, and no-fallback contracts are approved and replayed exactly across three locked-environment runs.",
"physicalProjectionCategories": [
"state"
],
"stateAbsoluteTolerance": 1e-09,
"checkpointTimeAbsoluteToleranceSeconds": 1e-12
},
"execution": {
"causalExecutorV2Default": true,
@@ -116,5 +138,33 @@
"deferAfterFailureOrTimeout": true,
"deferWhenPredictedWallExceedsSoftTimeout": true,
"longTestEnvironmentVariable": "RUN_TEST_MQL_FULL_BRANCHES_LONG_REGRESSION"
},
"acceptanceEvidence": {
"status": "approved",
"lane": "production",
"repeatCount": 3,
"physicalValuesIdenticalAcrossRepeats": true,
"reports": [
{
"path": "tests/baselines/simulation/test_mql_full_branches/runs/2026-08-18-opt00-historical-repeat-1.json",
"sha256": "4659ae850eef4f1541b3172f783b01a1859744a3f0a50030e094c2c25f93e195",
"bytes": 314699
},
{
"path": "tests/baselines/simulation/test_mql_full_branches/runs/2026-08-18-opt00-historical-repeat-2.json",
"sha256": "99892d0aca554baca7f72ae00412337f6815f62cb7a4349dfe3006db66b4549f",
"bytes": 315044
},
{
"path": "tests/baselines/simulation/test_mql_full_branches/runs/2026-08-18-opt00-historical-repeat-3.json",
"sha256": "569bf7e576bd56f956f2125921680a570ee16f445f5d3d7cbff16a4f4cc12f85",
"bytes": 315606
}
],
"workerWallSeconds2p10": {
"minimum": 113.49742291588336,
"median": 114.07500359974802,
"maximum": 115.86820452474058
}
}
}
+3 -3
View File
@@ -10196,9 +10196,9 @@
],
"simulation": {
"t_start": 0,
"t_stop": 5,
"step": 0.005,
"max_step": 0.005,
"t_stop": 10,
"step": 0.01,
"max_step": 0.001,
"method": "BDF"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<System name="test-mql-8" schemaVersion="3" unitSystem="SI">
<Simulation tStart="0" tStop="5" sampleStep="0.005" maxStep="0.005" method="BDF"/>
<Simulation tStart="0" tStop="10" sampleStep="0.01" maxStep="0.001" method="BDF"/>
<Components>
<Component id="pn_gas_data" type="amesim_helium_medium" modelVersion="0.1.0">
<Parameter name="gi" value="1"/>
+240 -6
View File
@@ -26,6 +26,13 @@ from app.simulation.benchmark_regression import (
)
PHYSICAL_STATE_V21_REPORT = (
DEFAULT_MANIFEST_PATH.parent
/ "runs"
/ "2026-08-18-production-physical-state-v2.1-0.2.json"
)
def _completed_result(request: RegressionCaseRequest, *, wall_seconds: float = 0.01):
signal_times = (
[0.04, 0.8]
@@ -125,16 +132,24 @@ class RegressionManifestTests(unittest.TestCase):
self.assertTrue(manifest["source"]["xmlIsAuthoritative"])
self.assertEqual(
manifest["source"]["sha256"],
"6eb2753a15af66fd8e91c169c4587e66b0ace2164c08c2cb23a67aab5fd09164",
"0a2d9331df9eb5974daec25a61c1238ba32b1742d933ffc8b16ce316c5627b0b",
)
self.assertEqual(Path(manifest["_sourcePath"]).name, "test-mql-8.xml")
self.assertEqual(
manifest["source"]["companionProject"]["sha256"],
"8d7a263691a9f09f39c92aba19ae1350911dc66b4904e98bae5f20592c5a3fcd",
"b44bf540ccd1c293fe2af2b9b9052b540abf83961ad955a0f6a4ab40fbe0bb18",
)
self.assertFalse(
manifest["source"]["companionProject"]["executionInput"]
)
self.assertEqual(
manifest["source"]["referenceArchive"]["role"],
"authoritativePhysicalBaseline",
)
self.assertEqual(
Path(manifest["_referenceArchivePath"]).name,
"test_mql.ame",
)
self.assertEqual(manifest["historicalReports"][0]["status"], "historicalOnly")
self.assertFalse(
manifest["historicalReports"][0]["compatibleWithCurrentSource"]
@@ -146,6 +161,86 @@ class RegressionManifestTests(unittest.TestCase):
self.assertFalse(
manifest["historicalReports"][2]["compatibleWithCurrentSource"]
)
physical_state_golden = manifest["_physicalStateV21Goldens"]["0.2s"][
"production"
]
self.assertEqual(physical_state_golden["approval"]["status"], "approved")
self.assertEqual(
physical_state_golden["_sha256"],
"6f0752afe8c1f7690599c2709a76d9a1342f99f726c6c8593321dc6267e175b2",
)
self.assertTrue(
physical_state_golden["amesimAlignmentAtGeneration"]["passed"]
)
self.assertEqual(
manifest["correctness"]["physicalBaselineAuthority"], "amesim"
)
self.assertTrue(manifest["correctness"]["compareAmesimOnEveryRun"])
self.assertEqual(
manifest["correctness"]["pythonGoldenRole"],
"determinismDiagnosticOnly",
)
self.assertEqual(
manifest["variants"]["0.2s"]["goldens"]["production"]["role"],
"pythonDeterminismRegression",
)
self.assertEqual(
manifest["variants"]["0.2s"]["physicalStateV21Goldens"][
"production"
]["role"],
"amesimPhysicalBaseline",
)
def test_manifest_rejects_physical_state_v21_golden_hash_mismatch(self) -> None:
manifest = json.loads(DEFAULT_MANIFEST_PATH.read_text(encoding="utf-8"))
manifest["variants"]["0.2s"]["physicalStateV21Goldens"]["production"][
"sha256"
] = "0" * 64
with tempfile.TemporaryDirectory(
dir=DEFAULT_MANIFEST_PATH.parents[4], prefix=".v21-manifest-test-"
) as temporary_directory:
manifest_path = Path(temporary_directory) / "manifest.json"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
with self.assertRaisesRegex(
RegressionManifestError,
"physical-state-v2.1 golden hash mismatch",
):
load_regression_manifest(manifest_path)
def test_manifest_rejects_amesim_archive_hash_mismatch(self) -> None:
manifest = json.loads(DEFAULT_MANIFEST_PATH.read_text(encoding="utf-8"))
manifest["source"]["referenceArchive"]["sha256"] = "0" * 64
with tempfile.TemporaryDirectory(
dir=DEFAULT_MANIFEST_PATH.parents[4], prefix=".amesim-manifest-test-"
) as temporary_directory:
manifest_path = Path(temporary_directory) / "manifest.json"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
with self.assertRaisesRegex(
RegressionManifestError,
"AMESim reference archive hash mismatch",
):
load_regression_manifest(manifest_path)
def test_manifest_rejects_amesim_golden_provenance_mismatch(self) -> None:
loaded = load_regression_manifest(DEFAULT_MANIFEST_PATH)
golden = json.loads(
json.dumps(loaded["_physicalStateV21Goldens"]["0.2s"]["production"])
)
golden["provenance"]["amesim"]["archiveSha256"] = "0" * 64
manifest = json.loads(DEFAULT_MANIFEST_PATH.read_text(encoding="utf-8"))
with tempfile.TemporaryDirectory(
dir=DEFAULT_MANIFEST_PATH.parents[4], prefix=".amesim-provenance-test-"
) as temporary_directory:
manifest_path = Path(temporary_directory) / "manifest.json"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
with patch(
"app.simulation.benchmark_regression.load_physical_state_v21_golden",
return_value=golden,
), self.assertRaisesRegex(
RegressionManifestError,
"AMESim baseline provenance",
):
load_regression_manifest(manifest_path)
def test_in_memory_derivative_does_not_change_authoritative_xml(self) -> None:
manifest = load_regression_manifest(DEFAULT_MANIFEST_PATH)
@@ -159,9 +254,9 @@ class RegressionManifestTests(unittest.TestCase):
max_step=0.005,
)
self.assertEqual(source_simulation_config(before)["tStop"], 5.0)
self.assertEqual(source_simulation_config(before)["sampleStep"], 0.005)
self.assertEqual(source_simulation_config(before)["maxStep"], 0.005)
self.assertEqual(source_simulation_config(before)["tStop"], 10.0)
self.assertEqual(source_simulation_config(before)["sampleStep"], 0.01)
self.assertEqual(source_simulation_config(before)["maxStep"], 0.001)
self.assertEqual(source_simulation_config(derived)["tStop"], 1.0)
self.assertEqual(source_simulation_config(derived)["sampleStep"], 0.02)
self.assertEqual(source_simulation_config(derived)["maxStep"], 0.005)
@@ -399,6 +494,145 @@ class BoundedChildProcessTests(unittest.TestCase):
class ProgressiveSuiteTests(unittest.TestCase):
@staticmethod
def _physical_state_v21_result_executor():
source_report = json.loads(
PHYSICAL_STATE_V21_REPORT.read_text(encoding="utf-8")
)
results = {case["caseId"]: case for case in source_report["cases"]}
def execute(request: RegressionCaseRequest) -> dict[str, object]:
return json.loads(json.dumps(results[request.case_id]))
return execute
def test_production_report_replays_through_active_v21_acceptance(self) -> None:
report = run_regression_suite(
DEFAULT_MANIFEST_PATH,
lane="production",
case_ids=["0.2s"],
case_executor=self._physical_state_v21_result_executor(),
)
case = report["cases"][1]
evaluation = case["acceptance"]["physicalStateV21Golden"]
self.assertEqual(case["outcome"], "completed")
self.assertTrue(case["acceptance"]["passed"])
self.assertTrue(evaluation["configured"])
self.assertTrue(evaluation["evaluated"])
self.assertTrue(evaluation["passed"])
self.assertEqual(evaluation["baselineAuthority"], "amesim")
self.assertEqual(evaluation["metricCount"], 33)
self.assertEqual(evaluation["availableBaselineValueCount"], 27)
self.assertEqual(evaluation["unavailableBaselineValueCount"], 6)
self.assertEqual(evaluation["comparedValueCount"], 25)
self.assertEqual(evaluation["excludedValueCount"], 2)
self.assertAlmostEqual(
evaluation["maxToleranceRatio"], 0.6967416724836332
)
self.assertAlmostEqual(
evaluation["worstValue"]["relativeError"],
0.0013934852135053246,
)
self.assertTrue(
all(
"actual" in metric and "amesimBaseline" in metric
for metric in evaluation["metrics"]
)
)
self.assertEqual(evaluation["issues"], [])
self.assertTrue(evaluation["amesimAlignmentAtGenerationPassed"])
regression = case["acceptance"]["regressionGolden"]
self.assertEqual(regression["role"], "determinismDiagnosticOnly")
self.assertFalse(regression["affectsPhysicalCorrectness"])
def test_v21_conservation_drift_fails_runner_acceptance(self) -> None:
execute_source = self._physical_state_v21_result_executor()
def drifted(request: RegressionCaseRequest) -> dict[str, object]:
result = execute_source(request)
if request.case_id == "0.2s":
result["worker"]["summary"]["physicalStateV21"]["checkpoints"][-1][
"values"
]["conservation.p4node2_8.massBalance"] = 2.0e-9
return result
report = run_regression_suite(
DEFAULT_MANIFEST_PATH,
lane="production",
case_ids=["0.2s"],
case_executor=drifted,
)
case = report["cases"][1]
evaluation = case["acceptance"]["physicalStateV21Golden"]
self.assertEqual(case["outcome"], "correctness_failed")
self.assertFalse(case["acceptance"]["passed"])
self.assertIn(
"physicalStateV21LocalInvariantMismatch",
case["acceptance"]["issues"],
)
self.assertFalse(evaluation["passed"])
self.assertEqual(
evaluation["localInvariants"]["worstValue"]["key"],
"conservation.p4node2_8.massBalance",
)
def test_python_state_drift_is_diagnostic_when_amesim_gate_passes(self) -> None:
execute_source = self._physical_state_v21_result_executor()
def drifted(request: RegressionCaseRequest) -> dict[str, object]:
result = execute_source(request)
if request.case_id == "0.2s":
values = result["worker"]["summary"]["physicalContract"][
"checkpoints"
][-1]["stateValues"]
first_key = next(iter(values))
values[first_key] += 1.0
return result
report = run_regression_suite(
DEFAULT_MANIFEST_PATH,
lane="production",
case_ids=["0.2s"],
case_executor=drifted,
)
case = report["cases"][1]
diagnostic = case["acceptance"]["regressionGolden"]
self.assertEqual(case["outcome"], "completed")
self.assertTrue(case["acceptance"]["passed"])
self.assertFalse(diagnostic["passed"])
self.assertIn("stateCheckpointGoldenValueMismatch", diagnostic["issues"])
self.assertEqual(diagnostic["acceptanceIssues"], [])
self.assertTrue(case["acceptance"]["physicalStateV21Golden"]["passed"])
def test_amesim_baseline_drift_is_the_physical_acceptance_gate(self) -> None:
execute_source = self._physical_state_v21_result_executor()
def drifted(request: RegressionCaseRequest) -> dict[str, object]:
result = execute_source(request)
if request.case_id == "0.2s":
result["worker"]["summary"]["physicalStateV21"]["checkpoints"][-1][
"values"
]["pressure.pnch012_8.absolute"] += 1.0e6
return result
report = run_regression_suite(
DEFAULT_MANIFEST_PATH,
lane="production",
case_ids=["0.2s"],
case_executor=drifted,
)
case = report["cases"][1]
evaluation = case["acceptance"]["physicalStateV21Golden"]
self.assertEqual(case["outcome"], "correctness_failed")
self.assertFalse(case["acceptance"]["passed"])
self.assertIn("amesimReferenceToleranceMismatch", case["acceptance"]["issues"])
self.assertFalse(evaluation["passed"])
self.assertEqual(evaluation["worstValue"]["amesimBaseline"], 3625053.2009413484)
def test_requesting_late_case_also_runs_every_predecessor(self) -> None:
calls: list[str] = []
@@ -585,7 +819,7 @@ class ProgressiveSuiteTests(unittest.TestCase):
self.assertEqual(
seen,
[("solver-only", 0.02, 0.05), ("production", 0.005, 0.005)],
[("solver-only", 0.02, 0.05), ("production", 0.01, 0.001)],
)
+98 -3
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
from importlib import metadata
import os
import platform
from pathlib import Path
import re
import sys
@@ -13,6 +14,11 @@ REQUIREMENTS_PATH = REPOSITORY_ROOT / "requirements.txt"
CONSTRAINTS_PATH = (
REPOSITORY_ROOT / "constraints" / "python312-direct.txt"
)
RELEASE_LOCK_PATH = (
REPOSITORY_ROOT
/ "constraints"
/ "python312-linux-x86_64.lock"
)
PYTHON_VERSION_PATH = REPOSITORY_ROOT / ".python-version"
VERIFY_ENVIRONMENT_VARIABLE = "SYSTEM_SIMULATION_VERIFY_LOCKED_ENV"
@@ -24,6 +30,37 @@ REFERENCE_DIRECT_VERSIONS = {
"scipy": "1.18.0",
"uvicorn": "0.52.3",
}
REFERENCE_LOCKED_VERSIONS = {
"annotated-doc": "0.0.5",
"annotated-types": "0.8.0",
"anyio": "4.14.2",
"click": "8.4.2",
"fastapi": "0.141.1",
"h11": "0.16.0",
"httptools": "0.8.0",
"idna": "3.18",
"lxml": "6.1.1",
"numpy": "2.5.2",
"pydantic": "2.13.4",
"pydantic-core": "2.46.4",
"python-dotenv": "1.2.3",
"pyyaml": "6.0.3",
"scipy": "1.18.0",
"starlette": "1.6.0",
"typing-extensions": "4.16.0",
"typing-inspection": "0.4.4",
"uvicorn": "0.52.3",
"uvloop": "0.22.1",
"watchfiles": "1.2.0",
"websockets": "17.0.1",
}
LOCK_REQUIREMENT_PATTERN = re.compile(
r"(?P<name>[A-Za-z0-9_.-]+)"
r"(?:\[(?P<extras>[A-Za-z0-9_,.-]+)\])?"
r"==(?P<version>[^\s;]+)\s+"
r"--hash=sha256:(?P<digest>[0-9a-f]{64})\Z"
)
def _active_lines(path: Path) -> tuple[str, ...]:
@@ -57,6 +94,33 @@ def _constraint_versions() -> dict[str, str]:
return result
def _release_lock_options() -> tuple[str, ...]:
return tuple(
line
for line in _active_lines(RELEASE_LOCK_PATH)
if line.startswith("--")
)
def _release_lock_entries() -> dict[str, tuple[str, str, str | None]]:
result: dict[str, tuple[str, str, str | None]] = {}
for line in _active_lines(RELEASE_LOCK_PATH):
if line.startswith("--"):
continue
match = LOCK_REQUIREMENT_PATTERN.fullmatch(line)
if match is None:
raise AssertionError(f"Invalid release-lock entry: {line!r}")
normalized = _normalized_name(match.group("name"))
if normalized in result:
raise AssertionError(f"Duplicate release-lock entry: {normalized}")
result[normalized] = (
match.group("version"),
match.group("digest"),
match.group("extras"),
)
return result
class DependencyConstraintContractTests(unittest.TestCase):
def test_python_reference_version_is_explicit(self) -> None:
self.assertEqual(
@@ -87,18 +151,49 @@ class DependencyConstraintContractTests(unittest.TestCase):
}.isdisjoint(_constraint_versions())
)
def test_linux_release_lock_pins_and_hashes_complete_environment(
self,
) -> None:
self.assertEqual(
_release_lock_options(),
("--only-binary=:all:", "--require-hashes"),
)
entries = _release_lock_entries()
self.assertEqual(
{name: entry[0] for name, entry in entries.items()},
REFERENCE_LOCKED_VERSIONS,
)
self.assertTrue(all(len(entry[1]) == 64 for entry in entries.values()))
self.assertEqual(entries["uvicorn"][2], "standard")
self.assertTrue(
all(
entry[2] is None
for name, entry in entries.items()
if name != "uvicorn"
)
)
self.assertEqual(
{
name: REFERENCE_LOCKED_VERSIONS[name]
for name in REFERENCE_DIRECT_VERSIONS
},
REFERENCE_DIRECT_VERSIONS,
)
@unittest.skipUnless(
os.getenv(VERIFY_ENVIRONMENT_VARIABLE, "").strip().lower()
in {"1", "true", "yes", "on"},
f"Set {VERIFY_ENVIRONMENT_VARIABLE}=1 to verify installed versions.",
)
def test_installed_environment_matches_reference_constraints(self) -> None:
def test_installed_environment_matches_linux_release_lock(self) -> None:
self.assertEqual(sys.version_info[:3], (3, 12, 3))
self.assertEqual(sys.platform, "linux")
self.assertEqual(platform.machine().lower(), "x86_64")
installed = {
name: metadata.version(name)
for name in REFERENCE_DIRECT_VERSIONS
for name in REFERENCE_LOCKED_VERSIONS
}
self.assertEqual(installed, REFERENCE_DIRECT_VERSIONS)
self.assertEqual(installed, REFERENCE_LOCKED_VERSIONS)
if __name__ == "__main__":
+64 -1
View File
@@ -1,12 +1,14 @@
from __future__ import annotations
import hashlib
import json
import os
from pathlib import Path
import unittest
from app.main import compile_system_xml_network
from app.simulation.benchmark_regression import (
evaluate_regression_golden,
load_regression_manifest,
run_regression_suite,
source_simulation_config,
@@ -85,6 +87,60 @@ class MqlFullBranchesStaticRegressionTests(unittest.TestCase):
expected["hasMechanicalStateEvents"],
)
def test_three_production_repeats_replay_approved_goldens_exactly(self) -> None:
repository_root = Path(self.manifest["_repositoryRoot"])
evidence = self.manifest["acceptanceEvidence"]
self.assertEqual(evidence["status"], "approved")
self.assertEqual(evidence["lane"], "production")
self.assertEqual(evidence["repeatCount"], 3)
self.assertTrue(evidence["physicalValuesIdenticalAcrossRepeats"])
expected_counts = {"0.81s": 480, "2.10s": 720}
stable_signatures: dict[str, object] = {}
for report_reference in evidence["reports"]:
report_path = repository_root / report_reference["path"]
payload = report_path.read_bytes()
self.assertEqual(len(payload), report_reference["bytes"])
self.assertEqual(
hashlib.sha256(payload).hexdigest(),
report_reference["sha256"],
)
report = json.loads(payload)
self.assertEqual(report["lane"], "production")
self.assertEqual(
report["source"]["sha256"], self.manifest["source"]["sha256"]
)
for case_id in self.manifest["sequence"]:
case = next(
item for item in report["cases"] if item["caseId"] == case_id
)
self.assertEqual(case["outcome"], "completed")
self.assertTrue(case["acceptance"]["passed"])
summary = case["worker"]["summary"]
golden = self.manifest["_goldens"][case_id]["production"]
audit = evaluate_regression_golden(summary, golden)
self.assertTrue(audit["passed"], audit)
self.assertEqual(
audit["comparedValueCount"], expected_counts[case_id]
)
self.assertEqual(audit["maxAbsoluteError"], 0.0)
self.assertEqual(audit["maxToleranceRatio"], 0.0)
self.assertEqual(audit["issues"], [])
diagnostics = dict(summary["diagnostics"])
diagnostics.pop("performance", None)
signature = {
"physicalContract": summary["physicalContract"],
"eventTrace": summary["eventTrace"],
"outputContract": summary["outputContract"],
"diagnosticsWithoutPerformance": diagnostics,
}
if case_id not in stable_signatures:
stable_signatures[case_id] = signature
else:
self.assertEqual(stable_signatures[case_id], signature)
def test_both_horizons_have_the_expected_signal_schedule(self) -> None:
assert self.validation.document is not None
system = GenericFluidSystem(
@@ -105,9 +161,16 @@ class MqlFullBranchesStaticRegressionTests(unittest.TestCase):
)
class MqlFullBranchesLongRegressionTests(unittest.TestCase):
def test_both_horizons_complete_within_their_safety_budgets(self) -> None:
report = run_regression_suite(MANIFEST_PATH, lane="solver-only")
report = run_regression_suite(MANIFEST_PATH, lane="production")
outcomes = [case["outcome"] for case in report["cases"]]
self.assertEqual(outcomes, ["completed", "completed"], report["cases"])
self.assertTrue(
all(
case["acceptance"]["regressionGolden"]["passed"]
for case in report["cases"]
),
report["cases"],
)
if __name__ == "__main__":
+487
View File
@@ -0,0 +1,487 @@
from __future__ import annotations
import copy
import hashlib
import json
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
from app.simulation.benchmark_regression import summarize_simulation_result
from app.simulation.physical_state_v21 import (
PHYSICAL_STATE_V21_ID,
PhysicalStateV21Error,
approve_candidate_golden,
assess_report_against_amesim,
build_amesim_reference,
build_candidate_golden,
compare_contract_to_amesim,
evaluate_physical_state_v21,
load_approved_golden,
project_physical_state_v21,
write_candidate_golden,
)
REPO_ROOT = Path(__file__).resolve().parents[1]
AMESIM_ARCHIVE = REPO_ROOT / "AmesimModels" / "test_mql.ame"
CANDIDATE_REPORT = (
REPO_ROOT
/ "tests/baselines/simulation/test_mql_8/runs"
/ "2026-08-18-production-amesim-aligned-v1-candidate-0.2.json"
)
def _variable(key: str, quantity: str, unit: str, category: str) -> dict[str, str]:
return {"key": key, "quantity": quantity, "unit": unit, "category": category}
def _synthetic_result() -> dict[str, object]:
times = [0.0, 0.04, 0.2]
variables: list[dict[str, str]] = []
series: dict[str, list[float]] = {"time": times}
def add(
key: str,
values: list[float],
quantity: str,
unit: str,
category: str,
) -> None:
variables.append(_variable(key, quantity, unit, category))
series[key] = values
add("amesim_pnch012_8.p", [100_000.0, 200_000.0, 300_000.0], "pressure", "Pa", "thermodynamic")
add("amesim_pnl0001_20.p", [100_000.0, 210_000.0, 310_000.0], "pressure", "Pa", "thermodynamic")
add("amesim_pnl0001_20.port_1.m_flow", [-0.1, -0.2, -0.3], "mass_flow", "kg/s", "port")
add("amesim_pnch012_8.port_1.m_flow", [0.1, 0.2, 0.3], "mass_flow", "kg/s", "port")
add("amesim_pnvo001_5.port_2.m_flow", [-0.2, -0.3, -0.4], "mass_flow", "kg/s", "port")
add("amesim_pnvo001_5.port_3.m_flow", [0.2, 0.3, 0.4], "mass_flow", "kg/s", "port")
for port, values in enumerate(
([0.4, 0.5, 0.6], [-0.1, -0.2, -0.3], [-0.2, -0.2, -0.2], [-0.1, -0.1, -0.1]),
start=1,
):
add(f"amesim_p4node2_8.port_{port}.m_flow", list(values), "mass_flow", "kg/s", "port")
add("amesim_pnvo001_5.xv", [0.0, 1.0, 1.0], "dimensionless", "", "derived")
add("amesim_mecmas21_9.x", [0.0, 0.0, 0.0], "length", "m", "state")
add("amesim_mecmas21_9.v", [0.0, 0.0, 0.0], "velocity", "m/s", "state")
add("amesim_mecmas21_9.port_1.f", [1.0, 1.0, 1.0], "force", "N", "port")
add("amesim_mecmas21_9.port_2.f", [2.0, 2.0, 2.0], "force", "N", "port")
add("amesim_mecmas21_10.x", [0.0, 0.0, 0.0], "length", "m", "state")
add("amesim_mecmas21_10.v", [0.0, 0.0, 0.0], "velocity", "m/s", "state")
add("amesim_mecmas21_10.port_1.f", [-2.0, -2.0, -2.0], "force", "N", "port")
add("amesim_mecmas21_10.port_2.f", [-1.0, -1.0, -1.0], "force", "N", "port")
add("storage_a.m", [1.0, 2.0, 3.0], "mass", "kg", "state")
add("storage_b.m", [4.0, 3.0, 2.0], "mass", "kg", "state")
return {
"success": True,
"status": "completed",
"partial": False,
"simulatedUntil": 0.2,
"requestedStopTime": 0.2,
"variables": variables,
"series": series,
"final": {key: values[-1] for key, values in series.items() if key != "time"},
"diagnostics": {},
}
class PhysicalStateProjectionTests(unittest.TestCase):
def test_projects_all_categories_with_units_and_strict_balances(self) -> None:
contract = project_physical_state_v21(
_synthetic_result(), checkpoint_times=(0.0, 0.04, 0.2), sample_step=0.04
)
self.assertEqual(contract["id"], PHYSICAL_STATE_V21_ID)
self.assertEqual(
contract["projectionCategories"],
["pressure", "massFlow", "conservation", "discreteMode"],
)
self.assertEqual(len(contract["layout"]["projectionKeys"]), 11)
endpoint = contract["checkpoints"][-1]["values"]
self.assertEqual(endpoint["conservation.totalStoredGasMass"], 5.0)
self.assertAlmostEqual(endpoint["conservation.p4node2_8.massBalance"], 0.0)
self.assertAlmostEqual(endpoint["conservation.pnvo001_5.massBalance"], 0.0)
self.assertAlmostEqual(
endpoint["conservation.pnl0001_20_pnch012_8.connectionMassBalance"], 0.0
)
self.assertEqual(endpoint["discrete.pnvo001_5.openMode"], 1.0)
self.assertEqual(endpoint["discrete.mecmas21_9.endstopMode"], 1.0)
self.assertEqual(endpoint["discrete.mecmas21_10.endstopMode"], -1.0)
pressure = contract["layout"]["projections"][0]["amesim"]
self.assertEqual(pressure["offset"], 101_300.0)
flow = contract["layout"]["projections"][2]["amesim"]
self.assertEqual(flow["scale"], -1.0e-3)
def test_fails_closed_on_wrong_source_unit(self) -> None:
result = _synthetic_result()
next(
variable
for variable in result["variables"]
if variable["key"] == "amesim_pnch012_8.p"
)["unit"] = "bar"
with self.assertRaisesRegex(PhysicalStateV21Error, "Unexpected metadata"):
project_physical_state_v21(result, checkpoint_times=(0.2,), sample_step=0.2)
def test_benchmark_summary_attaches_v21_only_to_matching_result(self) -> None:
summary = summarize_simulation_result(
_synthetic_result(), checkpoint_times=(0.0, 0.2), sample_step=0.2
)
self.assertEqual(summary["physicalStateV21"]["id"], PHYSICAL_STATE_V21_ID)
class AmesimReferenceTests(unittest.TestCase):
def test_reference_records_archive_hash_units_signs_and_known_endpoint(self) -> None:
reference = build_amesim_reference(AMESIM_ARCHIVE, checkpoint_times=(0.2,))
values = reference["checkpoints"][0]["values"]
self.assertAlmostEqual(values["pressure.pnch012_8.absolute"], 3_625_053.2009413484)
self.assertAlmostEqual(
values["massFlow.pnl0001_20.port_1.intoComponent"],
-0.3728806429214546,
)
self.assertEqual(values["discrete.mecmas21_9.endstopMode"], 1.0)
self.assertEqual(values["discrete.mecmas21_10.endstopMode"], -1.0)
provenance = reference["provenance"]
self.assertEqual(provenance["archivePath"], "AmesimModels/test_mql.ame")
self.assertEqual(len(provenance["archiveSha256"]), 64)
self.assertEqual(
[member["name"] for member in provenance["members"]],
["test_mql_.var", "test_mql_.results"],
)
def test_current_candidate_endpoint_passes_explicit_amesim_alignment_profile(self) -> None:
assessment = assess_report_against_amesim(
CANDIDATE_REPORT, archive_path=AMESIM_ARCHIVE
)
comparison = assessment["comparison"]
self.assertEqual(assessment["coverage"], "legacyEndpointOnly")
self.assertTrue(comparison["passed"])
self.assertEqual(comparison["comparedValueCount"], 9)
self.assertEqual(comparison["metricCount"], 11)
self.assertEqual(comparison["availableBaselineValueCount"], 9)
self.assertEqual(comparison["unavailableBaselineValueCount"], 2)
self.assertEqual(len(comparison["metrics"]), 11)
self.assertTrue(
all(
{
"requestedTime",
"key",
"category",
"actual",
"amesimBaseline",
"absoluteError",
"relativeError",
"evaluated",
"passed",
}
<= set(metric)
for metric in comparison["metrics"]
)
)
self.assertEqual(
comparison["worstValue"]["key"],
"massFlow.pnl0001_20.port_1.intoComponent",
)
self.assertAlmostEqual(
comparison["worstValue"]["relativeError"],
0.0013934852135053246,
)
self.assertEqual(
comparison["toleranceProfile"]["byCategory"]["discreteMode"]["relative"],
0.0,
)
def test_signal_jump_mass_flows_record_errors_but_are_not_evaluated(self) -> None:
contract = project_physical_state_v21(
_synthetic_result(), checkpoint_times=(0.0, 0.04, 0.2), sample_step=0.04
)
reference_checkpoints = []
for checkpoint in contract["checkpoints"]:
values = dict(checkpoint["values"])
values["conservation.p4node2_8.massBalance"] = None
values[
"conservation.pnl0001_20_pnch012_8.connectionMassBalance"
] = None
if checkpoint["requestedTime"] == 0.04:
values["massFlow.pnl0001_20.port_1.intoComponent"] += 100.0
values["massFlow.pnvo001_5.port_2.intoComponent"] -= 100.0
reference_checkpoints.append(
{"requestedTime": checkpoint["requestedTime"], "values": values}
)
comparison = compare_contract_to_amesim(
contract, {"checkpoints": reference_checkpoints}
)
excluded = [
metric
for metric in comparison["metrics"]
if metric["requestedTime"] == 0.04 and metric["category"] == "massFlow"
]
self.assertTrue(comparison["passed"])
self.assertEqual(comparison["metricCount"], 33)
self.assertEqual(comparison["availableBaselineValueCount"], 27)
self.assertEqual(comparison["unavailableBaselineValueCount"], 6)
self.assertEqual(comparison["comparedValueCount"], 25)
self.assertEqual(comparison["excludedValueCount"], 2)
self.assertEqual(len(comparison["metrics"]), 33)
self.assertEqual(len(excluded), 2)
self.assertTrue(all(metric["evaluated"] is False for metric in excluded))
self.assertTrue(all(metric["passed"] is None for metric in excluded))
self.assertTrue(all(metric["absoluteError"] > 99.0 for metric in excluded))
self.assertTrue(
all("LeftRightLimit" in metric["exclusionReason"] for metric in excluded)
)
self.assertNotIn(comparison["worstValue"], excluded)
unavailable = [
metric
for metric in comparison["metrics"]
if metric["amesimBaseline"] is None
]
self.assertEqual(len(unavailable), 6)
self.assertTrue(all(metric["evaluated"] is False for metric in unavailable))
self.assertTrue(all(metric["absoluteError"] is None for metric in unavailable))
self.assertTrue(all(metric["relativeError"] is None for metric in unavailable))
def test_relative_error_uses_the_amesim_baseline_denominator(self) -> None:
contract = project_physical_state_v21(
_synthetic_result(), checkpoint_times=(0.2,), sample_step=0.2
)
values = dict(contract["checkpoints"][0]["values"])
key = "pressure.pnch012_8.absolute"
values[key] = 0.5 * values[key]
comparison = compare_contract_to_amesim(
contract,
{"checkpoints": [{"requestedTime": 0.2, "values": values}]},
)
metric = next(item for item in comparison["metrics"] if item["key"] == key)
expected = abs(metric["actual"] - metric["amesimBaseline"]) / abs(
metric["amesimBaseline"]
)
self.assertAlmostEqual(metric["relativeError"], expected)
self.assertAlmostEqual(metric["relativeErrorPercent"], 100.0 * expected)
self.assertTrue(metric["relativeErrorDefined"])
def test_zero_amesim_baseline_has_undefined_relative_error(self) -> None:
contract = project_physical_state_v21(
_synthetic_result(), checkpoint_times=(0.2,), sample_step=0.2
)
values = dict(contract["checkpoints"][0]["values"])
key = "massFlow.pnl0001_20.port_1.intoComponent"
values[key] = -0.0
comparison = compare_contract_to_amesim(
contract,
{"checkpoints": [{"requestedTime": 0.2, "values": values}]},
)
metric = next(item for item in comparison["metrics"] if item["key"] == key)
self.assertIsNone(metric["relativeError"])
self.assertIsNone(metric["relativeErrorPercent"])
self.assertFalse(metric["relativeErrorDefined"])
self.assertEqual(metric["relativeErrorReason"], "zeroAmesimBaseline")
self.assertEqual(metric["comparisonBasis"], "absoluteNearZero")
self.assertEqual(
json.loads(json.dumps(comparison, allow_nan=False))["baselineAuthority"],
"amesim",
)
class ReviewedGoldenProtocolTests(unittest.TestCase):
def _report_and_reference(self):
contract = project_physical_state_v21(
_synthetic_result(), checkpoint_times=(0.0, 0.04, 0.2), sample_step=0.04
)
report = {
"generatedAt": "2026-08-18T00:00:00+00:00",
"source": {"sha256": "a" * 64},
"cases": [{
"caseId": "0.2s",
"lane": "production",
"worker": {"summary": {"physicalStateV21": contract}},
}],
}
keys = contract["layout"]["projectionKeys"]
reference_checkpoints = []
for checkpoint in contract["checkpoints"]:
reference_checkpoints.append({
"requestedTime": checkpoint["requestedTime"],
"values": {key: checkpoint["values"][key] for key in keys},
})
reference = {
"schemaVersion": 1,
"provenance": {
"archivePath": "AmesimModels/synthetic.ame",
"archiveBytes": 2,
"archiveSha256": "b" * 64,
"members": [
{"name": "synthetic.var", "bytes": 1, "sha256": "c" * 64},
{
"name": "synthetic.results",
"bytes": 1,
"sha256": "d" * 64,
},
],
},
"storedMassDataPaths": ["mgas@synthetic"],
"checkpoints": reference_checkpoints,
}
return contract, report, reference
def test_candidate_requires_exact_hash_and_is_approved_to_a_new_file(self) -> None:
contract, report, reference = self._report_and_reference()
with tempfile.TemporaryDirectory(
dir=REPO_ROOT, prefix=".physical-state-v21-test-"
) as temporary_directory:
root = Path(temporary_directory)
report_path = root / "report.json"
report_path.write_text(json.dumps(report), encoding="utf-8")
candidate_path = root / "candidate.json"
approved_path = root / "approved.json"
with patch(
"app.simulation.physical_state_v21.build_amesim_reference",
return_value=reference,
):
write_candidate_golden(report_path, candidate_path)
candidate = json.loads(candidate_path.read_text(encoding="utf-8"))
local_report = candidate["provenance"]["localSourceReport"]
self.assertFalse(Path(local_report["path"]).is_absolute())
self.assertEqual(local_report["bytes"], report_path.stat().st_size)
candidate_sha = hashlib.sha256(candidate_path.read_bytes()).hexdigest()
with self.assertRaisesRegex(PhysicalStateV21Error, "SHA mismatch"):
approve_candidate_golden(
candidate_path,
approved_path,
expected_candidate_sha256="0" * 64,
reviewed_by="reviewer",
note="reviewed",
)
approve_candidate_golden(
candidate_path,
approved_path,
expected_candidate_sha256=candidate_sha,
reviewed_by="reviewer",
note="units, signs, and errors reviewed",
)
golden = load_approved_golden(approved_path)
self.assertFalse(Path(golden["approval"]["candidatePath"]).is_absolute())
golden["checkpoints"][-1]["values"][0] += 1.0e9
self.assertTrue(evaluate_physical_state_v21(contract, golden)["passed"])
changed = copy.deepcopy(contract)
changed["checkpoints"][-1]["values"]["pressure.pnch012_8.absolute"] += 1000.0
failure = evaluate_physical_state_v21(changed, golden)
self.assertFalse(failure["passed"])
self.assertIn("amesimReferenceToleranceMismatch", failure["issues"])
def test_approval_rejects_failed_or_tampered_alignment(self) -> None:
_contract, report, reference = self._report_and_reference()
with tempfile.TemporaryDirectory() as temporary_directory:
root = Path(temporary_directory)
report_path = root / "report.json"
report_path.write_text(json.dumps(report), encoding="utf-8")
original_path = root / "candidate.json"
with patch(
"app.simulation.physical_state_v21.build_amesim_reference",
return_value=reference,
):
write_candidate_golden(report_path, original_path)
original = json.loads(original_path.read_text(encoding="utf-8"))
failed_alignment = copy.deepcopy(original)
failed_alignment["amesimAlignmentAtGeneration"]["passed"] = False
failed_path = root / "failed-alignment.json"
failed_path.write_text(json.dumps(failed_alignment), encoding="utf-8")
with self.assertRaisesRegex(
PhysicalStateV21Error, "AMESim alignment at generation"
):
approve_candidate_golden(
failed_path,
root / "failed-approved.json",
expected_candidate_sha256=hashlib.sha256(
failed_path.read_bytes()
).hexdigest(),
reviewed_by="reviewer",
note="must not override a failed alignment",
)
failed_metric = copy.deepcopy(original)
evaluated_metric = next(
metric
for metric in failed_metric["amesimAlignmentAtGeneration"]["metrics"]
if metric["evaluated"]
)
evaluated_metric["passed"] = False
failed_metric_path = root / "failed-metric.json"
failed_metric_path.write_text(json.dumps(failed_metric), encoding="utf-8")
with self.assertRaisesRegex(PhysicalStateV21Error, "evaluated metric"):
approve_candidate_golden(
failed_metric_path,
root / "failed-metric-approved.json",
expected_candidate_sha256=hashlib.sha256(
failed_metric_path.read_bytes()
).hexdigest(),
reviewed_by="reviewer",
note="must not override a failed metric",
)
def test_approval_rejects_local_conservation_residual(self) -> None:
_contract, report, reference = self._report_and_reference()
with tempfile.TemporaryDirectory() as temporary_directory:
root = Path(temporary_directory)
report_path = root / "report.json"
report_path.write_text(json.dumps(report), encoding="utf-8")
original_path = root / "candidate.json"
with patch(
"app.simulation.physical_state_v21.build_amesim_reference",
return_value=reference,
):
write_candidate_golden(report_path, original_path)
candidate = json.loads(original_path.read_text(encoding="utf-8"))
key = "conservation.p4node2_8.massBalance"
value_index = candidate["layout"]["projectionKeys"].index(key)
candidate["checkpoints"][1]["values"][value_index] = 2.0e-9
tampered_path = root / "conservation-residual.json"
tampered_path.write_text(json.dumps(candidate), encoding="utf-8")
with self.assertRaisesRegex(
PhysicalStateV21Error, "local conservation residual"
):
approve_candidate_golden(
tampered_path,
root / "residual-approved.json",
expected_candidate_sha256=hashlib.sha256(
tampered_path.read_bytes()
).hexdigest(),
reviewed_by="reviewer",
note="must not override a local conservation failure",
)
def test_candidate_writer_refuses_overwrite_and_unapproved_file_is_rejected(self) -> None:
_contract, report, reference = self._report_and_reference()
with tempfile.TemporaryDirectory() as temporary_directory:
root = Path(temporary_directory)
report_path = root / "report.json"
report_path.write_text(json.dumps(report), encoding="utf-8")
output = root / "candidate.json"
with patch(
"app.simulation.physical_state_v21.build_amesim_reference",
return_value=reference,
):
write_candidate_golden(report_path, output)
with self.assertRaisesRegex(PhysicalStateV21Error, "overwrite"):
write_candidate_golden(report_path, output)
with self.assertRaisesRegex(PhysicalStateV21Error, "approval.status"):
load_approved_golden(output)
if __name__ == "__main__":
unittest.main()
+85 -3
View File
@@ -154,6 +154,26 @@ class TestMql8StaticRegressionTests(unittest.TestCase):
self.assertEqual(audit["maxAbsoluteError"], 0.0)
self.assertEqual(audit["maxToleranceRatio"], 0.0)
def test_approved_production_golden_replays_its_source_report(self) -> None:
repository_root = Path(self.manifest["_repositoryRoot"])
reference = self.manifest["variants"]["0.2s"]["goldens"]["production"]
golden = load_regression_golden(
repository_root / reference["path"],
expected_sha256=reference["sha256"],
repository_root=repository_root,
)
source_report = golden["provenance"]["sourceReport"]
report = json.loads(
(repository_root / source_report["path"]).read_text(encoding="utf-8")
)
case = next(item for item in report["cases"] if item["caseId"] == "0.2s")
audit = evaluate_regression_golden(case["worker"]["summary"], golden)
self.assertTrue(audit["passed"], audit)
self.assertEqual(audit["comparedValueCount"], 426)
self.assertEqual(audit["maxAbsoluteError"], 0.0)
self.assertEqual(audit["maxToleranceRatio"], 0.0)
def test_historical_v2_extension_decision_remains_auditable(self) -> None:
repository_root = Path(self.manifest["_repositoryRoot"])
runs = repository_root / "tests/baselines/simulation/test_mql_8/runs"
@@ -202,7 +222,70 @@ class TestMql8StaticRegressionTests(unittest.TestCase):
self.assertEqual(first_decision["outcome"], "deferred")
self.assertTrue(decision["simulationWasNotStartedForDeferredCases"])
def test_periodic_main_lane_uses_the_current_production_source(self) -> None:
def test_extension_decision_is_bound_to_the_current_report_and_budget(self) -> None:
repository_root = Path(self.manifest["_repositoryRoot"])
evidence = self.manifest["acceptanceEvidence"]
decision_reference = evidence["extensionDecision"]
decision_path = repository_root / decision_reference["path"]
decision_payload = decision_path.read_bytes()
decision = json.loads(decision_payload)
report_reference = evidence["production0.2ApprovedReplay"]
report_path = repository_root / report_reference["path"]
report_payload = report_path.read_bytes()
report = json.loads(report_payload)
source_case = next(
item for item in report["cases"] if item["caseId"] == "0.2s"
)
first_decision = decision["decisions"][0]
self.assertEqual(
decision["sourceXmlSha256"], self.manifest["source"]["sha256"]
)
self.assertEqual(decision["sourceReport"], report_path.name)
self.assertEqual(len(report_payload), report_reference["bytes"])
self.assertEqual(
hashlib.sha256(report_payload).hexdigest(), report_reference["sha256"]
)
self.assertEqual(len(decision_payload), decision_reference["bytes"])
self.assertEqual(
hashlib.sha256(decision_payload).hexdigest(),
decision_reference["sha256"],
)
self.assertEqual(
hashlib.sha256(report_payload).hexdigest(),
decision["sourceReportSha256"],
)
self.assertTrue(source_case["acceptance"]["passed"])
self.assertEqual(
decision["observedCase"]["workerWallSeconds"],
source_case["worker"]["wallSeconds"],
)
expected_prediction = (
source_case["worker"]["wallSeconds"]
* self.manifest["variants"]["1s"]["stopTime"]
/ source_case["stopTime"]
* self.manifest["execution"]["predictionSafetyFactor"]
)
self.assertAlmostEqual(
first_decision["predictedWallSeconds"], expected_prediction
)
self.assertLessEqual(
first_decision["predictedWallSeconds"],
first_decision["softTimeoutSeconds"],
)
self.assertEqual(first_decision["outcome"], "eligible")
self.assertEqual(first_decision["reason"], "predictedWallWithinSoftBudget")
self.assertEqual(
[item["outcome"] for item in decision["decisions"]],
["eligible", "notEvaluated", "notEvaluated"],
)
self.assertTrue(decision["extensionWasNotStarted"])
self.assertEqual(
decision["scopeReason"],
"OPT-00 closes at the approved production 0.2 s gate; longer progressive horizons remain separately staged work.",
)
def test_periodic_main_lane_exercises_the_approved_production_golden(self) -> None:
workflow = (
Path(self.manifest["_repositoryRoot"])
/ ".github/workflows/solver-regression.yml"
@@ -210,12 +293,11 @@ class TestMql8StaticRegressionTests(unittest.TestCase):
self.assertIn("default: production", workflow)
self.assertIn("inputs.lane || 'production'", workflow)
self.assertNotIn("goldens", self.manifest["variants"]["0.2s"])
@unittest.skipUnless(
os.getenv(LONG_RUN_ENVIRONMENT, "").strip().lower() in {"1", "true", "yes"},
f"Set {LONG_RUN_ENVIRONMENT}=1 to run bounded 0.005/0.2/1/5/10 s integration.",
f"Set {LONG_RUN_ENVIRONMENT}=1 to run bounded 0.01/0.2/1/5/10 s integration.",
)
class TestMql8ProgressiveLongRegressionTests(unittest.TestCase):
@classmethod
+857
View File
@@ -0,0 +1,857 @@
from __future__ import annotations
import ast
from collections import Counter
from collections.abc import Mapping
from dataclasses import dataclass
import hashlib
import json
import math
import operator
from pathlib import Path
import re
import tarfile
import unittest
import xml.etree.ElementTree as ET
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/data/test-mql-8.json"
XML_PATH = REPOSITORY_ROOT / "tests/data/test-mql-8.xml"
MANIFEST_PATH = (
REPOSITORY_ROOT
/ "tests/baselines/simulation/test_mql_8/manifest.json"
)
EXPECTED_AME_SHA256 = (
"cbc3aadd4569a49b3a63e5d66d4143ec16126c0f950df73fb637e07673c20fbb"
)
EXPECTED_AME_BYTES = 21_708_800
EXPECTED_MODEL_TYPE_COUNT = 20
EXPECTED_NODE_COUNT = 157
EXPECTED_PARAMETER_COUNT = 1_092
EXPECTED_CONNECTION_COUNT = 178
EXPECTED_RESULT_POINT_COUNT = 1_002
AMESIM_REFERENCE_PRESSURE_PA = 101_300.0
_BINARY_OPERATORS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
}
_UNARY_OPERATORS = {ast.UAdd: operator.pos, ast.USub: operator.neg}
# Public component names for AMESim state/output values that are promoted to
# explicit initial-value parameters. Unlisted names map one-to-one.
_SOURCE_FIELD_BY_PARAMETER = {
"amesim_mecmas21": {"v0": "v1", "x0": "x1"},
"amesim_pnch012": {"p0": "press", "T0": "temp"},
"amesim_pnch023": {"p0": "press", "T0": "temp"},
"amesim_pnl0001": {"p0": "p2", "T0": "t2"},
"amesim_pnl0002": {"p0": "pctr", "T0": "tctr"},
"amesim_pnl0003": {
"p1_0": "p1",
"T1_0": "t1",
"p2_0": "p2",
"T2_0": "t2",
},
"amesim_step0": {"initial": "out0", "final": "out1", "time": "t0"},
}
_FIXED_PUBLIC_DEFAULTS = {
"amesim_pnch012": {
"vol1": 0.0,
"vol2": 0.0,
"vol3": 0.0,
"vol4": 0.0,
"dvol1": 0.0,
"dvol2": 0.0,
"dvol3": 0.0,
"dvol4": 0.0,
},
"amesim_pnvo001": {"opening0": 1.0},
}
_GAUGE_PRESSURE_FIELDS = {"press", "p1", "p2", "pctr"}
_SOURCE_TO_SI_SCALE = {
("mm", "m"): 1.0e-3,
("mm**2", "m2"): 1.0e-6,
("L", "m3"): 1.0e-3,
("N/mm", "N/m"): 1.0e3,
("N/(mm/s)", "N/(m/s)"): 1.0e3,
}
_EQUIVALENT_UNIT_PAIRS = {
("", ""),
("null", ""),
("degree", ""),
("J/m**2/K/s", "W/(m2*K)"),
("N/m**2", "Pa"),
("m**2", "m2"),
("N/(m/s)**2", "N/(m/s)^2"),
}
_SELECTABLE_UNITS_BY_QUANTITY = {
"area": {"m2", "cm2", "mm2"},
"heat_transfer_coefficient": {"W/(m2*K)"},
"pressure": {"Pa", "kPa", "MPa", "bar"},
"volume": {"m3", "L", "mL"},
"temperature": {"K", "degC"},
"length": {"m", "cm", "mm"},
}
@dataclass(frozen=True)
class AmesimValue:
expression: str
unit: str
@dataclass(frozen=True)
class AmesimContract:
simulation_values: tuple[float, ...]
node_types: Mapping[str, str]
parameters: Mapping[str, Mapping[str, float]]
edges: tuple[tuple[str, str], ...]
component_count: int
modeled_line_count: int
direct_line_count: int
contact_count: int
def _element_text(body: str, name: str) -> str | None:
match = re.search(rf"<{name}>(.*?)</{name}>", body, flags=re.DOTALL)
return match.group(1).strip() if match else None
def _required_element_text(body: str, name: str) -> str:
value = _element_text(body, name)
if value is None:
raise AssertionError(f"AME circuit element is missing <{name}>.")
return value
def _blocks(body: str, name: str) -> list[str]:
return re.findall(
rf"<{name}>\s*(.*?)\s*</{name}>", body, flags=re.DOTALL
)
def _evaluate_expression(expression: str, variables: Mapping[str, float]) -> float:
parsed = ast.parse(expression.strip().replace("^", "**"), mode="eval")
def evaluate(node: ast.AST) -> float:
if (
isinstance(node, ast.Constant)
and isinstance(node.value, (int, float))
and not isinstance(node.value, bool)
):
return float(node.value)
if isinstance(node, ast.Name):
if node.id not in variables:
raise ValueError(f"unknown AME expression variable {node.id!r}")
return float(variables[node.id])
if isinstance(node, ast.BinOp) and type(node.op) in _BINARY_OPERATORS:
return float(
_BINARY_OPERATORS[type(node.op)](
evaluate(node.left), evaluate(node.right)
)
)
if isinstance(node, ast.UnaryOp) and type(node.op) in _UNARY_OPERATORS:
return float(_UNARY_OPERATORS[type(node.op)](evaluate(node.operand)))
raise ValueError(f"unsupported expression node {type(node).__name__}")
value = evaluate(parsed.body)
if not math.isfinite(value):
raise ValueError(f"expression is not finite: {expression!r}")
return value
def _public_model_type(submodel: str) -> str:
return (
"amesim_helium_medium"
if submodel == "PNGD00"
else f"amesim_{submodel.lower()}"
)
def _value_fields(entity_body: str) -> dict[str, AmesimValue]:
fields: dict[str, AmesimValue] = {}
for kind in ("RPARAM", "IPARAM", "IVAR", "EVAR"):
for field_body in _blocks(entity_body, kind):
name = _element_text(field_body, "VARNAME")
value = _element_text(field_body, "VALUE")
if name is not None and value is not None:
fields[name] = AmesimValue(
value, _element_text(field_body, "UNITS") or ""
)
return fields
def _resolve_globals(cir_text: str) -> dict[str, float]:
remaining = {
_required_element_text(body, "GLOB_PARAM_NAME"): _required_element_text(
body, "VALUE"
)
for body in _blocks(cir_text, "GLOBALPARAM")
}
resolved: dict[str, float] = {}
while remaining:
progressed = False
for name, expression in tuple(remaining.items()):
try:
resolved[name] = _evaluate_expression(expression, resolved)
except ValueError:
continue
del remaining[name]
progressed = True
if not progressed:
raise AssertionError(
"Unable to resolve AME globals: " + ", ".join(sorted(remaining))
)
return resolved
def _source_value_in_public_units(
source: AmesimValue,
*,
target_unit: str,
source_name: str,
globals_: Mapping[str, float],
) -> float:
value = _evaluate_expression(source.expression, globals_)
if source_name in _GAUGE_PRESSURE_FIELDS:
if (source.unit, target_unit) != ("Pa", "Pa"):
raise AssertionError(
f"Unexpected gauge-pressure units for {source_name}: "
f"{source.unit!r} -> {target_unit!r}."
)
return value + AMESIM_REFERENCE_PRESSURE_PA
pair = (source.unit, target_unit)
if pair in _SOURCE_TO_SI_SCALE:
return value * _SOURCE_TO_SI_SCALE[pair]
if source.unit == target_unit or pair in _EQUIVALENT_UNIT_PAIRS:
return value
raise AssertionError(
f"No AME-to-public conversion for {source_name}: "
f"{source.unit!r} -> {target_unit!r}."
)
def _expected_parameters(
entity_body: str,
model_type: str,
globals_: Mapping[str, float],
) -> dict[str, float]:
spec = get_component_model_spec(model_type)
fields = _value_fields(entity_body)
expected = {
parameter.name: float(parameter.default) for parameter in spec.parameters
}
source_names = _SOURCE_FIELD_BY_PARAMETER.get(model_type, {})
fixed_defaults = _FIXED_PUBLIC_DEFAULTS.get(model_type, {})
unbound: list[str] = []
for parameter in spec.parameters:
source_name = source_names.get(parameter.name, parameter.name)
if source_name in fields:
expected[parameter.name] = _source_value_in_public_units(
fields[source_name],
target_unit=parameter.unit,
source_name=source_name,
globals_=globals_,
)
elif parameter.name in fixed_defaults:
expected[parameter.name] = fixed_defaults[parameter.name]
if float(parameter.default) != expected[parameter.name]:
raise AssertionError(
f"{model_type}.{parameter.name} changed its fixed public "
"default without an AME source-field mapping."
)
elif not (
(model_type == "amesim_forc" and parameter.name == "direction")
or (
model_type == "amesim_helium_medium"
and parameter.name == "property_model"
)
):
unbound.append(parameter.name)
if unbound:
raise AssertionError(
f"AME parameter mapping is incomplete for {model_type}: "
+ ", ".join(unbound)
)
if model_type == "amesim_forc":
geometry = _required_element_text(entity_body, "COMP_GEOMETRY")
if geometry not in {"2", "8"}:
raise AssertionError(f"Unsupported FORC geometry {geometry!r}.")
expected["direction"] = 1.0 if geometry == "2" else -1.0
if model_type == "amesim_helium_medium":
codes = tuple(
_evaluate_expression(fields[name].expression, globals_)
for name in ("fluidType", "eosType", "gasSetting")
)
if codes != (12.0, 6.0, 1.0):
raise AssertionError(
"PNGD00 no longer selects helium/Peng-Robinson."
)
expected["property_model"] = 0.0
return expected
def _load_ame_contract(path: Path) -> AmesimContract:
with tarfile.open(path) as archive:
cir_file = archive.extractfile("test_mql_.cir")
sim_file = archive.extractfile("test_mql_.sim")
if cir_file is None or sim_file is None:
raise AssertionError(
"AME archive is missing test_mql_.cir or test_mql_.sim."
)
cir_text = cir_file.read().decode("latin1")
simulation_values = tuple(
float(value)
for value in sim_file.read().decode("ascii").splitlines()[0].split()
)
globals_ = _resolve_globals(cir_text)
component_bodies = _blocks(cir_text, "COMP")
line_bodies = _blocks(cir_text, "LINE")
node_types: dict[str, str] = {}
parameters: dict[str, Mapping[str, float]] = {}
for index, body in enumerate(component_bodies):
key = f"component:{index}"
model_type = _public_model_type(
_required_element_text(body, "SUB_NAME")
)
node_types[key] = model_type
parameters[key] = _expected_parameters(body, model_type, globals_)
# PORT_CONNECT=1 is an AMESim contact; line-backed ports use value 2 and
# are reconstructed from LINES_LIST below.
directed_contacts: set[
tuple[tuple[int, int], tuple[int, int]]
] = set()
contacts: set[tuple[tuple[int, int], tuple[int, int]]] = set()
for component_index, body in enumerate(component_bodies):
for port_index, port_body in enumerate(_blocks(body, "COMP_PORT")):
if _required_element_text(port_body, "PORT_CONNECT") != "1":
continue
for connection_body in _blocks(port_body, "CONNECT"):
target_index = int(
_required_element_text(
connection_body, "CONNECT_ENTITY_NUM"
)
)
target_port = int(
_required_element_text(
connection_body, "CONNECT_ENTITY_PORT"
)
)
endpoint = (component_index, port_index)
target = (target_index, target_port)
directed_contacts.add((endpoint, target))
contacts.add(tuple(sorted((endpoint, target))))
for endpoint, target in directed_contacts:
if (target, endpoint) not in directed_contacts:
raise AssertionError(
"AME component contact is not reciprocal: "
f"{endpoint} -> {target}."
)
edges = [
(f"component:{first[0]}", f"component:{second[0]}")
for first, second in contacts
]
direct_line_count = 0
modeled_line_count = 0
for line_index, body in enumerate(line_bodies):
if (
_required_element_text(body, "LINE_START_TYPE") != "0"
or _required_element_text(body, "LINE_END_TYPE") != "0"
):
raise AssertionError(
"test_mql contains a non-component line endpoint."
)
start = (
"component:"
+ _required_element_text(body, "LINE_START_ENTITY")
)
end = (
"component:"
+ _required_element_text(body, "LINE_END_ENTITY")
)
if start not in node_types or end not in node_types:
raise AssertionError(
f"AME line references an unknown endpoint: {start}, {end}."
)
submodel = _required_element_text(body, "SUB_NAME")
if submodel == "DIRECT":
direct_line_count += 1
edges.append((start, end))
continue
modeled_line_count += 1
key = f"line:{line_index}"
model_type = _public_model_type(submodel)
node_types[key] = model_type
parameters[key] = _expected_parameters(
body, model_type, globals_
)
edges.extend(((start, key), (key, end)))
return AmesimContract(
simulation_values=simulation_values,
node_types=node_types,
parameters=parameters,
edges=tuple(edges),
component_count=len(component_bodies),
modeled_line_count=modeled_line_count,
direct_line_count=direct_line_count,
contact_count=len(contacts),
)
def _numeric_project_value(value: object) -> float:
if isinstance(value, bool):
raise AssertionError(
"Boolean is not a valid project parameter value."
)
if isinstance(value, (int, float)):
result = float(value)
elif isinstance(value, str):
result = _evaluate_expression(value, {})
else:
raise AssertionError(
f"Unsupported project parameter value {value!r}."
)
if not math.isfinite(result):
raise AssertionError(
f"Project parameter value is not finite: {value!r}."
)
return result
def _parameter_fingerprint(
values: Mapping[str, object],
) -> tuple[tuple[str, str], ...]:
# Fifteen significant digits collapse only normal IEEE-754 expression
# noise (for example 100000.00000000001 versus 100000).
return tuple(
sorted(
(name, format(_numeric_project_value(value), ".15g"))
for name, value in values.items()
)
)
def _topology_fingerprints(
node_types: Mapping[str, str],
edges: tuple[tuple[str, str], ...],
*,
rounds: int = 12,
) -> tuple[Counter[str], ...]:
adjacency = {node_id: [] for node_id in node_types}
for first, second in edges:
if first not in adjacency or second not in adjacency:
raise AssertionError(
f"Unknown topology endpoint: {first}, {second}."
)
adjacency[first].append(second)
adjacency[second].append(first)
colors = {
node_id: hashlib.sha256(model_type.encode()).hexdigest()
for node_id, model_type in node_types.items()
}
fingerprints: list[Counter[str]] = []
for _ in range(rounds):
colors = {
node_id: hashlib.sha256(
repr(
(
node_types[node_id],
colors[node_id],
sorted(
colors[neighbor]
for neighbor in adjacency[node_id]
),
)
).encode()
).hexdigest()
for node_id in node_types
}
fingerprints.append(Counter(colors.values()))
return tuple(fingerprints)
class TestMqlAmeAuthorityContractTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.ame_payload = AME_PATH.read_bytes()
cls.ame = _load_ame_contract(AME_PATH)
cls.project = json.loads(JSON_PATH.read_text(encoding="utf-8"))
cls.xml_root = ET.parse(XML_PATH).getroot()
cls.manifest = json.loads(
MANIFEST_PATH.read_text(encoding="utf-8")
)
def test_manifest_and_results_bind_the_exact_ame_archive(self) -> None:
self.assertEqual(len(self.ame_payload), EXPECTED_AME_BYTES)
self.assertEqual(
hashlib.sha256(self.ame_payload).hexdigest(),
EXPECTED_AME_SHA256,
)
reference = self.manifest["source"]["referenceArchive"]
self.assertEqual(
reference,
{
"path": "AmesimModels/test_mql.ame",
"sha256": EXPECTED_AME_SHA256,
"bytes": EXPECTED_AME_BYTES,
"role": "authoritativePhysicalBaseline",
"resultStartTime": 0.0,
"resultStopTime": 10.0,
"resultSampleStep": 0.01,
"resultPointCount": EXPECTED_RESULT_POINT_COUNT,
},
)
self.assertEqual(
(REPOSITORY_ROOT / reference["path"]).resolve(),
AME_PATH.resolve(),
)
# .sim: start, stop, print interval, ..., maximum integration step.
self.assertGreaterEqual(len(self.ame.simulation_values), 6)
self.assertEqual(self.ame.simulation_values[0], 0.0)
self.assertEqual(self.ame.simulation_values[1], 10.0)
self.assertEqual(self.ame.simulation_values[2], 0.01)
self.assertEqual(self.ame.simulation_values[5], 0.001)
results = load_test_mql_amesim_results(AME_PATH)
self.assertEqual(
len(results.times), EXPECTED_RESULT_POINT_COUNT
)
self.assertEqual(
results.times[0], reference["resultStartTime"]
)
self.assertEqual(
results.times[-1], reference["resultStopTime"]
)
self.assertTrue(
all(
left < right
for left, right in zip(
results.times, results.times[1:]
)
)
)
step = reference["resultSampleStep"]
self.assertTrue(
all(
math.isclose(
time,
round(time / step) * step,
rel_tol=0.0,
abs_tol=1.0e-10,
)
for time in results.times
)
)
def test_json_inventory_parameters_and_topology_derive_from_ame(
self,
) -> None:
nodes = self.project["nodes"]
edges = self.project["edges"]
json_node_types = {
node["id"]: node["data"]["modelType"] for node in nodes
}
json_edges = tuple(
(edge["source"], edge["target"]) for edge in edges
)
self.assertEqual(self.ame.component_count, 117)
self.assertEqual(self.ame.modeled_line_count, 40)
self.assertEqual(self.ame.direct_line_count, 44)
self.assertEqual(self.ame.contact_count, 54)
self.assertEqual(
len(self.ame.node_types), EXPECTED_NODE_COUNT
)
self.assertEqual(
len(self.ame.edges), EXPECTED_CONNECTION_COUNT
)
self.assertEqual(
len(set(self.ame.node_types.values())),
EXPECTED_MODEL_TYPE_COUNT,
)
self.assertEqual(len(nodes), EXPECTED_NODE_COUNT)
self.assertEqual(len(edges), EXPECTED_CONNECTION_COUNT)
self.assertEqual(
len(set(json_node_types.values())),
EXPECTED_MODEL_TYPE_COUNT,
)
self.assertEqual(
Counter(self.ame.node_types.values()),
Counter(json_node_types.values()),
)
expected_parameter_count = sum(
len(values) for values in self.ame.parameters.values()
)
actual_parameter_count = sum(
len(node["data"]["parameters"]) for node in nodes
)
self.assertEqual(
expected_parameter_count, EXPECTED_PARAMETER_COUNT
)
self.assertEqual(
actual_parameter_count, EXPECTED_PARAMETER_COUNT
)
expected_by_type: dict[
str, Counter[tuple[tuple[str, str], ...]]
] = {}
for node_id, model_type in self.ame.node_types.items():
expected_by_type.setdefault(model_type, Counter()).update(
[
_parameter_fingerprint(
self.ame.parameters[node_id]
)
]
)
actual_by_type: dict[
str, Counter[tuple[tuple[str, str], ...]]
] = {}
for node in nodes:
model_type = node["data"]["modelType"]
actual_by_type.setdefault(model_type, Counter()).update(
[
_parameter_fingerprint(
node["data"]["parameters"]
)
]
)
self.assertEqual(actual_by_type, expected_by_type)
expected_edge_types = Counter(
tuple(
sorted(
(
self.ame.node_types[first],
self.ame.node_types[second],
)
)
)
for first, second in self.ame.edges
)
actual_edge_types = Counter(
tuple(
sorted(
(
json_node_types[first],
json_node_types[second],
)
)
)
for first, second in json_edges
)
self.assertEqual(actual_edge_types, expected_edge_types)
self.assertEqual(
_topology_fingerprints(
json_node_types, json_edges
),
_topology_fingerprints(
self.ame.node_types, self.ame.edges
),
)
def test_json_units_and_component_contracts_are_complete(
self,
) -> None:
for node in self.project["nodes"]:
with self.subTest(component=node["id"]):
data = node["data"]
self.assertEqual(
data["componentType"], data["modelType"]
)
spec = get_component_model_spec(data["modelType"])
self.assertEqual(
data["modelVersion"], spec.model_version
)
self.assertEqual(
set(data["parameters"]),
{
parameter.name
for parameter in spec.parameters
},
)
self.assertEqual(
{port["name"] for port in data["ports"]},
{port.name for port in spec.ports},
)
units = data.get("parameterUnits", {})
self.assertEqual(
set(units),
{
parameter.name
for parameter in spec.parameters
if parameter.unit
},
)
for parameter in spec.parameters:
if not parameter.unit:
continue
selected = units[parameter.name]
allowed = _SELECTABLE_UNITS_BY_QUANTITY.get(
parameter.quantity, {parameter.unit}
)
self.assertIn(selected, allowed, parameter.name)
def test_json_and_xml_match_by_id_parameter_endpoint_and_time(
self,
) -> None:
xml_components = self.xml_root.findall(
"./Components/Component"
)
xml_connections = self.xml_root.findall(
"./Connections/Connection"
)
xml_component_by_id = {
component.attrib["id"]: component
for component in xml_components
}
xml_connection_by_id = {
connection.attrib["id"]: connection
for connection in xml_connections
}
json_node_by_id = {
node["id"]: node for node in self.project["nodes"]
}
json_edge_by_id = {
edge["id"]: edge for edge in self.project["edges"]
}
self.assertEqual(
len(xml_component_by_id), len(xml_components)
)
self.assertEqual(
len(xml_connection_by_id), len(xml_connections)
)
self.assertEqual(
set(xml_component_by_id), set(json_node_by_id)
)
self.assertEqual(
set(xml_connection_by_id), set(json_edge_by_id)
)
for node_id, node in json_node_by_id.items():
with self.subTest(component=node_id):
component = xml_component_by_id[node_id]
data = node["data"]
self.assertEqual(
component.attrib["type"], data["modelType"]
)
self.assertEqual(
component.attrib["modelVersion"],
data["modelVersion"],
)
xml_parameters = {
parameter.attrib["name"]: float(
parameter.attrib["value"]
)
for parameter in component.findall("./Parameter")
}
json_parameters = {
name: _numeric_project_value(value)
for name, value in data["parameters"].items()
}
self.assertEqual(
set(xml_parameters), set(json_parameters)
)
for name, value in json_parameters.items():
self.assertTrue(
math.isclose(
xml_parameters[name],
value,
rel_tol=1.0e-13,
abs_tol=1.0e-13,
),
(
f"{node_id}.{name}: "
f"XML={xml_parameters[name]!r}, "
f"JSON={value!r}"
),
)
for edge_id, edge in json_edge_by_id.items():
with self.subTest(connection=edge_id):
endpoints = {
(
endpoint.attrib["component"],
endpoint.attrib["port"],
)
for endpoint in xml_connection_by_id[
edge_id
].findall("./Endpoint")
}
self.assertEqual(
endpoints,
{
(
edge["source"],
edge["sourceHandle"],
),
(
edge["target"],
edge["targetHandle"],
),
},
)
xml_simulation = self.xml_root.find("./Simulation")
self.assertIsNotNone(xml_simulation)
assert xml_simulation is not None
project_simulation = self.project["simulation"]
expected_simulation = {
"tStart": float(project_simulation["t_start"]),
"tStop": float(project_simulation["t_stop"]),
"sampleStep": float(project_simulation["step"]),
"maxStep": float(project_simulation["max_step"]),
}
for name, expected in expected_simulation.items():
self.assertEqual(
float(xml_simulation.attrib[name]), expected
)
self.assertEqual(
xml_simulation.attrib["method"],
project_simulation["method"],
)
self.assertEqual(
project_simulation["t_start"],
self.ame.simulation_values[0],
)
self.assertEqual(
project_simulation["t_stop"],
self.ame.simulation_values[1],
)
self.assertEqual(
project_simulation["step"],
self.ame.simulation_values[2],
)
self.assertEqual(
project_simulation["max_step"],
self.ame.simulation_values[5],
)
if __name__ == "__main__":
unittest.main()
+45 -1
View File
@@ -1,10 +1,14 @@
from __future__ import annotations
import csv
import tempfile
import unittest
from pathlib import Path
from app.simulation.reporting.amesim_results import load_test_mql_amesim_results
from app.simulation.reporting.amesim_results import (
AmesimResults,
load_test_mql_amesim_results,
)
from app.simulation.reporting.test_mql_comparison import (
DEFAULT_TEST_MQL_ALIGNMENT_PATHS,
TestMqlComparisonError,
@@ -84,6 +88,46 @@ class TestMqlComparisonTests(unittest.TestCase):
self.assertAlmostEqual(metric.mean_abs_error, 1.0)
self.assertGreater(metric.max_rel_error, 0.0)
def test_zero_amesim_baseline_does_not_invent_a_relative_denominator(self) -> None:
amesim_results = AmesimResults(
times=(0.0, 1.0),
variables=(),
saved_variable_indices=(),
series_by_data_path={"zero@baseline": (0.0, -0.0)},
final_values_by_data_path={"zero@baseline": -0.0},
)
comparison = compare_test_mql_series(
python_times=(0.0, 1.0),
python_series_by_data_path={"zero@baseline": (0.0, 1.0e-6)},
amesim_results=amesim_results,
data_paths=("zero@baseline",),
)
metric = comparison.metric("zero@baseline")
self.assertEqual(metric.max_abs_error, 1.0e-6)
self.assertEqual(metric.undefined_rel_error_count, 2)
self.assertEqual(metric.near_zero_baseline_count, 2)
self.assertEqual(comparison.undefined_rel_error_count, 2)
self.assertEqual(metric.max_rel_error, 0.0)
with tempfile.TemporaryDirectory() as temp_dir:
comparison_path, _summary_path, _comparison = (
write_test_mql_comparison_csv(
output_dir=Path(temp_dir),
python_times=(0.0, 1.0),
python_series_by_data_path={
"zero@baseline": (0.0, 1.0e-6)
},
amesim_results=amesim_results,
data_paths=("zero@baseline",),
)
)
with comparison_path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.reader(handle))
self.assertEqual(rows[1][-1], "")
self.assertEqual(rows[2][-1], "")
def test_missing_data_path_is_rejected(self) -> None:
with self.assertRaises(TestMqlComparisonError):
compare_test_mql_series(