Merging: do not subtract a negative intensity's Poisson term

The expected-variance weights decompose an observation's sigma^2 into a
background part and a Poisson signal part, then rebuild the signal part
at the reflection's merged mean. The decomposition subtracted corr*I with
I taken as-is, so a negative I ADDED to the background part: an
observation at I = -1.5 with sigma^2 = 1 came out with a base variance of
2.7 rather than 1.

That inflates the variance of precisely the down-fluctuated observations
the correction exists for. Below about one photon they are then
under-weighted and the merged mean is biased high - the same direction of
error, in the same regime, that weighting by the observation's own sigma
produces. Subtract max(0, I) instead: a negative intensity has no Poisson
signal to remove.

Both users of the decomposition are fixed - the stills merge, where
expected-variance weighting is now the default, and the rotation combine
it was mirrored from, which had it first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-29 09:36:40 +02:00
co-authored by Claude Opus 5
parent 59977ae910
commit 6be94f2be0
2 changed files with 9 additions and 2 deletions
@@ -1213,7 +1213,9 @@ void RotationScaleMerge::Combine() {
const double corr = r2.corr;
const double I_corr = pooled_I(r2) * corr;
const double sigma_corr = static_cast<double>(r2.sigma) * corr;
const double bkg_var = sigma_corr * sigma_corr - corr * I_corr;
// max(0, I): a down-fluctuated partial carries no Poisson signal to remove, and
// removing a negative one inflates the background part instead of leaving it alone.
const double bkg_var = sigma_corr * sigma_corr - corr * std::max(0.0, I_corr);
double var = std::max(0.0, bkg_var) + corr * std::max(0.0, F);
if (!(var > 0.0)) var = sigma_corr * sigma_corr;
const double w = 1.0 / var;