test: fix shear-wave review findings (runner finiteness, source pin, contract)

- run_case: finite_row accepts list diagnostics; sample() shared by sample
  and READY paths; verify_source pins shear_wave.py path+SHA256 for
  --flow shear_wave (default TG flow unchanged, does not import it).
- assess_shear_wave.py: fixed N128 four-case contract and 4-JSON CLI,
  reusing assess_projected_pairs provenance/build/workload/time checks plus
  shear metadata; missing/duplicate/mixed/extra records -> INVALID_SET.
- tests: mocked refined runner sample (list diagnostics, NaN, TG unchanged),
  contract pass + reproduced false-pass mutations, divergence check uses
  analytic E.k=0 and a derived h^2 truncation + roundoff bound with
  h-halving scaling instead of a fixed 1e-12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-19 00:26:58 +00:00
1 parent 10069feadb
commit 3554cb2fde
4 files changed
+262 -12

No files matched your search

@@ -0,0 +1,81 @@
"""Fixed N128 four-case shear-wave contract: U128, U256, constant R128, projected R128.
Shared provenance, build, workload and sample-time checks are reused from
assess_projected_pairs (same cases, source pins and geometry); only the
Taylor-Green-specific checks are replaced by shear-wave metadata and physics.
"""
import argparse
import json
from pathlib import Path
import assess_projected_pairs as tg
import shear_wave as sw
from run_case import SHEAR_WAVE_SHA256
SHARED = ("complete_matrix", "no_extra_cases", "no_duplicate_cases", "finite_receipts",
"same_source_and_harness", "clean_a135252_numerical_source",
"same_recorded_runtime", "same_explicit_device", "same_early_sampling")
PER_CASE = ("complete_times", "scales", "actual_builds", "reference", "runtime_source",
"patch", "workload")
def metadata_ok(record, n):
return (record.get("flow") == "shear_wave" and record.get("mode") == list(sw.MODE)
and record.get("polarization") == sw.E.tolist()
and record.get("angle_deg") == sw.ANGLE_DEG and record.get("phase") == sw.PHI
and record.get("eps") == sw.eps(n)
and record.get("numerics") == tg.NUMERICS
and record.get("initialization") == sw.INITIALIZATION
and record.get("limits") == sw.LIMITS
and record.get("early_pressure") == dict(enabled=False, rows=[],
sampled_max_pressure_l2=None)
and (record.get("source") or {}).get("shear_wave_sha256") == SHEAR_WAVE_SHA256)
def assess(records, expected_revision=None):
if not isinstance(records, list) or not all(isinstance(r, dict) for r in records):
raise ValueError("records must be a list of receipt objects")
base_checks = tg.assess(records, expected_revision)["checks"]
checks = {name: base_checks.get(name, False) for name in SHARED}
indexed = {}
for r in records:
indexed.setdefault(tuple(r.get(k) for k in ("re_L", "n", "depth", "explosion")), r)
for key in tg.CASES:
_, n, depth, scheme = key
label = f"{n}/{depth}/{scheme}"
for name in PER_CASE:
checks[f"{label} {name}"] = base_checks.get(f"{label} {name}", False)
checks[f"{label} shear_metadata"] = key in indexed and metadata_ok(indexed[key], n)
contract_valid = all(checks.values())
result = dict(checks=checks, contract_valid=contract_valid,
cases=[list(k) for k in tg.CASES], expected_revision=expected_revision,
scope="2D periodic Re100 oblique (2,1) shear wave; one phase and one "
"rectangle; no placement robustness or convergence order")
if not contract_valid:
result.update(status="INVALID_SET", passed=False)
return result
try:
physics = sw.assess(indexed[tg.CASES[0]], indexed[tg.CASES[1]],
[indexed[tg.CASES[2]], indexed[tg.CASES[3]]])
except (KeyError, ValueError) as error:
result.update(status="INVALID_SET", passed=False, reason=str(error))
return result
result.update(physics=physics, status=physics["status"],
passed=physics["status"] == "PASS")
return result
if __name__ == "__main__":
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("files", type=Path, nargs=4)
p.add_argument("--output", type=Path, required=True)
p.add_argument("--expected-revision", required=True)
args = p.parse_args()
if args.output.exists():
p.error("Output already exists; choose a new path to retain prior evidence")
result = assess([json.loads(path.read_text()) for path in args.files], args.expected_revision)
text = json.dumps(result, indent=2, allow_nan=False) + "\n"
args.output.write_text(text)
print(text, end="")
raise SystemExit(0 if result["passed"] else 1)
+27 -10
View File
@@ -18,6 +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"
def flow_functions(flow="taylor_green"):
@@ -30,6 +31,19 @@ def flow_functions(flow="taylor_green"):
raise ValueError(f"unknown flow {flow!r}")
def finite_row(row):
"""Every scalar or list-valued diagnostic is finite (TG rows are all scalars)."""
return all(np.isfinite(np.asarray(value, dtype=float)).all() for value in row.values())
def sample(run, masks, step, n, viscosity, initial_mass, metrics, depth, label="sample"):
arrays = (run.download(),) if depth == 1 else run.download()
row = metrics(arrays, masks, step, n, viscosity, initial_mass)
if not finite_row(row):
raise FloatingPointError(f"nonfinite {label} diagnostics")
return row
def sample_steps(n, early_pressure=False):
"""Root synchronization steps; early ladder matches the 707afe6 diagnostic."""
main = [round(tstar * n / U) for tstar in TIMES]
@@ -74,7 +88,7 @@ def provenance(repo):
)
def verify_source(repo, revision):
def verify_source(repo, revision, flow="taylor_green"):
"""Exact clean harness commit, fixed production tree, and reviewed capture helper."""
import oracle
from tests.validation.multigrid import check_projected_pairs as gate
@@ -93,6 +107,13 @@ def verify_source(repo, revision):
if result["source_tree_sha256"] != SOURCE_SHA256:
raise RuntimeError("Wrong numerical source fingerprint")
result["build_capture_sha256"] = BUILD_CAPTURE_SHA256
if flow == "shear_wave":
import shear_wave
module = harness / "shear_wave.py"
if (Path(shear_wave.__file__).resolve() != module
or hashlib.sha256(module.read_bytes()).hexdigest() != SHEAR_WAVE_SHA256):
raise RuntimeError("Wrong shear_wave module checkout or content")
result["shear_wave_sha256"] = SHEAR_WAVE_SHA256
return result
@@ -175,7 +196,7 @@ def main():
p.error("--run-gpu and exactly one explicit CUDA_VISIBLE_DEVICES are required")
repo = args.repo.resolve()
sys.path[:0] = [str(repo / "src"), str(repo)]
source = verify_source(repo, args.expected_revision)
source = verify_source(repo, args.expected_revision, args.flow)
runtime = runtime_source(repo)
if args.dry_run:
print(json.dumps(dict(source=source, runtime_source=runtime, dry_run=True)))
@@ -267,10 +288,8 @@ def main():
initial_mass = None
for step in sorted(set(main_steps + early_steps)):
run.step(step - run.step_count)
arrays = (run.download(),) if args.depth == 1 else run.download()
row = metrics(arrays, masks, step, args.n, cfg.viscosity, initial_mass)
if not all(np.isfinite(value) for value in row.values()):
raise FloatingPointError("nonfinite sample diagnostics")
row = sample(run, masks, step, args.n, cfg.viscosity, initial_mass,
metrics, args.depth)
row["step"] = step
if initial_mass is None:
initial_mass = row["mass"]
@@ -297,10 +316,8 @@ def main():
# Exercise real kernels/halos/transfers on every level before READY.
# This is the first measured trajectory step, not discarded warmup.
run.step(1)
probe = (run.download(),) if args.depth == 1 else run.download()
probe_metrics = metrics(probe, masks, 1, args.n, cfg.viscosity, initial_mass)
if not all(np.isfinite(value) for value in probe_metrics.values()):
raise FloatingPointError("nonfinite READY diagnostics")
sample(run, masks, 1, args.n, cfg.viscosity, initial_mass,
metrics, args.depth, "READY")
report["ready_after_step"] = 1
save()
print(
@@ -79,8 +79,21 @@ class ConventionTests(unittest.TestCase):
dy = (f(x, y + h, t) - f(x, y - h, t)) / (2 * h)
lap = (f(x + h, y, t) + f(x - h, y, t) + f(x, y + h, t) + f(x, y - h, t) - 4 * z) / h**2
np.testing.assert_allclose(dt, NU * lap, atol=1e-9)
np.testing.assert_allclose(z[0] * dx + z[1] * dy, 0.0, atol=1e-12)
np.testing.assert_allclose(dx[0] + dy[1], 0.0, atol=1e-12)
# Analytic identities: div u = A cos(theta) (E.k) and (u.grad)u = A sin(theta) (E.k) u/A.
k = sw.wavevector(N)
self.assertLess(abs(float(sw.E @ k)), 1e-17)
# 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
# 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)
# 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)
np.testing.assert_allclose(div2 * 4, dx[0] + dy[1], rtol=1e-2, atol=6 * roundoff)
self.assertTrue(np.all(sw.exact(x, y, t, N, NU)[2] == 0))
@@ -256,6 +269,124 @@ class AssessTests(unittest.TestCase):
self.assertEqual(sw.assess(base, fine, [refined])["status"], "FAIL")
class FakeRun:
def __init__(self, arrays):
self.arrays = arrays
def download(self):
return self.arrays if len(self.arrays) > 1 else self.arrays[0]
class RunnerSampleTests(unittest.TestCase):
def test_refined_shear_sample_with_list_diagnostics(self):
masks = composite_masks(N)
arrays = [sw.initial(m.shape, level, N, NU) for level, m in enumerate(masks)]
row = run_case.sample(FakeRun(arrays), masks, 0, N, NU, None, sw.metrics, 2)
self.assertIsInstance(row["spurious_pressure_scaled_by_level"], list)
self.assertTrue(run_case.finite_row(row))
bad = [a.copy() for a in arrays]
bad[1][0, masks[1]] = np.nan
with self.assertRaises(FloatingPointError):
run_case.sample(FakeRun(bad), masks, 0, N, NU, None, sw.metrics, 2, "READY")
self.assertFalse(run_case.finite_row(dict(a=1.0, b=[0.0, float("inf")])))
def test_taylor_green_sample_unchanged(self):
masks = composite_masks(N)
arrays = [oracle.initial(m.shape, level, N, NU) for level, m in enumerate(masks)]
row = run_case.sample(FakeRun(arrays), masks, 0, N, NU, None, oracle.metrics, 2)
self.assertEqual(row, oracle.metrics(arrays, masks, 0, N, NU, None))
uniform = [oracle.initial((N, N), 0, N, NU)]
row = run_case.sample(FakeRun(uniform), [np.ones((N, N), bool)], 0, N, NU, None,
oracle.metrics, 1)
self.assertEqual(row["leaf_cells"], N * N)
def receipt(n, depth, explosion="constant"):
"""Synthetic receipt satisfying every contract field of the fixed four-case set."""
import assess_projected_pairs as tg
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)
cells = [n * n] if depth == 1 else [8704, 30720]
hops = [16 * n * 2**level for level in range(depth)]
builds = []
for level in range(depth):
size = n * 2**level
builds.append(dict(source_sha256=tg.BUILD_SOURCES[size], arch="sm_70",
flags=["--use_fast_math"], n_objects=0, ptx_sha256="a" * 64,
config=dict(nx=10, ny=10, nz=1, lattice_model="D2Q9", collision="SRT",
store_precision="FP32", streaming="double_buffer",
ddf_shifting=True, les_enabled=False, velocity=0.0,
viscosity=U * size / 100, rho=1.0,
omega=1 / (.5 + 3 * U * size / 100),
threads_per_block=256)))
checkout = "/x/CelerisLab"
return dict(
n=n, depth=depth, re_L=100, explosion=explosion, U=U, mach=U * math.sqrt(3),
viscosity=U * n / 100, tau_by_level=[0.5 + 3 * U * n / 100 * 2**l for l in range(depth)],
source=dict(revision="r", dirty=False, source_tree_sha256=run_case.SOURCE_SHA256,
harness_sha256={"x.py": "h"}, build_capture_sha256=run_case.BUILD_CAPTURE_SHA256,
shear_wave_sha256=run_case.SHEAR_WAVE_SHA256),
runtime_identity=dict(device=dict(name="V100", pci_bus_id="0", capability=[7, 0],
driver=1, runtime=[12, 0]),
nvcc_path="/nvcc", nvcc_version="v", python="3", numpy="1",
pycuda="2"),
runtime_source=dict(checkout=checkout, module=checkout + "/src/CelerisLab/__init__.py",
compiler=checkout + "/src/CelerisLab/cuda/compiler_v2.py",
kernel_root=checkout + "/src/CelerisLab/lbm/kernels"),
cuda_visible_devices="0", numerical_revision=run_case.NUMERICAL_REVISION,
reference_harness=run_case.REFERENCE_HARNESS, numerics=dict(tg.NUMERICS),
initialization=sw.INITIALIZATION, limits=dict(sw.LIMITS),
early_pressure=dict(enabled=False, rows=[], sampled_max_pressure_l2=None),
refinements=[] if depth == 1 else rectangles(n, depth),
workload=dict(active_cells_by_level=cells, level_hops=hops, root_intervals=16 * n,
active_collision_updates_by_level=[c * h for c, h in zip(cells, hops)],
active_collision_updates=sum(c * h for c, h in zip(cells, hops))),
actual_build_sidecars=builds, rows=rows, completed=True,
flow="shear_wave", mode=list(sw.MODE), polarization=sw.E.tolist(),
angle_deg=sw.ANGLE_DEG, phase=sw.PHI, eps=sw.eps(n))
class ContractTests(unittest.TestCase):
def setUp(self):
import assess_shear_wave
self.assess = assess_shear_wave.assess
self.records = [receipt(128, 1), receipt(256, 1), receipt(128, 2),
receipt(128, 2, "projected_linear")]
def test_complete_contract_passes(self):
result = self.assess(self.records, "r")
self.assertTrue(result["contract_valid"], [k for k, v in result["checks"].items() if not v])
self.assertEqual(result["status"], "PASS")
def test_reproduced_false_passes_are_rejected(self):
a = self.assess
self.assertEqual(a(self.records[:2], "r")["status"], "INVALID_SET") # empty refined
dup = [*self.records[:3], receipt(128, 2)]
self.assertEqual(a(dup, "r")["status"], "INVALID_SET") # duplicate constant-only
for mutate in (
lambda r: r.pop("source"),
lambda r: r.update(flow="taylor_green"),
lambda r: r.update(mode=[1, 2]),
lambda r: r.update(re_L=1000),
lambda r: r.update(refinements=[[(0, 0, 8, 8)]]),
lambda r: r["actual_build_sidecars"][0].update(arch="sm_80"),
lambda r: r["rows"][0].update(step=1, tstar=U / 128), # wrong initial time
lambda r: r["source"].update(shear_wave_sha256="0" * 64),
):
records = [dict(r, rows=[dict(x) for x in r["rows"]],
source=dict(r["source"]),
actual_build_sidecars=[dict(b) for b in r["actual_build_sidecars"]])
for r in self.records]
mutate(records[2])
self.assertEqual(a(records, "r")["status"], "INVALID_SET")
mixed = [dict(r) for r in self.records]
mixed[1] = dict(mixed[1], source=dict(mixed[1]["source"], revision="other"))
self.assertEqual(a(mixed, "r")["status"], "INVALID_SET")
self.assertEqual(a(self.records, "wrong-revision")["status"], "INVALID_SET")
class DefaultPathTests(unittest.TestCase):
def test_frozen_oracle_is_byte_identical(self):
digest = hashlib.sha256((HERE / "oracle.py").read_bytes()).hexdigest()