From 0cd8cb7ba354654efe3cff4101df3d335d665785 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 27 Jul 2026 21:38:07 +0200 Subject: [PATCH] Space-group search: name the veto that actually refused a promotion The refusal message fell through to the chi^2 branch whenever the systematic-b balloon veto was the binding test, so it reported a chi^2 ratio that did not justify the refusal at all - on one battery crystal it printed "merge chi^2 is 1.25x the subgroup's (bound 1.85)", i.e. a number comfortably inside its own bound, as the reason for processing in the lower symmetry. A diagnostic that names the wrong cause is worse than none: it sends the reader after the wrong statistic. Report the b test when it is what fired, with both b values and the bound. Co-Authored-By: Claude Opus 5 (1M context) --- image_analysis/scale_merge/SearchSpaceGroup.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/image_analysis/scale_merge/SearchSpaceGroup.cpp b/image_analysis/scale_merge/SearchSpaceGroup.cpp index b0e64d2e..c381f5f1 100644 --- a/image_analysis/scale_merge/SearchSpaceGroup.cpp +++ b/image_analysis/scale_merge/SearchSpaceGroup.cpp @@ -563,9 +563,12 @@ SearchSpaceGroupResult SearchSpaceGroup( // The parent b is floored (min_systematic_b_for_veto) so a near-zero parent on excellent data cannot // fabricate a huge ratio out of a still-tiny absolute b (a genuine 422 at b=0.05 over a 222 parent at // b=0.008 is not a twin - a real twin drives b to ~0.19 regardless). + bool b_refused = false; if (consistent && parent_b > 1e-4 - && c.b_extra > std::max(parent_b, opt.min_systematic_b_for_veto) * opt.max_systematic_b_veto) + && c.b_extra > std::max(parent_b, opt.min_systematic_b_for_veto) * opt.max_systematic_b_veto) { consistent = false; + b_refused = true; + } // The H test is a necessary condition for promotion where it can be computed: it is the only // statistic measured to separate genuine symmetry from a merohedral twin across data amounts. @@ -582,6 +585,11 @@ SearchSpaceGroupResult SearchSpaceGroup( refused_why = "operator disagreement H is " + FormatDouble(h_ratio, 2) + "x the parent's (bound " + FormatDouble(opt.max_operator_h_ratio, 2) + ") - the added operator relates unequal intensities, as a twin law does"; + else if (b_refused) + refused_why = "its merge's systematic error b is " + FormatDouble(c.b_extra, 3) + + " against the subgroup's " + FormatDouble(parent_b, 3) + + " (bound " + FormatDouble(opt.max_systematic_b_veto, 2) + + "x) - the added operator forces unequal intensities together"; else if (std::isfinite(c.chi2) && std::isfinite(chi2_ref)) refused_why = "merge chi^2 is " + FormatDouble(c.chi2 / chi2_ref, 2) + "x the subgroup's (bound " + FormatDouble(opt.max_merge_chi2_ratio, 2) + ")";