Files
Jungfraujoch/common/BraggIntegrationSettings.cpp
T
leonarski_fandClaude Opus 5.5 46da7fb359 Bragg integration: a CCD rotation sweep's sigmas at the measured gain, intensities in photons
The pre-scan measures the gain of an SMV/marCCD detector (background variance per unit mean) but the
integrator used it only for the background clip: sigma, var_bkg, the strong-spot flag (I/sigma >= 5)
and the profile-vs-box-sum guard (10 box sigmas) all treated one ADU as one photon, so on a Saturn
(g 4.7-5.8) the guard sat at ~4.5 true sigma. The new BraggIntegrationSettings::PixelGain (set by
rugnux from the same measurement, rotation only, at max(1, g)) takes every variance the engine forms
at the gain, and Finalize converts I, sigma, bkg and var_bkg to photons, which is what the merge's
variance model (var_bkg + I) assumes. CPU and CUDA engines; Metal runs the CPU code for this. Gain 1
(every photon-counting input, ADSC/marCCD whose measured ratio is below 1, and stills) is bit-identical.

Battery --only, 12 open-arm CCD/background-dominated sets vs the base build (987e812e2), GPU:
the six Saturn sets (g 4.7-5.8) all improve - guard fallbacks 4.1-17.3% -> 1.2-6.1%,
anomalous peak height 3mc4 x1.10, 3p85 x1.02, 3r6o x1.06, 5cc8 x1.03, 5uth x1.05, 5vml x1.04;
R_meas 0.880-0.993x on all six; R_model shell-scaled better on 5/6 (5cc8 +0.0005); ISa 5uth +1.5%,
5vml +15%, 3p85 +3%, the twinned 3mc4/3r6o -5%/-1%. 6cee, 6i3j, 6v2r, 7kcn, 8v4j, 9jzo unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
2026-10-23 01:26:44 +02:00

242 lines
7.9 KiB
C++

// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <cmath>
#include "BraggIntegrationSettings.h"
#include "JFJochException.h"
#define check_max(param, val, max) if ((val) > (max)) throw JFJochException(JFJochExceptionCategory::InputParameterAboveMax, param)
#define check_min(param, val, min) if ((val) < (min)) throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin, param)
#define check_finite(param, val) if (!std::isfinite(val)) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, param)
BraggIntegrationSettings &BraggIntegrationSettings::R1(float input) {
check_finite("Integration radius R1", input);
check_min("Integration radius R1", input, 0.1);
check_max("Integration radius R1", input, 20.0);
r_1 = input;
return *this;
}
BraggIntegrationSettings &BraggIntegrationSettings::R2(float input) {
check_finite("Background inner radius R2", input);
check_min("Background inner radius R2", input, 0.1);
check_max("Background inner radius R2", input, 30.0);
if (input <= r_1)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Background inner radius (R2) must be larger than integration radius (R1)");
r_2 = input;
return *this;
}
BraggIntegrationSettings &BraggIntegrationSettings::R3(float input) {
check_finite("Background outer radius R3", input);
check_min("Background outer radius R3", input, 0.1);
check_max("Background outer radius R3", input, 40.0);
if (input <= r_2)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Background outer radius (R3) must be larger than background inner radius (R2)");
r_3 = input;
return *this;
}
BraggIntegrationSettings &BraggIntegrationSettings::StencilKSigma(float input) {
check_finite("Integration stencil elongation", input);
check_min("Integration stencil elongation", input, 0.0);
check_max("Integration stencil elongation", input, 10.0);
stencil_k_sigma = input;
return *this;
}
BraggIntegrationSettings &BraggIntegrationSettings::DMinLimit_A(std::optional<float> input) {
if (input) {
check_finite("Minimum d-spacing", *input);
check_min("Minimum d-spacing", *input, 0.5);
check_max("Minimum d-spacing", *input, 100.0);
}
d_min_limit_A = input;
return *this;
}
BraggIntegrationSettings & BraggIntegrationSettings::FixedProfileRadius_recipA(std::optional<float> input) {
if (input) {
check_finite("Profile radius", input.value());
check_min("Profile radius [A^-1]", input.value(), 0.000001);
check_max("Profile radius [A^-1]", input.value(), 0.01);
}
fixed_profile_radius = input;
return *this;
}
std::optional<float> BraggIntegrationSettings::GetFixedProfileRadius_recipA() const {
return fixed_profile_radius;
}
BraggIntegrationSettings & BraggIntegrationSettings::ForcedPredictionMosaicity_deg(std::optional<float> input) {
if (input) {
check_finite("Prediction mosaicity", input.value());
check_min("Prediction mosaicity [deg]", input.value(), 0.001);
check_max("Prediction mosaicity [deg]", input.value(), 10.0);
}
forced_prediction_mosaicity_deg = input;
return *this;
}
std::optional<float> BraggIntegrationSettings::GetForcedPredictionMosaicity_deg() const {
return forced_prediction_mosaicity_deg;
}
BraggIntegrationSettings &BraggIntegrationSettings::Integrator(IntegratorMode input) {
integrator_mode = input;
return *this;
}
IntegratorMode BraggIntegrationSettings::GetIntegrator() const {
return integrator_mode;
}
float BraggIntegrationSettings::GetR1() const {
return r_1;
}
float BraggIntegrationSettings::GetR2() const {
return r_2;
}
float BraggIntegrationSettings::GetR3() const {
return r_3;
}
float BraggIntegrationSettings::GetStencilKSigma() const {
return stencil_k_sigma;
}
std::optional<float> BraggIntegrationSettings::GetDMinLimit_A() const {
return d_min_limit_A;
}
float BraggIntegrationSettings::GetMinimumSigmaInRegardsToI() const {
return minimum_sigma_in_regards_to_i;
}
BraggIntegrationSettings &BraggIntegrationSettings::BackgroundTrimFraction(float input) {
check_finite("Background trim fraction", input);
check_min("Background trim fraction", input, 0.0);
check_max("Background trim fraction", input, 0.49); // must leave a central majority after trimming
bkg_trim_fraction = input;
if (input > 0.0f)
bkg_clip_nsigma = 0.0f; // the two ring estimators are alternatives, not a stack
return *this;
}
float BraggIntegrationSettings::GetBackgroundTrimFraction() const {
return bkg_trim_fraction;
}
BraggIntegrationSettings &BraggIntegrationSettings::MaxHKL(std::optional<int> input) {
if (input) {
check_min("Maximum hkl index", *input, 1);
// The GPU predictor launches one thread per candidate, so the cost is (2n+1)^3: 511 is 1.1e9
// candidates per frame, already far past the point where prediction dominates a run.
check_max("Maximum hkl index", *input, 511);
}
max_hkl = input;
return *this;
}
std::optional<int> BraggIntegrationSettings::GetMaxHKL() const {
return max_hkl;
}
BraggIntegrationSettings &BraggIntegrationSettings::BackgroundClipNSigma(float input) {
check_finite("Background clip nsigma", input);
check_min("Background clip nsigma", input, 0.0);
bkg_clip_nsigma = input;
if (input > 0.0f)
bkg_trim_fraction = 0.0f; // the two ring estimators are alternatives, not a stack
return *this;
}
float BraggIntegrationSettings::GetBackgroundClipNSigma() const {
return bkg_clip_nsigma;
}
BraggIntegrationSettings &BraggIntegrationSettings::BackgroundPixelGain(float input) {
check_finite("Background pixel gain", input);
check_min("Background pixel gain", input, 1.0);
bkg_pixel_gain = input;
return *this;
}
float BraggIntegrationSettings::GetBackgroundPixelGain() const {
return bkg_pixel_gain;
}
BraggIntegrationSettings &BraggIntegrationSettings::PixelGain(float input) {
check_finite("Pixel gain", input);
check_min("Pixel gain", input, 1.0);
pixel_gain = input;
return *this;
}
float BraggIntegrationSettings::GetPixelGain() const {
return pixel_gain;
}
BraggIntegrationSettings &BraggIntegrationSettings::BackgroundRadialCorrection(std::optional<bool> input) {
bkg_radial_correction = input;
return *this;
}
std::optional<bool> BraggIntegrationSettings::GetBackgroundRadialCorrection() const {
return bkg_radial_correction;
}
BraggIntegrationSettings &BraggIntegrationSettings::Overlap(OverlapMode input) {
overlap_mode = input;
return *this;
}
OverlapMode BraggIntegrationSettings::GetOverlap() const {
return overlap_mode;
}
BraggIntegrationSettings &BraggIntegrationSettings::OverlapMinPeak(float input) {
check_finite("Overlap minimum peak fraction", input);
check_min("Overlap minimum peak fraction", input, 0.0);
check_max("Overlap minimum peak fraction", input, 1.0);
overlap_min_peak = input;
return *this;
}
float BraggIntegrationSettings::GetOverlapMinPeak() const {
return overlap_min_peak;
}
BraggIntegrationSettings &BraggIntegrationSettings::FlightPath(FlightPathMedium input) {
flight_path = input;
return *this;
}
FlightPathMedium BraggIntegrationSettings::GetFlightPath() const {
return flight_path;
}
BraggIntegrationSettings &BraggIntegrationSettings::Footprint(const SpotFootprint &input) {
if (input.sigma_rad.size() != input.sigma_tan.size()
|| input.sigma_rad.size() > static_cast<size_t>(SpotFootprint::MAX_BINS)
|| (!input.empty() && !(input.bin_px > 0.0f)))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Invalid spot footprint table");
footprint = input;
return *this;
}
const SpotFootprint &BraggIntegrationSettings::GetFootprint() const {
return footprint;
}