// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include #include #include "SpotToSave.h" struct Reflection { int32_t h; int32_t k; int32_t l; float image_number; // Can be in-between for 3D integration float delta_phi_deg; // phi angle from XDS - difference from middle of current frame (NOT an absolute angle) float predicted_x; float predicted_y; float observed_x; float observed_y; float d; float I; float bkg; float var_bkg; // non-signal (background) part of sigma^2, carried to the merge float sigma; float dist_ewald; // The reciprocal Lorentz factor (rotation only - a still's Lorentz factor is one) times the // reciprocal polarization factor, and nothing else. This is what LP means everywhere in the // field, and it is what the CBOR key "rlp" and the HDF5 dataset "lp" store the reciprocal of. // What it is not is a scale: the fitted per-image scale and the partiality stay out of it and // are divided in separately below. (Named after DIALS's prescaling_correction.) float prescaling_corr; // The sensor's angle-dependent efficiency, QE(0)/QE(alpha): always <= 1, and exactly 1 where the // sensor is opaque or its material and thickness are unknown. It is carried BESIDE // prescaling_corr rather than inside it, because LP and detector response are two different // things and every file the field reads keeps them apart. It is one of the three factors whose // product is the total deterministic correction - see flight_corr below, which arrived after // this comment was written and made "the two" wrong. Defaulted to 1 so a reflection read from a // file written before this existed is a no-op rather than a zero. float qe_corr = 1.0f; // The air in the sample-to-pixel flight path, exp(D/L * (1/cos(alpha) - 1)): always >= 1, and // exactly 1 when the air correction is switched off. Carried beside the two above for the same // reason they are carried apart - it is neither beam geometry nor detector response but the // medium in between, and unlike either of them it is set by the flight distance. The total // deterministic correction on a reflection is prescaling_corr * qe_corr * flight_corr, and every // site that corrects an intensity multiplies all three. Defaulted to 1 so a reflection read // from a file written before this existed is a no-op rather than a zero. float flight_corr = 1.0f; float partiality; // fraction of the reflection recorded in the sampled (rocking) slice float zeta; float image_scale_corr; // I_true = image_scale_corr * I; = prescaling_corr * qe_corr * flight_corr / (partiality * image_scale) bool observed = false; bool on_ice_ring = false; // sits on a hexagonal-ice powder ring: excluded from scaling, kept for merging }; struct MergedReflection { int32_t h = 0; int32_t k = 0; int32_t l = 0; float I = NAN; float sigma = NAN; float I_half[2] = {NAN, NAN}; float sigma_half[2] = {NAN, NAN}; float d = 0.0; bool rfree_flag = false; float F = NAN; // French-Wilson amplitude |F| (filled by ApplyFrenchWilson at end of merge) float sigmaF = NAN; // its sigma // Anomalous (Bijvoet) split of this reflection's own observations, kept even when the merge is // Friedel-averaged (I above is the Friedel mean). Lets I(+)/I(-) be written and CCano reported by // default without scaling anomalously; NaN when a hand was not measured or for centrics. float I_plus = NAN; float sigma_plus = NAN; float I_minus = NAN; float sigma_minus = NAN; // French-Wilson amplitudes of the two hands (filled by ApplyFrenchWilson from I_plus/I_minus). float F_plus = NAN; float sigmaF_plus = NAN; float F_minus = NAN; float sigmaF_minus = NAN; };