From c9b52857e075156a4ef8f4f2d3c9d88dbdc27526 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 27 Jul 2026 10:46:52 +0200 Subject: [PATCH] rugnux: warn when --min-image-cc is ignored; drop a dead robust estimator --min-image-cc is consumed only by the stills merge (MergeOnTheFly); RotationScaleMerge never reads it. On rotation data it was accepted and then silently did nothing, so a run that looked filtered was not. It now says so. FitProfileRadius_MAD had zero callers - a robust twin sitting uncalled next to the non-robust estimator that is actually used is a trap, so it goes. Neither changes any result: verified on a rotation dataset (indexing rate, cell, space group and merge statistics identical, warning emitted). Context for anyone tempted to wire that estimator in: I tested exactly that today and it is NOT justified. The population it would clip is truncated by construction - a spot is only marked `indexed` when its fractional-Miller norm is inside the indexing tolerance - and is measurably shorter-tailed than Gaussian (kurtosis 2.85). Across four serial-stills datasets a MAD-clipped variant only narrowed the prediction window (-17% integrated reflections everywhere), which was neutral on strong data and destroyed real signal on weak data (one set lost completeness 96.0 -> 93.9%), with R-free 0.3753 -> 0.3767. Co-Authored-By: Claude Opus 5 (1M context) --- image_analysis/indexing/FitProfileRadius.cpp | 26 -------------------- image_analysis/indexing/FitProfileRadius.h | 2 -- rugnux/rugnux_cli.cpp | 4 +++ 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/image_analysis/indexing/FitProfileRadius.cpp b/image_analysis/indexing/FitProfileRadius.cpp index bd63a708..43ef5f2f 100644 --- a/image_analysis/indexing/FitProfileRadius.cpp +++ b/image_analysis/indexing/FitProfileRadius.cpp @@ -5,32 +5,6 @@ #include // std::nth_element #include // std::fabs -std::optional FitProfileRadius_MAD(const std::vector& xs) { - std::vector absx; - absx.reserve(xs.size()); - for (const auto &s: xs) { - if (s.indexed) - absx.push_back(std::fabs(s.dist_ewald_sphere)); - } - - if (absx.empty()) - return std::nullopt; - - std::nth_element(absx.begin(), absx.begin() + absx.size() / 2, absx.end()); - float med; - if (absx.size() % 2 == 1) { - med = absx[absx.size() / 2]; - } else { - auto it1 = absx.begin() + (absx.size() / 2 - 1); - auto it2 = absx.begin() + (absx.size() / 2); - float a = *it1; - float b = *it2; - med = 0.5f * (a + b); - } - // Normal consistency factor for MAD - return 1.4826f * med; -} - std::optional FitProfileRadius(const std::vector& spots, float bandwidth_sigma, float wavelength_A) { double sum_squares = 0.0; // measured excitation-error variance (sum dist_ewald^2) diff --git a/image_analysis/indexing/FitProfileRadius.h b/image_analysis/indexing/FitProfileRadius.h index c63f7288..803d04cc 100644 --- a/image_analysis/indexing/FitProfileRadius.h +++ b/image_analysis/indexing/FitProfileRadius.h @@ -8,8 +8,6 @@ #include "../../common/SpotToSave.h" -std::optional FitProfileRadius_MAD(const std::vector& spots); - // Intrinsic excitation-error (mosaicity+divergence) width from the indexed-spot spread. When a finite // energy bandwidth is given, its radial smear (bandwidth_sigma*lambda/(2 d^2)) is deconvolved out, so // the result is the intrinsic width and bandwidth is not double-counted by prediction (which re-adds diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index d1e1e66f..383625d9 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -1425,6 +1425,10 @@ int main(int argc, char **argv) { scaling_settings.CaptureUncertaintyCoeff(capture_uncertainty_arg.value_or(rotation_indexing ? 1.0 : 0.0)); scaling_settings.ForcedMosaicity(forced_mosaicity_arg); scaling_settings.MinCCForImage(min_image_cc / 100.0); // --min-image-cc is in percent; the setting is a fraction + // The per-image CC filter is consumed by the stills merge (MergeOnTheFly) only - RotationScaleMerge + // never reads it - so on rotation data it would otherwise be accepted and silently do nothing. + if (min_image_cc > 0.0 && rotation_indexing) + logger.Warning("--min-image-cc is ignored for rotation data: it filters the stills merge only"); scaling_settings.OutlierRejectNsigma( outlier_reject_nsigma.value_or(rotation_indexing ? REJECT_OUTLIERS_DEFAULT_NSIGMA : 0.0));