Weight the corrected intensity, not its factors, in the shared reference

Folding the fit loop and the score loop into one reference() had to pick one of their two
spellings, and it picked the fit loop's: w * I * corr * a, where the score loop had built
Is = I * corr * a first and then summed w * Is. Those differ in the last place, and of the two
callers it is the score that decides whether a surface is kept at all - so a gate sitting on the
fence could go the other way for no reason but the order of three multiplications.

Sum w * Is, which leaves the deciding path spelled as it was and matches how the rest of this file
accumulates a weighted intensity. The fit's own reference moves by a last place instead; it is
iterated to convergence and then scored, so that is the cheaper place to absorb it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011n8riB6X59oRjkrSHzNPAU
This commit is contained in:
jungfrau
2026-08-23 08:26:36 -04:00
co-authored by Claude Opus 5
parent ca3ca7170e
commit 2eb9780fe2
@@ -1869,8 +1869,13 @@ void RotationScaleMerge::ApplyCellSurface(const std::vector<int32_t> &cell, int
std::fill(swI.begin(), swI.end(), 0.0);
for (const int32_t i : sel) {
const Obs &o = fulls[i];
const double a = A[cell[i]], sc = static_cast<double>(o.sigma) * o.corr * a, w = 1.0 / (sc * sc);
sw[o.group] += w; swI[o.group] += w * static_cast<double>(o.I) * o.corr * a;
// Keep the intensity's own product together before it is weighted: w * ((I*corr)*a) is
// what the loops this replaced summed, and w * I * corr * a rounds differently. The
// score decides whether a surface is kept, so a last-place difference here can flip a
// gain gate that is otherwise sitting on the fence.
const double a = A[cell[i]], Is = static_cast<double>(o.I) * o.corr * a;
const double sc = static_cast<double>(o.sigma) * o.corr * a, w = 1.0 / (sc * sc);
sw[o.group] += w; swI[o.group] += w * Is;
}
};