From 4869dbd984fca7aa389b816390a26fc8c86573ed Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 10 Aug 2026 05:52:33 +0200 Subject: [PATCH] Space-group search: do not judge centering on a non-positive present mean The centering test compares the absent class's mean intensity against half the present class's. With a present mean at or below zero - which happens on a merge dominated by noise - the bound is non-positive, and the comparison stops measuring whether the absences are weak and starts turning on the sign of the absent mean. Seen in an uncut merge: absent -0.16 against present -0.03, where a more negative absent class passes and one nearer zero fails, both by accident. Require a positive present mean before the mean-ratio branch can confirm a centering. The rate branch below it counts violations rather than averaging intensities, so it cannot change sign, and it already exists for exactly the weak-data case this leaves to it. No crystal in the battery changes, at matched limits or with the automatic cutoff. Co-Authored-By: Claude Opus 5 (1M context) --- image_analysis/scale_merge/SearchSpaceGroup.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/image_analysis/scale_merge/SearchSpaceGroup.cpp b/image_analysis/scale_merge/SearchSpaceGroup.cpp index 69a3d661..0c0d1fd0 100644 --- a/image_analysis/scale_merge/SearchSpaceGroup.cpp +++ b/image_analysis/scale_merge/SearchSpaceGroup.cpp @@ -863,8 +863,13 @@ SearchSpaceGroupResult SearchSpaceGroup( present_n > 0 ? static_cast(present_strong) / present_n : 0.0; const double centering_violation_rate = centering_absent > 0 ? static_cast(centering_violations) / centering_absent : 0.0; + // The mean-ratio test only means anything while the present class carries signal: with a + // present mean at or below zero the bound is non-positive and the comparison turns on the sign + // of the absent mean rather than on its size, accepting or rejecting a centering by accident. + // Leave that case to the rate test below, which counts violations and cannot change sign. const bool centering_ok = centering_absent == 0 - || (present_n > 0 && centering_absent_mean <= opt.max_absent_present_ratio * present_mean) + || (present_n > 0 && present_mean > 0.0 + && centering_absent_mean <= opt.max_absent_present_ratio * present_mean) || (present_strong_rate > 0.0 && centering_violation_rate <= opt.max_absent_present_ratio * present_strong_rate); const bool screw_ok = screw_absent == 0 ||