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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SVmAWnzCmRKAXVUCdc4iNi
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-09 09:13:49 +02:00
1 parent 136b9703f7
commit 91e63b6f52
1 file changed
+15 -8
+15 -8
View File
@@ -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<int>(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<void()> &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_) {