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) <noreply@anthropic.com>
This commit is contained in:
@@ -5,32 +5,6 @@
|
||||
#include <algorithm> // std::nth_element
|
||||
#include <cmath> // std::fabs
|
||||
|
||||
std::optional<float> FitProfileRadius_MAD(const std::vector<SpotToSave>& xs) {
|
||||
std::vector<float> 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<float> FitProfileRadius(const std::vector<SpotToSave>& spots,
|
||||
float bandwidth_sigma, float wavelength_A) {
|
||||
double sum_squares = 0.0; // measured excitation-error variance (sum dist_ewald^2)
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
#include "../../common/SpotToSave.h"
|
||||
|
||||
std::optional<float> FitProfileRadius_MAD(const std::vector<SpotToSave>& 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
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user