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) <noreply@anthropic.com>
This commit is contained in:
2026-08-10 05:52:33 +02:00
co-authored by Claude Opus 5
parent 01626a54e0
commit 4869dbd984
@@ -863,8 +863,13 @@ SearchSpaceGroupResult SearchSpaceGroup(
present_n > 0 ? static_cast<double>(present_strong) / present_n : 0.0;
const double centering_violation_rate =
centering_absent > 0 ? static_cast<double>(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 ||