Resolution estimate: predict past the edge of the detector

The spot-finding resolution estimate was clamped so it could never beat the
detector corner. On a crystal that diffracts past the corner that reports where
the DETECTOR stops, which is the one thing this number is not for - it is meant
to say how far a merge of data like these would reach, a property of the
crystal and the exposure. The clamp also hid the interesting case: an estimate
finer than what the run actually merged is the statement "this run was
detector-limited", and there was no way to make it.

The statistic already extrapolates. Its quantile sits in the middle of the
fall-off, well inside what the detector records, so it goes on measuring the
crystal's own decay when the detector cuts that decay short. Measured by
truncating the spot lists of 31 battery crystals at an artificial detector edge
and scoring the unclamped answer against each crystal's own measured CC1/2 =
0.30 crossing, it holds its 8-9% floor out to about 1.7x past the cut and only
then drifts pessimistic, which is the safe direction. Every genuinely
detector-limited crystal in the battery needs between 1.10x and 1.63x.

Against a truth corrected for censoring - the six crystals whose merge is cut
off by their own detector cannot have a measured crossing, so theirs is
extrapolated from multiplicity-corrected <I/sigma> and anchored on the 25 where
both exist: symmetric-log RMS 13.5 -> 9.4% over 37 crystals, 25 -> 28 within
0.2 A. On the six detector-limited ones 26.1 -> 9.8% and the bias goes +19 ->
-3%; on the 31 that are not, 9.23 -> 9.32%, i.e. it costs them nothing. The
0.30 tail fraction and the 2.25 reach were refit by leave-one-out against that
truth and did not move.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBumeJVx4oeXxiBRpkrE5H
This commit is contained in:
2026-08-28 10:41:34 +02:00
co-authored by Claude Opus 5
parent ac202a55a1
commit 1388b16d7a
6 changed files with 21 additions and 13 deletions
+5 -2
View File
@@ -39,8 +39,11 @@ TEST_CASE("GetResolution") {
REQUIRE(d.has_value());
CHECK(*d == Catch::Approx(1.0 / (2.25 * std::sqrt(0.8))).epsilon(1e-4));
// The merged data cannot beat the corner of the detector.
CHECK(*GetResolution(spots, 2.0f) == Catch::Approx(2.0));
// The answer is not limited to what a detector records. Keeping only the five spots a detector
// reaching 1/d^2 = 0.5 would have recorded leaves the quantile at 0.4, and the estimate still
// extrapolates 2.25x past it instead of stopping at the cut.
const std::vector<SpotToSave> cut(spots.begin(), spots.begin() + 5);
CHECK(*GetResolution(cut) == Catch::Approx(1.0 / (2.25 * std::sqrt(0.4))).epsilon(1e-4));
// Ice-flagged spots take no part, however strong they are.
std::vector<SpotToSave> with_ice = spots;