test: apply shear-wave review corrections

Modal magnitude R=hypot(a,b) with amplitude error and separate phase;
free-intercept ln R rate fit with per-interval rates, log-fit residual,
t* units and finite-positive check; spurious-pressure naming with raw rms;
INVALID_CONTROL vs CONTROL_SCREEN_FAIL (refined results kept), mixed-source
check, raw ratios to both controls; docs narrow the cubic-term scope,
startup and placement limitations. oracle.py still byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-19 00:18:20 +00:00
1 parent ffff742b69
commit 10069feadb
3 files changed
+197 -101

No files matched your search

+66 -18
View File
@@ -22,8 +22,10 @@ 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, pressure_rel=0.02, mass_relative=1e-5)
FLOORS = dict(rate_error=0.002, residual_l2=0.002, pressure_rel=0.02)
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.
FLOORS = dict(rate_error=0.002, residual_l2=0.002, spurious_pressure_scaled=0.02)
BASELINE_FACTOR = 2.0
@@ -103,7 +105,8 @@ def metrics(arrays, masks, t, length, viscosity, initial_mass=None):
fit = a_hat * s + b_hat * c
longitudinal = (ux * k[0] + uy * k[1]) / math.sqrt(k @ k)
amplitude = U * math.exp(-decay_rate(length, viscosity) * t)
scale = amplitude * amplitude / 4 # fixed pressure scale, see docs
scale = amplitude * amplitude / 4 # continuum reference scale, not the TG norm
modal = math.hypot(a_hat, b_hat)
p = (rho - 1) / 3
p = p - w @ p / volume # one global gauge
mass = float(w @ rho)
@@ -117,6 +120,8 @@ def metrics(arrays, masks, t, length, viscosity, initial_mass=None):
b_hat=float(b_hat),
amplitude_exact=amplitude,
amplitude_ratio=float(a_hat / amplitude),
modal_amplitude=modal,
amplitude_error=modal / amplitude - 1,
phase_drift=float(math.atan2(b_hat, a_hat)),
phase_drift_abs=abs(float(math.atan2(b_hat, a_hat))),
velocity_l2=float(
@@ -126,9 +131,10 @@ def metrics(arrays, masks, t, length, viscosity, initial_mass=None):
np.sqrt(w @ ((ux - fit * E[0]) ** 2 + (uy - fit * E[1]) ** 2) / exact_norm)
),
longitudinal_l2=float(np.sqrt(w @ (longitudinal**2) / exact_norm)),
pressure_rel=float(np.sqrt(w @ (p * p) / volume) / scale),
pressure_max_rel=float(np.max(np.abs(p)) / scale),
pressure_rel_by_level=[
spurious_pressure_rms=float(np.sqrt(w @ (p * p) / volume)),
spurious_pressure_scaled=float(np.sqrt(w @ (p * p) / volume) / scale),
spurious_pressure_max_scaled=float(np.max(np.abs(p)) / scale),
spurious_pressure_scaled_by_level=[
float(np.sqrt(w[lev == i] @ (p[lev == i] ** 2) / w[lev == i].sum()) / scale)
for i in range(len(masks))
],
@@ -140,15 +146,33 @@ def metrics(arrays, masks, t, length, viscosity, initial_mass=None):
def fit_rate(rows, length, viscosity):
"""Least-squares decay rate of ln(a_hat) over late samples."""
"""Free-intercept least squares of ln R, R = hypot(a_hat, b_hat), late samples.
The intercept absorbs only a constant amplitude prefactor once a single
exponential dominates; per-interval rates and the fit residual are kept so
a lingering transient stays visible. Rates are per root step of the record
(uniform 2N uses its own nu and doubled step counts).
"""
late = [r for r in rows if r["tstar"] >= FIT_FROM_TSTAR - 1e-12]
if len(late) < 2 or any(r["a_hat"] <= 0 for r in late):
raise ValueError("need >= 2 late samples with positive amplitude")
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()):
raise ValueError("need >= 3 late samples with finite positive R")
t = np.array([r["step"] for r in late], dtype=float)
slope = np.polyfit(t, np.log([r["a_hat"] for r in late]), 1)[0]
(slope, intercept) = np.polyfit(t, np.log(amp), 1)
residual = np.log(amp) - (slope * t + intercept)
exact_rate = decay_rate(length, viscosity)
return dict(rate=float(-slope), exact_rate=exact_rate,
rate_error=abs(-slope / exact_rate - 1))
to_tstar = length / U # root steps per unit t*
return dict(
rate=float(-slope), exact_rate=exact_rate,
rate_tstar=float(-slope * to_tstar),
exact_rate_tstar=exact_rate * to_tstar, # = 5 (2 pi)^2 / Re
rate_error=abs(-slope / exact_rate - 1),
interval_rate_errors=[
float(-np.log(amp[i + 1] / amp[i]) / (t[i + 1] - t[i]) / exact_rate - 1)
for i in range(len(t) - 1)
],
log_fit_residual_max=float(np.max(np.abs(residual))),
)
def summarize(record):
@@ -160,7 +184,9 @@ def summarize(record):
complete=record["completed"] and len(rows) == len(TIMES),
residual_l2=max(r["residual_l2"] for r in rows),
phase_drift_abs=max(r["phase_drift_abs"] for r in rows),
pressure_rel=max(r["pressure_rel"] for r in rows),
spurious_pressure_scaled=max(r["spurious_pressure_scaled"] for r in rows),
spurious_pressure_rms=max(r["spurious_pressure_rms"] for r in rows),
amplitude_error_abs=max(abs(r["amplitude_error"]) for r in rows),
velocity_l2=max(r["velocity_l2"] for r in rows),
mass_relative=max(r["mass_relative"] for r in rows),
)
@@ -170,25 +196,47 @@ def summarize(record):
return out
def _usable(record):
try:
out = summarize(record)
except (KeyError, 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
def assess(base_control, fine_control, refined):
"""Uniform root-spacing and finest-spacing controls versus refined runs."""
"""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.
"""
if base_control is None or fine_control is None:
return dict(status="INVALID_CONTROL", reason="missing control")
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")
base, fine = summarize(base_control), summarize(fine_control)
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"] = "INVALID_CONTROL"
report["status"] = "CONTROL_SCREEN_FAIL"
for r in refined:
s = summarize(r)
s["budget"] = {
key: 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["fine_control_ratio"] = {
key: (s[key] / fine[key] if fine[key] > 0 else None) for key in FLOORS
s["control_ratios"] = {
name: {key: (s[key] / c[key] if c[key] > 0 else None)
for key in (*FLOORS, "spurious_pressure_rms")}
for name, c in (("root", base), ("finest", fine))
}
s["pass"] = s["absolute_pass"] and s["budget_pass"]
report["refined"][r["explosion"]] = s