- Scaling: the 80%-of-card check runs right after the device ingest's sort and cut, where the
observation and distinct-hkl counts are first known, instead of after the geometry smoothing;
same counts, same decision, but a run that goes to the CPU no longer does the device smoothing
(6.9 s on an 8 GB card for a 33 M observation set) or holds its memory meanwhile.
- Rigid body: engines budgeted on a quarter of the card's total (the validation's plan), not
min(total/4, free - 1 GB). A busy card no longer sends the rigid body to the CPU (measured on an
8 GB card: ~80 s and a different fit); engine allocations wait for GPU work beside the run, and
an engine that cannot be set up fails the validation (RigidBodyGPUFailure) instead of falling
back to the CPU.
- Beam-centre FFT: GPU when its footprint is at most half of the card's total, otherwise CPU,
logged; no catch-and-rescore on the CPU after a GPU failure.
- FitPerFrameG reserves its per-frame vector (CPU scaling waited on the heap lock on Windows).
- get_gpu_free_memory(): cudaMemGetInfo, except on an integrated GPU under Linux, where free also
counts MemAvailable less a tenth of the total. Used only where free memory is waited on
(cuda_memory_available) or quoted in out-of-memory messages; it decides nothing.
The two card-filling [CUDAMemHelpers] tests skip on integrated GPUs.
- Out-of-memory messages point at rugnux --no-gpu instead of "the CPU build".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>