test: shear-wave modal-extinction handling; TG mock verify_source flow arg
- shear_wave.fit_rate: finite late R=0 returns rate_fit_valid=false with null fit quantities (no clipping, no invented rate); own screens and budgets treat it as a failure (CONTROL_SCREEN_FAIL / refined FAIL), other finite metrics retained, JSON-safe. - test_projected_pairs: dry-run/execution mocks expect the explicit 'taylor_green' flow argument now passed to verify_source. - run_case: SHEAR_WAVE_SHA256 repinned; extinction test added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f337652683
commit
de5b0cc080
@@ -262,6 +262,11 @@ Run set: uniform N, uniform 2N, and refined depth-2 runs at root N.
|
|||||||
`controlled_pass` is false, so it is never a controlled-comparison pass;
|
`controlled_pass` is false, so it is never a controlled-comparison pass;
|
||||||
- PASS/FAIL: every refined run's `controlled_pass` (own screens and
|
- PASS/FAIL: every refined run's `controlled_pass` (own screens and
|
||||||
budgets against valid, passing controls).
|
budgets against valid, passing controls).
|
||||||
|
- Finite modal extinction (late R = 0) is a physical failure, not
|
||||||
|
malformed data. The run gets `rate_fit_valid=false` with null fit
|
||||||
|
quantities, and its other metrics are kept. Nothing is clipped and no
|
||||||
|
rate is invented. The result is CONTROL_SCREEN_FAIL for a control, or
|
||||||
|
FAIL for a refined run.
|
||||||
- Screens are never retuned.
|
- Screens are never retuned.
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ SOURCE_SHA256 = "a5dbfd804befce868067d79a23e5fd47dce7f505b3f9b1bb2145fb57cb5f217
|
|||||||
BUILD_CAPTURE_SHA256 = "3dbaa6547cb0a4cb1541054f3b64ad8b6e18cc70527ac6166e56b944e9bd464a"
|
BUILD_CAPTURE_SHA256 = "3dbaa6547cb0a4cb1541054f3b64ad8b6e18cc70527ac6166e56b944e9bd464a"
|
||||||
REFERENCE_HARNESS = "11901a7f77557a901e017a171eeae1758d584fa5"
|
REFERENCE_HARNESS = "11901a7f77557a901e017a171eeae1758d584fa5"
|
||||||
INITIALIZATION = "physical FP64 equilibrium G at actual cell centers; rho=1+3p"
|
INITIALIZATION = "physical FP64 equilibrium G at actual cell centers; rho=1+3p"
|
||||||
SHEAR_WAVE_SHA256 = "4597cf1870c1a69b4525c231825a58cf601b3fe22efd2aecf8fca76601c972e8"
|
SHEAR_WAVE_SHA256 = "81d5c8ee60864372887cddf9239580c82fb6750d956038f1e3ee9de61aa4e877"
|
||||||
|
|
||||||
|
|
||||||
def flow_functions(flow="taylor_green"):
|
def flow_functions(flow="taylor_green"):
|
||||||
|
|||||||
@@ -155,15 +155,21 @@ def fit_rate(rows, length, viscosity):
|
|||||||
"""
|
"""
|
||||||
late = [r for r in rows if r["tstar"] >= FIT_FROM_TSTAR - 1e-12]
|
late = [r for r in rows if r["tstar"] >= FIT_FROM_TSTAR - 1e-12]
|
||||||
amp = np.array([math.hypot(r["a_hat"], r["b_hat"]) for r in late])
|
amp = np.array([math.hypot(r["a_hat"], r["b_hat"]) for r in late])
|
||||||
if len(late) < 3 or not (np.isfinite(amp).all() and (amp > 0).all()):
|
if len(late) < 3 or not np.isfinite(amp).all():
|
||||||
raise ValueError("need >= 3 late samples with finite positive R")
|
raise ValueError("need >= 3 late samples with finite R")
|
||||||
|
exact_rate = decay_rate(length, viscosity)
|
||||||
|
to_tstar = length / U # root steps per unit t*
|
||||||
|
if not (amp > 0).all():
|
||||||
|
# Finite modal extinction: a physical failure, not malformed data. No
|
||||||
|
# clipping and no invented rate; fit quantities are unavailable.
|
||||||
|
return dict(rate_fit_valid=False, rate=None, exact_rate=exact_rate,
|
||||||
|
rate_tstar=None, exact_rate_tstar=exact_rate * to_tstar,
|
||||||
|
rate_error=None, interval_rate_errors=None, log_fit_residual_max=None)
|
||||||
t = np.array([r["step"] for r in late], dtype=float)
|
t = np.array([r["step"] for r in late], dtype=float)
|
||||||
(slope, intercept) = np.polyfit(t, np.log(amp), 1)
|
(slope, intercept) = np.polyfit(t, np.log(amp), 1)
|
||||||
residual = np.log(amp) - (slope * t + intercept)
|
residual = np.log(amp) - (slope * t + intercept)
|
||||||
exact_rate = decay_rate(length, viscosity)
|
|
||||||
to_tstar = length / U # root steps per unit t*
|
|
||||||
return dict(
|
return dict(
|
||||||
rate=float(-slope), exact_rate=exact_rate,
|
rate_fit_valid=True, rate=float(-slope), exact_rate=exact_rate,
|
||||||
rate_tstar=float(-slope * to_tstar),
|
rate_tstar=float(-slope * to_tstar),
|
||||||
exact_rate_tstar=exact_rate * to_tstar, # = 5 (2 pi)^2 / Re
|
exact_rate_tstar=exact_rate * to_tstar, # = 5 (2 pi)^2 / Re
|
||||||
rate_error=abs(-slope / exact_rate - 1),
|
rate_error=abs(-slope / exact_rate - 1),
|
||||||
@@ -228,10 +234,11 @@ def summarize(record):
|
|||||||
velocity_l2=max(r["velocity_l2"] for r in rows),
|
velocity_l2=max(r["velocity_l2"] for r in rows),
|
||||||
mass_relative=max(r["mass_relative"] for r in rows),
|
mass_relative=max(r["mass_relative"] for r in rows),
|
||||||
)
|
)
|
||||||
if not all(_number(v) for v in (out["rate"], out["rate_error"], *out["interval_rate_errors"],
|
if out["rate_fit_valid"] and not all(
|
||||||
out["log_fit_residual_max"])):
|
_number(v) for v in (out["rate"], out["rate_error"], *out["interval_rate_errors"],
|
||||||
|
out["log_fit_residual_max"])):
|
||||||
raise ValueError("nonfinite rate fit")
|
raise ValueError("nonfinite rate fit")
|
||||||
out["own_screens_pass"] = out["complete"] and all(
|
out["own_screens_pass"] = out["complete"] and out["rate_fit_valid"] and all(
|
||||||
out[key] <= out["eps"] for key in ("rate_error", "phase_drift_abs", "residual_l2")
|
out[key] <= out["eps"] for key in ("rate_error", "phase_drift_abs", "residual_l2")
|
||||||
) and all(out[key] <= limit for key, limit in LIMITS.items())
|
) and all(out[key] <= limit for key, limit in LIMITS.items())
|
||||||
return out
|
return out
|
||||||
@@ -280,12 +287,16 @@ def assess(base_control, fine_control, refined):
|
|||||||
statuses.append("INVALID_RUN")
|
statuses.append("INVALID_RUN")
|
||||||
continue
|
continue
|
||||||
s["valid"] = True
|
s["valid"] = True
|
||||||
|
# rate_error is None when a modal fit is unavailable; never a pass.
|
||||||
s["budget"] = {
|
s["budget"] = {
|
||||||
key: max(BASELINE_FACTOR * base[key], floor) for key, floor in FLOORS.items()
|
key: (None if base[key] is None else max(BASELINE_FACTOR * base[key], floor))
|
||||||
|
for key, floor in FLOORS.items()
|
||||||
}
|
}
|
||||||
s["budget_pass"] = all(s[key] <= s["budget"][key] for key in FLOORS)
|
s["budget_pass"] = all(
|
||||||
|
s[key] is not None and s["budget"][key] is not None and s[key] <= s["budget"][key]
|
||||||
|
for key in FLOORS)
|
||||||
s["control_ratios"] = {
|
s["control_ratios"] = {
|
||||||
name: {key: (s[key] / c[key] if c[key] > 0 else None)
|
name: {key: (s[key] / c[key] if s[key] is not None and c[key] else None)
|
||||||
for key in (*FLOORS, "spurious_pressure_rms")}
|
for key in (*FLOORS, "spurious_pressure_rms")}
|
||||||
for name, c in (("root", base), ("finest", fine))
|
for name, c in (("root", base), ("finest", fine))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ class ProjectedPairsTests(unittest.TestCase):
|
|||||||
patch.object(run_case, "runtime_identity", side_effect=AssertionError("CUDA reached")), \
|
patch.object(run_case, "runtime_identity", side_effect=AssertionError("CUDA reached")), \
|
||||||
redirect_stdout(stdout):
|
redirect_stdout(stdout):
|
||||||
self.assertEqual(run_case.main(), 0)
|
self.assertEqual(run_case.main(), 0)
|
||||||
verify.assert_called_once_with(REPO, MOCK_REVISION)
|
verify.assert_called_once_with(REPO, MOCK_REVISION, "taylor_green")
|
||||||
self.assertTrue(json.loads(stdout.getvalue())["dry_run"])
|
self.assertTrue(json.loads(stdout.getvalue())["dry_run"])
|
||||||
self.assertFalse(output.exists())
|
self.assertFalse(output.exists())
|
||||||
|
|
||||||
@@ -412,7 +412,7 @@ class ProjectedPairsTests(unittest.TestCase):
|
|||||||
else:
|
else:
|
||||||
self.assertEqual(run_case.main(), 0)
|
self.assertEqual(run_case.main(), 0)
|
||||||
receipt = json.loads(output.read_text())
|
receipt = json.loads(output.read_text())
|
||||||
verify.assert_called_once_with(REPO, MOCK_REVISION)
|
verify.assert_called_once_with(REPO, MOCK_REVISION, "taylor_green")
|
||||||
self.assertEqual(selectors, ["projected_linear" if early else "constant"] if depth == 2 else [])
|
self.assertEqual(selectors, ["projected_linear" if early else "constant"] if depth == 2 else [])
|
||||||
self.assertEqual(receipt["source"], source())
|
self.assertEqual(receipt["source"], source())
|
||||||
self.assertEqual(receipt["runtime_identity"], RUNTIME)
|
self.assertEqual(receipt["runtime_identity"], RUNTIME)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Run from this directory with PYTHONPATH=<checkout>/src (same as test_oracle.py).
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
import math
|
import math
|
||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -286,6 +287,32 @@ class AssessTests(unittest.TestCase):
|
|||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
sw.assess(base, fine, [record(128, 2, rows_for(128)), record(128, 2, rows_for(128))])
|
sw.assess(base, fine, [record(128, 2, rows_for(128)), record(128, 2, rows_for(128))])
|
||||||
|
|
||||||
|
def test_modal_extinction_is_a_failure_not_malformed(self):
|
||||||
|
def extinct(rows):
|
||||||
|
for row in rows[1:]:
|
||||||
|
row.update(a_hat=0.0, b_hat=0.0, modal_amplitude=0.0, amplitude_ratio=0.0,
|
||||||
|
amplitude_error=-1.0)
|
||||||
|
return rows
|
||||||
|
base = record(128, 1, rows_for(128))
|
||||||
|
fine = record(256, 1, rows_for(256))
|
||||||
|
refined = record(128, 2, extinct(rows_for(128)))
|
||||||
|
report = sw.assess(base, fine, [refined])
|
||||||
|
self.assertEqual(report["status"], "FAIL")
|
||||||
|
own = report["refined"]["constant"]
|
||||||
|
self.assertTrue(own["valid"])
|
||||||
|
self.assertFalse(own["rate_fit_valid"])
|
||||||
|
self.assertIsNone(own["rate_error"])
|
||||||
|
self.assertIsNone(own["control_ratios"]["root"]["rate_error"])
|
||||||
|
self.assertFalse(own["own_screens_pass"] or own["controlled_pass"] or own["budget_pass"])
|
||||||
|
self.assertEqual(own["residual_l2"], 1e-4) # other finite metrics retained
|
||||||
|
json.loads(json.dumps(report, allow_nan=False))
|
||||||
|
control = sw.assess(record(128, 1, extinct(rows_for(128))), fine,
|
||||||
|
[record(128, 2, rows_for(128))])
|
||||||
|
self.assertEqual(control["status"], "CONTROL_SCREEN_FAIL")
|
||||||
|
self.assertIsNone(control["refined"]["constant"]["budget"]["rate_error"])
|
||||||
|
self.assertFalse(control["refined"]["constant"]["controlled_pass"])
|
||||||
|
json.loads(json.dumps(control, allow_nan=False))
|
||||||
|
|
||||||
def test_wrong_shapes(self):
|
def test_wrong_shapes(self):
|
||||||
base = record(128, 1, rows_for(128))
|
base = record(128, 1, rows_for(128))
|
||||||
fine = record(256, 1, rows_for(256))
|
fine = record(256, 1, rows_for(256))
|
||||||
|
|||||||
Reference in New Issue
Block a user