test: shear-wave row validation, controlled-pass semantics, bounds

- shear_wave: validate_rows before screens (exact scheduled steps/t*, no
  duplicates, all numeric/list fields finite, nonnegative error fields,
  rho_min>0; signed amplitude/phase/interval errors keep their sign);
  INVALID_RUN for invalid refined rows; own_screens_pass vs controlled_pass
  so a CONTROL_SCREEN_FAIL set never reports a controlled pass; empty or
  duplicate refined schemes rejected; pressure-budget redundancy stated.
- run_case: SHEAR_WAVE_SHA256 repinned to the updated module.
- tests: divergence/advection bounds use a(t) and the derived h^2 term;
  invalid-row mutations; signed errors allowed; controlled_pass checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-19 00:28:46 +00:00
1 parent 3554cb2fde
commit f337652683
4 files changed
+132 -32

No files matched your search

+16 -6
View File
@@ -243,15 +243,25 @@ Run set: uniform N, uniform 2N, and refined depth-2 runs at root N.
- velocity_l2 <= 0.05, mass <= 1e-5, spurious_pressure_scaled <= 0.02.
- Budgets keep the `assess.py` form, `refined <= max(2*control_N, floor)`:
- floors are 0.002 for rate and residual, 0.02 for pressure;
- because the pressure ceiling and floor are both 0.02, the comparative
pressure budget adds no constraint unless the root control exceeds 0.01;
- the comparative pressure budget is always redundant:
`max(2*control, 0.02)` is never below the 0.02 absolute ceiling;
- raw ratios to both the root and finest controls, including raw
pressure rms, are always reported.
- Every record is validated before any screen is applied:
- exact scheduled steps and t*, with no duplicates;
- every numeric field finite, including list fields and density;
- error quantities nonnegative and rho_min > 0;
- signed quantities (amplitudes, `amplitude_error`, `phase_drift`,
per-interval rate errors) keep their sign.
- Outcomes are kept distinct:
- INVALID_CONTROL: a control is missing, incomplete or nonfinite, or the
records come from mixed sources;
- CONTROL_SCREEN_FAIL: complete, finite controls that fail a screen.
Refined results are still computed and retained.
- INVALID_CONTROL: a control or refined run is missing, a control is
incomplete or invalid, or the records come from mixed sources;
- INVALID_RUN: a refined record fails validation;
- CONTROL_SCREEN_FAIL: complete, valid controls fail their own screens.
Each refined run's `own_screens_pass` is still reported, but
`controlled_pass` is false, so it is never a controlled-comparison pass;
- PASS/FAIL: every refined run's `controlled_pass` (own screens and
budgets against valid, passing controls).
- Screens are never retuned.
### Limitations
+1 -1
View File
@@ -18,7 +18,7 @@ SOURCE_SHA256 = "a5dbfd804befce868067d79a23e5fd47dce7f505b3f9b1bb2145fb57cb5f217
BUILD_CAPTURE_SHA256 = "3dbaa6547cb0a4cb1541054f3b64ad8b6e18cc70527ac6166e56b944e9bd464a"
REFERENCE_HARNESS = "11901a7f77557a901e017a171eeae1758d584fa5"
INITIALIZATION = "physical FP64 equilibrium G at actual cell centers; rho=1+3p"
SHEAR_WAVE_SHA256 = "49ab589d15d964d38ed90a49758e159c67a516191c7501c19fe2732e19742395"
SHEAR_WAVE_SHA256 = "4597cf1870c1a69b4525c231825a58cf601b3fe22efd2aecf8fca76601c972e8"
def flow_functions(flow="taylor_green"):
+76 -20
View File
@@ -23,8 +23,8 @@ INITIALIZATION = (
# Per-row screens. phase_drift_abs, residual_l2 and the run-level rate screen
# use eps(n) and are preregistered diagnostic choices, not error bounds.
LIMITS = dict(velocity_l2=0.05, spurious_pressure_scaled=0.02, mass_relative=1e-5)
# With equal absolute ceiling and floor (0.02) the comparative pressure budget
# adds no constraint unless the root control exceeds 0.01; raw ratios are kept.
# The comparative pressure budget max(2*control, 0.02) is never below the 0.02
# absolute ceiling, so it is always redundant; raw control ratios are kept.
FLOORS = dict(rate_error=0.002, residual_l2=0.002, spurious_pressure_scaled=0.02)
BASELINE_FACTOR = 2.0
@@ -175,13 +175,51 @@ def fit_rate(rows, length, viscosity):
)
NONNEGATIVE = ("tstar", "mass", "mass_relative", "amplitude_exact", "modal_amplitude",
"phase_drift_abs", "velocity_l2", "residual_l2", "longitudinal_l2",
"spurious_pressure_rms", "spurious_pressure_scaled",
"spurious_pressure_max_scaled", "rho_min", "rho_max", "max_speed")
SIGNED = ("a_hat", "b_hat", "amplitude_ratio", "amplitude_error", "phase_drift")
def _number(value):
return (isinstance(value, (int, float)) and not isinstance(value, bool)
and math.isfinite(value))
def validate_rows(record):
"""Exact schedule, finite numbers (incl. lists) and nonnegative error fields.
Signed quantities (amplitudes, amplitude/phase errors) keep their sign.
Raises ValueError so callers can report an invalid run before any screen.
"""
n, rows = record["n"], record["rows"]
steps = [round(tstar * n / U) for tstar in TIMES]
if not isinstance(rows, list) or len(rows) != len(steps):
raise ValueError("rows do not match the scheduled samples")
for row, step in zip(rows, steps):
if type(row.get("step")) is not int or row["step"] != step:
raise ValueError(f"unscheduled or duplicate step {row.get('step')!r}")
if not _number(row.get("tstar")) or abs(row["tstar"] - step * U / n) > 3e-17:
raise ValueError(f"wrong sample time at step {step}")
if not all(_number(row.get(key)) for key in (*NONNEGATIVE, *SIGNED)):
raise ValueError(f"missing or nonfinite diagnostic at step {step}")
if any(row[key] < 0 for key in NONNEGATIVE) or row["rho_min"] <= 0:
raise ValueError(f"negative error quantity or density at step {step}")
levels = row.get("spurious_pressure_scaled_by_level")
if (not isinstance(levels, list) or len(levels) != record["depth"]
or not all(_number(v) and v >= 0 for v in levels)):
raise ValueError(f"invalid per-level pressure at step {step}")
def summarize(record):
validate_rows(record)
rows = record["rows"]
n = record["n"]
out = fit_rate(rows, n, record["viscosity"])
out.update(
eps=eps(n),
complete=record["completed"] and len(rows) == len(TIMES),
complete=record["completed"] is True,
residual_l2=max(r["residual_l2"] for r in rows),
phase_drift_abs=max(r["phase_drift_abs"] for r in rows),
spurious_pressure_scaled=max(r["spurious_pressure_scaled"] for r in rows),
@@ -190,7 +228,10 @@ def summarize(record):
velocity_l2=max(r["velocity_l2"] for r in rows),
mass_relative=max(r["mass_relative"] for r in rows),
)
out["absolute_pass"] = out["complete"] and all(
if not all(_number(v) for v in (out["rate"], out["rate_error"], *out["interval_rate_errors"],
out["log_fit_residual_max"])):
raise ValueError("nonfinite rate fit")
out["own_screens_pass"] = out["complete"] and all(
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())
return out
@@ -199,36 +240,46 @@ def summarize(record):
def _usable(record):
try:
out = summarize(record)
except (KeyError, ValueError):
except (KeyError, TypeError, ValueError):
return None
finite = all(math.isfinite(v) for v in out.values() if isinstance(v, float))
return out if out["complete"] and finite else None
return out if out["complete"] else None
def assess(base_control, fine_control, refined):
"""Uniform root-spacing and finest-spacing controls versus refined runs.
INVALID_CONTROL: missing, incomplete, nonfinite or mixed-source controls.
CONTROL_SCREEN_FAIL: complete finite controls that fail a screen; refined
results are still computed and kept.
INVALID_CONTROL: missing, incomplete, invalid-row or mixed-source controls.
INVALID_RUN: a refined record has invalid rows (schedule, NaN, sign).
CONTROL_SCREEN_FAIL: complete valid controls that fail their own screens;
refined own-screen results are kept, but no controlled comparison passes.
PASS/FAIL: every refined run's controlled_pass (own screens and budgets).
"""
if base_control is None or fine_control is None:
return dict(status="INVALID_CONTROL", reason="missing control")
if base_control is None or fine_control is None or not refined:
return dict(status="INVALID_CONTROL", reason="missing control or refined run")
n = base_control["n"]
if (base_control["depth"], fine_control["depth"], fine_control["n"]) != (1, 1, 2 * n):
raise ValueError("controls must be uniform N and uniform 2N")
if any(r["depth"] != 2 or r["n"] != n for r in refined):
raise ValueError("refined runs must be depth 2 at root N")
if len({r.get("explosion") for r in refined}) != len(refined):
raise ValueError("duplicate refined explosion scheme")
sources = {repr(r.get("source")) for r in [base_control, fine_control, *refined]}
base, fine = _usable(base_control), _usable(fine_control)
if base is None or fine is None or len(sources) != 1:
return dict(status="INVALID_CONTROL",
reason="incomplete/nonfinite control or mixed source")
report = dict(base_control=base, fine_control=fine, refined={}, status="PASS")
if not (base["absolute_pass"] and fine["absolute_pass"]):
report["status"] = "CONTROL_SCREEN_FAIL"
reason="incomplete/invalid control or mixed source")
controls_pass = base["own_screens_pass"] and fine["own_screens_pass"]
report = dict(base_control=base, fine_control=fine, refined={},
controls_pass=controls_pass)
statuses = []
for r in refined:
s = summarize(r)
try:
s = summarize(r)
except (KeyError, TypeError, ValueError) as error:
report["refined"][r["explosion"]] = dict(valid=False, reason=str(error))
statuses.append("INVALID_RUN")
continue
s["valid"] = True
s["budget"] = {
key: max(BASELINE_FACTOR * base[key], floor) for key, floor in FLOORS.items()
}
@@ -238,8 +289,13 @@ def assess(base_control, fine_control, refined):
for key in (*FLOORS, "spurious_pressure_rms")}
for name, c in (("root", base), ("finest", fine))
}
s["pass"] = s["absolute_pass"] and s["budget_pass"]
s["controlled_pass"] = controls_pass and s["own_screens_pass"] and s["budget_pass"]
report["refined"][r["explosion"]] = s
if report["status"] == "PASS" and not s["pass"]:
report["status"] = "FAIL"
statuses.append("PASS" if s["controlled_pass"] else "FAIL")
if "INVALID_RUN" in statuses:
report["status"] = "INVALID_RUN"
elif not controls_pass:
report["status"] = "CONTROL_SCREEN_FAIL"
else:
report["status"] = "PASS" if all(v == "PASS" for v in statuses) else "FAIL"
return report
@@ -85,11 +85,13 @@ class ConventionTests(unittest.TestCase):
# Central differences do not cancel exactly: each derivative carries
# -(k_j h)^2/6 relative truncation, so div_h = -A cos(theta) h^2/6 sum_j E_j k_j^3
# + O(h^4) (nonzero for an oblique k). Bound it with that leading term plus roundoff.
trunc = U * h * h / 6 * float(np.abs(sw.E * k**3).sum()) * 1.01
a = U * math.exp(-sw.decay_rate(N, NU) * t)
trunc = a * h * h / 6 * float(np.abs(sw.E * k**3).sum()) * 1.01
# Argument rounding: eps*max|x|/h*U*|k| ~ 2.2e-16*21.6/1e-3*0.03*0.44 ~ 6e-14.
roundoff = 1e-13
self.assertLess(np.max(np.abs(dx[0] + dy[1])), trunc + roundoff)
self.assertLess(np.max(np.abs(z[0] * dx + z[1] * dy)), U * trunc + roundoff)
# Discrete advection: same truncation scaled by |u| <= a.
self.assertLess(np.max(np.abs(z[0] * dx + z[1] * dy)), a * trunc + roundoff)
# Halving h reduces the divergence truncation about fourfold.
hh = h / 2
div2 = ((f(x + hh, y, t) - f(x - hh, y, t))[0] + (f(x, y + hh, t) - f(x, y - hh, t))[1]) / (2 * hh)
@@ -206,6 +208,8 @@ class MetricTests(unittest.TestCase):
def record(n, depth, rows, explosion="constant", completed=True, source="s"):
for row in rows:
row["spurious_pressure_scaled_by_level"] = [row["spurious_pressure_scaled"]] * depth
return dict(n=n, depth=depth, viscosity=U * n / sw.RE, rows=rows, completed=completed,
explosion=explosion, source=source)
@@ -215,7 +219,11 @@ def rows_for(n, rate_error=0.0, residual=1e-4, pressure=1e-3, phase=0.0):
out = []
for tstar in TIMES:
step = round(tstar * n / U)
out.append(dict(tstar=tstar, step=step, a_hat=U * math.exp(-rate * step), b_hat=0.0,
amp = U * math.exp(-rate * step)
out.append(dict(tstar=step * U / n, step=step, a_hat=amp, b_hat=0.0,
amplitude_ratio=1.0, modal_amplitude=amp, amplitude_exact=amp,
phase_drift=phase, mass=float(n * n), rho_min=0.999, rho_max=1.001,
max_speed=amp, longitudinal_l2=0.0, spurious_pressure_max_scaled=pressure,
amplitude_error=0.0, residual_l2=residual, phase_drift_abs=phase,
spurious_pressure_scaled=pressure,
spurious_pressure_rms=pressure * U * U / 4,
@@ -245,7 +253,9 @@ class AssessTests(unittest.TestCase):
failing = record(128, 1, rows_for(128, residual=0.5)) # complete, > eps(128)
report = sw.assess(failing, fine, [refined])
self.assertEqual(report["status"], "CONTROL_SCREEN_FAIL")
self.assertIn("constant", report["refined"]) # refined results preserved
own = report["refined"]["constant"]
self.assertTrue(own["own_screens_pass"]) # refined own screens kept
self.assertFalse(own["controlled_pass"]) # but no controlled comparison passes
good = record(128, 1, rows_for(128))
self.assertEqual(sw.assess(None, fine, [refined])["status"], "INVALID_CONTROL")
incomplete = record(128, 1, rows_for(128), completed=False)
@@ -253,6 +263,29 @@ class AssessTests(unittest.TestCase):
mixed = record(256, 1, rows_for(256), source="other")
self.assertEqual(sw.assess(good, mixed, [refined])["status"], "INVALID_CONTROL")
def test_invalid_rows_are_rejected_before_screens(self):
base = record(128, 1, rows_for(128))
fine = record(256, 1, rows_for(256))
for mutate in (
lambda rows: rows[2].update(rho_min=float("nan")),
lambda rows: rows[2].update(spurious_pressure_scaled_by_level=[0.0, float("nan")]),
lambda rows: rows[2].update(step=rows[1]["step"], tstar=rows[1]["tstar"]),
lambda rows: rows[2].update(residual_l2=-1e-6),
lambda rows: rows[2].pop("longitudinal_l2"),
):
refined = record(128, 2, rows_for(128))
mutate(refined["rows"])
self.assertEqual(sw.assess(base, fine, [refined])["status"], "INVALID_RUN")
# Legitimate signs stay allowed: negative amplitude and phase errors.
refined = record(128, 2, rows_for(128))
for row in refined["rows"]:
row.update(amplitude_error=-1e-3, phase_drift=-1e-4, phase_drift_abs=1e-4)
report = sw.assess(base, fine, [refined])
self.assertEqual(report["status"], "PASS")
self.assertEqual(sw.assess(base, fine, [])["status"], "INVALID_CONTROL")
with self.assertRaises(ValueError):
sw.assess(base, fine, [record(128, 2, rows_for(128)), record(128, 2, rows_for(128))])
def test_wrong_shapes(self):
base = record(128, 1, rows_for(128))
fine = record(256, 1, rows_for(256))
@@ -307,7 +340,8 @@ def receipt(n, depth, explosion="constant"):
steps = run_case.sample_steps(n)[0]
rows = rows_for(n)
for row, step in zip(rows, steps):
row.update(step=step, tstar=step * U / n)
row.update(step=step, tstar=step * U / n,
spurious_pressure_scaled_by_level=[row["spurious_pressure_scaled"]] * depth)
cells = [n * n] if depth == 1 else [8704, 30720]
hops = [16 * n * 2**level for level in range(depth)]
builds = []