PutModelDensityOnGrid walked each atom's box as gemmi does, with every point's place relative to the
atom and its test against the atom's radius in double. The Apple GPU has no double, so a Metal gather
could only come to the same grid to rounding. The density now has its own walk, UseDensityBoxesByPlane:
the same planes, atoms, box and order as before, with the point's place in single precision in one
fixed order of operations (f = centre - (u, v, w); x = m00 fx + (m01 fy + m02 fz), y = m11 fy + m12 fz,
z = m22 fz with gemmi's upper-triangular orth_n; r2 = (y y + z z) + x x, taken where not above the
radius squared). A gather that spells the same arithmetic out then finds the same points with the same
bits, and gemmi's float expapprox and the Gaussian sums are already single precision. The solvent mask
keeps the double walk: its points are 0 or 1, and a point at the edge would flip.
The density differs from gemmi's in the last bits, so everything computed from it moves at that level.
9s02 on an Apple M6 against the previous commit: p.hkl unchanged; the MR decision (AGREES, P 21 21 2)
and every R-work/R-free within 0.001; validation R(all) 0.3719 -> 0.3718; the validation rigid body
took 29 evaluations instead of 22 to the same place (rotation 0.003 deg); Z of rejected candidates in
other groups by up to 0.7; the model and maps differ in their last digits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018My7tqt8a4LyKKjP3FhScT