Files
leonarski_fandClaude Opus 5 aa5a31b345
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
grid scan: an unstated beam is reported as the grid step, not as zero
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
2026-09-08 09:11:26 +02:00

35 lines
2.0 KiB
C

// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include "../../common/GridScanSettings.h"
#include "../../common/ScanResult.h"
#include "../../common/GridScanResult.h"
#include "../../common/GridScanAnalysisSettings.h"
// Finds the crystals in a completed grid scan: the per-image protein score is scattered back onto
// the raster, the map is thresholded and labelled, and each blob is reported as one crystal.
// Pure - no I/O, no state, no detector.
//
// The sizes reported are MEASURED, and the beam is still in them: what the raster sees is the
// crystal convolved with the beam, and nothing here takes the beam back out. That is deliberate -
// the beam size is in the file as incident_beam_size, so a downstream consumer can do the
// deconvolution itself, reproducibly and reversibly, and is not stuck with ours.
//
// If it does: removing an anisotropic beam is a subtraction of the two 2x2 covariance matrices
// followed by re-diagonalising the difference, NOT a per-axis quadrature removal of beam_x from
// major_um and beam_y from minor_um. Per-axis is silently wrong the moment the crystal is not
// aligned with the grid axes - a needle at 45 deg has both beam widths mixed into both of its own
// axes - and a needle at an arbitrary angle is exactly the case this whole design exists for.
// beam_size_x_um/beam_size_y_um are only copied into the result, so it says what the sizes contain.
// beam_size_x_um/beam_size_y_um are the beam at the sample. Pass 0 where it is not known: the grid
// step is then reported in its place, since a raster is stepped at about the beam size and a zero
// beam would tell a consumer the extents are exact when they still contain one.
GridScanResult AnalyzeGridScan(const ScanResult &scan,
const GridScanSettings &grid,
float beam_size_x_um,
float beam_size_y_um,
const GridScanAnalysisSettings &settings = {});