Bragg prediction: say so when a frame overflows the prediction buffer

Found while chasing a 12% run-to-run spread in the merged reflection count of one
crystal. The GPU kernels claim output slots with an atomicAdd and, on overflow,
undid the increment with an atomicSub - so the counter saturated at the capacity
and the host could not tell a full buffer from an overflowing one. Which
reflections survived was then decided by CUDA block scheduling and changed every
run. Measured on that dataset: every frame predicts 23000-44000 against a 20000
buffer, and the spread reached the merged output (161591 / 165193 / 166110 /
166479 unique across four runs of the same command). Single-threaded runs diverge
too - this is entirely GPU-side.

Stop clamping the counter, so the true number predicted reaches the host, and
warn once per predictor when it exceeds the buffer. Which reflections are kept is
unchanged: making that reproducible means deciding what to keep when a frame
predicts more than the pipeline carries, and the obvious answers are worse - the
capacity is not the real limit, kPredictionOutput (10000, selected by smallest
excitation error) is, and on this crystal both a bigger buffer and a strided
selection collapse the merge, because the rotation combine rebuilds fulls from
exactly the partials that a smallest-excitation-error cut throws away.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 18:22:39 +02:00
co-authored by Claude Opus 5
parent e8a7696ffe
commit 46bb3bdbab
4 changed files with 36 additions and 9 deletions
@@ -4,9 +4,21 @@
#include <algorithm>
#include "../../common/JFJochMath.h"
#include "../../common/Logger.h"
#include "BraggPrediction.h"
#include "../bragg_integration/SystematicAbsence.h"
void BraggPrediction::ReportOverflow(int predicted) {
if (overflow_reported)
return;
overflow_reported = true;
Logger("BraggPrediction").Warning(
"A frame predicted {} reflections but the buffer holds {} - the ones kept are whichever the GPU "
"wrote first, so this dataset does NOT process reproducibly. Results will differ between runs. "
"The cell is large enough that the prediction cap no longer fits it.",
predicted, kPredictionCapacity);
}
int BraggPrediction::TruncateToOutput(int count) {
if (count <= kPredictionOutput)
return count;