Footprint integration: GPU/CPU parity sections, docs, changelog

BraggIntegrationEngineGPU_MatchesCPU gains three footprint sections (spaced, crowded under overlap
exclude, with the radial background correction): both engines classify the summation ellipse, the
grown ring and the footprint Gaussian alike. Integration chapter and changelog describe the measured
footprint.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5K8jvPPbmCrbqnWkddTuB
This commit is contained in:
2026-10-04 02:42:09 +02:00
co-authored by Claude Opus 5.5
parent 9bd92962c0
commit a6378800b8
3 changed files with 33 additions and 2 deletions
+30 -2
View File
@@ -146,10 +146,16 @@ double CompareCpuVsGpu(IntegratorMode mode, std::optional<float> bandwidth_fwhm,
float stencil_k = 0.0f,
float r1 = 0.0f, float r2 = 0.0f, float r3 = 0.0f,
OverlapMode overlap = OverlapMode::Off, float companion_dx = 0.0f,
bool clip_spots = false, float odd_partiality = 1.0f) {
const DiffractionExperiment experiment =
bool clip_spots = false, float odd_partiality = 1.0f,
const SpotFootprint *footprint = nullptr) {
DiffractionExperiment experiment =
MakeExperiment(mode, bandwidth_fwhm, clip_nsigma, radial, DetJF(2), stencil_k, r1, r2, r3,
overlap);
if (footprint) {
BraggIntegrationSettings settings = experiment.GetBraggIntegrationSettings();
settings.Footprint(*footprint);
experiment.ImportBraggIntegrationSettings(settings);
}
const size_t width = experiment.GetXPixelsNum();
const size_t height = experiment.GetYPixelsNum();
const size_t npixel = experiment.GetPixelsNum();
@@ -254,6 +260,28 @@ TEST_CASE("BraggIntegrationEngineGPU_MatchesCPU") {
CompareCpuVsGpu(IntegratorMode::ProfileGaussian, 0.04f, 0.0f, false, 120, 4.0f, 6.0f, 8.0f, 12.0f);
}
SECTION("ProfileEmpirical") { CompareCpuVsGpu(IntegratorMode::ProfileEmpirical, std::nullopt); }
// A measured footprint wider than the r1 disk (SpotFootprint.h): the summation ellipse, the ring
// grown along and across the radius and the footprint-width Gaussian are all reflection-dependent
// geometry the two engines have to classify alike - spaced so the rings stay clear, and crowded
// so the grown neighbour mask and the ring gate come into play.
SpotFootprint fp;
fp.bin_px = 100.0f;
for (int b = 0; b < 8; ++b) {
fp.sigma_rad.push_back(1.5f + 0.2f * b);
fp.sigma_tan.push_back(1.8f + 0.3f * b);
}
SECTION("ProfileGaussian footprint") {
CompareCpuVsGpu(IntegratorMode::ProfileGaussian, std::nullopt, 4.0f, false, 120, 0.0f,
0.0f, 0.0f, 0.0f, OverlapMode::Off, 0.0f, false, 1.0f, &fp);
}
SECTION("ProfileGaussian footprint crowded exclude") {
CompareCpuVsGpu(IntegratorMode::ProfileGaussian, std::nullopt, 4.0f, false, 40, 0.0f,
0.0f, 0.0f, 0.0f, OverlapMode::Exclude, 0.0f, false, 1.0f, &fp);
}
SECTION("ProfileGaussian footprint radial") {
CompareCpuVsGpu(IntegratorMode::ProfileGaussian, std::nullopt, 4.0f, true, 120, 0.0f,
0.0f, 0.0f, 0.0f, OverlapMode::Off, 0.0f, false, 1.0f, &fp);
}
// Overlap treatment: companions 4 px apart put each reflection's centre inside its neighbour's
// signal disk, so the owner map, the excluded pixels and the profile fraction the two modes act on
// all have to come out the same in both engines - the ownership atomic in particular is settled by