From 442cf8889d14d19c8d905562ece9155fde43b941 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 5 Oct 2026 08:47:21 +0200 Subject: [PATCH] Battery: in-house standard proteins carry a reference model for R-free and anomalous signal Each lysozyme, thaumatin, insulin, cytochrome c and myoglobin set names the PDB entry of its crystal form in a `model` field; the runner fetches it like an open-arm deposition and passes it to --model, so the row carries rfree, rmodel_shell_scaled, rfree_deposited/rfree_ratio and the new anom_sigma (ANOMALOUS_SCATTERER_MEAN_SIGMA) - an external yardstick beside the XDS statistics. Reported only: the arm's scoring is unchanged, and the processing is identical with and without the model (19 sets vs all2-full: same space group modulo the model's enantiomorph label, d_min, R_meas, CC1/2, ISa, cell and REFRES table). depdata_check stays open-arm only. Models, chosen by cell/space group and confirmed by rugnux --model: 4AXT lysozyme (6G8A, the coordinates beside the data, has c 2% short: R-free +0.04), 5A47 thaumatin (6G89 +0.06), 2BN3 cubic and 4E7T rhombohedral insulin (bovine; pig 9INS +0.07, human 1MSO +0.08), 4DC8 horse heart myoglobin (b 28.8, not sperm whale's 30.8; 5ZZE equal), 6FF5 bovine cytochrome c - our crystals are a primitive relative of its H32 form (pseudo-centring TNCS), so R-free ~0.49 but the heme iron is the top anomalous peak; 8RKP, the cytochrome c' beside the data, does not fit at all (R-free 0.63, nothing at the iron). Run 20261005-0735_2624a6_inhouse-model (19 sets): R-free 0.18-0.30 on the good sets, 0.37-0.57 on the ISa~2 powder/split myoglobin sweeps and ~0.49 on cytochrome c; anom_sigma rises with wavelength (thaumatin 2.3 at 16 keV, 6.7 at 6.5 keV, 8.9 at 3.8 keV). Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01K5K8jvPPbmCrbqnWkddTuB --- tools/battery/README.md | 30 ++++++++++++---- tools/battery/battery.py | 21 ++++++----- tools/battery/inhouse.json | 74 +++++++++++++++++++------------------- tools/battery/report.py | 15 ++++++-- tools/battery/score.py | 8 +++-- 5 files changed, 91 insertions(+), 57 deletions(-) diff --git a/tools/battery/README.md b/tools/battery/README.md index c1201ab80..0c122bbf1 100644 --- a/tools/battery/README.md +++ b/tools/battery/README.md @@ -15,7 +15,7 @@ run needed): `cd tools/battery && python3 test_score.py`. | arm | datasets | reference | manifest | |---|---|---|---| | **open** | public PDB depositions of raw diffraction data, plus a few published small-molecule sets | the deposited space group, cell and resolution | `open.json` (committed) | -| **inhouse** | standard test crystals measured at the SLS (lysozyme, thaumatin, insulin, cytochrome C, myoglobin), small-molecule standards (aspirin, citric acid, HEPES, YAG, L-cystine), plus no-crystal controls | XDS, from the `CORRECT.LP` beside each dataset; for the small molecules the space group is the literature's (`ref_override`), as XDS reports only Sohncke groups; L-cystine, which XDS does not index, has only the literature reference | `inhouse.json` (committed) | +| **inhouse** | standard test crystals measured at the SLS (lysozyme, thaumatin, insulin, cytochrome C, myoglobin), small-molecule standards (aspirin, citric acid, HEPES, YAG, L-cystine), plus no-crystal controls | XDS, from the `CORRECT.LP` beside each dataset (the standard proteins also carry a reference model for R-free, reported only: `--model` below); for the small molecules the space group is the literature's (`ref_override`), as XDS reports only Sohncke groups; L-cystine, which XDS does not index, has only the literature reference | `inhouse.json` (committed) | | **private** | user data | XDS, like inhouse | outside the repository; the local site config gives its path | Scoring checks these things in order, and the first one that fails decides the verdict: did it @@ -84,7 +84,7 @@ Every set runs **once**, with one command per arm: | arm (reference) | command | |---|---| | open (deposition) | `rugnux -o p --no-export-unmerged --model `; sets without a deposited model (small molecules, unpublished sets, or coordinates that cannot be downloaded) run the same command without `--model` | -| inhouse, private (XDS) | `rugnux -o p --no-export-unmerged [-A] --report-resolution , ` | +| inhouse, private (XDS) | `rugnux -o p --no-export-unmerged [-A] --report-resolution , [--model ] `; `--model` where the set names one (the in-house standard proteins, below) | Neither `--model` nor `--report-resolution` changes the processing: the merged reflections, the resolution cut and every decision are the same with and without them (verified bit for bit). So @@ -103,6 +103,23 @@ the reference: A model that fits may put its enantiomorph on the space-group label; the scorer reads the data's own determination instead (`SOHNCKE_SPACE_GROUP` where `SPACE_GROUP_ENANTIOMORPH` is `ASSUMED_FROM_MODEL`) and keeps the label in `sg_label`. +- **`--model`** (in-house standard proteins): a set may name the PDB entry of its crystal form in + its `model` field; it is fetched and cached like an open-arm entry and passed to `--model`, so + the row carries the same model fields (`rfree`, `rmodel_shell_scaled`, `rfree_deposited`, + `rfree_ratio`, `anom_sigma`, ...) - quality on an external yardstick beside the XDS statistics. + They are reported, never scored: the arm is scored against XDS exactly as without them. One + entry per crystal form, chosen by cell and space group and confirmed by `rugnux --model` on our + sets (R-free of the placed model, not refined): + + | sets | model | why | + |---|---|---| + | `lyso*` (P4₃2₁2) | 4AXT | hen egg-white lysozyme, 1.1 Å, 100 K, a 78.2 c 37.5 - the middle of our sets' cells (76.9-78.4 / 37.0-37.9); rugnux re-fractionalises the model into the data cell, so the R-free of the placed model rises by a few points per % of cell difference (6G8A, a 78.9 c 36.9: R-free +0.04 on `lyso_x06da_ref`). No ions in the model, so `anom_sigma` is the sulfurs' | + | `thau*` (P4₁2₁2) | 5A47 | thaumatin from *T. daniellii*, 1.2 Å, a 57.9 c 150.6 (6G89, 2.4 Å: R-free +0.06) | + | `insu_I*` (I2₁3) | 2BN3 | bovine insulin, cubic, 1.4 Å, 100 K, a 77.9 (9INS, pig, a 78.9, no R-free deposited: R-free +0.07) | + | `insu_H*` (H3) | 4E7T | bovine T6 2Zn insulin, 1.4 Å, a 81.0 c 33.5 (1MSO, human: R-free +0.08 - our insulin is bovine) | + | `cytc*` (P3₁21 / P3₂21) | 6FF5 | bovine heart cytochrome c at high ionic strength, H32, a 83.4 c 92.5. Our crystals are a primitive relative of that form - the R centring survives as a pseudo-translation (TNCS, 1/3 2/3 1/3) and c is 4-6% shorter - so the model explains the centred part only and R-free stays near 0.48; the heme iron is still the strongest anomalous peak (17 sigma). Trend only. (8RKP, the cytochrome c' whose P6₂22 cell matches ours, does not fit: R-free 0.63, no density at the iron) | + | `myob*` (P2₁) | 4DC8 | horse heart myoglobin (b 28.8 Å; sperm whale's P2₁ form has b 30.8), met form with sulfate, 1.5 Å, 100 K, a 35.4 b 28.8 c 63.1 β 106.1 - our setting; the unexposed control of its deposition, so no ligand or modified heme (5ZZE, same form, R-free 0.235 vs 0.233) | + - **`--report-resolution`** (XDS arms): a second statistics table from the same merge, binned over XDS's own range (`dmin_xds` and XDS's d_max as given, 999 included, so it covers exactly what XDS's CORRECT.LP totals cover), and `-A` where XDS merged with `FRIEDEL'S_LAW=FALSE` (`"anomalous": true` in the @@ -415,9 +432,10 @@ was run `--unforced`), so its forced XDS-arm rows are left out the same way. | `isa_ratio`, `r_meas_ratio` | refres_isa / isa_ref and refres_r_meas / r_meas_ref | | `cc_half_noise_ratio` | XDS arms: (1/refres_cc_half - 1) / (1/(cc_half_ref - 0.0005) - 1), reported, not scored | | `lowres_d`, `lowres_r_meas`, `refres_lowres_d`, `refres_lowres_r_meas`, `lowres_d_ref`, `lowres_r_meas_ref`, `lowres_r_meas_ratio` | the lowest-resolution shell's high-resolution limit and R_meas: rugnux's own table, the reference-range table, XDS's; and refres_lowres_r_meas / lowres_r_meas_ref | -| `model`, `model_note` | open arm: the deposited coordinates given to `--model`, or why there were none | -| `rfree`, `rwork`, `cc_model`, `model_fit`, `rfree_deposited`, `rfree_ratio` | open arm with a model: R_FREE, R_WORK, CC_MODEL_OVERALL and MODEL_FIT as rugnux reports them (placement-only, own free set: trend fields), the published R-free, and rfree / rfree_deposited | -| `rmodel`, `rmodel_shell_scaled`, `radial_misfit` | open arm with a model: R_MODEL, R_MODEL_SHELL_SCALED and MODEL_RADIAL_MISFIT - R over all the reflections, with and without one free scale per resolution shell, and how big that rescale was. `rmodel_shell_scaled` is the R that compares between runs (below) | +| `model`, `model_note` | open arm, and in-house sets with a `model`: the coordinates given to `--model`, or why there were none | +| `rfree`, `rwork`, `cc_model`, `model_fit`, `rfree_deposited`, `rfree_ratio` | any row with a model: R_FREE, R_WORK, CC_MODEL_OVERALL and MODEL_FIT as rugnux reports them (placement-only, own free set: trend fields), the published R-free, and rfree / rfree_deposited | +| `anom_sigma` | any row with a model: ANOMALOUS_SCATTERER_MEAN_SIGMA, the anomalous difference map (F+ - F- on the model's phases) read at every model atom from phosphorus up (S of Met/Cys, metals, Cl, I, ...) and averaged, in map sigma - how much anomalous signal the merge carries; null where the merge kept no Bijvoet split or the model has no such atom | +| `rmodel`, `rmodel_shell_scaled`, `radial_misfit` | any row with a model: R_MODEL, R_MODEL_SHELL_SCALED and MODEL_RADIAL_MISFIT - R over all the reflections, with and without one free scale per resolution shell, and how big that rescale was. `rmodel_shell_scaled` is the R that compares between runs (below) | | `refmac_rfree`, `refmac_rwork`, `refmac_rfree_depflags`, `refmac_rfree_depdata`, `refmac_rfree_ratio`, `refmac_twin`, `refmac_untwinned`, `refmac_status`, `refmac_reason`, `refmac_refined_*`, `refmac_shared_free_n` | the REFMAC check (`--model-check`, open arm; see its section for which R-free is which) | | `dep_status`, `dep_reason`, `dep_kind`, `dep_d_min`, `dep_n_common`, `dep_cc_delta_all`, `dep_cc_delta_outer`, `dep_beyond_cc`, `dep_beyond_d` | open arm with a model: our merge against the depositor's data per shell (`depdata_check.py`, above) | | `shx_*` | small molecule with a `cod` structure: the SHELXL check (`shelx_check.py`, above) | @@ -437,7 +455,7 @@ SVG, no external files): the main metrics; - plots per set (HTML only; hovering a point shows the set): d_min(rugnux) / d_min(reference) for each arm; on the XDS arms the reference-range ISa, R_meas, lowest-shell R_meas and CC1/2 - noise over XDS's; on the open arm R_free / published R_free; + noise over XDS's; on every row with a model R_free / published R_free; - on the open arm, **against the depositor's data**: the per-shell CC difference and the CC past the deposited limit, one row per set, sorted by the outer-shell difference; - on the XDS arms the **like-for-like table**: the reference-range numbers beside XDS's, with the diff --git a/tools/battery/battery.py b/tools/battery/battery.py index 05ecefde0..b8f62781d 100644 --- a/tools/battery/battery.py +++ b/tools/battery/battery.py @@ -29,8 +29,9 @@ $JFJOCH_BATTERY_SITE, else site.json beside this script (not committed; site.exa 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). Neither option changes the processing, so the one run is both what a -user gets and the like-for-like comparison. +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 /__