RotationScaleMerge: gather the SHELX file's scaled fulls on all threads

The unmerged scaled fulls the final merge hands back for the .hkl were gathered by one thread
pushing back over every full. Which fulls is now KeptIndices (the same list, in the same order),
and each record is made side by side.

The same records in the same order: p.mtz and the .hkl byte-identical (myob, thau, cytc, kdp,
cytc -N 4 / -N 8, --prepass-fraction 1 on four sets, 8a1a, 8tyy, CPU build myob). 8a1a, no other
load, interleaved: the P61 merge's statistics stage 3.94 -> 3.71 s.

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:
2026-10-07 21:31:29 +02:00
co-authored by Claude Opus 5.5
parent 9cc8172da5
commit 9ced4c4448
@@ -5300,17 +5300,20 @@ RotationScaleMerge::Result RotationScaleMerge::MergeAndStats(int n_groups, bool
// not rejected by either outlier test, inside the cut (judged on the group's d, as the merged
// reflections are) - each with the sigma the merge weighted it by.
if (export_scaled_fulls && full_stats && !for_search) {
result.scaled_fulls.clear();
for (int i = 0; i < n_full; ++i) {
// Which fulls, in order, and then each one's record on its own - on all threads.
const std::vector<int32_t> kept = KeptIndices(n_full, nthreads, [&](int i) {
const int g = mf.group[i];
if (g < 0 || rejected_obs[i] || !std::isfinite(merged_I[g]))
continue;
if (effective_d_min && acc[g].d < *effective_d_min)
continue;
const Obs &o = fulls[i];
const float I_corr = o.I * o.corr;
result.scaled_fulls.push_back({o.h, o.k, o.l, I_corr, corrected_sigma(o, I_corr, o.sigma * o.corr)});
}
return g >= 0 && !rejected_obs[i] && std::isfinite(merged_I[g])
&& !(effective_d_min && acc[g].d < *effective_d_min);
});
result.scaled_fulls.resize(kept.size());
ParallelChunks(static_cast<int>(kept.size()), ThreadsForWork(kept.size(), nthreads), [&](int lo, int hi) {
for (int j = lo; j < hi; ++j) {
const Obs &o = fulls[kept[j]];
const float I_corr = o.I * o.corr;
result.scaled_fulls[j] = {o.h, o.k, o.l, I_corr, corrected_sigma(o, I_corr, o.sigma * o.corr)};
}
});
}
// Asymptotic I/sigma. ISa is by definition the I -> infinity limit of the signal-to-noise, i.e. the