Gate saved models on solver convergence and the virial milestone

This commit is contained in:
2026-09-16 15:17:38 -04:00
parent 9bd4af6996
commit 0470e3b2a1
4 changed files with 248 additions and 1 deletions

View File

@@ -0,0 +1,203 @@
#!/usr/bin/env python3
"""Report a plateau endpoint and gate strict solver convergence plus eight virials.
Usage: check_plateau_physics.py RESULT_FOLDER
Exit 0: solver_and_virial_milestone passes. Exit 3: it does not pass.
Exit 2: required endpoint data is missing or malformed. Standard-library only.
Local errors remain measurements; passing this gate is not full physical validation.
"""
import argparse
import csv
import json
import math
from pathlib import Path
import sys
THRESHOLD = 1.0e-5
QUADRATURES = (14, 18)
VIRIALS = {
"pressure_from_enthalpy_energy": "enthalpy_virial_error",
"pressure_from_enthalpy_force": "enthalpy_force_virial_error",
"pressure_from_density_energy": "virial_error",
"pressure_from_density_force": "force_virial_error",
}
SIGN_METRICS = (
"minimum_density", "maximum_density", "negative_density_samples",
"negative_density_mass_fraction", "negative_density_volume_fraction",
"minimum_enthalpy", "maximum_enthalpy", "negative_enthalpy_samples",
"negative_enthalpy_volume_fraction",
)
CONSERVATION_METRICS = (
"mass", "mass_absolute_error", "angular_momentum_z", "angular_momentum_absolute_error",
)
LOCAL_METRICS = (
"eos_density_mismatch_maximum", "eos_density_mismatch_maximum_scaled",
"eos_density_mismatch_rms", "eos_density_mismatch_rms_scaled",
"bernoulli_raw_rms", "bernoulli_raw_rms_scaled",
"bernoulli_mean_subtracted_rms", "bernoulli_mean_subtracted_rms_scaled",
"bernoulli_sampled_range", "hydrostatic_gradient_rms",
)
PRESSURE_METRICS = (
"pressure_from_density_defined_everywhere", "pressure_integral",
"pressure_from_enthalpy_defined_everywhere", "enthalpy_pressure_integral",
)
REQUIRED_METRICS = (
*VIRIALS.values(), *SIGN_METRICS, *CONSERVATION_METRICS,
*LOCAL_METRICS, *PRESSURE_METRICS, "complete_volume_measurement",
)
def measurement(value):
"""Keep JSON standard-compliant without losing the reason for a null value."""
result = {"value": value if math.isfinite(value) else None, "finite": math.isfinite(value)}
if not result["finite"]:
result["nonfinite_value"] = "nan" if math.isnan(value) else ("+inf" if value > 0 else "-inf")
return result
def read_metadata(path):
result = {}
for line in path.read_text().splitlines():
if not line.strip():
continue
if "=" not in line:
raise ValueError(f"Malformed metadata line in {path}: {line!r}")
key, value = line.split("=", 1)
if not key or key in result:
raise ValueError(f"Empty or duplicate metadata key in {path}: {key!r}")
result[key] = value
if result.get("converged") not in ("0", "1"):
raise ValueError("metadata.txt must contain a completed converged=0 or converged=1 result")
if "accepted_steps" not in result or int(result["accepted_steps"]) < 0:
raise ValueError("metadata.txt must contain a nonnegative accepted_steps count")
return result
def read_physical(path):
result = {q: {} for q in QUADRATURES}
with path.open(newline="") as stream:
reader = csv.DictReader(stream)
if reader.fieldnames != ["quadrature", "metric", "value"]:
raise ValueError("final_physical.csv requires the header quadrature,metric,value")
for row in reader:
if None in row or any(value is None for value in row.values()):
raise ValueError("Malformed row in final_physical.csv")
q = int(row["quadrature"])
if q not in result:
continue
key = row["metric"]
if not key or key in result[q]:
raise ValueError(f"Empty or duplicate physical metric at quadrature {q}: {key!r}")
result[q][key] = float(row["value"])
for q, values in result.items():
missing = [key for key in REQUIRED_METRICS if key not in values]
if missing:
raise ValueError(f"Missing quadrature {q} endpoint metrics: {', '.join(missing)}")
for key in ("complete_volume_measurement", "pressure_from_density_defined_everywhere",
"pressure_from_enthalpy_defined_everywhere"):
if values[key] not in (0.0, 1.0):
raise ValueError(f"Invalid boolean metric at quadrature {q}: {key}")
for key in VIRIALS.values():
if math.isfinite(values[key]) and values[key] < 0:
raise ValueError(f"A virial error must be nonnegative: quadrature {q}, {key}")
return result
def pressure_report(values, kind):
flag = values[f"pressure_from_{kind}_defined_everywhere"] == 1.0
integral = values["pressure_integral" if kind == "density" else "enthalpy_pressure_integral"]
return {
"defined_at_all_samples": flag,
"integral": measurement(integral),
"valid": flag and math.isfinite(integral),
}
def negative_samples_detected(values, kind):
minimum = values[f"minimum_{kind}"]
count = values[f"negative_{kind}_samples"]
if not math.isfinite(minimum) or not math.isfinite(count):
return None
return minimum < 0.0 or count > 0.0
def assess(folder):
meta = read_metadata(folder / "metadata.txt")
physical = read_physical(folder / "final_physical.csv")
per_quadrature = {}
all_virials_pass = True
for q, values in physical.items():
complete = values["complete_volume_measurement"] == 1.0
pressure = {kind: pressure_report(values, kind) for kind in ("enthalpy", "density")}
virials = {}
for name, key in VIRIALS.items():
value = values[key]
kind = "density" if "density" in name else "enthalpy"
check = measurement(value)
if not complete or not pressure[kind]["valid"] or not math.isfinite(value):
check["status"] = "undefined"
check["reason"] = (
"incomplete_volume_measurement" if not complete else
"pressure_not_defined_or_finite" if not pressure[kind]["valid"] else
"nonfinite_virial"
)
else:
check["status"] = "pass" if value <= THRESHOLD else "fail"
all_virials_pass = all_virials_pass and check["status"] == "pass"
virials[name] = check
per_quadrature[str(q)] = {
"complete_volume_measurement": complete,
"virials": virials,
"pressure": pressure,
"sampled_signs": {
"negative_density_detected": negative_samples_detected(values, "density"),
"negative_enthalpy_detected": negative_samples_detected(values, "enthalpy"),
**{key: measurement(values[key]) for key in SIGN_METRICS},
},
"conservation": {key: measurement(values[key]) for key in CONSERVATION_METRICS},
"local_measurements_without_acceptance_tolerances": {
key: measurement(values[key]) for key in LOCAL_METRICS
},
}
strict_converged = meta["converged"] == "1"
passed = strict_converged and all_virials_pass
report = {
"schema_version": 1,
"result_folder": str(folder.resolve()),
"solver_and_virial_milestone": "pass" if passed else "fail",
"strict_solver": {
"converged": strict_converged,
"accepted_steps": int(meta["accepted_steps"]),
"failure": meta.get("failure"),
"absolute_tolerance": meta.get("atol"),
"relative_tolerance": meta.get("rtol"),
},
"virial_threshold": THRESHOLD,
"all_eight_virial_checks_pass": all_virials_pass,
"quadratures": per_quadrature,
"interpretation": [
"The milestone requires strict recorded solver convergence and all four defined, finite virial errors <= 1e-5 at both quadratures.",
"The milestone is not full physical validation. Mass/J errors and local EOS/Bernoulli measurements have no acceptance tolerances here.",
"Density and enthalpy signs are quadrature samples; sampled extrema and local errors are not certified pointwise bounds.",
"An undefined pressure-from-density virial is kept separate from a finite virial that exceeds the threshold.",
],
}
return report, 0 if passed else 3
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("result_folder", type=Path)
args = parser.parse_args()
try:
report, code = assess(args.result_folder)
except (OSError, ValueError, csv.Error) as error:
report, code = {"schema_version": 1, "input_error": str(error)}, 2
print(json.dumps(report, indent=2, allow_nan=False))
return code
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,30 @@
# Plateau endpoint milestone checker
Run the standalone standard-library script with one completed result folder:
```sh
python3 experiments/check_plateau_physics.py /absolute/path/to/plateau-result
```
It reads `metadata.txt` and `final_physical.csv` and prints standard JSON. Exit codes:
- **0:** `solver_and_virial_milestone=pass`: recorded strict solver convergence and all eight defined, finite virial errors at or below 1e-5.
- **3:** those milestone checks do not all pass, including an undefined pressure integral.
- **2:** missing or malformed required endpoint data, including an incomplete quadrature table or missing convergence outcome.
The eight checks comprise energy and force virials with both P(h) and P(rho), each at quadrature orders 14 and 18. An undefined check is distinguished from a finite check exceeding the threshold. Pressure validity, density/enthalpy signs, negative-density budgets, mass/J errors, and local EOS/Bernoulli measurements are reported separately. Nonfinite numbers become JSON null with a retained finite flag and NaN/infinity classification.
Passing this gate is **not full physical validation**. No new tolerance is assigned to mass/J or local errors. Sampled signs and extrema are not certified pointwise bounds. The script trusts the diagnostic's recorded convergence flag and physical measurements; it does not reconstruct a model, validate provenance, or independently recompute integrals. It does not inspect or modify production code.
Verification on the September 16 campaign:
| Completed case | Expected and observed exit | Distinction checked |
| --- | ---: | --- |
| n=1, J=0.05, no central constraint, restart 160 | 0 | Strict convergence and all virials pass |
| Same model, restart 40 | 3 | Virials pass, strict convergence fails |
| n=1.5, J=0, no central constraint, restart 160 | 3 | P(h) virials pass; P(rho) is undefined |
| n=3 initial seed | 3 | P(h) virials exceed the threshold |
Threshold equality, one virial just above threshold, and missing required data
were also checked. Detailed verification JSON and reports remain with the raw
campaign artifacts in the audit workspace.

View File

@@ -129,3 +129,9 @@ folders should remain outside source commits; retain compact result tables and
reproduction instructions with each hypothesis branch. The controls omit the
central phase at n=1 and n=1.5. A separate n=3 control retains it to fix homology;
the production model API does not insert it implicitly.
Check a completed endpoint with
`python3 experiments/check_plateau_physics.py /absolute/path/to/result-folder`.
This gates strict recorded solver convergence and all eight finite virial errors
at 1e-5, while reporting local field errors separately. See
`check_plateau_physics_README.md` for the exit codes and interpretation limits.

View File

@@ -125,7 +125,10 @@ def main():
state_files = {}
for i, word in enumerate(case['arguments']):
if word == '--probe-state':
path = Path(case['arguments'][i + 1]).resolve()
path = Path(case['arguments'][i + 1])
if not path.is_absolute():
path = source / path
path = path.resolve()
state_files[str(path)] = digest(path)
print('START', name, flush=True)
start = time.monotonic()
@@ -140,6 +143,11 @@ def main():
record['timed_out'] = True
record['exit_code'] = None
record.update(seconds=time.monotonic() - start, finished_utc=utc())
record['output_hashes'] = {
name: digest(folder / name)
for name in ('accepted_state.txt', 'layout.csv', 'metadata.txt', 'final_physical.csv')
if (folder / name).is_file()
}
write_json(output / (name + '_run.json'), record)
summaries.append({**record, **summarize(folder)})
write_json(output / 'summary.json', summaries)