Files
SystemSimulationApp/app/simulation/physical_state_v21.py
lujingze a8c733883c 存档求解器回归基线与当前改动
纳管 AMESim 对齐基线、发布锁、回归测试及当前物理门禁调整。

更新日志仅记录已完成成果,并注明当前 HEAD 尚待真实 production 复跑与远端 workflow 验证。
2026-08-18 15:20:42 +00:00

1497 lines
61 KiB
Python

"""Physical-state-v2.1 projection and AMESim-baseline protocol for test_mql.
AMESim simulation values are the authoritative physical baseline. Every
runtime evaluation compares the current projected values directly with that
baseline and reports the relative error when the AMESim denominator is
non-zero. Frozen Python values are retained only as implementation and
determinism evidence; they are not a physical-correctness authority.
"""
from __future__ import annotations
import argparse
from collections.abc import Mapping, Sequence
from datetime import UTC, datetime
import hashlib
import json
import math
import os
from pathlib import Path
import tarfile
from typing import Any
PHYSICAL_STATE_V21_ID = "physical-state-v2.1"
PHYSICAL_STATE_V21_SCHEMA_VERSION = 1
PHYSICAL_STATE_V21_GOLDEN_SCHEMA_VERSION = 1
AMESIM_REFERENCE_PRESSURE_PA = 101_300.0
REPOSITORY_ROOT = Path(__file__).resolve().parents[2]
DEFAULT_AMESIM_ARCHIVE = REPOSITORY_ROOT / "AmesimModels" / "test_mql.ame"
_PNCH_PRESSURE = "amesim_pnch012_8.p"
_LINE_PRESSURE = "amesim_pnl0001_20.p"
_LINE_FLOW = "amesim_pnl0001_20.port_1.m_flow"
_ORIFICE_FLOW = "amesim_pnvo001_5.port_2.m_flow"
_ORIFICE_OPENING = "amesim_pnvo001_5.xv"
_NODE_FLOWS = tuple(
f"amesim_p4node2_8.port_{index}.m_flow" for index in range(1, 5)
)
_ORIFICE_BALANCE_FLOWS = (
"amesim_pnvo001_5.port_2.m_flow",
"amesim_pnvo001_5.port_3.m_flow",
)
_CONNECTION_BALANCE_FLOWS = (
"amesim_pnl0001_20.port_1.m_flow",
"amesim_pnch012_8.port_1.m_flow",
)
_MASS_9_MODE_SOURCES = (
"amesim_mecmas21_9.x",
"amesim_mecmas21_9.v",
"amesim_mecmas21_9.port_1.f",
"amesim_mecmas21_9.port_2.f",
)
_MASS_10_MODE_SOURCES = (
"amesim_mecmas21_10.x",
"amesim_mecmas21_10.v",
"amesim_mecmas21_10.port_1.f",
"amesim_mecmas21_10.port_2.f",
)
class PhysicalStateV21Error(ValueError):
"""Raised when a projection or golden is incomplete or unauditable."""
def _sha256(payload: bytes) -> str:
return hashlib.sha256(payload).hexdigest()
def _canonical_sha256(value: object) -> str:
return _sha256(
json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
)
def _valid_sha256(value: object) -> bool:
return (
isinstance(value, str)
and len(value) == 64
and all(character in "0123456789abcdef" for character in value)
)
def _portable_path(path: Path | str) -> str:
"""Keep repository provenance relocatable while preserving external paths."""
resolved = Path(path).resolve()
try:
return resolved.relative_to(REPOSITORY_ROOT).as_posix()
except ValueError:
return str(resolved)
def _finite(value: object, *, field: str) -> float:
if not isinstance(value, (int, float)) or not math.isfinite(float(value)):
raise PhysicalStateV21Error(f"{field} must be a finite number.")
return float(value)
def _metadata_and_series(
result: Mapping[str, object],
) -> tuple[dict[str, Mapping[str, object]], Mapping[str, object]]:
variables = result.get("variables")
series = result.get("series")
if not isinstance(variables, list) or not isinstance(series, Mapping):
raise PhysicalStateV21Error("Simulation result requires variables and series.")
metadata: dict[str, Mapping[str, object]] = {}
for index, variable in enumerate(variables):
if not isinstance(variable, Mapping) or not isinstance(variable.get("key"), str):
raise PhysicalStateV21Error(f"Variable {index} has no string key.")
key = str(variable["key"])
if key in metadata:
raise PhysicalStateV21Error(f"Duplicate result variable key: {key}.")
metadata[key] = variable
return metadata, series
def _require_source(
metadata: Mapping[str, Mapping[str, object]],
series: Mapping[str, object],
key: str,
quantity: str,
unit: str,
) -> None:
variable = metadata.get(key)
if variable is None:
raise PhysicalStateV21Error(f"Required v2.1 source is missing: {key}.")
if variable.get("quantity") != quantity or variable.get("unit") != unit:
raise PhysicalStateV21Error(
f"Unexpected metadata for {key}: quantity={variable.get('quantity')!r}, "
f"unit={variable.get('unit')!r}; expected {quantity!r}, {unit!r}."
)
values = series.get(key)
if not isinstance(values, Sequence) or isinstance(values, (str, bytes)):
raise PhysicalStateV21Error(f"Required v2.1 series is missing: {key}.")
def _projection(
key: str,
category: str,
unit: str,
sources: Sequence[str],
python_formula: str,
amesim: Mapping[str, object],
*,
relative: float,
absolute: float,
) -> dict[str, object]:
return {
"key": key,
"category": category,
"unit": unit,
"python": {"sourceKeys": list(sources), "formula": python_formula},
"amesim": dict(amesim),
"tolerance": {"relative": relative, "absolute": absolute},
}
def _projection_layout(
result: Mapping[str, object],
) -> tuple[dict[str, object], tuple[str, ...]]:
metadata, series = _metadata_and_series(result)
for key in (_PNCH_PRESSURE, _LINE_PRESSURE):
_require_source(metadata, series, key, "pressure", "Pa")
for key in set(
(
_LINE_FLOW,
_ORIFICE_FLOW,
*_NODE_FLOWS,
*_ORIFICE_BALANCE_FLOWS,
*_CONNECTION_BALANCE_FLOWS,
)
):
_require_source(metadata, series, key, "mass_flow", "kg/s")
_require_source(metadata, series, _ORIFICE_OPENING, "dimensionless", "")
for key in (_MASS_9_MODE_SOURCES[0], _MASS_10_MODE_SOURCES[0]):
_require_source(metadata, series, key, "length", "m")
for key in (_MASS_9_MODE_SOURCES[1], _MASS_10_MODE_SOURCES[1]):
_require_source(metadata, series, key, "velocity", "m/s")
for key in (*_MASS_9_MODE_SOURCES[2:], *_MASS_10_MODE_SOURCES[2:]):
_require_source(metadata, series, key, "force", "N")
stored_mass_keys = tuple(
sorted(
key
for key, variable in metadata.items()
if variable.get("category") == "state"
and variable.get("quantity") == "mass"
and variable.get("unit") == "kg"
)
)
if not stored_mass_keys:
raise PhysicalStateV21Error("No stored gas-mass states were found.")
for key in stored_mass_keys:
values = series.get(key)
if not isinstance(values, Sequence) or isinstance(values, (str, bytes)):
raise PhysicalStateV21Error(f"Stored mass series is missing: {key}.")
gauge_to_absolute = {
"sourceUnit": "Pa (gauge)",
"targetUnit": "Pa (absolute)",
"formula": "target = source + 101300",
"scale": 1.0,
"offset": AMESIM_REFERENCE_PRESSURE_PA,
}
g_s_to_generic = {
"sourceUnit": "g/s (AMESim component orientation)",
"targetUnit": "kg/s (positive into generic component)",
"formula": "target = -source * 1e-3",
"scale": -1.0e-3,
"offset": 0.0,
}
projections = [
_projection(
"pressure.pnch012_8.absolute", "pressure", "Pa", [_PNCH_PRESSURE],
"identity", {"dataPaths": ["press@pn_c1_8"], **gauge_to_absolute},
relative=2.0e-4, absolute=1.0e-3,
),
_projection(
"pressure.pnl0001_20.absolute", "pressure", "Pa", [_LINE_PRESSURE],
"identity", {"dataPaths": ["p2@pneumatic_69"], **gauge_to_absolute},
relative=2.0e-4, absolute=1.0e-3,
),
_projection(
"massFlow.pnl0001_20.port_1.intoComponent", "massFlow", "kg/s",
[_LINE_FLOW], "identity", {"dataPaths": ["dm1@pneumatic_69"], **g_s_to_generic},
relative=2.0e-4, absolute=1.0e-9,
),
_projection(
"massFlow.pnvo001_5.port_2.intoComponent", "massFlow", "kg/s",
[_ORIFICE_FLOW], "identity", {"dataPaths": ["dm2@pn_morifice_1"], **g_s_to_generic},
relative=2.0e-4, absolute=1.0e-9,
),
_projection(
"conservation.p4node2_8.massBalance", "conservation", "kg/s",
_NODE_FLOWS, "sum",
{"dataPaths": [], "note": "AMESim archive has no complete saved four-port tuple."},
relative=0.0, absolute=1.0e-9,
),
_projection(
"conservation.pnvo001_5.massBalance", "conservation", "kg/s",
_ORIFICE_BALANCE_FLOWS, "sum",
{
"dataPaths": ["dm2@pn_morifice_1", "dm3@pn_morifice_1"],
"sourceUnit": "g/s", "targetUnit": "kg/s",
"formula": "target = -(source1 + source2) * 1e-3",
},
relative=0.0, absolute=1.0e-9,
),
_projection(
"conservation.pnl0001_20_pnch012_8.connectionMassBalance",
"conservation", "kg/s", _CONNECTION_BALANCE_FLOWS, "sum",
{"dataPaths": [], "note": "PNCH012 port flow is not saved; local connector contract is authoritative."},
relative=0.0, absolute=1.0e-9,
),
_projection(
"conservation.totalStoredGasMass", "conservation", "kg",
stored_mass_keys, "sum",
{
"dataPathSelection": "units == 'g' and signal starts with 'mgas'",
"sourceUnit": "g", "targetUnit": "kg",
"formula": "target = sum(sources) * 1e-3",
},
relative=2.0e-4, absolute=1.0e-9,
),
_projection(
"discrete.pnvo001_5.openMode", "discreteMode", "1",
[_ORIFICE_OPENING], "1 if opening >= 0.5 else 0",
{
"dataPaths": ["xv@pn_morifice_1"], "sourceUnit": "1",
"targetUnit": "mode code {0,1}", "formula": "1 if source >= 0.5 else 0",
},
relative=0.0, absolute=0.0,
),
_projection(
"discrete.mecmas21_9.endstopMode", "discreteMode", "1",
_MASS_9_MODE_SOURCES,
"-1 lower, 0 free, +1 upper using boundary and net-force direction",
{
"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.",
},
relative=0.0, absolute=0.0,
),
_projection(
"discrete.mecmas21_10.endstopMode", "discreteMode", "1",
_MASS_10_MODE_SOURCES,
"-1 lower, 0 free, +1 upper using boundary and net-force direction",
{
"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.",
},
relative=0.0, absolute=0.0,
),
]
keys = [str(projection["key"]) for projection in projections]
hash_payload = {"projectionKeys": keys, "projections": projections}
return {
**hash_payload,
"projectionLayoutSha256": _canonical_sha256(hash_payload),
}, stored_mass_keys
def _value_at(series: Mapping[str, object], key: str, index: int) -> float:
values = series[key]
assert isinstance(values, Sequence)
if index >= len(values):
raise PhysicalStateV21Error(f"Series {key!r} is short at index {index}.")
return _finite(values[index], field=f"series.{key}[{index}]")
def _endstop_mode(
position: float,
velocity: float,
force_1: float,
force_2: float,
lower: float,
upper: float,
) -> float:
x_tol = 1.0e-12 * max(abs(position), abs(lower), abs(upper), 1.0)
v_tol = 1.0e-12 * max(abs(velocity), 1.0)
net_force = force_1 + force_2
if position <= lower + x_tol and velocity <= v_tol and net_force <= 0.0:
return -1.0
if position >= upper - x_tol and velocity >= -v_tol and net_force >= 0.0:
return 1.0
return 0.0
def _project_row(
series: Mapping[str, object],
index: int,
stored_mass_keys: Sequence[str],
) -> dict[str, float]:
mass_9 = [_value_at(series, key, index) for key in _MASS_9_MODE_SOURCES]
mass_10 = [_value_at(series, key, index) for key in _MASS_10_MODE_SOURCES]
return {
"pressure.pnch012_8.absolute": _value_at(series, _PNCH_PRESSURE, index),
"pressure.pnl0001_20.absolute": _value_at(series, _LINE_PRESSURE, index),
"massFlow.pnl0001_20.port_1.intoComponent": _value_at(series, _LINE_FLOW, index),
"massFlow.pnvo001_5.port_2.intoComponent": _value_at(series, _ORIFICE_FLOW, index),
"conservation.p4node2_8.massBalance": sum(_value_at(series, key, index) for key in _NODE_FLOWS),
"conservation.pnvo001_5.massBalance": sum(_value_at(series, key, index) for key in _ORIFICE_BALANCE_FLOWS),
"conservation.pnl0001_20_pnch012_8.connectionMassBalance": sum(
_value_at(series, key, index) for key in _CONNECTION_BALANCE_FLOWS
),
"conservation.totalStoredGasMass": sum(_value_at(series, key, index) for key in stored_mass_keys),
"discrete.pnvo001_5.openMode": float(_value_at(series, _ORIFICE_OPENING, index) >= 0.5),
"discrete.mecmas21_9.endstopMode": _endstop_mode(*mass_9, -0.72, 0.0),
"discrete.mecmas21_10.endstopMode": _endstop_mode(*mass_10, 0.0, 0.37),
}
def project_physical_state_v21(
result: Mapping[str, object],
*,
checkpoint_times: Sequence[float],
sample_step: float,
) -> dict[str, object]:
"""Project pressure, flow, conservation, and discrete-mode checkpoints."""
if not math.isfinite(float(sample_step)) or float(sample_step) <= 0.0:
raise PhysicalStateV21Error("sample_step must be finite and positive.")
layout, stored_mass_keys = _projection_layout(result)
_metadata, series = _metadata_and_series(result)
raw_times = series.get("time")
if not isinstance(raw_times, Sequence) or isinstance(raw_times, (str, bytes)):
raise PhysicalStateV21Error("Simulation result requires a time series.")
times = [_finite(value, field=f"series.time[{index}]") for index, value in enumerate(raw_times)]
if not times or any(first >= second for first, second in zip(times, times[1:])):
raise PhysicalStateV21Error("Time series must be non-empty and strictly increasing.")
keys = layout["projectionKeys"]
assert isinstance(keys, list)
tolerance = max(1.0e-12, 0.51 * float(sample_step))
checkpoints: list[dict[str, object]] = []
for raw_requested in checkpoint_times:
requested = _finite(raw_requested, field="checkpoint time")
index = min(range(len(times)), key=lambda candidate: abs(times[candidate] - requested))
actual = times[index]
if abs(actual - requested) > tolerance:
checkpoints.append({"requestedTime": requested, "available": False, "nearestTime": actual})
continue
values = _project_row(series, index, stored_mass_keys)
if list(values) != keys:
raise PhysicalStateV21Error("Internal v2.1 projection layout drifted.")
checkpoints.append(
{"requestedTime": requested, "actualTime": actual, "available": True, "values": values}
)
return {
"schemaVersion": PHYSICAL_STATE_V21_SCHEMA_VERSION,
"id": PHYSICAL_STATE_V21_ID,
"projectionCategories": ["pressure", "massFlow", "conservation", "discreteMode"],
"layout": layout,
"checkpoints": checkpoints,
"comparisonMode": "amesimAuthoritativeBaselineWithLocalInvariants",
}
def physical_state_v21_applicable(result: Mapping[str, object]) -> bool:
"""Return true only for results carrying the test_mql v2.1 signature."""
series = result.get("series")
return isinstance(series, Mapping) and _PNCH_PRESSURE in series
def _amesim_value(results: Any, data_path: str, time_s: float) -> float:
from app.simulation.reporting.test_mql_comparison import interpolate_series_value
if data_path not in results.series_by_data_path:
raise PhysicalStateV21Error(f"AMESim is missing Data_Path {data_path!r}.")
return interpolate_series_value(results.times, results.series(data_path), time_s)
def _amesim_boundary_mode(
results: Any,
alias: str,
time_s: float,
lower: float,
upper: float,
) -> float:
position = _amesim_value(results, f"x1@{alias}", time_s)
velocity = _amesim_value(results, f"v1@{alias}", time_s)
tolerance = 1.0e-10 * max(abs(position), abs(lower), abs(upper), 1.0)
if abs(velocity) <= 1.0e-10 and position <= lower + tolerance:
return -1.0
if abs(velocity) <= 1.0e-10 and position >= upper - tolerance:
return 1.0
return 0.0
def _archive_provenance(archive_path: Path) -> dict[str, object]:
archive_payload = archive_path.read_bytes()
with tarfile.open(archive_path) as archive:
names = set(archive.getnames())
preferred = ("test_mql_.var", "test_mql_.results")
if all(name in names for name in preferred):
members = preferred
else:
pairs = sorted(
(name, f"{name[:-4]}.results")
for name in names
if name.endswith(".var") and f"{name[:-4]}.results" in names
)
if len(pairs) != 1:
raise PhysicalStateV21Error("Could not resolve one AMESim result pair.")
members = pairs[0]
member_records = []
for name in members:
extracted = archive.extractfile(name)
if extracted is None:
raise PhysicalStateV21Error(f"AMESim member is missing: {name}.")
payload = extracted.read()
member_records.append(
{"name": name, "bytes": len(payload), "sha256": _sha256(payload)}
)
return {
"archivePath": _portable_path(archive_path),
"archiveBytes": len(archive_payload),
"archiveSha256": _sha256(archive_payload),
"members": member_records,
"referencePressurePa": AMESIM_REFERENCE_PRESSURE_PA,
"role": "external calibration reference; not an equality claim",
}
def build_amesim_reference(
archive_path: Path | str,
*,
checkpoint_times: Sequence[float],
) -> dict[str, object]:
"""Return transformed AMESim values with archive/member provenance."""
# Keep the 132-state legacy model catalog out of benchmark worker startup;
# it is needed only by this explicit AMESim-reference operation.
from app.simulation.reporting.amesim_results import load_test_mql_amesim_results
from app.simulation.reporting.test_mql_variables import (
build_test_mql_variable_catalog,
)
path = Path(archive_path).resolve()
times = tuple(_finite(value, field="AMESim checkpoint") for value in checkpoint_times)
results = load_test_mql_amesim_results(path, time_stop_s=max(times, default=0.0))
variable_catalog = build_test_mql_variable_catalog(results)
mass_paths = tuple(
variable.data_path
for variable in variable_catalog.variables
if variable.units == "g" and variable.signal_name.startswith("mgas")
)
if not mass_paths:
raise PhysicalStateV21Error("AMESim archive contains no stored gas mass.")
checkpoints: list[dict[str, object]] = []
for time_s in times:
dm2 = _amesim_value(results, "dm2@pn_morifice_1", time_s)
dm3 = _amesim_value(results, "dm3@pn_morifice_1", time_s)
values: dict[str, float | None] = {
"pressure.pnch012_8.absolute": _amesim_value(
results, "press@pn_c1_8", time_s
) + AMESIM_REFERENCE_PRESSURE_PA,
"pressure.pnl0001_20.absolute": _amesim_value(
results, "p2@pneumatic_69", time_s
) + AMESIM_REFERENCE_PRESSURE_PA,
"massFlow.pnl0001_20.port_1.intoComponent": -1.0e-3
* _amesim_value(results, "dm1@pneumatic_69", time_s),
"massFlow.pnvo001_5.port_2.intoComponent": -1.0e-3 * dm2,
"conservation.p4node2_8.massBalance": None,
"conservation.pnvo001_5.massBalance": -1.0e-3 * (dm2 + dm3),
"conservation.pnl0001_20_pnch012_8.connectionMassBalance": None,
"conservation.totalStoredGasMass": 1.0e-3
* sum(_amesim_value(results, data_path, time_s) for data_path in mass_paths),
"discrete.pnvo001_5.openMode": float(
_amesim_value(results, "xv@pn_morifice_1", time_s) >= 0.5
),
# Generic mass_9 is the 90,000 kg AMESim instance 10 (alias _19).
"discrete.mecmas21_9.endstopMode": _amesim_boundary_mode(
results, "mass_friction_endstops_19", time_s, -0.72, 0.0
),
# Generic mass_10 is the 170,000 kg AMESim instance 9 (alias _18).
"discrete.mecmas21_10.endstopMode": _amesim_boundary_mode(
results, "mass_friction_endstops_18", time_s, 0.0, 0.37
),
}
checkpoints.append({"requestedTime": time_s, "values": values})
return {
"schemaVersion": 1,
"provenance": _archive_provenance(path),
"storedMassDataPaths": list(mass_paths),
"checkpoints": checkpoints,
}
def _validate_contract(
contract: Mapping[str, object],
*,
require_available: bool = True,
) -> tuple[list[str], list[Mapping[str, object]]]:
if contract.get("schemaVersion") != PHYSICAL_STATE_V21_SCHEMA_VERSION:
raise PhysicalStateV21Error("Unsupported v2.1 contract schemaVersion.")
if contract.get("id") != PHYSICAL_STATE_V21_ID:
raise PhysicalStateV21Error("Unexpected v2.1 contract id.")
layout = contract.get("layout")
if not isinstance(layout, Mapping):
raise PhysicalStateV21Error("v2.1 layout must be an object.")
keys = layout.get("projectionKeys")
projections = layout.get("projections")
if (
not isinstance(keys, list)
or not keys
or not all(isinstance(key, str) and key for key in keys)
or len(set(keys)) != len(keys)
):
raise PhysicalStateV21Error("Projection keys must be unique strings.")
if not isinstance(projections, list) or len(projections) != len(keys):
raise PhysicalStateV21Error("Projection metadata layout is invalid.")
typed: list[Mapping[str, object]] = []
for key, projection in zip(keys, projections):
if not isinstance(projection, Mapping) or projection.get("key") != key:
raise PhysicalStateV21Error("Projection metadata order is invalid.")
tolerance = projection.get("tolerance")
if not isinstance(tolerance, Mapping):
raise PhysicalStateV21Error(f"Projection {key} has no tolerance.")
for field in ("relative", "absolute"):
value = _finite(tolerance.get(field), field=f"{key}.tolerance.{field}")
if value < 0.0:
raise PhysicalStateV21Error("Projection tolerance cannot be negative.")
typed.append(projection)
hash_payload = {"projectionKeys": keys, "projections": projections}
if layout.get("projectionLayoutSha256") != _canonical_sha256(hash_payload):
raise PhysicalStateV21Error("Projection layout hash mismatch.")
checkpoints = contract.get("checkpoints")
if not isinstance(checkpoints, list) or not checkpoints:
raise PhysicalStateV21Error("v2.1 contract requires checkpoints.")
for checkpoint in checkpoints:
if not isinstance(checkpoint, Mapping):
raise PhysicalStateV21Error("v2.1 checkpoint must be an object.")
if not checkpoint.get("available"):
if require_available:
raise PhysicalStateV21Error("Every golden checkpoint must be available.")
continue
_finite(checkpoint.get("requestedTime"), field="checkpoint.requestedTime")
_finite(checkpoint.get("actualTime"), field="checkpoint.actualTime")
values = checkpoint.get("values")
if not isinstance(values, Mapping) or list(values) != keys:
raise PhysicalStateV21Error("Checkpoint values do not match layout.")
for key in keys:
_finite(values[key], field=f"checkpoint.values.{key}")
return keys, typed
def _case_summary(
report: Mapping[str, object],
*,
case_id: str,
lane: str,
) -> Mapping[str, object]:
cases = report.get("cases")
if not isinstance(cases, list):
raise PhysicalStateV21Error("Regression report cases must be a list.")
matches = [
case
for case in cases
if isinstance(case, Mapping)
and case.get("caseId") == case_id
and case.get("lane") == lane
]
if len(matches) != 1:
raise PhysicalStateV21Error(
f"Expected one {case_id!r}/{lane!r} case, found {len(matches)}."
)
worker = matches[0].get("worker")
summary = worker.get("summary") if isinstance(worker, Mapping) else None
if not isinstance(summary, Mapping):
raise PhysicalStateV21Error("Regression case has no worker summary.")
return summary
def _case_contract(
report: Mapping[str, object],
*,
case_id: str,
lane: str,
) -> Mapping[str, object]:
summary = _case_summary(report, case_id=case_id, lane=lane)
contract = summary.get("physicalStateV21")
if not isinstance(contract, Mapping):
raise PhysicalStateV21Error(
"Report has no physicalStateV21 contract; regenerate it with v2.1 enabled."
)
return contract
def _final_result_from_summary(summary: Mapping[str, object]) -> dict[str, object]:
"""Adapt a pre-v2.1 report's final map for one honest endpoint assessment."""
final = summary.get("final")
simulated_until = summary.get("simulatedUntil")
if not isinstance(final, Mapping):
raise PhysicalStateV21Error("Legacy report summary has no final values.")
time_s = _finite(simulated_until, field="summary.simulatedUntil")
required = {
_PNCH_PRESSURE: ("pressure", "Pa", "thermodynamic"),
_LINE_PRESSURE: ("pressure", "Pa", "thermodynamic"),
_LINE_FLOW: ("mass_flow", "kg/s", "port"),
_ORIFICE_FLOW: ("mass_flow", "kg/s", "port"),
_ORIFICE_OPENING: ("dimensionless", "", "derived"),
**{key: ("mass_flow", "kg/s", "port") for key in _NODE_FLOWS},
**{key: ("mass_flow", "kg/s", "port") for key in _ORIFICE_BALANCE_FLOWS},
**{key: ("mass_flow", "kg/s", "port") for key in _CONNECTION_BALANCE_FLOWS},
_MASS_9_MODE_SOURCES[0]: ("length", "m", "state"),
_MASS_9_MODE_SOURCES[1]: ("velocity", "m/s", "state"),
_MASS_9_MODE_SOURCES[2]: ("force", "N", "port"),
_MASS_9_MODE_SOURCES[3]: ("force", "N", "port"),
_MASS_10_MODE_SOURCES[0]: ("length", "m", "state"),
_MASS_10_MODE_SOURCES[1]: ("velocity", "m/s", "state"),
_MASS_10_MODE_SOURCES[2]: ("force", "N", "port"),
_MASS_10_MODE_SOURCES[3]: ("force", "N", "port"),
}
mass_keys = sorted(
key
for key in final
if isinstance(key, str)
and (key.endswith(".m") or key.endswith(".m1") or key.endswith(".m2"))
)
variables = []
series: dict[str, list[float]] = {"time": [time_s]}
for key, (quantity, unit, category) in required.items():
if key not in final:
raise PhysicalStateV21Error(f"Legacy final map is missing {key}.")
variables.append({"key": key, "quantity": quantity, "unit": unit, "category": category})
series[key] = [_finite(final[key], field=f"summary.final.{key}")]
for key in mass_keys:
variables.append({"key": key, "quantity": "mass", "unit": "kg", "category": "state"})
series[key] = [_finite(final[key], field=f"summary.final.{key}")]
return {"variables": variables, "series": series}
def project_report_endpoint_v21(
report: Mapping[str, object],
*,
case_id: str = "0.2s",
lane: str = "production",
) -> dict[str, object]:
"""Project the endpoint of a report produced before v2.1 was connected."""
summary = _case_summary(report, case_id=case_id, lane=lane)
result = _final_result_from_summary(summary)
time_s = float(summary["simulatedUntil"])
return project_physical_state_v21(
result, checkpoint_times=(time_s,), sample_step=max(time_s, 1.0e-12)
)
def compare_contract_to_amesim(
contract: Mapping[str, object],
amesim_reference: Mapping[str, object],
) -> dict[str, object]:
"""Compare every projection with the authoritative AMESim baseline.
AMESim may not expose every internal conservation quantity. Those rows are
retained in ``metrics`` with an unavailable baseline instead of being
silently discarded. A zero AMESim baseline has no mathematically defined
relative error, so the report records ``None`` and relies on the declared
absolute tolerance for the pass/fail decision.
"""
keys, projections = _validate_contract(contract)
checkpoints = contract["checkpoints"]
references = amesim_reference.get("checkpoints")
if not isinstance(checkpoints, list) or not isinstance(references, list):
raise PhysicalStateV21Error("AMESim comparison checkpoints are missing.")
if len(checkpoints) != len(references):
raise PhysicalStateV21Error("AMESim checkpoint count differs from contract.")
# This is an external alignment profile, deliberately distinct from the
# tighter local non-regression tolerances embedded in the projection layout.
external_tolerance = {
"pressure": {"relative": 2.0e-3, "absolute": 1.0e-3},
"massFlow": {"relative": 2.0e-3, "absolute": 1.0e-9},
"conservation": {"relative": 2.0e-3, "absolute": 1.0e-9},
"discreteMode": {"relative": 0.0, "absolute": 0.0},
}
compared = 0
available = 0
unavailable = 0
excluded = 0
issues: list[str] = []
metrics: list[dict[str, object]] = []
worst: dict[str, object] | None = None
max_ratio = 0.0
worst_relative: dict[str, object] | None = None
max_relative_error: float | None = None
max_recorded_relative_error: float | None = None
for checkpoint, reference in zip(checkpoints, references):
assert isinstance(checkpoint, Mapping) and isinstance(reference, Mapping)
actual_values = checkpoint["values"]
reference_values = reference.get("values")
assert isinstance(actual_values, Mapping) and isinstance(reference_values, Mapping)
for key, projection in zip(keys, projections):
actual = float(actual_values[key])
requested_time = float(checkpoint["requestedTime"])
category = str(projection["category"])
expected = reference_values.get(key)
if expected is None:
unavailable += 1
metrics.append(
{
"requestedTime": requested_time,
"key": key,
"category": category,
"actual": actual,
"amesimBaseline": None,
"absoluteError": None,
"relativeError": None,
"relativeErrorPercent": None,
"relativeErrorDefined": False,
"relativeErrorReason": "amesimBaselineUnavailable",
"allowedError": None,
"toleranceRatio": None,
"unboundedToleranceRatio": False,
"comparisonBasis": "localInvariantOnly",
"evaluated": False,
"passed": None,
"exclusionReason": "amesimBaselineUnavailable",
}
)
continue
available += 1
expected_numeric = float(expected)
tolerance = external_tolerance[category]
allowed = tolerance["absolute"] + tolerance["relative"] * abs(expected_numeric)
error = abs(actual - expected_numeric)
ratio = error / allowed if allowed > 0.0 else (0.0 if error == 0.0 else math.inf)
relative_error = (
None if expected_numeric == 0.0 else error / abs(expected_numeric)
)
relative_error_percent = (
None if relative_error is None else 100.0 * relative_error
)
near_zero_threshold = (
tolerance["absolute"] / tolerance["relative"]
if tolerance["relative"] > 0.0
else 0.0
)
if category == "discreteMode":
comparison_basis = "exactMatch"
elif expected_numeric == 0.0 or abs(expected_numeric) <= near_zero_threshold:
comparison_basis = "absoluteNearZero"
else:
comparison_basis = "mixedAbsoluteRelativeTolerance"
excluded_at_signal_jump = category == "massFlow" and math.isclose(
requested_time, 0.04, rel_tol=0.0, abs_tol=1.0e-12
)
metric = {
"requestedTime": requested_time,
"key": key,
"category": category,
"actual": actual,
"amesimBaseline": expected_numeric,
"absoluteError": error,
"relativeError": relative_error,
"relativeErrorPercent": relative_error_percent,
"relativeErrorDefined": relative_error is not None,
"relativeErrorReason": (
None if relative_error is not None else "zeroAmesimBaseline"
),
"allowedError": allowed,
"toleranceRatio": ratio if math.isfinite(ratio) else None,
"unboundedToleranceRatio": not math.isfinite(ratio),
"nearZeroThreshold": near_zero_threshold,
"comparisonBasis": comparison_basis,
}
if relative_error is not None and (
max_recorded_relative_error is None
or relative_error > max_recorded_relative_error
):
max_recorded_relative_error = relative_error
if excluded_at_signal_jump:
excluded += 1
metric.update(
{
"evaluated": False,
"passed": None,
"exclusionReason": (
"signalDiscontinuityLeftRightLimitSemanticsNotComparableAt0.04s"
),
}
)
metrics.append(metric)
continue
metric.update({"evaluated": True, "passed": ratio <= 1.0})
metrics.append(metric)
compared += 1
if ratio > max_ratio:
max_ratio = ratio
worst = metric
if relative_error is not None and (
max_relative_error is None or relative_error > max_relative_error
):
max_relative_error = relative_error
worst_relative = metric
if ratio > 1.0:
issues.append("amesimReferenceToleranceMismatch")
deduplicated = list(dict.fromkeys(issues))
return {
"passed": not deduplicated,
"issues": deduplicated,
"baselineAuthority": "amesim",
"metricCount": len(metrics),
"availableBaselineValueCount": available,
"unavailableBaselineValueCount": unavailable,
"comparedValueCount": compared,
"excludedValueCount": excluded,
"maxToleranceRatio": (
max_ratio if compared and math.isfinite(max_ratio) else None
),
"maxToleranceRatioUnbounded": bool(compared and not math.isfinite(max_ratio)),
"worstValue": worst,
"maxRelativeError": max_relative_error,
"maxRelativeErrorPercent": (
None if max_relative_error is None else 100.0 * max_relative_error
),
"worstRelativeErrorValue": worst_relative,
"maxRecordedRelativeError": max_recorded_relative_error,
"maxRecordedRelativeErrorPercent": (
None
if max_recorded_relative_error is None
else 100.0 * max_recorded_relative_error
),
"metrics": metrics,
"toleranceProfile": {
"id": "amesim-alignment-reviewed-v1",
"byCategory": external_tolerance,
"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": (
"AMESim simulation values are the authoritative physical baseline and "
"are compared on every run; unavailable AMESim quantities remain "
"separate local-invariant checks."
),
}
def build_candidate_golden(
report_path: Path | str,
*,
archive_path: Path | str = DEFAULT_AMESIM_ARCHIVE,
case_id: str = "0.2s",
lane: str = "production",
golden_id: str | None = None,
) -> dict[str, object]:
"""Build an unapproved candidate from a report containing full v2.1 data."""
source_path = Path(report_path).resolve()
payload = source_path.read_bytes()
try:
report = json.loads(payload)
except json.JSONDecodeError as exc:
raise PhysicalStateV21Error(f"Regression report is invalid JSON: {exc}") from exc
if not isinstance(report, Mapping):
raise PhysicalStateV21Error("Regression report must be an object.")
contract = _case_contract(report, case_id=case_id, lane=lane)
keys, _projections = _validate_contract(contract)
checkpoints = contract["checkpoints"]
assert isinstance(checkpoints, list)
times = [float(checkpoint["requestedTime"]) for checkpoint in checkpoints]
amesim = build_amesim_reference(archive_path, checkpoint_times=times)
references = amesim["checkpoints"]
assert isinstance(references, list)
source = report.get("source")
source_sha = source.get("sha256") if isinstance(source, Mapping) else None
if not isinstance(source_sha, str) or len(source_sha) != 64:
raise PhysicalStateV21Error("Report source SHA-256 is missing.")
frozen = []
for checkpoint, reference in zip(checkpoints, references):
assert isinstance(checkpoint, Mapping) and isinstance(reference, Mapping)
values = checkpoint["values"]
reference_values = reference["values"]
assert isinstance(values, Mapping) and isinstance(reference_values, Mapping)
frozen.append(
{
"requestedTime": checkpoint["requestedTime"],
"values": [values[key] for key in keys],
"amesimReferenceValues": [reference_values[key] for key in keys],
}
)
layout = contract["layout"]
assert isinstance(layout, Mapping)
alignment = compare_contract_to_amesim(contract, amesim)
return {
"schemaVersion": PHYSICAL_STATE_V21_GOLDEN_SCHEMA_VERSION,
"id": golden_id
or f"test_mql_8-{lane}-{case_id}-{PHYSICAL_STATE_V21_ID}-candidate",
"contractId": PHYSICAL_STATE_V21_ID,
"caseId": case_id,
"lane": lane,
"sourceXmlSha256": source_sha,
"approval": {
"status": "candidate",
"generatedAt": datetime.now(UTC).isoformat(),
"warning": "The correctness gate rejects this file until explicit approval.",
},
"provenance": {
"localSourceReport": {
"path": _portable_path(source_path),
"bytes": len(payload),
"sha256": _sha256(payload),
"generatedAt": report.get("generatedAt"),
},
"amesim": amesim["provenance"],
"amesimStoredMassDataPaths": amesim["storedMassDataPaths"],
"comparisonPolicy": (
"Transformed AMESim values are the authoritative physical baseline; "
"local Python values are retained only for deterministic and "
"implementation-regression diagnostics."
),
},
"layout": dict(layout),
"checkpoints": frozen,
"amesimAlignmentAtGeneration": alignment,
}
def _exclusive_json_write(path: Path | str, value: object) -> Path:
output = Path(path).resolve()
output.parent.mkdir(parents=True, exist_ok=True)
serialized = json.dumps(
value, ensure_ascii=False, indent=2, allow_nan=False
) + "\n"
try:
descriptor = os.open(output, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
except FileExistsError as exc:
raise PhysicalStateV21Error(f"Refusing to overwrite {output}.") from exc
try:
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
handle.write(serialized)
except BaseException:
output.unlink(missing_ok=True)
raise
return output
def write_candidate_golden(
report_path: Path | str,
output_path: Path | str,
**kwargs: object,
) -> Path:
return _exclusive_json_write(
output_path,
build_candidate_golden(report_path, **kwargs),
)
def _validate_golden(
golden: Mapping[str, object],
*,
allowed_status: str = "approved",
) -> tuple[list[str], list[Mapping[str, object]]]:
if golden.get("schemaVersion") != PHYSICAL_STATE_V21_GOLDEN_SCHEMA_VERSION:
raise PhysicalStateV21Error("Unsupported v2.1 golden schemaVersion.")
if golden.get("contractId") != PHYSICAL_STATE_V21_ID:
raise PhysicalStateV21Error("Golden contract id is invalid.")
for field in ("id", "caseId", "lane"):
if not isinstance(golden.get(field), str) or not golden[field]:
raise PhysicalStateV21Error(f"Golden {field} must be non-empty.")
if not _valid_sha256(golden.get("sourceXmlSha256")):
raise PhysicalStateV21Error("Golden source XML SHA-256 is invalid.")
approval = golden.get("approval")
if not isinstance(approval, Mapping) or approval.get("status") != allowed_status:
raise PhysicalStateV21Error(
f"Golden approval.status must be {allowed_status!r}."
)
provenance = golden.get("provenance")
amesim = provenance.get("amesim") if isinstance(provenance, Mapping) else None
local_report = (
provenance.get("localSourceReport")
if isinstance(provenance, Mapping)
else None
)
if (
not isinstance(local_report, Mapping)
or not isinstance(local_report.get("path"), str)
or not local_report["path"]
or not isinstance(local_report.get("bytes"), int)
or int(local_report["bytes"]) <= 0
or not _valid_sha256(local_report.get("sha256"))
):
raise PhysicalStateV21Error("Golden local report provenance is invalid.")
if (
not isinstance(amesim, Mapping)
or not isinstance(amesim.get("archivePath"), str)
or not amesim["archivePath"]
or not isinstance(amesim.get("archiveBytes"), int)
or int(amesim["archiveBytes"]) <= 0
or not _valid_sha256(amesim.get("archiveSha256"))
):
raise PhysicalStateV21Error("Golden AMESim archive provenance is invalid.")
members = amesim.get("members")
if not isinstance(members, list) or len(members) != 2:
raise PhysicalStateV21Error("Golden AMESim member provenance is incomplete.")
member_names = []
for member in members:
if (
not isinstance(member, Mapping)
or not isinstance(member.get("name"), str)
or not _valid_sha256(member.get("sha256"))
or not isinstance(member.get("bytes"), int)
or int(member["bytes"]) <= 0
):
raise PhysicalStateV21Error("Golden AMESim member provenance is invalid.")
member_names.append(str(member["name"]))
if not any(name.endswith(".var") for name in member_names) or not any(
name.endswith(".results") for name in member_names
):
raise PhysicalStateV21Error("Golden AMESim provenance lacks .var/.results members.")
layout = golden.get("layout")
keys = layout.get("projectionKeys") if isinstance(layout, Mapping) else None
checkpoints = golden.get("checkpoints")
if not isinstance(keys, list) or not isinstance(checkpoints, list) or not checkpoints:
raise PhysicalStateV21Error("Golden layout/checkpoints are incomplete.")
contract_checkpoints = []
for checkpoint in checkpoints:
if not isinstance(checkpoint, Mapping):
raise PhysicalStateV21Error("Golden checkpoint must be an object.")
values = checkpoint.get("values")
references = checkpoint.get("amesimReferenceValues")
if not isinstance(values, list) or len(values) != len(keys):
raise PhysicalStateV21Error("Golden values have the wrong layout.")
if not isinstance(references, list) or len(references) != len(keys):
raise PhysicalStateV21Error("AMESim references have the wrong layout.")
for index, value in enumerate(values):
_finite(value, field=f"golden.values[{index}]")
for index, value in enumerate(references):
if value is not None:
_finite(value, field=f"golden.amesimReferenceValues[{index}]")
contract_checkpoints.append(
{
"requestedTime": checkpoint.get("requestedTime"),
"actualTime": checkpoint.get("requestedTime"),
"available": True,
"values": dict(zip(keys, values)),
}
)
synthetic_contract = {
"schemaVersion": PHYSICAL_STATE_V21_SCHEMA_VERSION,
"id": PHYSICAL_STATE_V21_ID,
"layout": layout,
"checkpoints": contract_checkpoints,
}
return _validate_contract(synthetic_contract)
def _validate_candidate_approval(
candidate: Mapping[str, object],
keys: Sequence[str],
projections: Sequence[Mapping[str, object]],
) -> None:
"""Fail closed before promoting an externally aligned local candidate."""
alignment = candidate.get("amesimAlignmentAtGeneration")
if not isinstance(alignment, Mapping) or alignment.get("passed") is not True:
raise PhysicalStateV21Error(
"Candidate AMESim alignment at generation must have passed."
)
metrics = alignment.get("metrics")
if not isinstance(metrics, list):
raise PhysicalStateV21Error("Candidate AMESim alignment metrics are invalid.")
evaluated_count = 0
for index, metric in enumerate(metrics):
if not isinstance(metric, Mapping):
raise PhysicalStateV21Error(
f"Candidate AMESim alignment metric {index} is invalid."
)
evaluated = metric.get("evaluated")
if evaluated is True:
evaluated_count += 1
if metric.get("passed") is not True:
raise PhysicalStateV21Error(
f"Candidate AMESim evaluated metric {index} did not pass."
)
elif evaluated is False:
exclusion_reason = metric.get("exclusionReason")
if (
metric.get("passed") is not None
or not isinstance(exclusion_reason, str)
or not exclusion_reason.strip()
):
raise PhysicalStateV21Error(
f"Candidate AMESim excluded metric {index} is not auditable."
)
else:
raise PhysicalStateV21Error(
f"Candidate AMESim alignment metric {index} lacks evaluated status."
)
if evaluated_count <= 0 or alignment.get("comparedValueCount") != evaluated_count:
raise PhysicalStateV21Error(
"Candidate AMESim comparedValueCount does not match evaluated metrics."
)
residual_indices: list[tuple[int, str, float]] = []
for index, (key, projection) in enumerate(zip(keys, projections)):
if (
projection.get("category") != "conservation"
or projection.get("unit") != "kg/s"
):
continue
tolerance = projection.get("tolerance")
assert isinstance(tolerance, Mapping)
absolute = _finite(
tolerance.get("absolute"), field=f"{key}.tolerance.absolute"
)
residual_indices.append((index, key, absolute))
checkpoints = candidate.get("checkpoints")
assert isinstance(checkpoints, list)
for checkpoint_index, checkpoint in enumerate(checkpoints):
assert isinstance(checkpoint, Mapping)
values = checkpoint.get("values")
assert isinstance(values, list)
for value_index, key, absolute in residual_indices:
residual = abs(
_finite(
values[value_index],
field=f"golden.checkpoints[{checkpoint_index}].{key}",
)
)
if residual > absolute:
raise PhysicalStateV21Error(
f"Candidate local conservation residual {key} at checkpoint "
f"{checkpoint_index} is {residual}, exceeding {absolute}."
)
def approve_candidate_golden(
candidate_path: Path | str,
output_path: Path | str,
*,
expected_candidate_sha256: str,
reviewed_by: str,
note: str,
) -> Path:
"""Approve an exact candidate hash into a new file, never in place."""
path = Path(candidate_path).resolve()
payload = path.read_bytes()
actual_sha = _sha256(payload)
if actual_sha != expected_candidate_sha256:
raise PhysicalStateV21Error(
f"Candidate SHA mismatch: expected {expected_candidate_sha256}, got {actual_sha}."
)
candidate = json.loads(payload)
if not isinstance(candidate, dict):
raise PhysicalStateV21Error("Candidate must be an object.")
keys, projections = _validate_golden(candidate, allowed_status="candidate")
_validate_candidate_approval(candidate, keys, projections)
if not reviewed_by.strip() or not note.strip():
raise PhysicalStateV21Error("Approval requires reviewer and note.")
approved = dict(candidate)
approved["approval"] = {
"status": "approved",
"approvedAt": datetime.now(UTC).isoformat(),
"reviewedBy": reviewed_by.strip(),
"note": note.strip(),
"candidatePath": _portable_path(path),
"candidateSha256": actual_sha,
}
return _exclusive_json_write(output_path, approved)
def load_approved_golden(path: Path | str) -> dict[str, object]:
golden_path = Path(path).resolve()
payload = golden_path.read_bytes()
try:
golden = json.loads(payload)
except json.JSONDecodeError as exc:
raise PhysicalStateV21Error(f"Golden is invalid JSON: {exc}") from exc
if not isinstance(golden, dict):
raise PhysicalStateV21Error("Golden must be an object.")
_validate_golden(golden)
golden["_path"] = str(golden_path)
golden["_sha256"] = _sha256(payload)
return golden
def evaluate_physical_state_v21(
contract: Mapping[str, object],
golden: Mapping[str, object],
) -> dict[str, object]:
"""Evaluate a v2.1 contract against frozen AMESim reference values.
The approved artifact pins AMESim provenance, layout, checkpoint times, and
transformed AMESim values. Its frozen Python values are intentionally not
used as the physical baseline.
"""
keys, projections = _validate_contract(contract)
golden_keys, _ = _validate_golden(golden)
if keys != golden_keys:
return {
"passed": False,
"issues": ["physicalStateV21LayoutMismatch"],
"comparedValueCount": 0,
}
actual_checkpoints = contract["checkpoints"]
expected_checkpoints = golden["checkpoints"]
assert isinstance(actual_checkpoints, list) and isinstance(expected_checkpoints, list)
if len(actual_checkpoints) != len(expected_checkpoints):
return {
"passed": False,
"issues": ["physicalStateV21CheckpointCountMismatch"],
"comparedValueCount": 0,
}
issues: list[str] = []
reference_checkpoints: list[dict[str, object]] = []
invariant_metrics: list[dict[str, object]] = []
invariant_max_ratio = 0.0
invariant_worst: dict[str, object] | None = None
for actual_checkpoint, expected_checkpoint in zip(actual_checkpoints, expected_checkpoints):
assert isinstance(actual_checkpoint, Mapping) and isinstance(expected_checkpoint, Mapping)
requested = float(expected_checkpoint["requestedTime"])
if not math.isclose(
float(actual_checkpoint["requestedTime"]),
requested,
rel_tol=0.0,
abs_tol=1.0e-12,
) or not math.isclose(
float(actual_checkpoint["actualTime"]),
requested,
rel_tol=0.0,
abs_tol=1.0e-12,
):
issues.append("physicalStateV21CheckpointTimeMismatch")
actual_values = actual_checkpoint["values"]
amesim_values = expected_checkpoint["amesimReferenceValues"]
assert isinstance(actual_values, Mapping) and isinstance(amesim_values, list)
reference_values = dict(zip(keys, amesim_values))
reference_checkpoints.append(
{"requestedTime": requested, "values": reference_values}
)
for key, projection, amesim_baseline in zip(
keys, projections, amesim_values
):
if amesim_baseline is not None:
continue
actual = float(actual_values[key])
tolerance = projection["tolerance"]
assert isinstance(tolerance, Mapping)
allowed = float(tolerance["absolute"])
error = abs(actual)
ratio = error / allowed if allowed > 0.0 else (0.0 if error == 0.0 else math.inf)
metric = {
"requestedTime": requested,
"key": key,
"category": projection["category"],
"actual": actual,
"amesimBaseline": None,
"invariantTarget": 0.0,
"absoluteError": error,
"relativeError": None,
"relativeErrorPercent": None,
"relativeErrorDefined": False,
"relativeErrorReason": "amesimBaselineUnavailable",
"allowedError": allowed,
"toleranceRatio": ratio if math.isfinite(ratio) else None,
"unboundedToleranceRatio": not math.isfinite(ratio),
"comparisonBasis": "localInvariantAbsoluteTolerance",
"passed": ratio <= 1.0,
}
invariant_metrics.append(metric)
if ratio > invariant_max_ratio:
invariant_max_ratio = ratio
invariant_worst = metric
if ratio > 1.0:
issues.append("physicalStateV21LocalInvariantMismatch")
comparison = compare_contract_to_amesim(
contract,
{"schemaVersion": 1, "checkpoints": reference_checkpoints},
)
raw_comparison_issues = comparison.get("issues")
if isinstance(raw_comparison_issues, list):
issues.extend(
str(issue)
for issue in raw_comparison_issues
if isinstance(issue, str)
)
deduplicated = list(dict.fromkeys(issues))
provenance = golden.get("provenance")
amesim_provenance = (
provenance.get("amesim") if isinstance(provenance, Mapping) else None
)
result = dict(comparison)
result.update({
"passed": not deduplicated,
"issues": deduplicated,
"baselineAuthority": "amesim",
"baselineSource": "approvedArtifact.amesimReferenceValues",
"amesimProvenance": (
dict(amesim_provenance)
if isinstance(amesim_provenance, Mapping)
else None
),
"pythonSnapshotRole": "determinismDiagnosticOnly",
"localInvariants": {
"passed": not any(
metric["passed"] is False for metric in invariant_metrics
),
"metricCount": len(invariant_metrics),
"maxToleranceRatio": (
invariant_max_ratio
if invariant_metrics and math.isfinite(invariant_max_ratio)
else None
),
"maxToleranceRatioUnbounded": bool(
invariant_metrics and not math.isfinite(invariant_max_ratio)
),
"worstValue": invariant_worst,
"metrics": invariant_metrics,
},
"goldenPath": golden.get("_path"),
"goldenSha256": golden.get("_sha256"),
})
return result
def assess_report_against_amesim(
report_path: Path | str,
*,
archive_path: Path | str = DEFAULT_AMESIM_ARCHIVE,
case_id: str = "0.2s",
lane: str = "production",
) -> dict[str, object]:
"""Assess a full or pre-v2.1 report and expose every comparable error."""
path = Path(report_path).resolve()
payload = path.read_bytes()
report = json.loads(payload)
if not isinstance(report, Mapping):
raise PhysicalStateV21Error("Report must be an object.")
summary = _case_summary(report, case_id=case_id, lane=lane)
if isinstance(summary.get("physicalStateV21"), Mapping):
contract = summary["physicalStateV21"]
coverage = "fullCheckpoints"
else:
contract = project_report_endpoint_v21(report, case_id=case_id, lane=lane)
coverage = "legacyEndpointOnly"
assert isinstance(contract, Mapping)
checkpoints = contract["checkpoints"]
assert isinstance(checkpoints, list)
times = [float(checkpoint["requestedTime"]) for checkpoint in checkpoints]
reference = build_amesim_reference(archive_path, checkpoint_times=times)
comparison = compare_contract_to_amesim(contract, reference)
return {
"schemaVersion": 1,
"reportPath": str(path),
"reportSha256": _sha256(payload),
"caseId": case_id,
"lane": lane,
"coverage": coverage,
"amesimProvenance": reference["provenance"],
"comparison": comparison,
}
def _parse_arguments(argv: Sequence[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Physical-state-v2.1 local gate.")
subparsers = parser.add_subparsers(dest="command", required=True)
assess = subparsers.add_parser("assess-amesim")
assess.add_argument("--report", type=Path, required=True)
assess.add_argument("--amesim-archive", type=Path, default=DEFAULT_AMESIM_ARCHIVE)
assess.add_argument("--case", default="0.2s")
assess.add_argument("--lane", default="production")
assess.add_argument("--output", type=Path)
candidate = subparsers.add_parser("candidate")
candidate.add_argument("--report", type=Path, required=True)
candidate.add_argument("--amesim-archive", type=Path, default=DEFAULT_AMESIM_ARCHIVE)
candidate.add_argument("--case", default="0.2s")
candidate.add_argument("--lane", default="production")
candidate.add_argument("--id")
candidate.add_argument("--output", type=Path, required=True)
approve = subparsers.add_parser("approve")
approve.add_argument("--candidate", type=Path, required=True)
approve.add_argument("--expected-candidate-sha256", required=True)
approve.add_argument("--reviewed-by", required=True)
approve.add_argument("--note", required=True)
approve.add_argument("--output", type=Path, required=True)
check = subparsers.add_parser("check")
check.add_argument("--report", type=Path, required=True)
check.add_argument("--golden", type=Path, required=True)
check.add_argument("--case", default="0.2s")
check.add_argument("--lane", default="production")
return parser.parse_args(argv)
def main(argv: Sequence[str] | None = None) -> int:
arguments = _parse_arguments(argv)
if arguments.command == "assess-amesim":
assessment = assess_report_against_amesim(
arguments.report,
archive_path=arguments.amesim_archive,
case_id=arguments.case,
lane=arguments.lane,
)
serialized = json.dumps(
assessment, ensure_ascii=False, indent=2, allow_nan=False
) + "\n"
if arguments.output is None:
print(serialized, end="")
else:
_exclusive_json_write(arguments.output, assessment)
print(f"Assessment written: {arguments.output.resolve()}")
return 0 if assessment["comparison"]["passed"] else 1
if arguments.command == "candidate":
output = write_candidate_golden(
arguments.report, arguments.output,
archive_path=arguments.amesim_archive,
case_id=arguments.case, lane=arguments.lane, golden_id=arguments.id,
)
print(f"Candidate written without approval: {output}")
print(f"sha256={_sha256(output.read_bytes())}")
return 0
if arguments.command == "approve":
output = approve_candidate_golden(
arguments.candidate, arguments.output,
expected_candidate_sha256=arguments.expected_candidate_sha256,
reviewed_by=arguments.reviewed_by, note=arguments.note,
)
print(f"Approved golden written: {output}")
print(f"sha256={_sha256(output.read_bytes())}")
return 0
report = json.loads(arguments.report.read_bytes())
contract = _case_contract(report, case_id=arguments.case, lane=arguments.lane)
evaluation = evaluate_physical_state_v21(contract, load_approved_golden(arguments.golden))
print(json.dumps(evaluation, ensure_ascii=False, indent=2, allow_nan=False))
return 0 if evaluation["passed"] else 1
if __name__ == "__main__":
raise SystemExit(main())