From 5d24eac3cee66e0b7f4199b1924e0dd0f144f546 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Sun, 8 Nov 2026 08:41:10 +0100 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK --- docs/RUGNUX_CLI.md | 5 ++-- rugnux/Rugnux.cpp | 8 +++--- rugnux/Rugnux.h | 1 - rugnux/RugnuxCommandLine.cpp | 9 +++---- rugnux/RugnuxLive.cpp | 1 - rugnux/rugnux_cli.cpp | 26 +++---------------- tests/RugnuxLargeTest.cpp | 3 --- tests/RugnuxTest.cpp | 1 - viewer/windows/JFJochProcessingJobsWindow.cpp | 1 - 9 files changed, 12 insertions(+), 43 deletions(-) diff --git a/docs/RUGNUX_CLI.md b/docs/RUGNUX_CLI.md index 6dcde4822..310140a31 100644 --- a/docs/RUGNUX_CLI.md +++ b/docs/RUGNUX_CLI.md @@ -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 ` | 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 ` | 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 ` | 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 ` | 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 | diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index c74c1fe59..d998dbfd3 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -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 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(*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 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_) diff --git a/rugnux/Rugnux.h b/rugnux/Rugnux.h index b062da167..1d7384164 100644 --- a/rugnux/Rugnux.h +++ b/rugnux/Rugnux.h @@ -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; diff --git a/rugnux/RugnuxCommandLine.cpp b/rugnux/RugnuxCommandLine.cpp index 92f96410d..a668d774a 100644 --- a/rugnux/RugnuxCommandLine.cpp +++ b/rugnux/RugnuxCommandLine.cpp @@ -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"); diff --git a/rugnux/RugnuxLive.cpp b/rugnux/RugnuxLive.cpp index 28ee9d6c4..1732e781c 100644 --- a/rugnux/RugnuxLive.cpp +++ b/rugnux/RugnuxLive.cpp @@ -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; diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index 93ce1abcb..ef1481222 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -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 rotation_scale; // --rotation-scale: asserted stage calibration int rotation_indexing_image_count = RUGNUX_ROTATION_INDEXING_IMAGES; double prepass_fraction = 0.5; - std::optional rotation_indexing_range; std::optional 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 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(); diff --git a/tests/RugnuxLargeTest.cpp b/tests/RugnuxLargeTest.cpp index 95dea17ff..0833e8171 100644 --- a/tests/RugnuxLargeTest.cpp +++ b/tests/RugnuxLargeTest.cpp @@ -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; diff --git a/tests/RugnuxTest.cpp b/tests/RugnuxTest.cpp index 4fc9b27e8..52fcc22a9 100644 --- a/tests/RugnuxTest.cpp +++ b/tests/RugnuxTest.cpp @@ -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(); diff --git a/viewer/windows/JFJochProcessingJobsWindow.cpp b/viewer/windows/JFJochProcessingJobsWindow.cpp index 224d1dee1..6e21a97ec 100644 --- a/viewer/windows/JFJochProcessingJobsWindow.cpp +++ b/viewer/windows/JFJochProcessingJobsWindow.cpp @@ -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);