From 3d631d2f69b062466074bc4b9a5124500fa6621b Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Sun, 8 Nov 2026 09:01:33 +0100 Subject: [PATCH] RotationIndexer: drop real_time Only rugnux runs the rotation indexer now, and it always passed false: the candidate-cell refinements are bounded by iteration count, the post-search fixed-point walk and tilt hold always run, and the memo key loses the flag. IndexAndRefine keeps its own real_time, which bounds the per-image refinement of the online receivers and live stills. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK --- image_analysis/IndexAndRefine.cpp | 2 +- image_analysis/IndexAndRefine.h | 8 +++---- .../rotation_indexer/RotationIndexer.cpp | 22 +++++++++---------- .../rotation_indexer/RotationIndexer.h | 9 +++----- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/image_analysis/IndexAndRefine.cpp b/image_analysis/IndexAndRefine.cpp index 7f8e982ce..0a12975da 100644 --- a/image_analysis/IndexAndRefine.cpp +++ b/image_analysis/IndexAndRefine.cpp @@ -72,7 +72,7 @@ IndexAndRefine::IndexAndRefine(const DiffractionExperiment &x, IndexerThreadPool experiment(x), indexer_(indexer) { if (indexer && x.IsRotationIndexing()) - rotation_indexer = std::make_unique(x, *indexer, real_time); + rotation_indexer = std::make_unique(x, *indexer); // Only retain the whole-run per-image reflections when a later scaling/merge pass will read them. if (retain_outcomes_) { integration_outcome.resize(x.GetImageNum()); diff --git a/image_analysis/IndexAndRefine.h b/image_analysis/IndexAndRefine.h index ffe2bbe6f..5809a8864 100644 --- a/image_analysis/IndexAndRefine.h +++ b/image_analysis/IndexAndRefine.h @@ -221,10 +221,10 @@ class IndexAndRefine { std::unique_ptr reindex_resolver; void ScaleImage(DataMessage &msg, IntegrationOutcome& outcome); public: - // real_time: bound the geometry refinements - the per-image one here and the candidate-cell ones - // in the rotation indexer - by WALL CLOCK, as online acquisition must, it having a real budget. - // Offline (rugnux, the viewer) passes false and they are bounded by iteration count instead, so the - // same file reprocesses to the same answer regardless of what else the machine was doing. + // real_time: bound the per-image geometry refinement by WALL CLOCK, as online acquisition must, it + // having a real budget. Offline (rugnux, the viewer) passes false and it is bounded by iteration + // count instead, so the same file reprocesses to the same answer regardless of what else the + // machine was doing. IndexAndRefine(const DiffractionExperiment &x, IndexerThreadPool *indexer, bool retain_outcomes = true, bool real_time = false); diff --git a/image_analysis/rotation_indexer/RotationIndexer.cpp b/image_analysis/rotation_indexer/RotationIndexer.cpp index 09b52f45e..2bf090d1c 100644 --- a/image_analysis/rotation_indexer/RotationIndexer.cpp +++ b/image_analysis/rotation_indexer/RotationIndexer.cpp @@ -43,7 +43,7 @@ namespace { // How far from a whole number the volume ratio may sit and still count as an axis multiple. constexpr double ROT_SUPERCELL_INTEGER_TOL = 0.15; - // Iteration bound for the candidate-cell refinements when the run is not real-time. Without one, + // Iteration bound for the candidate-cell refinements. Without one, // XtalOptimizerData falls back to its wall-clock bound and the cell a candidate refines to - and // therefore which candidate wins - depends on how busy the machine was. Same value as Ceres' own // default iteration limit, so it only bites where the clock was biting before. @@ -116,11 +116,9 @@ namespace { } } -RotationIndexer::RotationIndexer(const DiffractionExperiment &x, IndexerThreadPool &indexer, - bool real_time) +RotationIndexer::RotationIndexer(const DiffractionExperiment &x, IndexerThreadPool &indexer) : experiment(x), index_ice_rings(x.GetIndexingSettings().GetIndexIceRings()), - real_time(real_time), v_(experiment.GetImageNum()), angle_deg_(experiment.GetImageNum()), axis_(x.GetGoniometer()), @@ -211,7 +209,7 @@ namespace { } std::vector RotationIndexer::InputKey() const { - std::vector k{static_cast(real_time), static_cast(v_.size()), + std::vector k{static_cast(v_.size()), static_cast(experiment.GetImageNum()), static_cast(experiment.GetSpaceGroupOrP1().number)}; if (const auto uc = experiment.GetUnitCell()) @@ -374,7 +372,7 @@ void RotationIndexer::RunIndexingLocked() { .refine_detector_angles = true, .refine_rotation_axis = true, .index_ice_rings = experiment.GetIndexingSettings().GetIndexIceRings(), - .max_iterations = real_time ? 0 : ROT_REFINE_ITERATIONS, + .max_iterations = ROT_REFINE_ITERATIONS, .axis = orig_axis }; if (d.crystal_system == gemmi::CrystalSystem::Trigonal) @@ -578,7 +576,7 @@ void RotationIndexer::RunIndexingLocked() { // answer near the header into a per-step limit that lets the pair walk to their joint optimum - // pinning its anchor across the rounds instead leaves the tilt at 0.014 deg and the data worse // than doing nothing. The winner only: the other candidates are discarded, and these solves are - // the dominant first-pass cost. Not in real time, where the budget is wall-clock. + // the dominant first-pass cost. // // The chain is a trajectory, and its last point is not always its best one. Every solve ends // by fitting only the spots inside its tightest gate (0.1), so a cell with a direction the @@ -662,7 +660,7 @@ void RotationIndexer::RunIndexingLocked() { best = std::move(kept); return best; }; - if (have_best && !real_time) + if (have_best) best_data = drive_to_fixed_point(best_data); // The tilt is refined freely above because on a sweep whose spots reach far enough in 2theta @@ -694,7 +692,7 @@ void RotationIndexer::RunIndexingLocked() { // tilt standing only when it leads by more than the count's own noise. Held, not bounded - a // box the fit lands on is the same wrong answer at a smaller size. constexpr double ROT_TILT_PRIOR_DEG = 1.0; - if (have_best && !real_time) { + if (have_best) { const double walk_deg = std::hypot(best_data.geom.GetPoniRot1_rad() - geom_.GetPoniRot1_rad(), best_data.geom.GetPoniRot2_rad() - geom_.GetPoniRot2_rad()) * 180.0 / PI; @@ -731,7 +729,7 @@ void RotationIndexer::RunIndexingLocked() { } } } - if (have_best && !real_time) + if (have_best) best_frac = IndexedFraction(best_data.latt, accumulate(best_data.geom, best_data.axis), index_tol); if (have_best) { @@ -783,7 +781,7 @@ void RotationIndexer::RunIndexingLocked() { .refine_unit_cell = false, .refine_rotation_axis = false, .index_ice_rings = experiment.GetIndexingSettings().GetIndexIceRings(), - .max_iterations = real_time ? 0 : ROT_REFINE_ITERATIONS, + .max_iterations = ROT_REFINE_ITERATIONS, .axis = axis_ }; @@ -877,7 +875,7 @@ RotationIndexer::RefineConstrained(const CrystalLattice &latt, gemmi::CrystalSys .refine_detector_angles = false, .refine_rotation_axis = false, .index_ice_rings = index_ice_rings, - .max_iterations = real_time ? 0 : ROT_REFINE_ITERATIONS, + .max_iterations = ROT_REFINE_ITERATIONS, .axis = axis_ }; diff --git a/image_analysis/rotation_indexer/RotationIndexer.h b/image_analysis/rotation_indexer/RotationIndexer.h index fa37bd774..fded3566d 100644 --- a/image_analysis/rotation_indexer/RotationIndexer.h +++ b/image_analysis/rotation_indexer/RotationIndexer.h @@ -97,7 +97,6 @@ private: bool verify_memo = false; const bool index_ice_rings; - const bool real_time; // see the constructor std::vector> v_; // Per-image rotation angle (mid-exposure, deg) supplied by the caller; falls back to the @@ -129,11 +128,9 @@ private: [[nodiscard]] IndexingOutcome Outcome() const; void RunIndexingLocked(); public: - // real_time: bound the candidate-cell refinements by WALL CLOCK, as online acquisition must - it - // has a real budget. Offline (rugnux, the viewer) passes false and they are bounded by iteration - // count instead, so the same file reprocesses to the same cell regardless of what else the machine - // was doing at the time. - RotationIndexer(const DiffractionExperiment& x, IndexerThreadPool& indexer, bool real_time = false); + // The candidate-cell refinements are bounded by iteration count, not wall clock, so the same file + // reprocesses to the same cell regardless of what else the machine was doing at the time. + RotationIndexer(const DiffractionExperiment& x, IndexerThreadPool& indexer); // Keep only this many spots per image in the accumulated cloud (strongest, non-ring first). void MaxSpotsPerImage(size_t n) { max_spots_per_image = n > 0 ? n : DEFAULT_MAX_SPOTS_PER_IMAGE; } // Recompute on a memo hit and throw if the two differ - the tests' check that the memo is exact.