From f25fea70243e6b1f52d84819e5ba468a29cd6ee1 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 3 Aug 2026 22:08:21 +0200 Subject: [PATCH] rugnux: keep 1000 spots per image instead of 250 Offline reprocessing is not bound by the online spot budget, and the cap is applied at the end of SpotAnalyze, so it is exactly the spot list the indexer and the per-image refinement see. jfjoch_viewer already sends 1000, so the two front ends now agree on the same file. Measured as a paired A/B over the 37-crystal rotation battery, de novo, with the resolution and Friedel setting matched to the XDS reference, both arms from the same binary bar this constant: R_meas low shell 16 better 0 worse 19 unchanged R_meas 14 better 4 worse 17 unchanged ISa 14 better 6 worse 15 unchanged CC1/2 6 better 3 worse 26 unchanged Low-resolution R_meas is a clean sweep. Around half the battery is bit-identical: those frames never reach 250 spots, so the cap never bound. Wall clock is unchanged (10m00s vs 10m44s, uncontrolled for page cache). Known cost, and the reason this is its own commit: one crystal in the battery reproducibly loses symmetry, tetragonal 422 -> orthorhombic 222, doubling its asymmetric unit. Its R_meas and ISa "improve" there, but that is what merging in too low a symmetry always does, and the lower symmetry then admits a merohedral indexing ambiguity. An intermediate cap of 500 demotes it too, so it buys none of the safety. This is the known point-group-decision-moves-with-data-amount fragility of the space-group search rather than an argument for starving the indexer of spots - the search is the thing to fix. Co-Authored-By: Claude Opus 5 (1M context) --- docs/RUGNUX.md | 2 +- rugnux/rugnux_cli.cpp | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/RUGNUX.md b/docs/RUGNUX.md index 0a6fe4df..c8baafa1 100644 --- a/docs/RUGNUX.md +++ b/docs/RUGNUX.md @@ -210,7 +210,7 @@ Spot finding: | `--spot-high-resolution ` | High-resolution limit for spot finding, Å. Omitted (or 0): no resolution clipping — spot finding extends as far as the detector reaches, for rotation data as well as stills | | `--spot-low-resolution ` | Low-resolution limit for spot finding, Å (default: 50; lower it, e.g. 24, to exclude the direct-beam halo on weak serial data) | | `--min-pix-per-spot ` | Minimum connected strong pixels per spot. **If omitted, min-pix is chosen per image** (stills indexing): the frame is indexed at min-pix 3/2/1 and the one maximising indexed-spot count × indexed fraction is kept. Give an explicit value to force a fixed min-pix instead. | -| `--max-spots ` | Maximum spot count (default: 250) | +| `--max-spots ` | Maximum spots kept per image (the strongest ones) and handed to indexing (default: 1000) | | `--detect-ice-rings[=on\|off]` | Flag ice-ring spots (de-prioritised in indexing) and exclude ice-ring reflections from scaling/merging; overrides the dataset/master-file setting (default: use the dataset value) | Azimuthal integration (the radial profile behind the per-image ice-ring score): diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index 71c3a34b..65417482 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -38,6 +38,14 @@ #include "RugnuxDefaults.h" #include "ModelValidation.h" +// Spots kept per image (the strongest ones) and handed to indexing. Offline reprocessing is not +// bound by the online spot budget, so this is rugnux's own default rather than the 250 the +// DatasetSettings constructor uses for the detector. Measured over the 37-crystal rotation battery, +// lifting it to 1000 leaves low-resolution R_meas better or equal on every crystal (16 better, 0 +// worse, the rest untouched because their frames never reach the cap), with R_meas 14/4 and ISa 14/6 +// in its favour and no measurable cost in wall clock. It is also what jfjoch_viewer already sends. +constexpr int64_t RUGNUX_MAX_SPOT_COUNT = 1000; + // Default rot3d per-frame scale-G smoothing range (XDS DELPHI-like), in degrees of rotation. constexpr double SMOOTH_G_DEFAULT_DEG = 5.0; @@ -69,7 +77,7 @@ void print_usage() { std::cout << " --spot-false-pixels Adaptive detection operating point: expected noise pixels tolerated per frame (default: 100; implies --adaptive-spots)" << std::endl; std::cout << " --spot-high-resolution High resolution limit for spot finding. If omitted (or 0), spot finding is not clipped in resolution and extends as far as the detector reaches" << std::endl; std::cout << " --spot-low-resolution Low resolution limit for spot finding, in A (default: 50; lower it, e.g. 24, to exclude the direct-beam halo on weakly-diffracting serial data)" << std::endl; - std::cout << " --max-spots Max spot count (default: 250)" << std::endl; + std::cout << " --max-spots Max spot count per image, the strongest ones, handed to indexing (default: 1000)" << std::endl; std::cout << " --detect-ice-rings[=on|off] Flag ice-ring spots (de-prioritised in indexing) and exclude ice-ring reflections from scaling/merging; overrides the dataset/master-file setting (default: use dataset value)" << std::endl; std::cout << std::endl; @@ -1442,10 +1450,9 @@ static int RunRugnux(int argc, char **argv) { refine_geometry = 200; } - if (max_spot_count_override.has_value()) { - experiment.MaxSpotCount(max_spot_count_override.value()); + experiment.MaxSpotCount(max_spot_count_override.value_or(RUGNUX_MAX_SPOT_COUNT)); + if (max_spot_count_override.has_value()) logger.Info("Max spot count overridden to {}", max_spot_count_override.value()); - } // X-ray bandwidth: CLI overrides the value carried in the dataset; otherwise // keep whatever the dataset provided (0 / none -> monochromatic).