Files
Jungfraujoch/image_analysis/bragg_integration/ProfileIntegrate2D.h
T
leonarski_fandClaude Opus 4.8 e4b3064254 Scaling: reference dataset, 3D combine (-P rot3d), R-meas, profile-fit integrator
Reference dataset (a): LoadReferenceMtz adds column selection + cell/SG/resolution +
a data-vs-reference consistency check; jfjoch_process/jfjoch_scale gain
--reference-column; the viewer gets a Reference section in the MX settings dock
(worker-owned, independent of the loaded dataset) that flows into reprocessing jobs.

3D combine (-P rot3d): Combine3D weight-sums a reflection's per-frame partials into one
counting-limited full before merging (orthogonal ScalingSettings::combine_3d flag, not a
partiality model), with a de-biased Poisson variance. Crystal 2: ISa 1.7->8.4, R-meas
~67%->18.9%, intensities unchanged (CCref held).

Quality metrics (b): R-meas (Diederichs-Karplus) + redundancy columns in MergeStats; ISa
logged. jfjoch fulls 18.9% vs XDS 4.5% (same ASU/run).

Profile-fit integrator (experimental): ProfileIntegrate2D (--integrator gaussian|empirical)
is a reference-free, rot3d-compatible profile-fit extraction (the decomposed PixelRefine
intensity step). Gaussian: R-meas 18.9->14.6%, ISa ->9.5. Anisotropy/per-region add nothing
(the discriminating info is in the discarded rocking direction). See NEXTGEN_INTEGRATOR.md.
--dump-observations exports the unmerged fulls for XDS comparison.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 20:43:04 +02:00

53 lines
3.2 KiB
C++

// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
// =============================================================================
// ProfileIntegrate2D — profile-fitting 2D integrator (DRAFT / not yet wired in)
// =============================================================================
//
// A drop-in alternative to BraggIntegrate2D that replaces uniform box summation with
// profile-fitted extraction - the part of PixelRefine that does NOT need reference
// intensities (PixelRefine = this extraction + a reference-based scaling step; here only
// the extraction is kept). See NEXTGEN_INTEGRATOR.md for the rationale: the residual ~4x
// R-meas/ISa gap to XDS is the box-sum method (a fixed disk captures a width-dependent
// fraction of each spot -> ~18% multiplicative per-observation floor on strong reflections).
//
// Output is a vector<Reflection> with I, sigma, partiality, d - IDENTICAL in shape to
// BraggIntegrate2D - so ScaleOnTheFly, Combine3D (-P rot3d) and the merge consume it
// unchanged, and it works for both stills and rotation.
//
// Algorithm (per frame):
// A. Box-sum every reflection (rough I + observed centroid); pick strong spots (signif>=5).
// B. Build the profile per resolution shell from the strong spots: a Gaussian of the measured
// second moment (ProfileGaussian, the keeper) or the empirical average grid (ProfileEmpirical).
// NOTE (measured 2026-06): radial/tangential ANISOTROPY and per-detector-region profiles were
// tried and add nothing - the 2D detector-plane spots are ~round, the real anisotropy is in the
// discarded rocking direction - so an isotropic per-shell width is kept. The empirical grid
// under-performs the Gaussian as built (per-frame, integer-binned -> sub-pixel-smeared + noisy).
// C. Profile-fit each reflection (Kabsch): I = sum P(c-B)/v over sum P^2/v, de-biased
// variance v = B + max(I,0)*P (iterate), sigma = sqrt(1/sum P^2/v). Carry the rotation
// partiality exactly as BraggIntegrate2D does.
//
// Staging: v1 = measured-R1 Gaussian (lifts PixelRefine's extraction out, proves the
// decomposition + rot3d-combine compatibility); v2 = empirical per-shell (the expected win);
// v3 = empirical per detector-region x shell (XDS-grade).
//
// EXPERIMENTAL: selected by BraggIntegrationSettings::Integrator (ProfileGaussian = v1,
// ProfileEmpirical = v2); jfjoch_process exposes it as `--integrator gaussian|empirical` (default
// box-sum). For A/B vs XDS_ASCII.HKL via --dump-observations. Not yet exposed in the OpenAPI / viewer.
// =============================================================================
#include <vector>
#include "../../common/DiffractionExperiment.h"
#include "../../common/Reflection.h"
#include "../../common/CompressedImage.h"
std::vector<Reflection> ProfileIntegrate2D(const DiffractionExperiment &experiment,
const CompressedImage &image,
const std::vector<Reflection> &predicted,
size_t npredicted,
int64_t image_number);