Build Packages / Unit tests (push) Successful in 58m42s
Build Packages / build:windows:cuda (push) Successful in 18m14s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 13m7s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m4s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 13m30s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 12m44s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 13m26s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 14m25s
Build Packages / build:rpm (rocky8) (push) Successful in 21m46s
Build Packages / build:rpm (rocky9) (push) Successful in 13m20s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 12m15s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 11m20s
Build Packages / DIALS test (push) Successful in 12m40s
Build Packages / XDS test (durin plugin) (push) Successful in 8m12s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 9m37s
Build Packages / XDS test (neggia plugin) (push) Successful in 6m23s
Build Packages / Generate python client (push) Successful in 30s
Build Packages / Build documentation (push) Successful in 1m11s
Build Packages / Create release (push) Skipped
Build Packages / build:windows:nocuda (push) Successful in 9m59s
The profile was learned and applied on the integer pixel round(predicted), so a shared profile sits up to 0.5 px off the true spot (and stacking spots with random sub-pixel offsets broadens the learned profile). Build the Gaussian per reflection instead, centred on the predicted sub-pixel offset -- noise-free geometry, unlike the observed centroid, which hurt -- and elongated radially as before. HEWL rotation @1.0A: ISa 15.7->16.2, CCref band 89.9->90.0, CCxds 94.8->95.0 (high-res 1.00A CCref 66.0->66.9); sharp serial stills 1.68A CC1/2 61.6->62.5; anomalous S peak 0.92x XDS (no accuracy traded). De-broadening the learned width by the 1/12 px^2/axis integer-binning floor was tested and rejected (it over-narrows). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
57 lines
3.6 KiB
C++
57 lines
3.6 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 (the DEFAULT integrator)
|
|
// =============================================================================
|
|
//
|
|
// A drop-in alternative to BraggIntegrate2D that replaces uniform box summation with
|
|
// profile-fitted extraction (no reference intensities needed). 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), and for the lineage (this is the extraction half of the former,
|
|
// now-removed PixelRefine, which beat whole-PixelRefine on the serial test).
|
|
//
|
|
// 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).
|
|
// The width 2nd-moment is taken over the signal disk r1 on the monochromatic path (the wide grid
|
|
// corners only carry neighbour leakage + rectified noise, which inflate the learned width several-
|
|
// fold) and over the full grid on the broadband path (sparse spots, generous width wanted).
|
|
// NOTE (2026-06-30): an earlier test found radial/tangential ANISOTROPY "adds nothing", but that
|
|
// was confounded by the then-contaminated (over-wide) width; with the de-contaminated width, a
|
|
// RADIAL elongation does help at high resolution - see step C and NEXTGEN_INTEGRATOR.md round 3.
|
|
// 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. The Gaussian is built per reflection, centred on the
|
|
// predicted SUB-PIXEL position (the integer grid otherwise mis-places it up to 0.5 px) and elongated
|
|
// along the RADIAL direction by sigma^2_radial = sigma^2_intrinsic + (parallax + bandwidth +
|
|
// capture)*tan^2(2theta) (analytic sensor parallax always; bandwidth streak + capture term per path).
|
|
//
|
|
// Staging: v1 = measured-R1 Gaussian (the keeper); v2 = empirical per-shell; v3 = empirical per
|
|
// detector-region x shell (XDS-grade).
|
|
//
|
|
// Selected by BraggIntegrationSettings::Integrator: ProfileGaussian (the DEFAULT, v1) or
|
|
// ProfileEmpirical (v2); BoxSum (BraggIntegrate2D) is the fallback. jfjoch_process exposes it as
|
|
// `--integrator boxsum|gaussian|empirical`. For A/B vs XDS_ASCII.HKL via --dump-observations.
|
|
// =============================================================================
|
|
|
|
#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);
|