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>
53 lines
3.2 KiB
C++
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);
|