rugnux: remove --single-pass-rotation

Rotation data are indexed in two passes, always: the online-like single pass
(--single-pass-rotation[=deg]) is gone with its min-angular-range argument,
and ProcessConfig::two_pass_rotation, which only that flag ever set false,
goes with it and with the conditions that read it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-11-08 08:41:10 +01:00
1 parent b00e2fbe19
commit 5d24eac3ce
9 files changed
+12 -43

No files matched your search

+2 -3
View File
@@ -242,8 +242,8 @@ The radial profile behind the per-image ice-ring score, and what `--mode azint`
A dataset with a **rotation goniometer axis** is processed as rotation data (two-pass rotation
indexing) by default; a dataset without one is processed as independent stills. `--force-still`
overrides the former; the `-R` / `--single-pass-rotation` / `--force-rotation-lattice` flags request
rotation explicitly and pick the pass or lattice.
overrides the former; the `-R` / `--force-rotation-lattice` flags request rotation explicitly and
set the first pass's image count or skip it with a given lattice.
The FFT search looks for cell axes between 10 Å (`--fft-min-unit-cell`) and a default **longest
axis of 500 Å**, which has no flag of its own — a reference cell (`-C`) moves both bounds to cover
@@ -270,7 +270,6 @@ For very long axes the direction grid's angular resolution binds well below 500
| `-S, --space-group <num\|symbol>` | Space group number (`96`) or Hermann-Mauguin symbol (`P43212`) — for indexing and scaling. On serial stills whose group admits two indexings, it also lets the crystals be put on one from the data ([the indexing ambiguity](RUGNUX_ADVANCED.md#the-indexing-ambiguity)) |
| `-r, --geom-refine <txt>` | Geometry refinement: `none` \| `orientation` \| `beam_and_lattice` (default) \| `flex` (try all three per image, keep whichever indexes the most spots; alias `multi`) |
| `-R, --two-pass-rotation[=num]` | Two-pass offline rotation indexing (default for goniometer data; optional first-pass image count, default 100) |
| `--single-pass-rotation[=num]` | Online-like single-pass rotation indexing (optional min angular range, deg) |
| `--force-rotation-lattice <vec>` | Force rotation lattice (9 floats, Å), skipping the first pass |
| `--rotation-no-postrefine` | Rotation: disable the default-on two-pass geometry post-refine (see [Rotation data](RUGNUX_TUTORIAL.md#rotation-data)) |
| `--prepass-fraction <f>` | Rotation: the two-pass geometry pre-pass integrates the first fraction `f` of the sweep, but at least 120° of it (0.05-1, default **0.5**; `1` integrates the whole sweep twice). The second pass always integrates every frame. The pre-scan and the first-pass indexing sample the same part of the sweep |
+4 -4
View File
@@ -883,7 +883,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
// The run as this pass finds it, for the re-check of a near tie its first pass may decide early.
std::shared_ptr<Rugnux> recheck;
if (!decide_late_ && !config_.fast_live && config_.mode == ProcessMode::FullAnalysis && config_.rotation_indexing
&& config_.two_pass_rotation && !force_rotation_result_.has_value()
&& !force_rotation_result_.has_value()
&& !config_.forced_rotation_lattice.has_value() && experiment_.IsRotationIndexing())
recheck = std::make_shared<Rugnux>(*this);
@@ -1024,7 +1024,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
// that pass found before anything is built for it - the mapping below alone is 0.15 s on a 16M
// detector. Nothing between here and the first pass changes what the key reads.
if (full && indexing_probe_only_ && !force_rotation_result_.has_value()
&& !config_.forced_rotation_lattice.has_value() && config_.rotation_indexing && config_.two_pass_rotation
&& !config_.forced_rotation_lattice.has_value() && config_.rotation_indexing
&& !config_.verify_first_pass_memo) {
const std::vector<double> first_pass_key = FirstPassInputKey();
const auto memo = std::find_if(first_pass_memo_.begin(), first_pass_memo_.end(),
@@ -1142,7 +1142,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
// parallel (the main per-image loop then uses the forced lattice, no FFT). Size the pool to
// those two so a rotation run doesn't pay to initialise (cuFFT plan + GPU alloc) indexers it
// never uses. Stills fire the FFT once per image across all workers, so keep the default there.
if (config_.rotation_indexing && config_.two_pass_rotation && experiment_.IsRotationIndexing()) {
if (config_.rotation_indexing && experiment_.IsRotationIndexing()) {
indexing_settings.IndexingThreads(FirstPassIndexerThreads());
// Two indexer threads leave the rest of the machine idle for the whole first pass, which is
// a third of the run. Split each scheme's candidate-cell refinement over -N/2 threads so the
@@ -1250,7 +1250,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
} else if (indexing && config_.forced_rotation_lattice.has_value()) {
indexer->ForceRotationIndexerLattice(*config_.forced_rotation_lattice);
logger.Info("Rotation indexer lattice forced externally - skipping first pass");
} else if (indexing && config_.rotation_indexing && config_.two_pass_rotation) {
} else if (indexing && config_.rotation_indexing) {
TimingMark mark(logger, "first-pass indexing");
const bool probe_done = FirstPassRotationIndexing(p);
if (recheck && near_tie_carried_)
-1
View File
@@ -154,7 +154,6 @@ struct ProcessConfig {
// Rotation indexing (FullAnalysis)
bool rotation_indexing = false;
bool two_pass_rotation = true;
int rotation_indexing_image_count = 100;
// The geometry pre-pass integrates this fraction of the sweep, from its start (pass 2 reads all of it).
double prepass_fraction = 0.5;
+3 -6
View File
@@ -192,12 +192,9 @@ std::string RugnuxCommandLine(const ProcessConfig &config,
add("--integration-high-resolution", num(*d_min));
if (config.rotation_indexing) {
if (config.two_pass_rotation)
// -R takes an optional argument, which getopt only accepts attached (-R100), never as a
// separate token - so emit it joined or the copied command line will not re-parse.
args.push_back("-R" + std::to_string(config.rotation_indexing_image_count));
else
args.emplace_back("--single-pass-rotation");
// -R takes an optional argument, which getopt only accepts attached (-R100), never as a
// separate token - so emit it joined or the copied command line will not re-parse.
args.push_back("-R" + std::to_string(config.rotation_indexing_image_count));
} else if (experiment.GetGoniometer().has_value()) {
// rotation dataset processed as stills -> the user overrode the default with --force-still
args.emplace_back("--force-still");
-1
View File
@@ -48,7 +48,6 @@ RugnuxLiveSetup MakeRugnuxLiveSetup(const JFJochReaderDataset &dataset, int nthr
c.export_unmerged = false;
c.spot_finding = RugnuxDefaultSpotFinding(true);
c.rotation_indexing = true;
c.two_pass_rotation = true;
c.rotation_postrefine_geometry = true;
c.rotation_indexing_image_count = RUGNUX_ROTATION_INDEXING_IMAGES;
c.detect_beam_stop = RUGNUX_DETECT_BEAM_STOP_FRAMES;
+3 -23
View File
@@ -227,7 +227,6 @@ enum {
OPT_RESOLUTION_CC_TARGET,
OPT_RESOLUTION_SHELLS,
OPT_REPORT_RESOLUTION,
OPT_SINGLE_PASS_ROTATION,
OPT_FORCE_ROTATION_LATTICE,
OPT_MIN_INDEXED_SPOTS,
OPT_ROTATION_NO_POSTREFINE,
@@ -372,7 +371,6 @@ static option long_options[] = {
{"geom-refine", required_argument, nullptr, 'r'},
{"two-pass-rotation", optional_argument, nullptr, 'R'},
{"single-pass-rotation", optional_argument, nullptr, OPT_SINGLE_PASS_ROTATION},
{"force-still", no_argument, nullptr, OPT_FORCE_STILL},
{"azim-q-spacing", required_argument, nullptr, 'q'},
{"azim-min-q", required_argument, nullptr, OPT_AZIM_MIN_Q},
@@ -885,7 +883,6 @@ struct RugnuxCli {
bool calibration_refine_tilt = true; // --no-refine-tilt
bool rotation_indexing = false;
bool force_still = false; // --force-still: process a rotation dataset as stills (indexing + scaling)
bool two_pass_rotation = true;
// Set by any spot-finding option. The two-pass rotation first pass reuses the spots stored in the
// file when it has them, so those options would otherwise not reach the pass that determines the
// lattice - the setting would appear to do nothing at all on rotation data.
@@ -893,7 +890,6 @@ struct RugnuxCli {
std::optional<float> rotation_scale; // --rotation-scale: asserted stage calibration
int rotation_indexing_image_count = RUGNUX_ROTATION_INDEXING_IMAGES;
double prepass_fraction = 0.5;
std::optional<float> rotation_indexing_range;
std::optional<int64_t> min_indexed_spots; // --min-indexed-spots: spots on the lattice for a frame to count as indexed
bool run_scaling = true; // merge is on by default; --no-merge turns it off
std::optional<bool> scale_fulls_arg; // --scale-fulls / --no-scale-fulls; default on for rot3d
@@ -1109,22 +1105,10 @@ int RugnuxCli::Parse(int argc, char **argv) {
exit(EXIT_FAILURE);
}
rotation_indexing = true;
two_pass_rotation = true;
if (optarg)
rotation_indexing_image_count = atoi(optarg);
break;
case OPT_SINGLE_PASS_ROTATION:
if (rotation_indexing) {
logger.Error("Rotation indexing already enabled");
exit(EXIT_FAILURE);
}
rotation_indexing = true;
two_pass_rotation = false;
if (optarg)
rotation_indexing_range = atof(optarg);
break;
case OPT_ROTATION_NO_POSTREFINE:
rotation_postrefine_geometry = false;
break;
@@ -3340,13 +3324,13 @@ int RugnuxInputRun::PrepareRugnuxInput() {
// Rotation vs stills. A dataset collected on a rotation goniometer is processed as rotation data
// (two-pass indexing) by default; --force-still forces per-frame stills. The rotation flags
// (-R / --single-pass-rotation / --force-rotation-lattice) still request rotation explicitly and
// choose the pass/lattice; at this point they show up as rotation_indexing already being set.
// (-R / --force-rotation-lattice) still request rotation explicitly and
// choose the first-pass size/lattice; at this point they show up as rotation_indexing already being set.
// A stationary axis is stills (RugnuxRotationData).
const bool has_goniometer = RugnuxRotationData(experiment);
if (force_still) {
if (rotation_indexing) {
logger.Error("--force-still conflicts with -R / --single-pass-rotation / --force-rotation-lattice");
logger.Error("--force-still conflicts with -R / --force-rotation-lattice");
throw RugnuxInputFailed{EXIT_FAILURE};
}
if (has_goniometer)
@@ -3356,7 +3340,6 @@ int RugnuxInputRun::PrepareRugnuxInput() {
experiment.Goniometer(std::nullopt);
} else if (!rotation_indexing && has_goniometer) {
rotation_indexing = true;
two_pass_rotation = true;
logger.Info("Dataset has a rotation goniometer axis: processing as rotation data (two-pass "
"indexing). Use --force-still to treat it as stills.");
}
@@ -3391,8 +3374,6 @@ int RugnuxInputRun::PrepareRugnuxInput() {
indexing_settings.Algorithm(indexing_algorithm);
if (min_indexed_spots) indexing_settings.ViableCellMinSpots(min_indexed_spots.value());
indexing_settings.RotationIndexing(rotation_indexing);
if (rotation_indexing_range.has_value())
indexing_settings.RotationIndexingMinAngularRange_deg(rotation_indexing_range.value());
indexing_settings.GeomRefinementAlgorithm(refinement_algorithm);
indexing_settings.IndexIceRings(index_ice_rings);
if (fft_min_unit_cell_A) indexing_settings.FFT_MinUnitCell_A(fft_min_unit_cell_A.value());
@@ -3641,7 +3622,6 @@ int RugnuxInputRun::PrepareRugnuxInput() {
config.nthreads_auto = nthreads_auto;
config.spot_finding = spot_settings;
config.rotation_indexing = rotation_indexing;
config.two_pass_rotation = two_pass_rotation;
config.detect_beam_stop = detect_beam_stop;
config.beam_center_check = beam_center_check;
config.detector_distance_given = detector_distance_mm.has_value();
-3
View File
@@ -69,7 +69,6 @@ TEST_CASE("Rugnux_Rotation", "[large]") {
config.spot_finding = DiffractionExperiment::DefaultDataProcessingSettings();
config.spot_finding.indexing = true;
config.rotation_indexing = true;
config.two_pass_rotation = true;
Rugnux process(reader, experiment, *dataset->pixel_mask, config);
ProcessResult result;
@@ -114,7 +113,6 @@ TEST_CASE("Rugnux_FirstPassMemoMatchesRecomputation", "[large]") {
config.spot_finding = DiffractionExperiment::DefaultDataProcessingSettings();
config.spot_finding.indexing = true;
config.rotation_indexing = true;
config.two_pass_rotation = true;
config.rotation_postrefine_geometry = true;
config.run_scaling = true;
config.verify_first_pass_memo = true;
@@ -159,7 +157,6 @@ TEST_CASE("Rugnux_HostBudgetChangesOnlyTheSchedule", "[large]") {
config.spot_finding = DiffractionExperiment::DefaultDataProcessingSettings();
config.spot_finding.indexing = true;
config.rotation_indexing = true;
config.two_pass_rotation = true;
config.rotation_postrefine_geometry = true;
config.run_scaling = true;
config.host_memory_budget_bytes = budget;
-1
View File
@@ -421,7 +421,6 @@ TEST_CASE("RugnuxCommandLine_Full", "[process]") {
config.nthreads = 8;
config.end_image = 500;
config.rotation_indexing = true;
config.two_pass_rotation = true;
config.rotation_indexing_image_count = 30;
config.spot_finding = DiffractionExperiment::DefaultDataProcessingSettings();
@@ -382,7 +382,6 @@ ProcessConfig JFJochProcessingJobsWindow::buildConfig(const JobSpec &spec, const
// Rotation indexing follows the panel's rotation axis (= the experiment's indexing
// setting); the first pass finds the lattice on as many images as rugnux's.
config.rotation_indexing = inputs.experiment.GetIndexingSettings().GetRotationIndexing();
config.two_pass_rotation = true;
config.rotation_indexing_image_count = RUGNUX_ROTATION_INDEXING_IMAGES;
// Measured in the pre-scan on rotation data, unless the panel's radii were changed by hand.
config.adaptive_integration_radius = config.rotation_indexing && !radii_set_by_hand(inputs.experiment);