Beam-centre gates: decide on the strong-reflection count, not on how a scaling loop stopped
MeasuredCentreClearlyLoses, different-lattice branch: the measured centre loses on the pre-pass subset when it measures clearly fewer strong reflections (Poisson, 2 sigma) than the file's centre. The subset CC1/2 is no longer read there (still logged). A third of the frames in five blocks merges to a multiplicity of ~1.09 in P1, so its CC1/2 reads the relative scale of the blocks: on 9gdj the file centre's subset CC1/2 moved 0.899 / 0.880 / 0.832 / 0.846 with the scaling path (plain 1e-3 / 1e-5, Anderson 1e-3 / 1e-5), ~50x the Fisher noise the gate allowed, and flipped the early verdict. The count is comparable across lattice classes (taken on the primitive lattice) and stable across paths: 36.6-38.6k against 103.4-103.6k on 9gdj. MeasuredCentreWins, same-lattice branch: the measured centre has to be ahead on the strong count by more than 2 Poisson sigmas; within that the file's centre stays (it adopted at 1874 vs 1873 on one in-house small-molecule set, and at 0.4 sigma on 9zmu). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXc1zD1yxHqaR1DGdCTHL4
This commit is contained in:
1 parent
0894230671
commit
e724f3d26a
3 files changed
+36
-19
No files matched your search
+5
-4
@@ -839,10 +839,11 @@ bool ArmsHoldSameLattice(const ProcessResult &a, const ProcessResult &b);
|
||||
std::string MeasuredCentreWins(const ProcessResult &file_arm, const ProcessResult &measured_arm);
|
||||
// The search-merge numbers of a pass, as MeasuredCentreWins reads them.
|
||||
SearchMergeSignal SearchSignal(const ProcessResult &r);
|
||||
// Whether the measured centre loses on every line MeasuredCentreWins draws by more than `sigmas`
|
||||
// times that line's noise - the Poisson noise of a count, the standard error of a CC1/2 - with both
|
||||
// arms merged from the same frames. The run asks it at the near-tie distance of its first passes
|
||||
// (FirstPassRun::NEAR_TIE_SIGMA); anything else, a near tie or a win, is decided on all the frames.
|
||||
// Whether the measured centre is behind on the strong-reflection count by more than `sigmas` times
|
||||
// its Poisson noise - and, on the same lattice, also behind on the inner-shell CC1/2 line
|
||||
// MeasuredCentreWins draws - with both arms merged from the same frames. The run asks it at the
|
||||
// near-tie distance of its first passes (FirstPassRun::NEAR_TIE_SIGMA); anything else, a near tie or
|
||||
// a win, is decided on all the frames.
|
||||
bool MeasuredCentreClearlyLoses(const SearchMergeSignal &file_arm, const SearchMergeSignal &measured_arm,
|
||||
bool same_lattice, double sigmas);
|
||||
|
||||
|
||||
+21
-10
@@ -89,6 +89,9 @@ namespace {
|
||||
// How much better the alternative beam centre's search merge has to be for the run to move off
|
||||
// the centre the file gave it (the beam-centre arbitration in RunAllPasses).
|
||||
constexpr double MAX_CC_HALF_LOSS = 0.05;
|
||||
// How far, in Poisson sigmas, the alternative's strong-reflection count has to be ahead on the same
|
||||
// lattice - the near-tie distance of the first passes (FirstPassRun::NEAR_TIE_SIGMA).
|
||||
constexpr double STRONG_COUNT_SIGMAS = 2.0;
|
||||
|
||||
// Say so rather than printing a zero that reads as a measurement. ("not compared", not "not
|
||||
// measured": completeness IS measured and reported; what did not happen is its use in arbitrating
|
||||
@@ -292,7 +295,11 @@ std::string MeasuredCentreWins(const ProcessResult &file_arm, const ProcessResul
|
||||
// two arms here integrate with the same settings, so the geometry is the only difference, and the
|
||||
// file's centre has no standing the measured one lacks - neither indexed the whole spot list.
|
||||
if (ArmsHoldSameLattice(file_arm, measured_arm)) {
|
||||
if (measured_arm.search_merge_strong_reflections > file_arm.search_merge_strong_reflections)
|
||||
// Ahead by more than the Poisson noise of the two counts; within it the file's centre stays.
|
||||
const int64_t strong_m = measured_arm.search_merge_strong_reflections;
|
||||
const int64_t strong_f = file_arm.search_merge_strong_reflections;
|
||||
if (static_cast<double>(strong_m - strong_f)
|
||||
> STRONG_COUNT_SIGMAS * std::sqrt(static_cast<double>(strong_m + strong_f)))
|
||||
return fmt::format("the measured centre measures more of the crystal - {} reflections at "
|
||||
"I/sigma >= 2 before corrections against {}",
|
||||
measured_arm.search_merge_strong_reflections,
|
||||
@@ -331,15 +338,19 @@ bool MeasuredCentreClearlyLoses(const SearchMergeSignal &file_arm, const SearchM
|
||||
const auto loses = [sigmas](double margin, double noise) {
|
||||
return std::isfinite(margin) && std::isfinite(noise) && margin < -sigmas * noise;
|
||||
};
|
||||
if (same_lattice)
|
||||
return loses(static_cast<double>(measured_arm.strong - file_arm.strong),
|
||||
std::sqrt(static_cast<double>(measured_arm.strong + file_arm.strong)))
|
||||
&& loses(measured_arm.inner_cc_half - file_arm.inner_cc_half - MAX_CC_HALF_LOSS,
|
||||
std::hypot(cc_se(measured_arm.inner_cc_half, measured_arm.inner_reflections),
|
||||
cc_se(file_arm.inner_cc_half, file_arm.inner_reflections)));
|
||||
return loses(measured_arm.cc_half - file_arm.cc_half - MAX_CC_HALF_LOSS,
|
||||
std::hypot(cc_se(measured_arm.cc_half, measured_arm.reflections),
|
||||
cc_se(file_arm.cc_half, file_arm.reflections)));
|
||||
// Both arms integrate the same frames with the same settings, and the strong reflections are
|
||||
// counted on the primitive lattice, so the counts compare across lattice classes as well. The
|
||||
// subset's CC1/2 does not decide the different-lattice case: a third of the frames in blocks merges
|
||||
// to a multiplicity near one in P1, its CC1/2 then reads the relative scale of the blocks, and
|
||||
// measured it moved by 0.07 with how far the scaling loop ran - fifty times its Fisher noise.
|
||||
const bool behind_on_signal = loses(static_cast<double>(measured_arm.strong - file_arm.strong),
|
||||
std::sqrt(static_cast<double>(measured_arm.strong + file_arm.strong)));
|
||||
if (!same_lattice)
|
||||
return behind_on_signal;
|
||||
return behind_on_signal
|
||||
&& loses(measured_arm.inner_cc_half - file_arm.inner_cc_half - MAX_CC_HALF_LOSS,
|
||||
std::hypot(cc_se(measured_arm.inner_cc_half, measured_arm.inner_reflections),
|
||||
cc_se(file_arm.inner_cc_half, file_arm.inner_reflections)));
|
||||
}
|
||||
|
||||
ProcessResult Rugnux::RunAllPasses(RugnuxObserver *observer) {
|
||||
|
||||
+10
-5
@@ -645,6 +645,8 @@ TEST_CASE("MeasuredCentreWins", "[process]") {
|
||||
// Same lattice, same CC1/2: the arm that measured more signal wins, whichever centre it is...
|
||||
CHECK(!MeasuredCentreWins(file, arm(swapped, tri, 0.87, 9500)).empty());
|
||||
CHECK(MeasuredCentreWins(file, arm(swapped, tri, 0.87, 8500)).empty());
|
||||
// ...but not by less than twice the Poisson noise of the two counts (sqrt(18100) = 135)...
|
||||
CHECK(MeasuredCentreWins(file, arm(swapped, tri, 0.87, 9100)).empty());
|
||||
// ...a tie keeps the file's centre, and a higher CC1/2 alone does not move it.
|
||||
CHECK(MeasuredCentreWins(file, arm(swapped, tri, 0.99, 9000)).empty());
|
||||
|
||||
@@ -676,12 +678,15 @@ TEST_CASE("MeasuredCentreClearlyLoses", "[process]") {
|
||||
m.inner_reflections = 20;
|
||||
CHECK_FALSE(MeasuredCentreClearlyLoses(inconsistent, m, true, 2.0));
|
||||
|
||||
// Different lattices: the CC1/2 line, 0.05 above the file's centre.
|
||||
// Different lattices: the count alone - the subset's CC1/2 is not read.
|
||||
SearchMergeSignal d = file;
|
||||
d.cc_half = 0.80;
|
||||
d.strong = 3000;
|
||||
d.cc_half = 0.99;
|
||||
CHECK(MeasuredCentreClearlyLoses(file, d, false, 2.0));
|
||||
d.cc_half = 0.95; // exactly on the line
|
||||
d.strong = 8900; // 100 behind: within two sigmas
|
||||
d.cc_half = 0.50;
|
||||
CHECK_FALSE(MeasuredCentreClearlyLoses(file, d, false, 2.0));
|
||||
// Nothing measured is no loss: it is decided on all the frames.
|
||||
CHECK_FALSE(MeasuredCentreClearlyLoses(file, SearchMergeSignal{}, false, 2.0));
|
||||
// A merge with no strong reflection in it measured none of the crystal. (An arm without a merge
|
||||
// is never asked: RunAllPasses judges it on all the frames.)
|
||||
CHECK(MeasuredCentreClearlyLoses(file, SearchMergeSignal{}, false, 2.0));
|
||||
}
|
||||
Reference in new issue
Block a user