diff --git a/rugnux/Rugnux.h b/rugnux/Rugnux.h index 24b901afc..f63008b89 100644 --- a/rugnux/Rugnux.h +++ b/rugnux/Rugnux.h @@ -938,6 +938,11 @@ class Rugnux { // lattice on the validation frames (ProcessResult::validation_evidence), before a single image is // integrated. The rotation-scale walk (see RunAllPasses) compares angle models on that score. bool indexing_probe_only_ = false; + // Where a pass stops right after its first pass: when that pass ends exactly on this outcome + // (first_pass_outcome_). The demoted arm of the metric-symmetry check (see RefinedPasses) is + // given the promoted class's own, because a demoted arm that indexes the same lattice the same + // way integrates the same reflections and can only tie. + std::string stop_at_first_pass_outcome_; // --- Near ties: decided early, re-checked late (see FirstPassRun::DecideLate) --- // diff --git a/rugnux/RugnuxFirstPass.cpp b/rugnux/RugnuxFirstPass.cpp index 7f8281b00..e1bd20d49 100644 --- a/rugnux/RugnuxFirstPass.cpp +++ b/rugnux/RugnuxFirstPass.cpp @@ -444,6 +444,11 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) { 100.0 * verdict.pooled, 100.0 * verdict.pooled_chance); return true; } + if (!stop_at_first_pass_outcome_.empty() && first_pass_outcome_ == stop_at_first_pass_outcome_) { + logger.Info("First pass: scheme '{}' ends exactly where the promoted class's first pass does, " + "so this pass stops before integrating", best.name); + return true; + } run.RejectChanceLattice(best, verdict); run.AdoptLattice(best, verdict); if (!p.geometry_prepass && !postrefine_probe_only_) diff --git a/rugnux/RugnuxPasses.cpp b/rugnux/RugnuxPasses.cpp index 212851a93..77eb1b006 100644 --- a/rugnux/RugnuxPasses.cpp +++ b/rugnux/RugnuxPasses.cpp @@ -916,6 +916,26 @@ std::optional Rugnux::RefinedPasses(RugnuxObserver *observer, Pro // budget its first pass measures, which the canonical pass would otherwise start from - // except the geometry its post-refinement asks for, which the promoted arm reads (see // ScaleMergeAndSymmetry), and its first-pass memo. + // + // Before it, the promoted class's own first pass, as an indexing probe: the demoted arm + // stops after its first pass where that ends exactly where this one does. Released from the + // equality, the indexing can still find the same lattice - on the same spots, by the same + // path, to the last bit - and then it integrates the same reflections the canonical pass + // will and realises the same residual, which cannot beat it. Measured: an I-centred + // orthorhombic arm integrated a whole sweep to report "I-ortho does not beat I-ortho". + Rugnux promoted_probe(*this); + promoted_probe.indexing_probe_only_ = true; + promoted_probe.postrefine_probe_ = false; + promoted_probe.first_pass_memo_.clear(); // a memo answers without the outcome + try { + promoted_probe.RunPipeline(observer, /*write_output=*/false, /*geometry_prepass=*/false); + } catch (const std::exception &e) { + if (IsFatalResourceError(e)) throw; + promoted_probe.first_pass_outcome_.clear(); + } + ++arm_passes; + for (const auto &m : promoted_probe.first_pass_memo_) + AddFirstPassMemo(m); Rugnux demoted_run(*this); demoted_run.postrefine_probe_ = true; demoted_run.postrefine_probe_only_ = true; @@ -923,12 +943,25 @@ std::optional Rugnux::RefinedPasses(RugnuxObserver *observer, Pro demoted_settings.LatticeClassDemoted(length_arm); demoted_run.experiment_.ImportIndexingSettings(demoted_settings); demoted_run.lattice_angles_demoted_ = angle_arm; + demoted_run.stop_at_first_pass_outcome_ = promoted_probe.first_pass_outcome_; demoted_arm = demoted_run.RunPipeline(observer, /*write_output=*/false, /*geometry_prepass=*/false); ++arm_passes; - prepass_detector_geometry_ = demoted_run.prepass_detector_geometry_; for (const auto &m : demoted_run.first_pass_memo_) AddFirstPassMemo(m); - lattice_arm_check_ = LatticeArmCheck{.demoted = demoted_arm->post_refine}; + const bool same_first_pass = !promoted_probe.first_pass_outcome_.empty() + && demoted_run.first_pass_outcome_ == promoted_probe.first_pass_outcome_; + if (same_first_pass) { + logger.Info("Metric symmetry: released from the equality the first pass ends exactly where " + "the promoted class's does, so the two arms are one and the higher symmetry " + "stands with nothing to compare"); + lattice_arm = fmt::format( + "the metric symmetry stands: released from the {} the first pass indexes the same " + "lattice, so there was nothing to compare", length_arm ? "a = b equality" : "90 deg angle"); + note("the metric symmetry stands: the demoted arm indexes the same lattice"); + } else { + prepass_detector_geometry_ = demoted_run.prepass_detector_geometry_; + lattice_arm_check_ = LatticeArmCheck{.demoted = demoted_arm->post_refine}; + } } else { logger.Info("Metric symmetry: {} - the free refinement departs from it by {:.2f} {}, " "{:.1f} px at the far corner, inside the integration disc of {:.1f} px, so "