stills fixed cost: geometry refinement and pre-scan do the same work in less wall time

Four changes to the per-run fixed cost of a stills run (Step 2 geometry refinement, Step 3 pre-scan),
none of which changes what is computed:

- Module placement: the four held-out fits and the placement on every strong frame are independent, and
  one Ceres fit keeps only ~4.6-6.5 of 32 cores busy (perf, JF16M, 4000 frames / 150888 spots). They
  now run side by side on a fifth of the threads each; the placement fit is computed speculatively and
  used only where the held-out residuals call for it, as before. Baseline (quiet): held-out 12.3 s +
  placement 5.1 s on JF16M, 16.4 + 6.5 s on JF4M, 19.0 + 7.6 s on MPCCD.
- Module scan (+-25 px grid per module ahead of the placement) was one thread: every grid shift is now
  scored on the workers and the grid then walked in its old order, so the first best still wins
  (3.3 s on JF16M, ~5 s on JF4M/MPCCD before). New test ScanModuleShifts_FindsShiftedModule checks the
  shift is found and that 1 and 4 threads give the same answer.
- Stills geometry trial: each hypothesis (centre, distance, frame sample) is indexed once. The distance
  grid ran twice on the same centre whenever the file's centre was kept (40 of ~275 hypotheses,
  ~15% of the trial: 6.2 s on JF16M, 6.7 s on the flat JF8M), and the final here/there check repeated
  the first trial. Repeats always gave the same count (checked on three logs).
- The saturation histogram (40 frames, 0.1-2.2 s) reads only frames and the mask: it is taken beside
  the geometry refinement and the limit decided where it was, after it.

Plus TIMING marks for the sub-steps (geometry refinement sample, module scan, module placement fits).

Outputs identical to the baseline binary (bb7c9c) where checked: JF16M int16 15 runs (geometry, all
32 module shifts, held-out residuals, cell), JF4M int16, MPCCD 236 (indexing rate, CC1/2, R_split,
R-free with --model identical), PAL 90 and LCLS 222 (identical reports).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-21 17:48:09 +02:00
1 parent 987e812e23
commit 96322a8ec9
6 files changed
+171 -64

No files matched your search

@@ -9,6 +9,7 @@
#include <vector>
#include "../../common/JFJochMath.h" // PI
#include "../../common/ParallelFor.h"
#include "LatticeReduction.h"
#include "XtalResidual.h"
#include "ceres/ceres.h"
@@ -560,7 +561,7 @@ GeometryRefinerResult RefineGlobalGeometry(const DiffractionGeometry &nominal_ge
std::vector<ModuleScan> ScanModuleShifts(const DiffractionGeometry &geom,
const std::vector<GeomRefineFrame> &frames,
const std::vector<ModuleShift> &modules,
double range_px) {
double range_px, size_t nthreads) {
// A spot is on its lattice when every fractional index is within this of a whole number.
constexpr double ON_LATTICE = 0.15;
@@ -602,14 +603,21 @@ std::vector<ModuleScan> ScanModuleShifts(const DiffractionGeometry &geom,
const double step = pass == 0 ? 1.0 : 0.5;
const double half = pass == 0 ? range_px : 1.0;
const double cx = r.dx, cy = r.dy;
for (double dy = -half; dy <= half; dy += step)
for (double dx = -half; dx <= half; dx += step) {
const int n = score(on_module[m], modules[m], cx + dx, cy + dy);
if (n > r.on_lattice_at_best) {
r.on_lattice_at_best = n;
r.dx = cx + dx;
r.dy = cy + dy;
}
std::vector<double> offsets;
for (double d = -half; d <= half; d += step)
offsets.push_back(d);
// Every shift of the grid is scored on the workers, then the grid is walked in order: the first
// shift with the most spots wins, as it would on one thread.
const int n = static_cast<int>(offsets.size());
std::vector<int> on_lattice(n * n);
ParallelFor(n * n, nthreads, [&](int k) {
on_lattice[k] = score(on_module[m], modules[m], cx + offsets[k % n], cy + offsets[k / n]);
});
for (int k = 0; k < n * n; k++)
if (on_lattice[k] > r.on_lattice_at_best) {
r.on_lattice_at_best = on_lattice[k];
r.dx = cx + offsets[k % n];
r.dy = cy + offsets[k / n];
}
}
}