#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" JOBS="${JOBS:-$(nproc)}" PYTHON_BIN="${PYTHON_BIN:-${ROOT_DIR}/.venv/bin/python}" REPORT="" PASSTHRU=() while [[ $# -gt 0 ]]; do case "$1" in --report) if [[ $# -lt 2 ]]; then echo "--report requires a path" >&2 exit 2 fi REPORT="$2" PASSTHRU+=("$1" "$2") shift 2 ;; --report=*) REPORT="${1#--report=}" PASSTHRU+=("$1") shift ;; --backend|--backend=*) echo "verify_gpu_rans_solver.sh always requests --backend gpu; do not pass --backend" >&2 exit 2 ;; *) PASSTHRU+=("$1") shift ;; esac done if [[ -z "${REPORT}" ]]; then echo "--report is required so the full GPU RANS guard can inspect verifier evidence" >&2 exit 2 fi if [[ ! -x "${PYTHON_BIN}" ]]; then uv sync --dev fi if ! PYTHONPATH= "${PYTHON_BIN}" -c 'import pybind11, numpy, quadrants' >/dev/null 2>&1; then uv sync --dev fi "${ROOT_DIR}/scripts/build_openfoam_airfrans_subset.sh" >/dev/null set +u source "${ROOT_DIR}/OpenFOAM-14/etc/bashrc" \ WM_MPLIB=Dummy \ ParaView_TYPE=none \ SCOTCH_TYPE=none \ ZOLTAN_TYPE=none set -u unset FOAM_SIGFPE "${ROOT_DIR}/scripts/build_python_stepper.sh" >/dev/null set +e PYTHONPATH= "${PYTHON_BIN}" "${ROOT_DIR}/scripts/verify_airfrans_stepper.py" --backend gpu "${PASSTHRU[@]}" VERIFY_STATUS=$? set -e PYTHONPATH= "${PYTHON_BIN}" - "${REPORT}" "${VERIFY_STATUS}" <<'PY' from __future__ import annotations import json import sys from pathlib import Path report_path = Path(sys.argv[1]) verify_status = int(sys.argv[2]) required_fields = ("U", "p", "phi", "nut", "k", "omega") required_modes = ("run_one", "split") failures: list[str] = [] if not report_path.exists(): failures.append(f"report missing: {report_path}") report = {} else: try: report = json.loads(report_path.read_text()) except Exception as exc: # pragma: no cover - shell guard diagnostic failures.append(f"report is not valid JSON: {exc}") report = {} def require(condition: bool, message: str) -> None: if not condition: failures.append(message) backend = report.get("backend") if isinstance(report.get("backend"), dict) else {} verifier_evidence = report.get("verifier_evidence") if isinstance(report.get("verifier_evidence"), dict) else {} modes = report.get("modes") if isinstance(report.get("modes"), dict) else {} provider = str(backend.get("provider") or "").lower() device = str(backend.get("device") or "").lower() require(verify_status == 0, f"underlying AirfRANS verifier exited {verify_status}") require(report.get("status") == "passed", f"report.status is {report.get('status')!r}, expected 'passed'") require(backend.get("requested") == "gpu", f"backend.requested is {backend.get('requested')!r}, expected 'gpu'") require(backend.get("selected") == "gpu", f"backend.selected is {backend.get('selected')!r}, expected 'gpu'") require("cpu" not in provider and provider not in {"foam_stepper_cpu", "openfoam"}, f"backend.provider looks CPU-backed: {backend.get('provider')!r}") require(device not in {"host", "cpu"}, f"backend.device looks CPU-backed: {backend.get('device')!r}") require(backend.get("counts_as_gpu_algorithm_progress") is not False, "backend explicitly says it does not count as GPU progress") require(verifier_evidence.get("passed") is True, "verifier_evidence.passed is not true") for mode_name in required_modes: mode = modes.get(mode_name) if isinstance(modes.get(mode_name), dict) else {} require(mode.get("enabled") is True, f"modes.{mode_name}.enabled is not true") mode_backend = mode.get("backend") if isinstance(mode.get("backend"), dict) else backend require(mode_backend.get("selected") == "gpu", f"modes.{mode_name}.backend.selected is not 'gpu'") execution_path = str(mode.get("execution_path") or "").lower() require("gpu" in execution_path, f"modes.{mode_name}.execution_path does not identify a GPU path: {mode.get('execution_path')!r}") comparisons = mode.get("comparisons") if isinstance(mode.get("comparisons"), dict) else {} for field in required_fields: comparison = comparisons.get(field) if isinstance(comparisons.get(field), dict) else {} require(comparison.get("allclose") is True, f"modes.{mode_name}.comparisons.{field}.allclose is not true") require("actual_shape" in comparison or "shape" in comparison, f"modes.{mode_name}.comparisons.{field} has no actual shape evidence") require("expected_shape" in comparison or "shape" in comparison, f"modes.{mode_name}.comparisons.{field} has no expected shape evidence") require("max_abs" in comparison or "max_abs_error" in comparison, f"modes.{mode_name}.comparisons.{field} has no max error evidence") if failures: print("full GPU RANS solver guard failed:", file=sys.stderr) for failure in failures: print(f"- {failure}", file=sys.stderr) print(f"report={report_path}", file=sys.stderr) sys.exit(1) print("full GPU RANS solver guard passed") print(f"report={report_path}") PY