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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-10 23:56:15 +02:00
1 parent 1eda55ab38
commit de69e6d0d7
1 file changed
+17 -13
+17 -13
View File
@@ -912,21 +912,22 @@ std::optional<ProcessResult> 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<ProcessResult> 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<ProcessResult> 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);