# CPU-side crystallographic data analysis (Jungfraujoch) This document describes the crystallographic algorithms implemented in Jungfraujoch for **CPU**- and **GPU**-side real‑time and near‑real‑time data analysis. **Scope.** The pipeline covered here comprises: 1. geometry mapping and corrections, 2. azimuthal integration (powder/radial profiles), 3. Bragg spot finding (strong pixels → connected components → spot descriptors), 4. indexing (still and rotation modes), 5. Bravais lattice / centering inference, 6. geometry and lattice refinement, 7. reflection prediction (still and rotation), 8. Bragg integration by either 2D box summation or profile fitting (Kabsch, reference-free), 9. scaling and merging, 10. merge-level error modelling and outlier rejection, 11. auxiliary statistics (Wilson plot, ⟨I/σ(I)⟩, CC1/2, CCref). ## References The methods are inspired and reuising solutions implemented in: - W. Kabsch, “XDS”, *Acta Cryst.* **D66** (2010), 125–132 and related XDS papers (rotation geometry, partiality, scaling concepts). - W. Kabsch, “Integration, scaling, space-group assignment and post-refinement”, *Acta Cryst.* **D66** (2010), 133–144 (mosaicity/partiality likelihood treatment; notation such as ζ and rotation factors). - T. A. White et al., CrystFEL method papers (spot finding, three‑ring integration, serial/still diffraction processing concepts). - J. Kieffer & J. P. Wright, "PyFAI: a Python library for high performance azimuthal integration on GPU", *Powder Diffraction* **28** (2013), S339-S350 (detector geometry definition, azimuthal integration) - H. Powell, "The Rossmann Fourier autoindexing algorithm in MOSFLM", *Acta Cryst.* **D55** (1999), 1690-1695 (FFT indexing) (list is not exhaustive) ## 1. Geometry, reciprocal-space mapping, and basic quantities ### 1.1 Coordinate conventions For a pixel coordinate $(x,y)$ (in pixels), Jungfraujoch converts to a laboratory direction vector via: 1. shift by direct-beam position $(x_\mathrm{beam}, y_\mathrm{beam})$, 2. scale by pixel size $p$ (mm), 3. set detector distance $D$ (mm), 4. apply detector orientation rotation $R_\mathrm{det}$ (PyFAI-like parameterization). The unnormalized detector coordinate (mm) is: $ \mathbf{r}_\mathrm{det}(x,y) = \begin{pmatrix} (x-x_\mathrm{beam})p\\ (y-y_\mathrm{beam})p\\ D \end{pmatrix}. $ The lab-frame vector is: $ \mathbf{r}_\mathrm{lab} = R_\mathrm{det}\,\mathbf{r}_\mathrm{det}. $ Let the incident wavevector magnitude be $k = 1/\lambda$ in Å$^{-1}$, and define: $ \mathbf{S}_0 = (0,0,k). $ The **reciprocal-space scattering vector** associated with pixel $(x,y)$ is: $ \mathbf{s}(x,y) = k\,\frac{\mathbf{r}_\mathrm{lab}}{\lVert \mathbf{r}_\mathrm{lab}\rVert} - \mathbf{S}_0. $ This $\mathbf{s}$ is the fundamental quantity used for spot finding (resolution filters), indexing, and refinement. ### 1.2 Two-theta, azimuth, resolution and $q$ The scattering angle $2\theta$ is computed from $\mathbf{r}_\mathrm{lab}$ via: $ 2\theta = \arctan\!\left(\frac{\sqrt{x_\mathrm{lab}^2 + y_\mathrm{lab}^2}}{z_\mathrm{lab}}\right). $ Resolution (Å) at a pixel is: $ d = \frac{\lambda}{2\sin(\theta)} = \frac{\lambda}{2\sin(2\theta/2)}. $ The magnitude $q = 2\pi/d$ is used for radial binning and ice-ring handling. ### 1.3 Distance from the Ewald sphere For a reciprocal lattice point $\mathbf{p}$ (Å$^{-1}$), define: $ \Delta_\mathrm{Ewald}(\mathbf{p}) = \lVert \mathbf{p} + \mathbf{S}_0\rVert - k. $ Jungfraujoch uses $|\Delta_\mathrm{Ewald}|$ as an operational proxy for excitation error. This appears in: - still prediction (accept if $|\Delta_\mathrm{Ewald}|\le \Delta_\mathrm{cut}$), - profile radius estimation (see §11.1), - still partiality option in scaling/merging (§10.2). --- ## 2. Azimuthal integration (radial profiles) Azimuthal integration produces a radial profile $I(q)$ or $I(d)$ by histogramming pixels into radial bins. Pixels are **not split** across bins; each pixel contributes wholly to a single bin. By default the profile is purely radial (a single azimuthal bin), but the azimuth can optionally be split into up to 512 $\phi$ sectors (`azim_bins`, `--azim-phi-bins`), giving a **2D $q\times\phi$ profile** that exposes azimuthal anisotropy such as detector shadowing or sample texture. ### 2.1 Histogram estimator Let bin index $b(x,y)$ be precomputed from $q(x,y)$ (or equivalently from $d(x,y)$) and, when $\phi$ sectors are enabled, the azimuth $\phi(x,y)$ — so $b = b_q + b_\phi B_q$. For each bin $b$: - accumulate corrected intensity and its square: $ S_b = \sum_{(x,y):\,b(x,y)=b} I(x,y)\,C(x,y),\qquad S^{(2)}_b = \sum I(x,y)^2\,C(x,y)^2, $ - and count: $ N_b = \#\{(x,y):\,b(x,y)=b \text{ and pixel is valid}\}. $ The profile reports both the mean $\bar{I}_b = S_b / N_b$ (when $N_b>0$) and a per-bin sample standard deviation $\sigma_b = \sqrt{(S^{(2)}_b - S_b^2/N_b)/(N_b-1)}$ (a spread/error estimate for each radial point). Invalid pixels (masked, saturated, detector error codes) are excluded. ### 2.2 Corrections applied Two standard corrections are available: **(i) Solid angle / geometric correction.** A flat pixel's solid angle falls off with the **incidence angle $\alpha$ between the scattered ray and the detector normal**. With the in-plane detector offsets $u=(x-x_\mathrm{beam})p$ and $v=(y-y_\mathrm{beam})p$ (§1.1) and detector distance $D$, $ \cos\alpha = \frac{D}{\sqrt{u^2+v^2+D^2}},\qquad C_\Omega = \cos^3\alpha, $ applied — like the polarization term below — as a **divisor** (intensities are scaled by $1/\cos^3\alpha$), so pixels at oblique incidence, which subtend a smaller solid angle, are boosted. Because $\alpha$ is evaluated in the detector's own frame it is **invariant under detector tilt** ($\mathrm{rot1}/\mathrm{rot2}/\mathrm{rot3}$), matching PyFAI's `solidAngleArray` and MAX IV azint. It reduces to the commonly quoted $\cos^3(2\theta)$ form only for an untilted detector, where the incidence angle coincides with the scattering angle. **(ii) Polarization correction.** With polarization coefficient $P$ (beamline dependent) and azimuth $\phi$: $ C_\mathrm{pol}(2\theta,\phi) = \frac{1}{2}\left(1+\cos^2(2\theta) - P\cos(2\phi)\left(1-\cos^2(2\theta)\right)\right), $ applied as a divisor to intensities (i.e. scale by $1/C_\mathrm{pol}$) when enabled. ### 2.3 Background estimate for profiles A background estimate is derived from the profile as its mean intensity over a fixed low-to-mid $Q$ window (default $2\pi/5$ to $2\pi/3$ Å$^{-1}$). This background is used for monitoring and diagnostics; it is **not** the same as the local Bragg-spot background used in summation integration (§9.2). --- ## 3. Spot finding (strong pixels → Bragg spots) Spot finding is a two-stage process: 1. **Strong-pixel selection** using intensity and/or local signal-to-noise criteria. 2. **Connected-component labeling (CCL)** to group strong pixels into candidate spots, followed by spot-level filtering and feature extraction. ### 3.1 Strong-pixel detection by local statistics For each pixel $i$ with value $v_i$, consider a square window (nominally $31\times 31$ pixels) around it. Let the window contain $n$ valid pixels (excluding masked/bad/saturated), and define: $ \Sigma = \sum v,\qquad \Sigma_2 = \sum v^2. $ To avoid biasing the local statistics by the test pixel itself, Jungfraujoch evaluates the pixel against the window with the pixel removed: $ \Sigma' = \Sigma - v_i,\quad \Sigma_2' = \Sigma_2 - v_i^2,\quad n' = n-1. $ A variance-like quantity proportional to $n'^2$ is formed: $ V = n'\Sigma_2' - (\Sigma')^2, $ and the deviation-from-mean quantity: $ \Delta = v_i n' - \Sigma'. $ A pixel is considered strong if: - it is above a photon/count threshold, and - its window contains enough valid neighbours (more than 100), so the local statistics are meaningful, and - $\Delta>0$, and - the squared deviation exceeds a scaled variance: $ \Delta^2 > V\cdot T^2, $ where $T$ is the configured signal-to-noise threshold. This is equivalent to a local z-score criterion but implemented in integer arithmetic to be robust and fast. Special cases: - saturated pixels can be forced to “strong” (useful for detecting overloaded Bragg spots), - invalid pixels are never strong. ### 3.2 Resolution and ice-ring handling Spot finding can be restricted to a resolution range $[d_\mathrm{high}, d_\mathrm{low}]$ by masking pixels outside the range. Optionally, spots in identified ice-ring regions can be tagged so that subsequent indexing/refinement may include or exclude them (see §4 and §6). A single per-image **ice-ring score** is derived from the azimuthally-integrated radial profile: for each hexagonal-ice powder ring (positions $d$ from Moreau *et al.*, Acta Cryst D77, 2021), the profile intensity at the ring is divided by a smooth background estimated from the *whole* profile — a running median of the non-ice bins, interpolated under each ring — and the strongest ring's ratio is reported (1 = no ice, $>1$ = ice above background). A whole-profile background is used rather than a couple of adjacent shoulder bins so the estimate is robust to the radial binning: at a coarse Q-spacing a local shoulder can be only ~1 bin and would double-count the ring's own edge (offline processing defaults to a fine 0.01 1/Å spacing, `--azim-q-spacing`, so the rings are well resolved). (A significance/z-score was considered but is uninformative here: with many photons any real ice ring is highly significant, so the discriminating quantity is the ice *magnitude*, i.e. this ratio.) It is stored per image (`ice_ring_score`, HDF5 `/entry/MX/iceRingScore`) as a monitoring quantity, distinct from the merge-time ice masking, which is data-driven from the per-ring merged CC1/2. A further optional safeguard removes isolated high-resolution “spur” spots by detecting large gaps in $1/d$ (or $q$) space and discarding spots beyond the gap. This is intended for macromolecular diffraction where edge-of-detector backgrounds can be extremely low. ### 3.3 Connected-component labeling (CCL) Strong pixels are grouped into connected components (adjacent strong pixels) using a CCL algorithm. Each component yields a candidate spot with: - centroid $(x,y)$ (often intensity-weighted), - pixel count (spot size), - integrated spot intensity proxy (sum of pixel values), - resolution $d$ at the centroid (or mean over pixels), - and quality flags (e.g. ice-ring classification). Spot-level filters include minimum/maximum pixel count and resolution limits. --- ## 4. Indexing overview Indexing maps observed reciprocal-space vectors $\mathbf{s}_i$ to a lattice such that: $ \mathbf{s}_i \approx h_i\mathbf{a}^* + k_i\mathbf{b}^* + l_i\mathbf{c}^*, $ with integer $(h_i,k_i,l_i)$. Jungfraujoch supports two complementary indexing strategies: 1. **FFT-based indexing** (Rossmann-type): does not require an a priori unit cell; suitable for unknown samples. 2. **Fast-feedback indexing** (TORO-like): requires an approximate unit cell; optimized for speed and feedback. Both feed into a common robust refinement/selection stage which maximizes the number of inliers under an indexing tolerance, and which can return **more than one lattice** per image (multi-lattice indexing; see §5.4). ### 4.1 Indexed-spot decision (inlier test) Given a trial lattice with direct basis vectors $\mathbf{a},\mathbf{b},\mathbf{c}$ (used here as reciprocal-space dot-test vectors), fractional indices are estimated by: $ h_f = \mathbf{s}\cdot\mathbf{a},\quad k_f = \mathbf{s}\cdot\mathbf{b},\quad l_f = \mathbf{s}\cdot\mathbf{c}. $ Let $(h,k,l)=(\mathrm{round}(h_f),\mathrm{round}(k_f),\mathrm{round}(l_f))$ and define the fractional residual: $ \delta^2 = (h_f-h)^2 + (k_f-k)^2 + (l_f-l)^2. $ A spot is indexed if $\delta^2 < \tau^2$, where $\tau$ is the configured tolerance. For indexed spots, the reciprocal lattice point $\mathbf{p} = h\mathbf{a}^*+k\mathbf{b}^*+l\mathbf{c}^*$ is used to compute $\Delta_\mathrm{Ewald}(\mathbf{p})$ (stored as a diagnostic and later used in profile-radius estimation). --- ## 5. FFT indexing (unknown unit cell) FFT indexing follows a classical approach: detect dominant periodicities by projecting reciprocal-space points onto many directions and Fourier transforming the resulting 1D histograms. ### 5.1 Directional projections and histograms Choose a set of unit vectors $\{\mathbf{u}_d\}$ on a half-sphere (a near-uniform distribution generated via a golden-angle construction). For each direction $d$, form a histogram in the scalar projection: $ t_{id} = \left|\mathbf{u}_d\cdot \mathbf{s}_i\right|. $ Bin width is chosen approximately as: $ \Delta t \approx \frac{1}{2 L_\mathrm{max}}, $ where $L_\mathrm{max}$ is the maximum expected real-space unit-cell edge (Å). The histogram extent is tied to the maximum $q$ used (set by a high-resolution cutoff for indexing). ### 5.2 FFT peak picking and candidate vectors For each direction, the FFT magnitude spectrum is computed; peaks correspond to periodicities along $\mathbf{u}_d$. Each direction yields a candidate real-space length $L$ chosen **not** by raw magnitude but by **maximum prominence above a running-mean local background** (subtracting the broad low-frequency envelope that otherwise dominates on weak or pink-beam frames), subject to $L\ge L_\mathrm{min}$. Candidate vectors are $\mathbf{v}_d = L_d\,\mathbf{u}_d$. A collinearity filter removes nearly parallel vectors (e.g. within 5°) and attempts to resolve harmonic ambiguity: shorter “fundamental” vectors may be preferred over longer harmonics if their peak magnitude is sufficiently strong relative to the dominant peak. ### 5.3 Lattice reduction and cell candidates Triples of candidate vectors are combined to form candidate bases $(\mathbf{A},\mathbf{B},\mathbf{C})$, each reduced to its **Niggli-reduced cell** (Gruber-vector reduction) before comparison, and filtered by allowed length and angle ranges. Two passes are run: a standard pass forms shortest-vector triples from the ~30 strongest filtered directions; if the best cell then indexes fewer than half the spots, a **widened fallback** anchors the two shortest axes and lets the third range over up to ~60 candidate vectors (deduplicated by Niggli cell), catching large, elongated or superstructure cells the first pass misses. ### 5.4 Robust refinement and best-cell selection Candidate bases are refined against observed spots using an iterative inlier‑focused least‑squares procedure (trimmed/contracting threshold). Candidates are then ranked: 1. more indexed spots wins — **unless** two candidates index within ~10 % of each other, in which case 2. the **smaller-volume** cell is preferred (when the volumes differ by more than ~5 %), avoiding a doubled supercell, then 3. the smaller refinement score, then the spot count again. Selection is **not limited to a single lattice**: after the best cell is accepted, further lattices are added as separate crystals provided fewer than ~40 % of their indexed spots overlap an already-accepted lattice (up to two extra by default), so split or multi-lattice crystals are indexed rather than discarded. An optional reference unit cell (if supplied) restricts acceptance to cells within a relative distance tolerance in edge lengths (permutation-invariant). --- ## 6. Bravais lattice / centering inference (“lattice search”) If the space group is supplied by the user, its lattice constraints are assumed for refinement and subsequent processing. If not, Jungfraujoch attempts to infer the most plausible Bravais lattice type from the metric tensor after Niggli reduction: 1. **Niggli reduction** is performed to obtain a reduced cell in $G^6$ representation (Gruber vector). 2. The reduced cell is compared against a list of Niggli classes corresponding to Bravais lattices and centerings. 3. The highest-symmetry class that matches within tolerances is selected (relative metric tolerance and angular tolerance). The output includes: - a conventional cell, - crystal system (triclinic, monoclinic, …), - centering symbol (one of $P, C, I, F, R$; the $A/B$ variants are not emitted here — they are handled only later as prediction absences, §8.4). This stage provides centering information used for systematic absences in prediction (§8.4) and for reporting. **Note.** In ambiguous or special cases, forcing space group to $P1$ (no symmetry assumptions) is recommended. --- ## 7. Geometry and lattice refinement Refinement adjusts experimental geometry and crystal parameters to minimize discrepancies between observed spot reciprocal vectors and those predicted by a lattice model with integer indices. ### 7.1 Parameterization The refinement jointly optimizes, depending on mode and constraints: - beam center $(x_\mathrm{beam}, y_\mathrm{beam})$, - detector distance $D$, - detector tilt angles (two-angle model; third rotation often held at 0), - rotation axis direction (for rotation datasets), - crystal orientation (a global rotation), - unit-cell parameters, with constraints determined by inferred crystal system. By default only the beam center, unit cell and crystal orientation are refined; the detector distance, tilt angles and rotation-axis direction are held fixed unless explicitly enabled. A lighter **orientation-only** mode refines just the crystal orientation (with a weak small-rotation prior on the poorly-determined out-of-plane component), for stills whose geometry is already trusted. For higher symmetries, constraints are enforced, e.g. - cubic: $a=b=c,\ \alpha=\beta=\gamma=90^\circ$, - tetragonal: $a=b$, - hexagonal: $a=b,\ \gamma=120^\circ$, - monoclinic (unique axis $b$): $\alpha=\gamma=90^\circ$, $\beta$ refined. ### 7.2 Residuals and objective For each indexed spot assigned integer $(h,k,l)$, compute: - observed reciprocal vector $\mathbf{s}_\mathrm{obs}$ from its detector position and current geometry, - predicted reciprocal vector $\mathbf{s}_\mathrm{pred}(h,k,l;\ \text{lattice params})$. Residual is: $ \mathbf{r} = \mathbf{s}_\mathrm{obs} - \mathbf{s}_\mathrm{pred}. $ A non-linear least squares solver minimizes $\sum \|\mathbf{r}\|^2$ over all selected inlier spots. ### 7.3 Rotation datasets: bringing observations to a common reference frame For oscillation/rotation data, each image corresponds to a rotation angle $\phi$ about an axis $\mathbf{m}_2$. Observed reciprocal vectors are rotated “back to start” so that all images are refined in a single reference crystal frame: $ \mathbf{s}_\mathrm{obs,ref} = R(\phi)\,\mathbf{s}_\mathrm{obs}, $ with $R(\phi)$ constructed from the axis-angle representation of the goniometer model. The angle $\phi$ is taken at the centre of each frame's oscillation (the frame angle plus half the oscillation width). ### 7.4 Multi-stage tightening of inlier tolerance Refinement is performed in stages with decreasing acceptance tolerance for including reflections (three stages, indexing tolerance $0.3\to0.2\to0.1$), which stabilizes convergence when starting from imperfect indexing and approximate geometry. --- ## 8. Reflection prediction Jungfraujoch predicts reflection positions for integration by enumerating Miller indices within a resolution cutoff and accepting those that satisfy a diffraction condition model. ### 8.1 Enumerating reciprocal lattice points For a maximum resolution $d_\mathrm{min}$, accept $(h,k,l)$ such that: $ \lVert \mathbf{p}(h,k,l)\rVert^2 = \lVert h\mathbf{a}^* + k\mathbf{b}^* + l\mathbf{c}^*\rVert^2 \le \left(\frac{1}{d_\mathrm{min}}\right)^2. $ ### 8.2 Still prediction (excitation-error cutoff) For still images, the diffracting condition is approximated by an excitation-error cutoff: $ \left|\Delta_\mathrm{Ewald}(\mathbf{p})\right| \le \Delta_\mathrm{cut}. $ Accepted reflections are projected to the detector by intersecting the diffracted direction $\mathbf{S}=\mathbf{S}_0+\mathbf{p}$ with the detector plane, using the current geometry. When the beam has a finite energy bandwidth, this window is **broadened radially per reflection**: the cutoff is combined in quadrature with a bandwidth smear, $\sqrt{\Delta_\mathrm{cut}^2 + (3\,\sigma_\mathrm{bw})^2}$, where $\sigma_\mathrm{bw}\propto|p_z|$ (the reciprocal-space depth along the beam, growing as $\sim 1/d^2$). This keeps high-resolution reflections — smeared by the bandwidth into radial streaks — from being clipped. The same $\sigma_\mathrm{bw}$ is deconvolved from the measured profile radius (§11.1), so it is not double-counted. ### 8.3 Rotation prediction (Laue equation + partiality model) For rotation/oscillation datasets, Jungfraujoch solves for rotation angles $\phi$ where the rotated reciprocal lattice point satisfies the Ewald-sphere condition. In an XDS-like notation, define: - rotation axis unit vector $\mathbf{m}_2$, - $\mathbf{S}_0$ incident vector, - $\mathbf{S}(\phi)=\mathbf{S}_0+\mathbf{p}(\phi)$. A key quantity is: $ \zeta = \left|\mathbf{m}_2\cdot \mathbf{e}_1\right|,\quad \mathbf{e}_1 = \frac{\mathbf{S}\times \mathbf{S}_0}{\lVert \mathbf{S}\times \mathbf{S}_0\rVert}, $ which also appears in XDS as the Lorentz component linked to the rotation axis. A Gaussian mosaicity model yields a partiality fraction over an oscillation width $\Delta\phi$: $ P(\phi;\sigma_M,\zeta,\Delta\phi) = \frac{1}{2}\left[\mathrm{erf}\!\left(\frac{\phi+\Delta\phi/2}{\sqrt{2}\,\sigma_M/\zeta}\right) - \mathrm{erf}\!\left(\frac{\phi-\Delta\phi/2}{\sqrt{2}\,\sigma_M/\zeta}\right)\right], $ with mosaicity $\sigma_M$ in radians. Reflections are predicted if they meet minimum $\zeta$ and mosaicity-window criteria, and their predicted detector coordinates fall on the active detector area. ### 8.4 Systematic absences (centering) Systematic absences are applied at least at the centering level (prior to full space-group symmetry). For centering symbol $C$: - $I$: absent if $h+k+l$ odd, - $A$: absent if $k+l$ odd, - $B$: absent if $h+l$ odd, - $C$: absent if $h+k$ odd, - $F$: absent if any of $h+k, h+l, k+l$ is odd, - $R$: absent if $(-h+k+l)\bmod 3 \ne 0$, - $P$: no centering absences. --- ## 9. 2D Bragg integration (profile fitting over a three-ring ROI) Jungfraujoch integrates each predicted reflection in the detector plane over a CrystFEL-inspired “three-ring” region of interest (§9.1). The **default** extraction is **profile fitting** (Kabsch; §9.3), which weights each pixel by a fitted spot profile and so recovers weak reflections far better than plain summation; plain box summation (§9.2) is retained as the seed for the profile and as a fallback. Both methods share the same ROI and background model, and emit the same per-reflection $(I,\sigma,\text{partiality},d)$, so scaling, the rotation combine (§10.6) and merging consume either unchanged. ### 9.1 Regions of interest For each predicted reflection at $(x_p,y_p)$, define three radii: - $r_1$: inner signal radius, - $r_2$: inner background radius, - $r_3$: outer background radius. Pixels are classified by their squared distance $r^2=(x-x_p)^2+(y-y_p)^2$: - **signal region:** $r^2 < r_1^2$, - **background annulus:** $r_2^2 \le r^2 < r_3^2$. Invalid pixels (masked/bad/saturated) are excluded from both sums. In addition, pixels lying inside the signal disk ($r