Every accumulator in this file that could be an integer already is one, and the spot finder's
reduce_rings_shared says why: a preprocessed pixel is an exact int32, integer addition is
associative, and a threshold that moves in its last bits between runs flips every pixel sitting on
it. Four accumulators here were still floats, and they reach the intensity rather than a diagnostic.
* The radial background curve. s_radv and rad_sum sum int32 pixel values, so int64 is not an
approximation of the old sum, it IS the old sum - and the curve is subtracted from every
reflection's background.
* The learned profile grid and its second moments, which are sums of (px - bkg) / I over every
strong reflection of the frame. There is no exact integer form, so these are fixed point at 2^20:
a quantum of 1e-6 of one I-normalised pixel, far below the Poisson noise of the pixel it came
from, and some five orders of headroom inside a signed 64-bit accumulator.
* The normalisation total in build_profiles, which divides every cell of the profile - 128 lanes on
one address, in arrival order. Now summed as integers, exactly, from the grid it normalises.
* The fit's own reductions, s_num and s_den among them, which ARE the fitted intensity. These stay
float, so fixed point would be a real precision trade over an unbounded range; instead each warp
leaves its total in a slot of its own and every thread adds the slots up by warp index.
WARP_ATOMIC_ADD is order-independent for the integer accumulators it was written for and not for
these, which is what block_sum is for.
With the prediction ordering of the previous commit, a run is now reproducible: the same command on
the same images writes byte-identical .hkl and .mtz, at -N 1 and at -N 48, on a 16 Mpx rotation set
and on a large-cell one. Before, all four differed.
The battery is unchanged where it was ever stable: space group identical on all 24 crystals,
reflection count on 20, R_meas on 22. The two that move are the two the battery has always seen
move between runs of an unchanged binary - which is the point, since they stop moving now. Total
8m02s against 7m55s, inside the noise of per-crystal times quantised to a second.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011n8riB6X59oRjkrSHzNPAU