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 <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 09:01:33 +01:00
1 parent 0d2a1c6783
commit 3d631d2f69
4 files changed
+18 -23

No files matched your search

+1 -1
View File
@@ -72,7 +72,7 @@ IndexAndRefine::IndexAndRefine(const DiffractionExperiment &x, IndexerThreadPool
experiment(x),
indexer_(indexer) {
if (indexer && x.IsRotationIndexing())
rotation_indexer = std::make_unique<RotationIndexer>(x, *indexer, real_time);
rotation_indexer = std::make_unique<RotationIndexer>(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());
+4 -4
View File
@@ -221,10 +221,10 @@ class IndexAndRefine {
std::unique_ptr<ReindexAmbiguityResolver> 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);
@@ -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<double> RotationIndexer::InputKey() const {
std::vector<double> k{static_cast<double>(real_time), static_cast<double>(v_.size()),
std::vector<double> k{static_cast<double>(v_.size()),
static_cast<double>(experiment.GetImageNum()),
static_cast<double>(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_
};
@@ -97,7 +97,6 @@ private:
bool verify_memo = false;
const bool index_ice_rings;
const bool real_time; // see the constructor
std::vector<std::vector<SpotToSave>> 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.