Files
CelerisLab/tests/validation/multigrid/channel_parity_diagnostic/discover.py
T

105 lines
5.3 KiB
Python

"""Discover the first failure of the unchanged matched-root GPU test, then replay.
This script never runs the body/Jet test, changes the solver, or changes its
assertions. GPU admission remains the caller's responsibility.
"""
import argparse
import hashlib
import importlib.util
import json
from pathlib import Path
import subprocess
import sys
import traceback
def capture_case(exc):
"""Extract the original test frame; no inferred profile/block/configuration."""
frames = []
tb = exc.__traceback__
while tb is not None:
frames.append(tb.tb_frame)
tb = tb.tb_next
caller = next((f for f in frames if f.f_code.co_name == 'test_uniform_matched_root'), None)
if caller is None or not any(f.f_code.co_name == 'assert_array_equal' for f in frames):
return None
local = caller.f_locals
required = ('cfg', 'shifted', 'profile', 'clamp', 'damping', 'block', 'expected', 'actual')
if not all(key in local for key in required):
return None
cfg = local['cfg']
return dict(shifted=int(local['shifted']), profile=local['profile'],
clamp=int(local['clamp']), damping=float(local['damping']),
block=[int(v) for v in local['block']], steps=8,
config_repr=repr(cfg)), local['expected'].copy(), local['actual'].copy()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--checkout', type=Path, default=Path.cwd())
parser.add_argument('--output', type=Path, required=True, help='New directory; existing evidence is never overwritten')
args = parser.parse_args()
checkout, output = args.checkout.resolve(), args.output.resolve()
output.mkdir(parents=True, exist_ok=False)
import os
os.chdir(checkout)
sys.path.insert(0, str(checkout / 'src'))
import numpy as np
gate_path = checkout / 'tests/validation/multigrid/test_channel_root_gpu.py'
spec = importlib.util.spec_from_file_location('original_channel_gpu_gate', gate_path)
gate = importlib.util.module_from_spec(spec)
spec.loader.exec_module(gate)
revision = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True, text=True, check=True).stdout.strip()
manifest = dict(checkout=str(checkout), revision=revision,
gate_sha256=hashlib.sha256(gate_path.read_bytes()).hexdigest(),
test='ChannelRootGPU.test_uniform_matched_root')
(output / 'manifest.json').write_text(json.dumps(manifest, indent=2))
test = gate.ChannelRootGPU('test_uniform_matched_root')
try:
# The original method and np.testing.assert_array_equal are unchanged.
# Its existing loops naturally stop at the first failing assertion.
test.test_uniform_matched_root()
except AssertionError as exc:
original_trace = traceback.format_exc()
(output / 'original_failure.txt').write_text(original_trace)
print(original_trace, file=sys.stderr, flush=True)
case = capture_case(exc)
if case is None:
(output / 'status.json').write_text(json.dumps(dict(status='different_assertion_failure', replay_started=False), indent=2))
return 3
config, expected, actual = case
(output / 'failed_case.json').write_text(json.dumps(config, indent=2))
np.savez(output / 'original_failure_arrays.npz', expected=expected, actual=actual)
print('CAPTURED_CASE ' + json.dumps(config), flush=True)
except Exception:
(output / 'discovery_error.txt').write_text(traceback.format_exc())
(output / 'status.json').write_text(json.dumps(dict(status='discovery_error', replay_started=False), indent=2))
raise
else:
(output / 'status.json').write_text(json.dumps(dict(status='original_failure_not_reproduced', replay_started=False), indent=2))
print('The original matched-root test completed without reproducing the failure. No targeted replay was started.', flush=True)
return 2
# Discovery's context has closed. Run targeted diagnostics in a fresh process,
# serially, never concurrently with another compilation or GPU run here.
replay = Path(__file__).resolve().with_name('replay.py')
command = [sys.executable, str(replay), '--shifted', str(config['shifted']),
'--profile', config['profile'], '--clamp', str(config['clamp']),
'--damping', str(config['damping']), '--block', *map(str, config['block']),
'--steps', str(config['steps']), '--output', str(output / 'targeted')]
(output / 'replay_command.json').write_text(json.dumps(command, indent=2))
with (output / 'targeted_stdout.txt').open('w') as stdout, (output / 'targeted_stderr.txt').open('w') as stderr:
result = subprocess.run(command, cwd=checkout, stdout=stdout, stderr=stderr)
targeted = output / 'targeted/result.json'
details = json.loads(targeted.read_text()) if targeted.exists() else None
status = dict(status='original_assertion_failure_retained', replay_started=True,
replay_returncode=result.returncode, replay_result=details)
(output / 'status.json').write_text(json.dumps(status, indent=2))
print(json.dumps(status), flush=True)
# Preserve the original failed gate even if replay fails or does not reproduce.
return 1
if __name__ == '__main__':
raise SystemExit(main())