From 06a5a118bf29dcf16c2964c0e9a2f6317eeb6f22 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Tue, 11 Aug 2026 00:07:30 +0200 Subject: [PATCH] Bragg integration: widen the background ring to r3 = 13 The background is estimated from the r2..r3 ring and then subtracted from every pixel of the r1 disk, so the ring mean's own error enters the intensity n_inner times over: var(I) carries n_inner^2 * bkg / n_B. That term is first-order in sigma, and it is set by how many pixels the ring holds - not by anything about the reflection. At r3 = 10 the ring holds about 200 px against the disk's 50. Widening it to 13 roughly doubles that. The signal disk is untouched, and the pixels gained lie further from the reflection rather than nearer, so nothing is traded for them. The effect is not subtle once looked for. Matched observation by observation on one crystal, halving the ring's pixel count leaves the intensity alone and inflates sigma by 4.7%, and the inflation rank-orders with the ring collapse across the battery. Over the whole rotation battery, against the same binary at r3 = 10: ISa better on 14 crystals and worse on 4, the summed shortfall against the reference 164.7 -> 155.9, the summed low-resolution R_meas excess 69.7 -> 59.1 percentage points, and one more crystal reaching the reference space group (33/37 -> 34/37, a trigonal case that was over-promoting). Largest gains where the ring was starved worst; the four losses are 0.25 to 2.16 in ISa and none of them changes a space group. Co-Authored-By: Claude Opus 5 (1M context) --- common/BraggIntegrationSettings.h | 7 ++++++- docs/CHANGELOG.md | 1 + docs/CPU_DATA_ANALYSIS.md | 2 +- image_analysis/bragg_integration/BraggIntegrationEngine.h | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/common/BraggIntegrationSettings.h b/common/BraggIntegrationSettings.h index 58327127..b3b64104 100644 --- a/common/BraggIntegrationSettings.h +++ b/common/BraggIntegrationSettings.h @@ -20,7 +20,12 @@ class BraggIntegrationSettings { IntegratorMode integrator_mode = IntegratorMode::ProfileGaussian; float r_1 = 4; float r_2 = 6; - float r_3 = 10; + // The background ring's precision is set by how many pixels it averages, not by how big the + // reflection is: the ring mean's error enters the intensity n_inner times over, so var(b)/n_B is + // a first-order term in sigma. At r3 = 10 the ring holds ~200 px against the r1 disk's ~50, and + // widening it to 13 roughly doubles that for no cost in signal - the disk is untouched, and the + // extra pixels sit further from the reflection, not closer. + float r_3 = 13; // How many times the beam's radial streak to push the r2..r3 background ring out by, per // reflection. A bandwidth streaks a spot radially by bw_sigma*Rpx, and against a fixed pixel ring // that puts the background annulus on the reflection's own tails at high resolution, where it diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index be240e67..46157b99 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,6 +4,7 @@ This is an UNSTABLE release. It includes many experimental features, as well as many AI generated fixes. We recommend using rc.152 for production use. * rugnux: The error-model **a** and **b** are reported in XDS's convention, and `_reflns.jfjoch_diffrn_ISa` now carries the whole-range `1/sqrt(a*b)` that XDS's ISa denotes; the strong-reflection asymptote moves to `_reflns.jfjoch_diffrn_ISa_asymptotic`. **A file written by an earlier version carries the asymptote under the old name.** +* Bragg integration: the background ring's outer radius default changes from 10 px to **13 px**, which roughly doubles the pixels behind each background estimate; the signal disk is unchanged. * rugnux: The background ring can be **elongated radially per reflection** for broadband data (`--integration-stencil `, default 0 = the fixed circular ring), by `k` times the beam's radial streak; the `r1` signal box stays circular. * rugnux: New **beam-stop shadow detection**, **on by default** (`--detect-beam-stop[=N|off]`), finds the beam stop and its holder in a projection of N images (default 60) and adds them to the pixel mask as bit 9, which is cleared at the start of every run. * Viewer: the detected beam-stop shadow is drawn in coral, with a "Show beam stop" switch in the side panel. diff --git a/docs/CPU_DATA_ANALYSIS.md b/docs/CPU_DATA_ANALYSIS.md index 2b792ac9..5b34550d 100644 --- a/docs/CPU_DATA_ANALYSIS.md +++ b/docs/CPU_DATA_ANALYSIS.md @@ -567,7 +567,7 @@ Pixels are classified by their squared distance $r^2=(x-x_p)^2+(y-y_p)^2$: Invalid pixels (masked/bad/saturated) are excluded from both sums. In addition, pixels lying inside the signal disk ($r`, default 0).** The three radii above are fixed pixel counts, identical for every reflection at every resolution. A reflection is not round, though: a finite bandwidth streaks it radially by $\sigma_\mathrm{bw}=\text{bandwidth}\cdot R_\mathrm{px}$. On a radially smeared spot the fixed $6\ldots10$ px ring therefore sits only $\approx1.3$–$2.2$ radial $\sigma$ from the centre — on the reflection's own tails, which it then measures as background. +**Radially elongated background ring (opt-in, `--integration-stencil `, default 0).** The three radii above are fixed pixel counts, identical for every reflection at every resolution. A reflection is not round, though: a finite bandwidth streaks it radially by $\sigma_\mathrm{bw}=\text{bandwidth}\cdot R_\mathrm{px}$. On a radially smeared spot the fixed $6\ldots13$ px ring therefore sits only $\approx1.3$–$2.2$ radial $\sigma$ from the centre — on the reflection's own tails, which it then measures as background. With $k>0$ the **background ring becomes an ellipse**, elongated along the beam→reflection direction by $k\sigma_\mathrm{bw}$. The **radial** semi-axes become $r_2+k\sigma_\mathrm{bw}$ and $r_3+k\sigma_\mathrm{bw}$; the **tangential** half-widths stay $r_2$ and $r_3$; and the growth is capped at $2r_3$, which bounds what a mis-declared bandwidth can do to the bounding box. Pixels are then classified as diff --git a/image_analysis/bragg_integration/BraggIntegrationEngine.h b/image_analysis/bragg_integration/BraggIntegrationEngine.h index 7deca572..057e5143 100644 --- a/image_analysis/bragg_integration/BraggIntegrationEngine.h +++ b/image_analysis/bragg_integration/BraggIntegrationEngine.h @@ -53,7 +53,7 @@ constexpr double C_CAPTURE = 2.5; // weak-spot radial capture term ( // Lower bound on the background term of the Kabsch fit weights (v = max(bkg, floor) + signal). It // guards the background ESTIMATE, not the detector: the r2..r3 ring mean of a high-angle reflection // can come out exactly zero, and v = 0 makes the weights P^2/v diverge. A ring of n pixels cannot -// resolve a background below ~1/n (0.005..0.02 for the default r2=6/r3=10 stencil), so that is the +// resolve a background below ~1/n (0.003..0.02 for the default r2=6/r3=13 stencil), so that is the // scale the floor has to work at. Anything larger over-regularizes: the floor multiplies the reported // variance by floor/bkg for every pixel below it, so the previous 1/12 inflated sigma by 1.3x at // 0.05 ct/px and 1.7x at 0.03 - exactly where the weakest high-resolution data live. Digitisation @@ -70,7 +70,7 @@ constexpr double WEIGHT_VARIANCE_MIN_FRACTION = 0.5; // unbounded vector and would otherwise keep trimming where the GPU had silently stopped. Note that // an elongated ring makes this a function of resolution rather than a property of the dataset - the // ring area grows with the elongation, so on a wide enough stencil the estimator changes at a fixed -// detector radius. The growth cap below keeps the default r2=6/r3=10 ring under the bound at any +// detector radius. The growth cap below keeps the default r2=6/r3=13 ring under the bound at any // bandwidth; the wider stills radii can cross it, and only ever with --background-trim, which is // off by default and kept for back compatibility. constexpr int BKG_TRIM_MAX = 512;