From 91e63b6f523b16906e4e88773e343c44eab64ee2 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Fri, 9 Oct 2026 07:46:01 +0200 Subject: [PATCH] rugnux: ask the end wedge once, after the first-pass rescues, not inside every pass 79ec61c14 asked the 'end wedge' scheme inside pick_best, so every pass the axis-sign, beam-centre and long-axis rescues run carried it, and its count went into the pass those rescues are gated on. A consecutive wedge indexes part of the validation frames at the WRONG rotation-axis sign as well: on 7kcn (CBF, P4_122, the file's axis sign wrong for pass 2) the spread and the wedge gave 0/60, the end wedge 33/60, which lifted the pass over the sign rescue's majority gate - so the flip, where the spread indexes 60/60, was never asked. Pass 2 then integrated on the end wedge's lattice (33-49/60 frames): ISa 11.8 -> 4.7, R_meas 7.9 -> 15.3 %, wall 27 -> 43 s. The same mechanism let it defend a file beam centre the measured one beats on 6p8j. The end wedge is now asked once, after the rescues, at the geometry they settled on, and only where nothing has indexed a majority; it is judged against the standing pass as before. It can no longer displace a pass that indexes a majority, and the passes the rescues run no longer pay for it. Battery subset vs e99dd4949 (GPU): 7kcn back to 7c9a6c (p.mtz md5-identical to 7c9a6c, ISa 11.8, R_meas 7.9 %); 5nw5 still pass (P212121, d 7.10, ISa 8.9); 6p8j first pass back to 7c9a6c's (measured centre, spread 60/60). p.mtz md5-identical to e99dd4949 on 3mc4, 5ky6, 5reo, 7atg, 8v4o, 9qw8, 9rci, cuhf2, cytidine, myob_x06da_powder_1/2, myob_x06da_split/sparse, insu_I_x06da_low_isa, kdp/lcystine/yag/thau inhouse sets, nothing_1 (no lattice) and six private sets. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01SVmAWnzCmRKAXVUCdc4iNi --- rugnux/RugnuxFirstPass.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/rugnux/RugnuxFirstPass.cpp b/rugnux/RugnuxFirstPass.cpp index b0fc41f99..0feb426d9 100644 --- a/rugnux/RugnuxFirstPass.cpp +++ b/rugnux/RugnuxFirstPass.cpp @@ -746,8 +746,8 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) { {"spread", spread_ordinals}, {"wedge", all_consecutive}, }; - // A third sample of the same frames, asked only when the two above index no majority (see - // pick_best): the wedge read back from the END of the first part. Which orientations a sweep + // A third sample of the same frames, asked only when nothing else indexes a majority (see + // add_end_wedge): the wedge read back from the END of the first part. Which orientations a sweep // passes through first is chance, and the first part can open on frames that see the long axis // badly - measured on a sparse crystal with a ~390 A axis, the spread and the wedge over its // first 120 deg returned wrong cells (5/60 and 0/60 validation frames) at every spread density @@ -982,9 +982,14 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) { return bp; }; - // Where the schemes index no majority of the validation frames, the end wedge is asked too and - // judged against them as any later scheme is. Only then: on a run the schemes index it would be - // one more lattice for the axis-harmonic arbiter, which is not decided on frame counts. + // Where the first pass indexes no majority of the validation frames, the end wedge is asked too + // and judged against the standing pass as any later scheme is. Only then: on a run the schemes + // index it would be one more lattice for the axis-harmonic arbiter, which is not decided on + // frame counts. Asked once, after the axis-sign, beam-centre and long-axis rescues (below), not + // inside every pass they run: a consecutive wedge indexes part of the validation frames at the + // WRONG axis sign too (measured: 33/60 where the spread at the right sign indexes 60/60), and + // counted in the pass a rescue is gated on, it lifts that pass past the gate and the rescue + // that would have indexed every frame is never asked. auto add_end_wedge = [&](FirstPass bp, IndexerThreadPool &pool, IndexAndRefine &idx) -> FirstPass { if (cancelled_ || bp.score > static_cast(validation.size()) / 2) return bp; @@ -994,7 +999,7 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) { return score_schemes(ris, idx, &end_wedge, std::move(bp)); }; - // Feed the schemes, index them concurrently, and keep the best (score_schemes, add_end_wedge). + // Feed the schemes, index them concurrently, and keep the best (score_schemes). // alongside: started once the schemes are fed and before they are indexed, to run beside them. auto pick_best = [&](IndexerThreadPool &pool, IndexAndRefine &idx, const std::function &alongside = {}) -> FirstPass { @@ -1005,7 +1010,7 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) { for (const auto &ri : ris) rp.push_back(ri.get()); run_indexing(rp); - return add_end_wedge(score_schemes(ris, idx), pool, idx); + return score_schemes(ris, idx); }; // Powder-ring rescue - and, with it, the lean seed the rotation first pass never had. @@ -1677,7 +1682,7 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) { entries.emplace_back(centre_spec->key, centre_spec->spots); } } - alt = add_end_wedge(score_schemes(centre_spec->ris, *indexer), *indexer_pool, *indexer); + alt = score_schemes(centre_spec->ris, *indexer); } else alt = pick_best(*indexer_pool, *indexer); centre_spec.reset(); @@ -2044,6 +2049,8 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) { if (!long_axis_asked) if (auto rescued = long_axis_rescue(best)) best = *rescued; + // The end wedge (above), at the geometry the rescues settled on. + best = add_end_wedge(std::move(best), *indexer_pool, *indexer); release_spot_engines(); // no more frames to read; give the cards their memory back if (!cancelled_) {