Stills keep an overloaded observation as a lower bound instead of dropping it
The stills merge dropped every observation with a saturated pixel. On a still the saturated observations are the fully recorded ones of the strongest low-resolution reflections, so what survived were their partial observations, whose partiality correction then set the merged intensity: on clipped CCD stills the merged low-resolution intensities came out at 0.4-0.6x of the deposited ones, a ramp no Wilson B absorbs, invisible to CC1/2 and R_split. The pixels are really clipped (flat-topped plateaus a few per mille wide, a pile-up in the pixel-value histogram from the per-pixel ADC limit), so the auto ceiling is right; the drop was the defect. A still now keeps the observation: the box sum with every saturated pixel counted at the saturation limit, unflagged and marked clipped, counted in BraggIntegrationCounts and reported as OVERLOADED_REFLECTIONS_KEPT. Rotation data (IsRotationIndexing) are unchanged and keep the flag for the rocking-event drop. Measured on a clipped-CCD stills subset (dimple R-free, deposited free set): dropped 0.247, lower bound 0.230 (same as switching saturation off). A Kabsch fit to the unsaturated wings read lower than the lower bound (0.237), and inflating the lower bound's sigma lost (0.255). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
987e812e23
commit
72cc542d78
15 files changed
+152
-20
No files matched your search
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
@@ -112,12 +113,14 @@ TEST_CASE("BraggIntegrationEngineCPU_CrowdingCountedWithoutImage", "[Integration
|
||||
}
|
||||
}
|
||||
|
||||
// A saturated pixel (INT32_MAX, as the preprocessor marks it) at the peak of a spot: the reflection is
|
||||
// not measured but kept, flagged overloaded, so that a rotation merge can drop its whole rocking event.
|
||||
// The same spot without the saturated pixel is an ordinary measurement.
|
||||
// A saturated pixel (INT32_MAX, as the preprocessor marks it) at the peak of a spot on rotation data: the
|
||||
// reflection is not measured but kept, flagged overloaded, so that a rotation merge can drop its whole
|
||||
// rocking event. The same spot without the saturated pixel is an ordinary measurement.
|
||||
TEST_CASE("BraggIntegrationEngineCPU_SaturatedPeakIsFlaggedNotDropped", "[Integration][portable]") {
|
||||
DiffractionExperiment experiment(DetJF(2));
|
||||
experiment.DetectorDistance_mm(100.0f).IncidentEnergy_keV(WVL_1A_IN_KEV).BeamX_pxl(400.0f).BeamY_pxl(400.0f);
|
||||
experiment.DetectorDistance_mm(100.0f).IncidentEnergy_keV(WVL_1A_IN_KEV).BeamX_pxl(400.0f).BeamY_pxl(400.0f)
|
||||
.Goniometer(GoniometerAxis("omega", 0.0f, 0.1f, Coord(-1, 0, 0), {}));
|
||||
experiment.ImportIndexingSettings(IndexingSettings().RotationIndexing(true));
|
||||
experiment.ImportBraggIntegrationSettings(BraggIntegrationSettings());
|
||||
const size_t width = experiment.GetXPixelsNum(), npixel = experiment.GetPixelsNum();
|
||||
|
||||
@@ -148,3 +151,61 @@ TEST_CASE("BraggIntegrationEngineCPU_SaturatedPeakIsFlaggedNotDropped", "[Integr
|
||||
CHECK(saturated[0].overloaded);
|
||||
CHECK(saturated[0].clipped);
|
||||
}
|
||||
|
||||
// A still has no rocking event to drop, so an overloaded observation is kept as a lower bound: the box sum
|
||||
// with every saturated pixel at the saturation limit. A spot with its core clipped at the limit, the
|
||||
// clipped pixels marked saturated as the preprocessor marks them: the still gives exactly the clipped
|
||||
// sum, unflagged and clipped (it may be low), and below the unclipped spot; rotation data keep the flag.
|
||||
TEST_CASE("BraggIntegrationEngineCPU_StillKeepsOverloadAsLowerBound", "[Integration][portable]") {
|
||||
DiffractionExperiment experiment(DetJF(2));
|
||||
experiment.DetectorDistance_mm(100.0f).IncidentEnergy_keV(WVL_1A_IN_KEV).BeamX_pxl(400.0f).BeamY_pxl(400.0f);
|
||||
experiment.ImportBraggIntegrationSettings(BraggIntegrationSettings());
|
||||
const int32_t limit = 500, bkg = 12;
|
||||
experiment.Detector().SaturationLimit(limit);
|
||||
const size_t width = experiment.GetXPixelsNum(), npixel = experiment.GetPixelsNum();
|
||||
|
||||
const int cx = 600, cy = 300;
|
||||
const float r1 = experiment.GetBraggIntegrationSettings().GetR1();
|
||||
ImagePreprocessorBuffer image(npixel);
|
||||
for (size_t i = 0; i < npixel; ++i)
|
||||
image[i] = bkg;
|
||||
double clipped_sum = 0.0, true_sum = 0.0;
|
||||
int n_saturated = 0;
|
||||
for (int dy = -6; dy <= 6; ++dy)
|
||||
for (int dx = -6; dx <= 6; ++dx) {
|
||||
const int32_t v = bkg + static_cast<int32_t>(std::lround(2000.0 * std::exp(-(dx * dx + dy * dy) / (2 * 1.3 * 1.3))));
|
||||
image[(cy + dy) * width + cx + dx] = v >= limit ? INT32_MAX : v;
|
||||
if (dx * dx + dy * dy < r1 * r1) {
|
||||
clipped_sum += std::min(v, limit) - bkg;
|
||||
true_sum += v - bkg;
|
||||
n_saturated += v >= limit;
|
||||
}
|
||||
}
|
||||
REQUIRE(n_saturated > 1);
|
||||
|
||||
Reflection r{};
|
||||
r.h = 1; r.k = 2; r.l = 3;
|
||||
r.predicted_x = cx; r.predicted_y = cy;
|
||||
r.d = 2.0f;
|
||||
r.prescaling_corr = 1.0f;
|
||||
r.partiality = 1.0f;
|
||||
const std::vector<Reflection> predicted{r};
|
||||
|
||||
BraggIntegrationEngineCPU still(experiment, PixelMask(experiment));
|
||||
const auto kept = still.Run(image, predicted, 1, 0);
|
||||
REQUIRE(kept.size() == 1);
|
||||
CHECK_FALSE(kept[0].overloaded);
|
||||
CHECK(kept[0].clipped);
|
||||
CHECK(kept[0].I == Catch::Approx(clipped_sum).epsilon(0.01));
|
||||
CHECK(kept[0].I < 0.8 * true_sum);
|
||||
CHECK(kept[0].sigma >= std::sqrt(kept[0].I));
|
||||
CHECK(still.Counts().overloads_kept == 1);
|
||||
|
||||
experiment.Goniometer(GoniometerAxis("omega", 0.0f, 0.1f, Coord(-1, 0, 0), {}))
|
||||
.ImportIndexingSettings(IndexingSettings().RotationIndexing(true));
|
||||
BraggIntegrationEngineCPU rotation(experiment, PixelMask(experiment));
|
||||
const auto flagged = rotation.Run(image, predicted, 1, 0);
|
||||
REQUIRE(flagged.size() == 1);
|
||||
CHECK(flagged[0].overloaded);
|
||||
CHECK(rotation.Counts().overloads_kept == 0);
|
||||
}
|
||||
Reference in new issue
Block a user