rugnux: index both beam centres always, without a significance gate first

The second first pass was skipped where the measured centre sat within three times the fit's own
sigma of the file's - a quarter of the non-SLS corpus, eight of thirty-two runs. That is a decision
taken at a threshold before the evidence is in, and the evidence here costs a median 0.55 s: the
spots are already found and cached, and their positions do not depend on the centre, so a second
first pass is a median 20 % of what the first one costs.

Worse, the gate removed exactly the case worth asking about. A centre error ALONG the spindle does
not fail - it holds 96-100 % of frames indexed and quietly returns an axis harmonic - and the scale
it turns on is a fraction of a pixel: measured on real data, 0.12 px of centre is the whole
difference between the deposited cell and a halved axis. No sigma small enough to gate on would
notice that, so the gate could only ever hide the question, never answer it.

The three-sigma test stays where it belongs, in the report: it says whether the difference is a
measurement or noise, which is what a reader wants to know. It no longer decides whether to look.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3yNBXk4wKdMZy1ak2NY7f
This commit is contained in:
2026-08-30 10:28:35 +02:00
co-authored by Claude Opus 5
parent d8816169e1
commit bf41b1fab1
+10 -3
View File
@@ -894,7 +894,9 @@ void Rugnux::PreScan(int start_image, int images_to_process, int frame_count, Ru
// g', confidently. A move it cannot resolve is not evidence about the header either way.
if (moved <= 3.0f * background_center_->sigma_pxl)
logger.Info("Beam centre check: that is under three times the fit's own sigma, so the "
"file's centre is as good as this measurement can tell");
"file's centre is as good as this measurement can tell - the second first "
"pass below indexes it anyway, because a fraction of a pixel is enough to "
"decide a cell and no sigma small enough to gate on would notice");
else if (across && *across > need)
// Info and not a warning: `need` is where the FFT amplitude at a 200 A axis has fallen
// to 0.70, which is a floor under what matters and not a threshold for failure - it is
@@ -2224,8 +2226,13 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
const float measured_x = measured.beam_x_pxl;
const float measured_y = measured.beam_y_pxl;
const float moved = std::hypot(measured_x - header_x, measured_y - header_y);
// A move the fit cannot resolve is not a different hypothesis, only the same one twice.
if (moved > 3.0f * measured.sigma_pxl) {
// No gate on how significant the move is. It is tempting to skip the second pass when the
// two centres are within the fit's own noise - it would save it on a quarter of the runs
// here - but that is deciding at a threshold before the evidence is in, and the evidence
// is cheap. It would also gate away exactly the case worth asking about: measured on real
// data, a 0.12 px change of centre is the whole difference between the deposited cell and
// a halved axis, and 0.12 px is well inside the sigma of any fit that could gate it.
{
const int majority = static_cast<int>(validation.size()) / 2;
experiment_.BeamX_pxl(measured_x).BeamY_pxl(measured_y);
FirstPass alt = pick_best(*indexer_pool, *indexer);