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:
1 parent
925701a1ce
commit
e97b902603
6 files changed
+96
-4
No files matched your search
@@ -535,8 +535,15 @@ void MXAnalysisWithoutFPGA::UpdateMaskResolution(const SpotFindingSettings &sett
|
||||
std::vector<Reflection> MXAnalysisWithoutFPGA::IntegrateOnImage(const std::vector<Reflection> &predicted,
|
||||
size_t npredicted, int64_t image_number) {
|
||||
const auto &modules = experiment.GetModules();
|
||||
if (modules.empty())
|
||||
return bragg_engine->Run(*preprocessor_buffer, predicted, npredicted, image_number);
|
||||
const auto drop_shadow_clipped = [this](std::vector<Reflection> &reflections) {
|
||||
DropShadowClipped(reflections, mask.GetMask(), static_cast<int>(experiment.GetXPixelsNumConv()),
|
||||
static_cast<int>(experiment.GetYPixelsNumConv()), experiment.GetBraggIntegrationSettings().GetR1());
|
||||
};
|
||||
if (modules.empty()) {
|
||||
auto ret = bragg_engine->Run(*preprocessor_buffer, predicted, npredicted, image_number);
|
||||
drop_shadow_clipped(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Predictions are where the reflections fall on the detector plane; the engine reads the image, so
|
||||
// each goes to where its panel put those pixels, and comes back to the plane once integrated. One in
|
||||
@@ -557,6 +564,7 @@ std::vector<Reflection> MXAnalysisWithoutFPGA::IntegrateOnImage(const std::vecto
|
||||
on_image.push_back(r);
|
||||
}
|
||||
auto ret = bragg_engine->Run(*preprocessor_buffer, on_image, on_image.size(), image_number);
|
||||
drop_shadow_clipped(ret);
|
||||
for (auto &r: ret) {
|
||||
const int64_t panel = modules.ModuleOfImagePixel(r.predicted_x, r.predicted_y);
|
||||
const auto [px, py] = modules.ImageToPlane(panel, r.predicted_x, r.predicted_y);
|
||||
@@ -570,3 +578,26 @@ std::vector<Reflection> MXAnalysisWithoutFPGA::IntegrateOnImage(const std::vecto
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// A signal disk that lost pixels to the mask is otherwise kept, its profile fitted over the pixels that
|
||||
// remain. That holds for a gap or a dead pixel, which take nothing from their neighbours, but not for the
|
||||
// beam-stop shadow: the mask is drawn where the BACKGROUND is dimmed, and the Bragg signal's half-shadow
|
||||
// does not end where the background's does, so the rest of such a disk is attenuated too. Measured against
|
||||
// their symmetry mates, these observations came out 12-60 % low on the sweeps whose shadow edge crosses
|
||||
// busy low-resolution reflections (CCD and pixel detectors alike), and level with them elsewhere.
|
||||
void DropShadowClipped(std::vector<Reflection> &reflections, const std::vector<uint32_t> &mask,
|
||||
int W, int H, float r1) {
|
||||
std::erase_if(reflections, [&](const Reflection &r) {
|
||||
const int x0 = std::max(0, static_cast<int>(std::floor(r.predicted_x - r1)));
|
||||
const int x1 = std::min(W - 1, static_cast<int>(std::ceil(r.predicted_x + r1)));
|
||||
const int y0 = std::max(0, static_cast<int>(std::floor(r.predicted_y - r1)));
|
||||
const int y1 = std::min(H - 1, static_cast<int>(std::ceil(r.predicted_y + r1)));
|
||||
for (int y = y0; y <= y1; y++)
|
||||
for (int x = x0; x <= x1; x++) {
|
||||
const float dx = static_cast<float>(x) - r.predicted_x, dy = static_cast<float>(y) - r.predicted_y;
|
||||
if (dx * dx + dy * dy < r1 * r1 && (mask[static_cast<size_t>(y) * W + x] & (1u << PixelMask::BeamStopPixelBit)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user