Files
CelerisLab/tests/validation/taylor_green/run_case.py
T
ClaudeandClaude Opus 5.5 36ffc8b1b2 test: gate V1 side files and V3 legacy receipts; early output rejection
- analyze_jet_cv_continuation: samples are analyzed only if the sibling
  re*_s*.json receipt is the same case with run_status COMPLETE and
  source_recheck UNCHANGED; receipts without run_status (pre-recheck
  runners) are refused unless --legacy-receipts, and labelled
  LEGACY_NO_FINAL_SOURCE_RECHECK. Results carry receipt_gate.
- V3: receipts are schema 2 and need source_recheck UNCHANGED; a missing
  recheck is accepted only for frozen-mode schema 1 receipts whose
  run_case.py hash is one of the pre-recheck runners (9805383, 2cf9853,
  db3d098; hashes checked against git history in a test).
- V1/V3 refuse --output inside the checkout before source checks or CUDA
  (shared source_provenance.reject_output_inside).
- A failing recheck is recorded as RECHECK_FAILED (still invalid); the
  in-run V1 receipt write is tmp+replace.
Negative host tests for each gate. No threshold, solver or evidence edits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:13:59 +00:00

354 lines
15 KiB
Python

"""One bounded GPU case, with explicit opt-in experimental explosion."""
import argparse
import hashlib
import json
import os
import shutil
from pathlib import Path
import subprocess
import sys
import time
import numpy as np
from oracle import U, TIMES, LIMITS, initial, metrics, rectangles
NUMERICAL_REVISION = "a135252ae78b47b57d7e77d19001700b3bedac8b"
SOURCE_SHA256 = "a5dbfd804befce868067d79a23e5fd47dce7f505b3f9b1bb2145fb57cb5f2178"
BUILD_CAPTURE_SHA256 = "3dbaa6547cb0a4cb1541054f3b64ad8b6e18cc70527ac6166e56b944e9bd464a"
REFERENCE_HARNESS = "11901a7f77557a901e017a171eeae1758d584fa5"
INITIALIZATION = "physical FP64 equilibrium G at actual cell centers; rho=1+3p"
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]
early = list(range(0, 4 * n + 1, n // 16)) if early_pressure else []
return main, early
def runtime_source(repo):
"""Fail before CUDA if Python or the compiler snapshot comes from another tree."""
import CelerisLab
from CelerisLab.cuda import compiler_v2
paths = dict(module=Path(CelerisLab.__file__).resolve(),
compiler=Path(compiler_v2.__file__).resolve(),
kernel_root=Path(compiler_v2.kernel_path("blocks.cu")).resolve().parent)
package = repo / "src/CelerisLab"
expected = dict(module=package / "__init__.py", compiler=package / "cuda/compiler_v2.py",
kernel_root=package / "lbm/kernels")
if paths != expected:
raise RuntimeError("Wrong imported source or CUDA source/include root")
return dict(checkout=str(repo), **{key: str(path) for key, path in paths.items()})
def provenance(repo):
git = lambda *a: subprocess.check_output(
["git", "-C", str(repo), *a], text=True
).strip()
source_hash = hashlib.sha256()
for path in sorted((repo / "src").rglob("*")):
if path.is_file() and "__pycache__" not in path.parts and path.suffix != ".pyc":
source_hash.update(
str(path.relative_to(repo)).encode() + b"\0" + path.read_bytes() + b"\0"
)
return dict(
revision=git("rev-parse", "HEAD"),
dirty=bool(git("status", "--porcelain")),
source_tree_sha256=source_hash.hexdigest(),
harness_sha256={
p.name: hashlib.sha256(p.read_bytes()).hexdigest()
for p in Path(__file__).parent.glob("*.py")
},
)
CURRENT_CAPTURE = "current checkout, not the frozen a135252 qualification"
def verify_source(repo, revision, current=False):
"""Exact clean harness commit, fixed production tree, and reviewed capture helper.
current=True records the clean checkout itself instead of requiring the frozen
numerical tree; such receipts carry source_mode="current_checkout" and are only
assessed with assess(..., current=True), never as the frozen qualification.
"""
import oracle
from tests.validation.multigrid import check_projected_pairs as gate
harness = repo / "tests/validation/taylor_green"
if (Path(__file__).resolve() != harness / "run_case.py"
or Path(oracle.__file__).resolve() != harness / "oracle.py"):
raise RuntimeError("Wrong runner or oracle checkout")
helper = repo / "tests/validation/multigrid/check_projected_pairs.py"
if (Path(gate.__file__).resolve() != helper
or hashlib.sha256(helper.read_bytes()).hexdigest() != BUILD_CAPTURE_SHA256):
raise RuntimeError("Wrong build-capture helper")
if current:
result = provenance(repo)
if result["revision"] != revision or result["dirty"]:
raise ValueError("requires the exact expected clean checkout")
result.update(source_mode="current_checkout", source_capture=CURRENT_CAPTURE,
build_capture_sha256=BUILD_CAPTURE_SHA256)
return result
if subprocess.run(["git", "-C", str(repo), "cat-file", "-e", f"{NUMERICAL_REVISION}^{{commit}}"],
capture_output=True).returncode:
raise RuntimeError(f"frozen numerical revision {NUMERICAL_REVISION} is not available "
"in this clone; the frozen qualification cannot be checked")
if gate.preflight(repo, revision, "candidate") != NUMERICAL_REVISION:
raise RuntimeError("Wrong production revision")
result = provenance(repo)
if result["source_tree_sha256"] != SOURCE_SHA256:
raise RuntimeError("Wrong numerical source fingerprint")
result["build_capture_sha256"] = BUILD_CAPTURE_SHA256
return result
def build_capture(report, save):
from tests.validation.multigrid.check_projected_pairs import capture_builds
return capture_builds(report, save)
def runtime_identity(ctx):
import pycuda
import pycuda.driver as cuda
nvcc = shutil.which("nvcc") or "/usr/local/cuda/bin/nvcc"
return dict(device=dict(name=ctx.device.name(), pci_bus_id=ctx.device.pci_bus_id(),
capability=list(ctx.device.compute_capability()),
driver=cuda.get_driver_version(), runtime=list(cuda.get_version())),
nvcc_path=str(Path(nvcc).resolve()),
nvcc_version=subprocess.check_output([nvcc, "--version"], text=True).strip(),
python=sys.version, numpy=np.__version__, pycuda=pycuda.VERSION_TEXT)
def config(n, re):
from CelerisLab.config import LBMConfig
cfg = LBMConfig(
nx=n,
ny=n,
viscosity=U * n / re,
velocity=0.0,
rho=1.0,
lattice_model="D2Q9",
collision="SRT",
streaming="double_buffer",
store_precision="FP32",
ddf_shifting=True,
les_enabled=False,
)
cfg.validate()
return cfg
def workload(masks, base_steps):
cells = [int(np.count_nonzero(m)) for m in masks]
hops = [base_steps * 2**level for level in range(len(masks))]
updates = [count * hop for count, hop in zip(cells, hops)]
return dict(
active_cells_by_level=cells,
level_hops=hops,
active_collision_updates_by_level=updates,
active_collision_updates=sum(updates),
root_intervals=base_steps,
excludes="ghost streaming, halo copies, transfers, initialization and diagnostics",
)
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--repo", type=Path, required=True)
p.add_argument("--expected-revision", required=True)
p.add_argument("--run-gpu", action="store_true")
p.add_argument("--dry-run", action="store_true")
p.add_argument("--re", type=int, choices=[100, 1000], required=True)
p.add_argument("--n", type=int, choices=[32, 64, 128, 256], required=True)
p.add_argument("--depth", type=int, choices=[1, 2, 3], required=True)
p.add_argument("--output", type=Path, required=True)
p.add_argument("--explosion", choices=["constant", "projected_linear"], default="constant")
p.add_argument("--current-checkout", action="store_true",
help="record the clean current checkout instead of the frozen a135252 tree")
p.add_argument("--early-pressure", action="store_true",
help="also save 65 scalar pressure samples at t*=k*.001875, k=0..64")
args = p.parse_args()
if args.output.exists():
p.error("Output already exists; choose a new path to retain prior evidence")
if args.explosion != "constant" and args.depth != 2:
p.error("projected_linear requires depth 2")
selected = os.environ.get("CUDA_VISIBLE_DEVICES", "")
if not args.dry_run and (not args.run_gpu or not selected or "," in selected or selected == "-1"):
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)]
from tests.validation.source_provenance import reject_output_inside
reject_output_inside(args.output, repo)
source = verify_source(repo, args.expected_revision, current=args.current_checkout)
runtime = runtime_source(repo)
if args.dry_run:
print(json.dumps(dict(source=source, runtime_source=runtime, dry_run=True)))
return 0
from CelerisLab.cuda.context import CudaContext
from CelerisLab.lbm.blocks import BlockLayout2D
from CelerisLab.lbm.block_stepper import PeriodicBlockStepper
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
from CelerisLab.lbm.refinement import ACTIVE
from CelerisLab.lbm.hierarchy import build_hierarchy, leaf_partition
cfg = config(args.n, args.re)
refinements = [] if args.depth == 1 else rectangles(args.n, args.depth)
h = build_hierarchy(cfg, refinements)
np.testing.assert_array_equal(leaf_partition(h), np.ones((args.n, args.n)))
masks = [k == ACTIVE for k in h.kinds]
main_steps, early_steps = sample_steps(args.n, args.early_pressure)
report = dict(
schema=2, # 2: final source recheck (source_recheck) is recorded
source=source,
numerical_revision=None if args.current_checkout else NUMERICAL_REVISION,
**({"source_mode": "current_checkout"} if args.current_checkout else {}),
reference_harness=REFERENCE_HARNESS,
actual_build_sidecars=[],
runtime_identity=None,
runtime_source=runtime,
historical_baseline_revision="9805383f9c50d867e8555471baf5dbda8333a652",
explosion=args.explosion,
n=args.n,
re_L=args.re,
depth=args.depth,
refinements=refinements,
U=U,
mach=U * np.sqrt(3),
viscosity=cfg.viscosity,
tau_by_level=[0.5 + 3 * c.viscosity for c in h.configs],
initialization=INITIALIZATION,
numerics=dict(lattice_model=cfg.lattice_model, collision=cfg.collision,
store_precision=cfg.store_precision, ddf_shifting=cfg.ddf_shifting,
streaming=cfg.streaming, les_enabled=cfg.les_enabled,
boundary="periodic", block_shape=[8, 8],
sample_phase="postcollision G at root synchronization"),
cuda_visible_devices=selected,
device_id=0,
limits=LIMITS,
workload=workload(masks, round(TIMES[-1] * args.n / U)),
rows=[],
early_pressure=dict(enabled=args.early_pressure, rows=[],
sampled_max_pressure_l2=None),
completed=False,
)
args.output.parent.mkdir(parents=True, exist_ok=True)
def save():
temporary = args.output.with_suffix(args.output.suffix + ".tmp")
text = json.dumps(report, indent=2, allow_nan=False) + "\n"
if len(text.encode()) > 1024 * 1024:
raise RuntimeError("Scalar receipt exceeds 1 MiB cap")
temporary.write_text(text)
temporary.replace(args.output)
save()
started = time.monotonic()
with CudaContext(0) as ctx, build_capture(report, save):
report["runtime_identity"] = runtime_identity(ctx)
save()
if args.depth == 1:
layout = BlockLayout2D.build(args.n, args.n, 8, 8, periodic=(True, True))
run = PeriodicBlockStepper(cfg, layout, context=ctx)
else:
run = FixedPatchStepper(
cfg,
context=ctx,
coarse_boundary="periodic",
refinements=refinements,
level_blocks=[(8, 8)] * args.depth,
explosion=args.explosion,
)
with run:
run.initialize(
*[
initial(m.shape, level, args.n, cfg.viscosity)
for level, m in enumerate(masks)
]
)
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["step"] = step
if initial_mass is None:
initial_mass = row["mass"]
if step in main_steps:
report["rows"].append(row)
if step in early_steps:
trace = report["early_pressure"]
trace["rows"].append({key: row[key] for key in ("step", "tstar", "pressure_l2")})
trace["sampled_max_pressure_l2"] = max(r["pressure_l2"] for r in trace["rows"])
save()
print(
json.dumps(
dict(
event="PROGRESS" if step in main_steps else "EARLY_PRESSURE",
n=args.n,
re_L=args.re,
depth=args.depth,
**(row if step in main_steps else report["early_pressure"]["rows"][-1]),
)
),
flush=True,
)
if step == 0:
# 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")
report["ready_after_step"] = 1
save()
print(
json.dumps(
dict(
event="READY",
step=1,
n=args.n,
re_L=args.re,
depth=args.depth,
workload=report["workload"],
)
),
flush=True,
)
from tests.validation.source_provenance import final_source_check
recheck = final_source_check(
dict(source=source, runtime_source=runtime),
lambda: dict(source=verify_source(repo, args.expected_revision,
current=args.current_checkout),
runtime_source=runtime_source(repo)))
report.update(recheck)
if recheck["source_recheck"] != "UNCHANGED":
report.update(invalid="SOURCE_CHANGED_DURING_RUN") # completed stays False
save()
raise RuntimeError("Source changed during the run; receipt marked invalid")
report.update(
completed=True,
seconds=time.monotonic() - started,
screening_pass=all(
row[key] <= limit for row in report["rows"] for key, limit in LIMITS.items()
),
)
save()
print(
json.dumps(
{k: report[k] for k in ("n", "re_L", "depth", "screening_pass", "seconds")}
),
flush=True,
)
return 0 if report["screening_pass"] else 1
if __name__ == "__main__":
raise SystemExit(main())