#!/usr/bin/env python3 """Rugnux validation battery: run, score, report, compare. Arms and where their truth comes from: open public PDB depositions (and a few published small-molecule sets); scored against the deposited space group, cell and resolution. Manifest: open.json (committed). inhouse standard test crystals measured at PSI (lysozyme, thaumatin, insulin, cytochrome C, myoglobin) plus no-crystal controls; scored against XDS (point group only - XDS never tests a screw axis). Manifest: inhouse.json (committed). private user data, scored like inhouse. Its manifest lives OUTSIDE the repository (path in the site config) and a private run gets its own run directory and report. Usage (README.md in this directory is the full guide): battery.py run --rugnux BIN [--label L] [--arm open,inhouse|private] [--only a,b | --tier smoke|ci] [--build-dir DIR] [--gpulock] [--threads N] [--model-check] [--nullmodel | --no-nullmodel] [--baseline RUN|none] --tier ci also runs the wrong-model null (nullmodel.py) battery.py nullmodel --rugnux BIN [--label L] [--build-dir DIR] [--gpulock] [--threads N] the wrong-model null on its own, in a run directory of its own: exit 1 if a pair failed (CI runs it as its own step, after run --tier ci --no-nullmodel) battery.py timing --rugnux BIN [--benchmark standard|inhouse] [--label L] [--build-dir DIR] [--gpulock] [--threads N] [--baseline RUN|none] the timing benchmark (timing.json). standard (the default, open data only): 9zlo, 9s02 and 9gjx from their AlphaFold DB models, 4nwv from its deposited model moved away; inhouse: myob_x10sa and cytc_x10sa from AlphaFold DB models, and 4nwv. All placed by molecular replacement; checks each answer and prints wall and step times; exit 1 if one is wrong battery.py keep RUN --as workstation|server copy a finished open/inhouse run's report.md, results.json and manifest.json into results/// (committed); refuses a private run battery.py stress --rugnux BIN (--only a,b | --tier T) [--repeat N] [--concurrent K] [--sanitizer memcheck|racecheck|synccheck|initcheck] [--launch-blocking] crash hunt: exit 1 if any run crashed battery.py list runs under the runs root, with their state battery.py compare RUN_A RUN_B [--all] [--rerun-changed] battery.py report RUN --out DIR [--baseline RUN] re-render a run's report into DIR/.{md,html} battery.py table RUN the one-line-per-set table a run ends with battery.py abort RUN [--reason TEXT] mark an unfinished run as aborted battery.py recheck RUN [--only a,b] [--jobs N] rerun the model checks of a finished run's open arm on its p.mtz files; recorded in the manifest battery.py discover [--refresh] open arm: propose inputs for new directories battery.py refs --arm inhouse|private [--write] re-read the XDS references from CORRECT.LP battery.py remap --arm A MAP.json [--write] follow a rename of the data directories The site config (data roots, runs root, lock, gpulock, private manifest, baselines) is --site, else $JFJOCH_BATTERY_SITE, else site.json beside this script (not committed; site.example.json is). Every set runs once, with one command per arm (COMMAND_DOC): the open arm with the deposited model (--model), the XDS arms with XDS's range as a report-only second table (--report-resolution) and, where a set names the PDB entry of its crystal form, that model (--model). Neither option changes the processing, so the one run is both what a user gets and the like-for-like comparison. A run writes /__