Beam stop: a reflection whose signal disk reaches the shadow is not integrated; no-data pixels are dead, not shadow

The profile fit keeps a signal disk that lost pixels to the mask and fits the pixels that remain. That
holds for a gap or a dead pixel, but not for the beam-stop shadow: the mask is drawn where the background
is dimmed, and the Bragg half-shadow does not end there, so the rest of such a disk is attenuated too.
Measured on summed rocking events against their symmetry mates (stratified by resolution and zeta, the
detector edge excluded), observations whose disk touches the shadow read low by 12-60 % on 5uth, 7qij,
5nw5, 3r6o, 3meb (CCD and PILATUS alike) and level on the in-house photon-counting sweeps; observations
with a clean disk are unbiased except on 5nw5, whose half-shadow reaches ~20 px past the mask. The test
sits in MXAnalysisWithoutFPGA::IntegrateOnImage, so rotation, stills, the _process.h5 and --mode scale see
the same reflections; with no shadow in the mask (the broker) it does nothing.

The shadow finder also reports pixels holding a detector's no-data mark (PILATUS CBF module gaps at -1):
they are the darkest pixels there are. The pre-scan now masks those as dead (bit 10) instead of beam-stop
shadow (bit 9), so that the shadow test does not drop every reflection clipped by a module gap and the
reported shadow share is the shadow's. A mean of zero is not taken as dead: a deep shadow on a still
counts nothing in the projected frames.

CPU subset battery vs rc180 (REFMAC 0-cycle R-free): 3meb -0.0004, 5uth -0.0004, 6h2p -0.0003,
6oel -0.0002, 3r6o +0.0001, 7qij/7rji 0, 5nw5 +0.002 (its lowest-shell CC1/2 0.78 -> 0.89, ISa 8.4 -> 9.3);
in-house refres ISa up 0.1-0.3 on lyso/thau/insu/cytc/myob, half_image refres R_meas 75 -> 64 %, no set
lost quality. Stills (lcls242_s/t, lcls222, euxfel83, sacla236): R-free within +-0.002, the noise of a
perturbed run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
(cherry picked from commit a7610d847694b5540dacc9ef609344ea7c7ae8fd)
This commit is contained in:
leonarski_f committed 2026-10-25 07:20:21 +01:00
1 parent 925701a1ce
commit e97b902603
6 files changed
+96 -4

No files matched your search

+32
View File
@@ -13,6 +13,8 @@
#include "../common/DiffractionExperiment.h"
#include "../common/JFJochMath.h"
#include "../common/Reflection.h"
#include "../common/PixelMask.h"
#include "../image_analysis/MXAnalysisWithoutFPGA.h"
#include "../image_analysis/bragg_integration/BraggIntegrationEngineCPU.h"
#include "../image_analysis/image_preprocessing/ImagePreprocessorBuffer.h"
@@ -540,3 +542,33 @@ TEST_CASE("BraggIntegrationEngineCPU_AduIntegratesAsPhotons", "[Integration][por
}
}
}
// A reflection is left out only when its signal disk reaches the beam-stop shadow: a shadow pixel just
// outside the disk, or a pixel masked for any other reason inside it, keeps it.
TEST_CASE("DropShadowClipped_SignalDiskTouchingTheShadow", "[Integration][portable]") {
const int W = 64, H = 32;
const float r1 = 4.0f;
std::vector<uint32_t> mask(W * H, 0);
for (int y = 0; y < H; y++)
mask[y * W + 30] = 1u << PixelMask::BeamStopPixelBit; // a shadow column at x = 30
mask[16 * W + 10] = 1u << PixelMask::ModuleGapPixelBit; // a gap pixel at (10, 16)
auto at = [](float x, float y) {
Reflection r{};
r.predicted_x = x;
r.predicted_y = y;
return r;
};
std::vector<Reflection> refl = {at(10.0f, 16.0f), // a gap pixel at its centre: kept
at(26.5f, 16.0f), // 3.5 px from the shadow: dropped
at(25.0f, 16.0f), // 5 px from it: kept
at(33.9f, 5.0f), // 3.9 px on the other side: dropped
at(34.0f, 5.0f), // 4 px, the disk's own edge: kept
at(1.0f, 1.0f)}; // near the image corner: kept
DropShadowClipped(refl, mask, W, H, r1);
REQUIRE(refl.size() == 4);
CHECK(refl[0].predicted_x == 10.0f);
CHECK(refl[1].predicted_x == 25.0f);
CHECK(refl[2].predicted_x == 34.0f);
CHECK(refl[3].predicted_x == 1.0f);
}