From de69e6d0d77f6db9377d254beced9825a4e55891 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Sat, 10 Oct 2026 23:56:15 +0200 Subject: [PATCH] rugnux: the metric-symmetry check's demoted arm runs on a copy; the canonical pass no longer starts from its spot budget The demoted arm is a probe pass, run on the run itself before the canonical pass (the promoted arm). Its first pass measures a spot budget and writes it onto the experiment, and nothing put it back, so the canonical pass started from the probe's budget instead of the full spot list - measured, 446 spots per image instead of 1000 on one in-house crystal (validation spots 22055 instead of 43582) and 58 instead of 1000 on 6yqf. The same held where the arm lost: the demoted canonical pass started from the budget the stopped promoted pass measured. The demoted arm now runs on a copy of the run and gives back only the geometry its post-refinement asks for (which the promoted arm reads, as before) and its first-pass memo; where it wins, the demoted canonical pass starts from the experiment the promoted one started from. The indexing-settings and flag juggling around the probe goes. GPU, gpulock, shared machine, on the 22 sets whose baseline runs the check: p.hkl and MTZ reflection data identical to the base binary on 20; lcystine_x10sa_20keV fails with a cuFFT out-of-memory on the shared card with both binaries (four attempts). Changed: 6yqf only (its canonical first pass now reads the full spot list): merge 3.23 -> 3.15 A, R_meas 0.654 -> 0.568, ISa 4.97 -> 5.36; rugnux model R_free 0.459 -> 0.504 at its own d_min, phenix.model_vs_data R_free at the common 3.23 A 0.443 -> 0.435 (0.443 -> 0.447 at full resolution). The check's decision is unchanged on all 22. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3 --- rugnux/RugnuxPasses.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/rugnux/RugnuxPasses.cpp b/rugnux/RugnuxPasses.cpp index a1f73d2ec..212851a93 100644 --- a/rugnux/RugnuxPasses.cpp +++ b/rugnux/RugnuxPasses.cpp @@ -912,21 +912,22 @@ std::optional Rugnux::RefinedPasses(RugnuxObserver *observer, Pro // The demoted arm runs first, as a probe: it is compared on the post-refinement's held-out // residual, so it has to measure one, and a canonical pass only does where the pre-pass // moved the geometry. The canonical pass below is the promoted arm (lattice_arm_check_). - const bool probe_was = postrefine_probe_; - postrefine_probe_ = true; - postrefine_probe_only_ = true; + // The probe runs on a copy of the run, so it leaves nothing on the run - not the spot + // 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. + Rugnux demoted_run(*this); + demoted_run.postrefine_probe_ = true; + demoted_run.postrefine_probe_only_ = true; IndexingSettings demoted_settings = experiment_.GetIndexingSettings(); demoted_settings.LatticeClassDemoted(length_arm); - experiment_.ImportIndexingSettings(demoted_settings); - lattice_angles_demoted_ = angle_arm; - demoted_arm = RunPipeline(observer, /*write_output=*/false, /*geometry_prepass=*/false); + demoted_run.experiment_.ImportIndexingSettings(demoted_settings); + demoted_run.lattice_angles_demoted_ = angle_arm; + demoted_arm = demoted_run.RunPipeline(observer, /*write_output=*/false, /*geometry_prepass=*/false); ++arm_passes; - postrefine_probe_only_ = false; - postrefine_probe_ = probe_was; - IndexingSettings promoted_settings = experiment_.GetIndexingSettings(); - promoted_settings.LatticeClassDemoted(false); - experiment_.ImportIndexingSettings(promoted_settings); - lattice_angles_demoted_ = false; + 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}; } else { logger.Info("Metric symmetry: {} - the free refinement departs from it by {:.2f} {}, " @@ -942,6 +943,7 @@ std::optional Rugnux::RefinedPasses(RugnuxObserver *observer, Pro base_prefix); speculate_geometry_probe_ = true; // a walk that follows starts with an indexing probe rerun_if_starved_ = true; + const DiffractionExperiment before_pass2 = experiment_; auto pass2 = RunPipeline(observer, /*write_output=*/true, /*geometry_prepass=*/false); if (lattice_arm_check_) { // The metric-symmetry check above, decided inside the pass just run (the promoted arm). @@ -970,8 +972,10 @@ std::optional Rugnux::RefinedPasses(RugnuxObserver *observer, Pro length_arm ? "axes" : "an angle", length_arm ? 100.0 * split : split * 180.0 / PI, length_arm ? "% apart" : "deg from 90", demoted_class, realised_demoted, realised_promoted); - // The promoted arm stopped before its merge; the demoted class is the canonical pass. + // The promoted arm stopped before its merge; the demoted class is the canonical pass, and + // it starts where the promoted one did, not from the spot budget that one measured. ++arm_passes; + experiment_ = before_pass2; IndexingSettings demoted_settings = experiment_.GetIndexingSettings(); demoted_settings.LatticeClassDemoted(length_arm); experiment_.ImportIndexingSettings(demoted_settings);