Rotation: predict without truncating, and keep the better of the two passes

Digging into the selection logic showed the caps were not deciding the science -
the two-pass geometry post-refinement was, and the caps only fed it randomness.

Caps. The prediction buffer now grows to whatever a frame predicts instead of
keeping an arbitrary subset of it, and the per-image reflection limit is raised
to 65536, with the image-buffer transport headroom derived from the same
constant so the two cannot drift. Measured: bit-identical output on five battery
crystals, because a normal cell never approached the old limits - only a large
cell (~2.8e6 A^3, ~30000-44000 predictions per frame) ever did.

Pass-2 guard. The refined pass is normally the better answer, which is why it is
the canonical output, but it was adopted whatever it produced. On that same
crystal it merged more unique reflections than its own cell can hold -
completeness "117%", which is arithmetically impossible - while the header-
geometry pass sat at 92.6% and CC1/2 0.98. Compare the two and, when the refined
pass is not credible, go back to the header geometry and re-run so the canonical
files are the ones that are kept. Both bounds are set where only a failure
reaches them.

Together on that crystal: 111639 unique against XDS's 118730 (was 88000-99000
and different every run), CC1/2 98.0% (was 96.9-97.7%), ISa 8.54, and two runs
now agree bit for bit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 19:20:51 +02:00
co-authored by Claude Opus 5
parent 46bb3bdbab
commit ade61eea60
9 changed files with 116 additions and 29 deletions
+3 -1
View File
@@ -451,7 +451,9 @@ void IndexAndRefine::QuickPredictAndIntegrate(DataMessage &msg,
auto integration_end_time = std::chrono::steady_clock::now();
msg.integration_time_s = std::chrono::duration<float>(integration_end_time - integration_start_time).count();
constexpr size_t kMaxReflections = 10000;
// The per-image reflection list is bounded by the same constant the prediction and the transport
// headroom use, so the three cannot drift apart.
constexpr size_t kMaxReflections = BraggPrediction::kPredictionOutput;
if (i_outcome.reflections.size() > kMaxReflections) {
// Keep only smallest d (highest resolution)
std::nth_element(i_outcome.reflections.begin(),