Merging: do not floor the merged sigma at the systematic term
The merged sigma was floored at b*|I|, so I/sigma could never exceed the reported ISa. On one dataset every merged reflection came out at I/sigma <= 12.96 with a 99th percentile of 12.77 in every resolution shell alike, while the scatter of the observations implied about 44 and XDS reported 58. The floor is wrong in principle. `b` is fitted from the scatter BETWEEN a reflection's symmetry equivalents, i.e. from the part that is not common to them, so it averages down with multiplicity exactly like the counting term. 1/sqrt(sum_w) with the b-inflated per-observation sigma already gives b*I/sqrt(n); flooring at b*|I| puts the sqrt(n) back. That is the whole effect: 12.96 * sqrt(21.6) = 60, against XDS's 58. It was introduced on a comparison of our MERGED I/sigma against XDS's UNMERGED I/sigma. XDS's own merged low-resolution I/sigma exceeds its reported ISa on 30 of the 39 reference datasets here, median ratio 1.78 and up to 4.23. Merged low-shell I/sigma now lands where XDS's does: 22.4 -> 46.2 against 46.2 on one crystal, 26.7 -> 115.7 against 96.6 on another, 12.5 -> 45.0 against 58.0 on a third. Over the 38-crystal battery the space groups, the merged reflection sets, R_meas and CC1/2 are all unchanged - every one of them is sigma-independent, which is what makes them the right control - and <I/sigma> rises on 35 crystals with none worse. The asymptotic estimator that fed the floor stays, for the reported ISa only, and is repaired in the process: it subtracts a*sigma^2 rather than the raw sigma^2 (at a < 1 the difference is the same size as the b^2 being measured, which is what made it flip between 10.9 and 62.7 on consecutive passes of the same data), it rescales each group's variance median-unbiased before subtracting an unbiased counting term, its I/sigma gate uses the same convention, and it is bounded by the whole-range b - an asymptote exists to refine 1/b upward, not to report 0.3 because "strong" was selected on a sigma scale the fit itself rejects. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -221,14 +221,11 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
pass_absence[i] = in_range;
|
||||
}
|
||||
|
||||
// The merge floors the merged sigma at b|I| (Merge.h SigmaWithSystematicFloor) so that ISa = 1/b
|
||||
// stays the asymptotic I/sigma ceiling: no reflection in the merge can read above it. A fixed
|
||||
// I/sigma cut is therefore not a per-reflection test but a switch on the error model - every
|
||||
// reflection at the floor reads 1/b exactly, however strong it is, and on a merge whose ISa falls
|
||||
// below the cut NOTHING passes, so every operator is left with no pairs and the point group
|
||||
// collapses to 1. Measured over the rotation battery: max I/sigma equals 1/b on every merge, and
|
||||
// the four crystals that lose symmetry are exactly the four whose search merge sits at ISa 3.5-4.0
|
||||
// - just above this cut - and drops below 3 when the integration background changes.
|
||||
// A fixed I/sigma cut is a statement about the error model as much as about a reflection: on a weak
|
||||
// merge, whose error-model sigmas are large for every reflection, NOTHING passes, every operator is
|
||||
// left with no pairs and the point group collapses to 1. Measured over the rotation battery, the four
|
||||
// crystals that lose symmetry are exactly the four whose search merge is weakest - just above this
|
||||
// cut - and that drop below it when the integration background changes.
|
||||
// So cap the cut at the merge's own I/sigma quantile: the correlation stage always keeps at least
|
||||
// its strongest quarter. This is a no-op on any merge where the fixed cut already keeps that many.
|
||||
constexpr double MIN_PRESENT_FRACTION = 0.25;
|
||||
@@ -754,14 +751,13 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
// screw-axis violation; it only relaxes "present", so it cannot over-call a screw whose
|
||||
// predicted-absent class carries real intensity.
|
||||
//
|
||||
// present_cut, not the fixed cut: merged sigma is floored at b*|I| (SigmaWithSystematicFloor),
|
||||
// so no reflection can read I/sigma above ISa = 1/b. On a merge whose ISa sits at or below the
|
||||
// fixed cut NOTHING is ever present - screw_violations is identically zero, so every screw axis
|
||||
// passes unchallenged, and present_strong is zero, so the centering rescue below switches
|
||||
// itself off on exactly the weak data it exists for. Stage A already caps the cut at the
|
||||
// merge's own 75th percentile; reusing it here keeps the two stages on one definition. Where
|
||||
// the fixed cut is already the smaller of the two - any merge with ISa comfortably above it -
|
||||
// present_cut EQUALS it and this is a no-op.
|
||||
// present_cut, not the fixed cut: on a merge weak enough that nothing clears the fixed cut,
|
||||
// screw_violations is identically zero, so every screw axis passes unchallenged, and
|
||||
// present_strong is zero, so the centering rescue below switches itself off on exactly the
|
||||
// weak data it exists for. Stage A already caps the cut at the merge's own 75th percentile;
|
||||
// reusing it here keeps the two stages on one definition. Where the fixed cut is already the
|
||||
// smaller of the two - any merge with a healthy I/sigma - present_cut EQUALS it and this is
|
||||
// a no-op.
|
||||
const bool present = IoverSigma[i] > present_cut &&
|
||||
(opt.present_e_squared <= 0.0 || Esq[i] > opt.present_e_squared);
|
||||
|
||||
@@ -971,7 +967,7 @@ std::string SearchSpaceGroupResultToText(const SearchSpaceGroupResult& result,
|
||||
os << " absent/viol = reflections the group predicts absent, and how many are nonetheless present.\n"
|
||||
" E2 screw / E2 row = median I/<I>(shell) of the SCREW-absent reflections and of the rest of\n"
|
||||
" their axial rows - a real screw leaves the first far below the second. The <I/s> columns are\n"
|
||||
" the centering evidence; they say little about screws (the merged sigma floor pins I/sigma).\n";
|
||||
" the centering evidence; they say little about screws.\n";
|
||||
|
||||
if (result.best_space_group.has_value()) {
|
||||
os << "Best space group: " << result.best_space_group->short_name();
|
||||
|
||||
Reference in New Issue
Block a user