rugnux: joint sweeps release the members' fulls, and the joint input holds no spare capacity

The members' fulls were "released" with `= {}`, an assignment from an empty initializer list that
keeps the vectors' memory (about 0.6 GB per sweep of a ~600 A cell, ~11 GB for 20 sweeps, held
through the joint merge); they are now assigned empty vectors. The joint merge's input grew each
frame's reflections one by one, up to twice their size in spare capacity; each frame is now reserved
at its count first. Nothing the merge reads changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-11-12 11:59:10 +01:00
1 parent 59ab1935e4
commit 30770ddb1d
2 files changed
+19 -5

No files matched your search

+16 -3
View File
@@ -574,11 +574,24 @@ JointMergeInput BuildJointMergeInput(const std::vector<JointMemberSweep> &sweeps
out.outcomes.resize(first + sweep.n_frames);
for (int64_t f = 0; f < sweep.n_frames; ++f)
out.outcomes[first + f].latt = lattice;
const auto usable = [](const ScaledFull &full) {
return std::isfinite(full.I) && std::isfinite(full.var_bkg) && full.var_per_I > 0.0f && full.d > 0.0f;
};
const auto frame_of = [&](const ScaledFull &full) {
return std::clamp<int64_t>(std::llround(full.image_number), 0, sweep.n_frames - 1);
};
// Each frame's reflections reserved at their count first: grown one by one, a large cell's
// sweeps held up to twice their size in spare capacity.
std::vector<size_t> count(sweep.n_frames, 0);
for (const ScaledFull &full : sweep.fulls)
if (usable(full))
count[frame_of(full)]++;
for (int64_t f = 0; f < sweep.n_frames; ++f)
out.outcomes[first + f].reflections.reserve(count[f]);
for (const ScaledFull &full : sweep.fulls) {
if (!std::isfinite(full.I) || !std::isfinite(full.var_bkg) || !(full.var_per_I > 0.0f)
|| !(full.d > 0.0f))
if (!usable(full))
continue;
const auto frame = std::clamp<int64_t>(std::llround(full.image_number), 0, sweep.n_frames - 1);
const auto frame = frame_of(full);
const gemmi::Op::Miller hkl = rel.cob.apply_to_hkl({{full.h, full.k, full.l}});
// A full as the merge takes a pre-combined observation, so that the combine rebuilds the variance
// its own sweep's merge gave it: a count c of intensity I/c at correction c = var_per_I is a
+3 -2
View File
@@ -4262,9 +4262,10 @@ int RugnuxCli::RunJointSweeps(const RugnuxSweepPlan &plan) {
JointMergeInput input = BuildJointMergeInput(members, relation);
// The joint merge works on its own copy of the fulls (input.outcomes): the members' go, so that a large
// cell's sweeps are not held twice.
// (Assigned an empty vector, not {}: that is an assignment from an empty list, which keeps the memory.)
for (JointMemberSweep &m : members) {
m.fulls = {};
m.merged = {};
m.fulls = std::vector<ScaledFull>();
m.merged = std::vector<MergedReflection>();
}
if (input.segments.size() < 2)
logger.Warning("Only {} sweep goes into the joint merge", input.segments.size());