rugnux --model: a wrong model confirms no shell, fits no hand, and fits the GPU reserve

Three defects the battery's wrong-model null found (5 sets x 5 deposited models of unrelated proteins):

CC(model, data) significance. The per-shell sigma was Fisher's transform on independent reflections,
but the data and a scaled model share the falloff across a shell, the anisotropic B fitted to these
very data, the bulk solvent and the symmetry enhancement epsilon - whatever the model is. 21 of 24
wrong models read CC_MODEL_CONFIRMED_TO_D_MIN to the data's d_min (10-15 sigma in most shells).
The correlation is now formed on I/epsilon and |F_model|^2/epsilon, and its null is empirical: the
data at h against the model at h + d for the 26 nearest lattice offsets, which keeps everything smooth
in reciprocal space and loses what the model knows of h. SIGMA = (z - mean) / max(sd, 1/sqrt(N-3)) in
Fisher z over those 26, a new CC_NULL column shows the null, and a shell confirms only beside a coarser
shell that also clears 3 sigma (one shell alone did so once in 216 wrong-model shells). Prototyped on
the p_maps.mtz of a full open-arm run and the null runs: wrong models confirm 0/24 (was 21/24), the
deposited models confirm 197/200 (was 198/200 in the prototype's reading; the one lost has CC_MODEL
0.05-0.13 in every shell). The report no longer says a model cannot agree by accident.

MODEL_FIT gate. Accepted at 3.0 sample sds of a nine-draw null, as if the sd were known; an unrelated
P1 helical model on a P3(1) helical crystal was ACCEPTED at +3.05. Against 48 draws it reads +2.09 and
the best random placement fits as well as it does, and drawing the null about the cell axes instead
changes nothing (+2.15): the cause is the nine-draw sd, not the orientation. The gate is now 3 sigma
allowing for that: t(8) * sqrt(1 + 1/9) = 4.51. The deposited models of the open arm read +7.0 and up.
The indexing lead stays at 3 sigma (INDEXING_MARGIN_SIGMA): it is read only behind the fit gate, and
a deposited model settles one at +4.15.

Rigid-body GPU reserve. The finest zone's observations were counted by the data's d, but the zones
count them by the d of the index in the model's ASU, and a model's group need not keep the data's
metric (an I2(1)3 model on I222 data: 2320 in the zone against 1712 reserved), so the validation died
with "zone over the engine's reserve". Counted the zones' way, under every indexing probed.

Tests: ModelValidation_CCModelUnrelatedModelConfirmsNothing (Fisher alone reads +4.7 on an unrelated
model with an anisotropic falloff; the new sigma stays under 1), ModelValidation_WrongModelInAGroupOffTheDataMetric
(fails with the old count), ResultReport_ModelValidationSection (CC_NULL column, lone-shell rule).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-11 21:30:34 +02:00
1 parent bd3c4efab6
commit ee350f1f74
7 files changed
+255 -60

No files matched your search

+14 -5
View File
@@ -461,10 +461,11 @@ TEST_CASE("ResultReport_ModelValidationSection", "[Diagnostics]") {
CHECK(untested_text.find("was tried and REJECTED") == std::string::npos);
// CC(model, data) by shell, with the deepest shell short of significance: the confirmed limit is
// the last shell that reached it, and it is a lower bound - the shell past it is not thereby empty.
// the last shell that reached it beside a coarser one that did, and it is a lower bound - the
// shell past it is not thereby empty.
ModelValidationResult with_cc = good;
with_cc.cc_model_shells = {{3.20f, 0.9412, 4210, 91.4}, {2.10f, 0.5533, 3980, 40.2},
{1.80f, 0.0412, 2110, 1.9}};
with_cc.cc_model_shells = {{3.20f, 0.9412, 4210, 41.4, 0.1211}, {2.10f, 0.5533, 3980, 20.2, 0.0832},
{1.80f, 0.0412, 2110, 1.9, 0.0105}};
with_cc.cc_model_overall = 0.8123;
with_cc.cc_model_n = 10300;
result.model_validation = with_cc;
@@ -472,8 +473,8 @@ TEST_CASE("ResultReport_ModelValidationSection", "[Diagnostics]") {
CHECK(cc_text.find("\nCC_MODEL_OVERALL= 0.8123\n") != std::string::npos);
CHECK(cc_text.find("\nCC_MODEL_REFLECTIONS= 10300\n") != std::string::npos);
CHECK(cc_text.find("\nCC_MODEL_CONFIRMED_TO_D_MIN= 2.10\n") != std::string::npos);
CHECK(cc_text.find("D_MIN CC_MODEL N SIGMA") != std::string::npos);
CHECK(cc_text.find(" 1.80 0.0412 2110 +1.9") != std::string::npos);
CHECK(cc_text.find("D_MIN CC_MODEL N CC_NULL SIGMA") != std::string::npos);
CHECK(cc_text.find(" 1.80 0.0412 2110 0.0105 +1.9") != std::string::npos);
CHECK(cc_text.find("The table reads in ONE direction only") != std::string::npos);
// The comparable R rides on the same shells. R_WORK and R_FREE are what they always were - the
@@ -510,6 +511,14 @@ TEST_CASE("ResultReport_ModelValidationSection", "[Diagnostics]") {
CHECK(RenderResultReport("p", "in.h5", x, result)
.find("\nCC_MODEL_CONFIRMED_TO_D_MIN= NONE\n") != std::string::npos);
// One shell over the bar on its own, its neighbours not: chance among a dozen shells, not signal.
ModelValidationResult lone = with_cc;
lone.cc_model_shells[0].sigma = 0.4;
lone.cc_model_shells[1].sigma = 5.7;
result.model_validation = lone;
CHECK(RenderResultReport("p", "in.h5", x, result)
.find("\nCC_MODEL_CONFIRMED_TO_D_MIN= NONE\n") != std::string::npos);
result.model_validation = good;
ModelValidationResult failed;