grid scan: an unstated beam is reported as the grid step, not as zero
Build Packages / build:windows:nocuda (push) Failing after 9m18s
Build Packages / build:viewer-tgz:cpu (push) Successful in 14m24s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 13m14s
Build Packages / build:viewer-tgz:cuda (push) Successful in 15m15s
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 6m50s
Build Packages / build:rpm (rocky8_nocuda) (push) Failing after 3m42s
Build Packages / build:rpm (rocky9_nocuda) (push) Failing after 3m51s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Failing after 3m35s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Failing after 3m5s
Build Packages / build:rpm (rocky8_sls9) (push) Failing after 3m44s
Build Packages / build:rpm (rocky9_sls9) (push) Failing after 4m3s
Build Packages / build:rpm (rocky8) (push) Failing after 4m2s
Build Packages / build:rpm (rocky9) (push) Failing after 3m55s
Build Packages / build:rpm (ubuntu2204) (push) Failing after 3m54s
Build Packages / build:rpm (ubuntu2404) (push) Failing after 3m55s
Build Packages / Generate python client (push) Successful in 14s
Build Packages / build:rugnux:windows (push) Successful in 17m49s
Build Packages / Build documentation (push) Successful in 48s
Build Packages / Create release (push) Skipped
Build Packages / build:windows:cuda (push) Successful in 20m19s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 6m48s
Build Packages / XDS test (durin plugin) (push) Successful in 7m43s
Build Packages / XDS test (neggia plugin) (push) Successful in 6m54s
Build Packages / DIALS test (push) Successful in 12m41s
Build Packages / Unit tests (push) Successful in 59m43s

A raster is stepped at about the beam size, so the step is the best proxy there is
when neither --beam-size nor the file's incident_beam_size says anything. Zero is the
worse answer: the reported crystal extents still contain a whole beam, and a zero
tells a consumer deconvolving them that they are already exact.

The substitution is in AnalyzeGridScan, so it holds for the broker and for rugnux
alike. The extents themselves do not move - they are measured either way, and the
test pins that. BEAM_SIZE_SOURCE in the raster report gains GRID_STEP, so a reader
can still tell a measured beam from a stood-in one, which matters because removing an
anisotropic beam is a covariance subtraction and a wrong one rotates the crystal axis.

The comment at the rugnux call site had argued for the old behaviour in as many
words; it now describes what the code does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
This commit is contained in:
2026-09-08 09:11:26 +02:00
co-authored by Claude Opus 5
parent e381d2fd50
commit aa5a31b345
6 changed files with 46 additions and 12 deletions
+23
View File
@@ -331,3 +331,26 @@ TEST_CASE("AnalyzeGridScan follows the snake and the step signs", "[AnalyzeGridS
CHECK(a.y_um == Catch::Approx(b.y_um));
CHECK(snake.Rearrange(b.image_number) == plain.Rearrange(a.image_number));
}
TEST_CASE("AnalyzeGridScan falls back to the grid step for an unstated beam", "[AnalyzeGridScan]") {
auto grid = MakeGrid(8, 8, 20.0f, 16.0f);
const std::vector<std::pair<int64_t, int64_t>> blob{{2, 2}, {3, 2}, {2, 3}, {3, 3}};
const auto scan = MakeScan(8, 8, blob, 0.9f);
// Stated: echoed back untouched, whatever the step is.
const auto stated = AnalyzeGridScan(scan, grid, 80.0f, 20.0f);
CHECK(stated.beam_size_x_um == Catch::Approx(80.0));
CHECK(stated.beam_size_y_um == Catch::Approx(20.0));
// Unstated: the step stands in. Zero would tell a consumer the extents are exact when they
// still contain a whole beam, and the two axes must not collapse to one value.
const auto unstated = AnalyzeGridScan(scan, grid, 0.0f, 0.0f);
CHECK(unstated.beam_size_x_um == Catch::Approx(20.0));
CHECK(unstated.beam_size_y_um == Catch::Approx(16.0));
// The extents themselves are measured and must not move with the beam.
REQUIRE(stated.crystals.size() == 1);
REQUIRE(unstated.crystals.size() == 1);
CHECK(stated.crystals[0].major_um == Catch::Approx(unstated.crystals[0].major_um));
CHECK(stated.crystals[0].minor_um == Catch::Approx(unstated.crystals[0].minor_um));
}