bragg integration: trimmed-mean background, on by default for rotation
Build Packages / build:windows:nocuda (push) Failing after 1m40s
Build Packages / build:windows:cuda (push) Failing after 1m39s
Build Packages / build:viewer-tgz:cpu (push) Successful in 7m50s
Build Packages / build:viewer-tgz:cuda (push) Successful in 8m45s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 13m12s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 13m43s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 13m54s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m3s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 14m3s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m50s
Build Packages / build:rpm (rocky8) (push) Successful in 12m1s
Build Packages / XDS test (durin plugin) (push) Successful in 8m9s
Build Packages / Generate python client (push) Successful in 34s
Build Packages / Build documentation (push) Successful in 1m3s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky9) (push) Successful in 12m31s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 12m58s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 13m19s
Build Packages / DIALS test (push) Successful in 14m25s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m45s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m22s
Build Packages / Unit tests (push) Successful in 58m49s
Build Packages / build:windows:nocuda (push) Failing after 1m40s
Build Packages / build:windows:cuda (push) Failing after 1m39s
Build Packages / build:viewer-tgz:cpu (push) Successful in 7m50s
Build Packages / build:viewer-tgz:cuda (push) Successful in 8m45s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 13m12s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 13m43s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 13m54s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m3s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 14m3s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m50s
Build Packages / build:rpm (rocky8) (push) Successful in 12m1s
Build Packages / XDS test (durin plugin) (push) Successful in 8m9s
Build Packages / Generate python client (push) Successful in 34s
Build Packages / Build documentation (push) Successful in 1m3s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky9) (push) Successful in 12m31s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 12m58s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 13m19s
Build Packages / DIALS test (push) Successful in 14m25s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m45s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m22s
Build Packages / Unit tests (push) Successful in 58m49s
The local Bragg background is the mean over the r2..r3 ring. That mean reads high because the contaminants that survive the signal-disk mask - neighbour- spot wings, tails, zingers - are one-sided (positive), so it over-subtracts. Since a weak intensity is a small difference of large numbers (I = S - nS*b), a per-pixel background bias is fractionally largest at the resolution edge, exactly where it hurts most. Replace the ring mean with a symmetric trimmed mean (sort the ring, drop the lowest and highest fraction f, average the rest), controlled by a new BraggIntegrationSettings field and the rugnux `--background-trim <f>` option (default f=0.10; 0 restores the plain mean). Default on for monochromatic (rotation) data; broadband (stills) keep their tuned high-side sigma-clip, so the base engine forces the trim to 0 there. Implemented in both the CPU engine and the GPU kernel (shared-memory bitonic sort per block, flat-mean fallback above BKG_TRIM_MAX ring pixels); the two agree. 25-crystal rotation battery (fixed SG/cell): <I/sigma> improved on every crystal (median +50%), ISa on 20/22, resolution-edge R_meas fell several-fold (e.g. lyso_ref 1.0 A 108%->43%). Last-shell CC1/2 is rescued where the plain mean had collapsed to noise (Thau_9 at ~2.0 A 3.8%->64%, ~0.5 A of resolution regained; cytC_10 0.2%->10%) at a small cost (1-3%) in already-clean shells - it flattens the CC1/2 fall-off rather than shifting it. Stills unchanged. Documented in CPU_DATA_ANALYSIS.md section 9.2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -105,3 +105,15 @@ BraggIntegrationSettings &BraggIntegrationSettings::StillPartiality(bool input)
|
||||
bool BraggIntegrationSettings::GetStillPartiality() const {
|
||||
return still_partiality;
|
||||
}
|
||||
|
||||
BraggIntegrationSettings &BraggIntegrationSettings::BackgroundTrimFraction(float input) {
|
||||
check_finite("Background trim fraction", input);
|
||||
check_min("Background trim fraction", input, 0.0);
|
||||
check_max("Background trim fraction", input, 0.49); // must leave a central majority after trimming
|
||||
bkg_trim_fraction = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float BraggIntegrationSettings::GetBackgroundTrimFraction() const {
|
||||
return bkg_trim_fraction;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ class BraggIntegrationSettings {
|
||||
std::optional<float> fixed_profile_radius;
|
||||
float minimum_sigma_in_regards_to_i = 0.02;
|
||||
bool still_partiality = false; // experimental stills excitation-error partiality (rugnux --still-partiality)
|
||||
// Symmetric trimmed-mean fraction for the r2..r3 background ring: drop the lowest and highest this
|
||||
// fraction of ring pixels before averaging. Resists the high-side contamination (neighbour-spot
|
||||
// wings, tails, zingers) that biases the plain ring mean up and makes it over-subtract weak
|
||||
// high-angle reflections. Applied to monochromatic (rotation) data; the integration engine keeps
|
||||
// the tuned high-side sigma-clip for stills instead. 0 = plain ring mean (rugnux --background-trim).
|
||||
float bkg_trim_fraction = 0.10f;
|
||||
|
||||
public:
|
||||
BraggIntegrationSettings& R1(float input);
|
||||
@@ -30,6 +36,7 @@ public:
|
||||
BraggIntegrationSettings& FixedProfileRadius_recipA(std::optional<float> input);
|
||||
BraggIntegrationSettings& Integrator(IntegratorMode input);
|
||||
BraggIntegrationSettings& StillPartiality(bool input);
|
||||
BraggIntegrationSettings& BackgroundTrimFraction(float input);
|
||||
|
||||
|
||||
[[nodiscard]] IntegratorMode GetIntegrator() const;
|
||||
@@ -41,4 +48,5 @@ public:
|
||||
|
||||
[[nodiscard]] float GetMinimumSigmaInRegardsToI() const;
|
||||
[[nodiscard]] bool GetStillPartiality() const;
|
||||
[[nodiscard]] float GetBackgroundTrimFraction() const;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ This is an UNSTABLE release. It includes many experimental features, as well as
|
||||
* rugnux: Write anomalous data as a standard CCP4 anomalous MTZ - one row per reflection with `IMEAN`, `I(+)`/`I(-)` (and the matching `F`/`F(+)`/`F(-)` amplitudes) - instead of two `IMEAN` rows per Bijvoet pair, so `aimless`/`mtz2sca`/ANODE read it directly. The non-anomalous MTZ output is unchanged.
|
||||
* rugnux: Always write the merged reflections as both `<prefix>.mtz` and `<prefix>.cif` (each has its uses downstream - MTZ for the CCP4/phenix tools, mmCIF for deposition). The `--scaling-output` format selector and the plain-text `.hkl` output are removed.
|
||||
* rugnux: Add a detector-plane **modulation** (flat-field) correction surface to rotation scaling - a smooth multiplicative factor over the detector position where a reflection lands, fitted against the merged reference (symmetry-equivalents land at different positions as the crystal rotates) and cross-validated. It corrects detector-response/geometric systematics that inflate R-meas; on the rotation battery it lowers R-meas by several to tens of percent (e.g. lysoC 23->16%, lyso_2 47->28%, EcwtAL500 53->28%) with CC1/2 held or improved and the anomalous signal preserved. On by default with decay/absorption (`--no-scaling-corrections` disables all). The correction-surface cross-validation now scores a sigma-independent R-meas-like agreement instead of a studentized chi^2, so a surface can no longer pass by reshaping the sigmas (which also hardens the absorption surface against a mis-indexed-data regression).
|
||||
* Bragg integration: The local background under each Bragg spot is now a **symmetric trimmed mean** of the background ring rather than the plain mean, on by default for monochromatic/rotation data (`--background-trim <f>`, default 0.10; 0 restores the plain mean). The plain ring mean reads high because neighbour-spot wings that survive the signal-disk mask, tails and zingers are one-sided contaminants, so it over-subtracts - and since a weak intensity is a small difference of large numbers, that bias is fractionally largest at the resolution edge. Dropping the lowest and highest fraction of ring pixels removes it while leaving a clean Poisson ring essentially unchanged. On the rotation battery `<I/sigma>` improved on every crystal (median +50%) and resolution-edge R-meas fell several-fold (e.g. lyso_ref 1.0 A 108->43%); the high-resolution CC1/2 is rescued where the plain mean had collapsed it (e.g. a thaumatin case regains ~0.5 A) at a small CC1/2 cost in already-clean shells. Implemented identically in the CPU and GPU integrators; stills keep their existing high-side sigma-clip (§9.2 of CPU_DATA_ANALYSIS.md). The dead, never-read `Reflection.completeness` field was removed.
|
||||
|
||||
### 1.0.0-rc.159
|
||||
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.
|
||||
|
||||
@@ -446,7 +446,9 @@ $
|
||||
$
|
||||
with a Poisson-like uncertainty $\sigma(\hat{I})=\max\!\big(1,\ r_\sigma\hat{I},\ \sqrt{S}\big)$, i.e. $\sqrt{S}$ floored both at 1 and at a small fraction $r_\sigma$ of the intensity. A reflection is accepted as “observed” only if all signal pixels were valid and $n_B$ exceeds a minimum. This box sum is the classical estimator; it is used directly with `--integrator boxsum`, and otherwise seeds the profile fit below.
|
||||
|
||||
For the **profile-fit path on broadband (still) data**, the background mean is additionally computed with a single high-outlier reject (drop ring pixels above $\hat{b}+3\sqrt{\hat{b}}$, then recompute): a bandwidth-streaked high-resolution spot or a close neighbour can leak into the ring and bias the mean high, over-subtracting and driving weak high-resolution intensities negative. A clean Poisson background is essentially unchanged by the cut. The reject is **not** applied to plain box summation (`--integrator boxsum`) or to monochromatic/rotation data.
|
||||
**Trimmed-mean background (rotation, default on).** On monochromatic/rotation data the ring mean $\hat{b}$ is by default replaced with a **symmetric trimmed mean**: the ring pixels are sorted, the lowest and highest fraction $f$ are dropped, and the central $(1-2f)$ are averaged ($f=0.10$ by default, `--background-trim`; $f=0$ restores the plain mean). Because $\hat{I}=S-n_S\hat{b}$ is a small difference of large numbers for weak reflections, a per-pixel background bias $\delta\hat{b}$ becomes a *fractional* intensity bias $\approx n_S\,\delta\hat{b}/\hat{I}$ that grows as $\hat{I}$ shrinks — worst at the resolution edge. The plain mean reads high there because neighbour-spot wings that survive the signal-disk mask, tails and zingers are one-sided (positive) contaminants; dropping the extreme ring pixels removes that bias while a clean Poisson ring is essentially unchanged. In practice this lowers the resolution-edge $R_\text{meas}$ several-fold, raises $\langle I/\sigma\rangle$, and rescues the high-resolution CC$_{1/2}$ on data where the plain mean had collapsed it (at a small CC$_{1/2}$ cost in already-clean shells, from the slightly higher variance of the trimmed estimate). It is applied to the shared background used by both the box sum and the profile fit.
|
||||
|
||||
For the **profile-fit path on broadband (still) data**, the trimmed mean is *not* used; instead the background mean is computed with a single high-outlier reject (drop ring pixels above $\hat{b}+3\sqrt{\hat{b}}$, then recompute): a bandwidth-streaked high-resolution spot or a close neighbour can leak into the ring and bias the mean high, over-subtracting and driving weak high-resolution intensities negative. A clean Poisson background is essentially unchanged by the cut. Neither robustification is applied to plain box summation (`--integrator boxsum`).
|
||||
|
||||
### 9.3 Profile-fitted extraction (default)
|
||||
|
||||
|
||||
@@ -68,6 +68,10 @@ BraggIntegrationEngine::BraggIntegrationEngine(const DiffractionExperiment &expe
|
||||
beam_y = geom.GetBeamY_pxl();
|
||||
use_ellipse = !empirical && (bw_sigma > 0.0 || c_radial > 0.0);
|
||||
|
||||
// Trimmed-mean background applies to monochromatic (rotation) data; broadband (stills) keep the tuned
|
||||
// high-side sigma-clip, so the trim is forced off there. The fraction itself comes from the settings.
|
||||
bkg_trim = broadband ? 0.0f : settings.GetBackgroundTrimFraction();
|
||||
|
||||
polarization = experiment.GetPolarizationFactor();
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,10 @@ protected:
|
||||
double c_radial; // radial variance coefficient of tan^2(2theta): parallax + capture
|
||||
double F_px; // detector distance expressed in pixels
|
||||
float beam_x, beam_y;
|
||||
// Effective symmetric trimmed-mean background fraction (BraggIntegrationSettings): the configured
|
||||
// fraction for monochromatic (rotation) data, forced to 0 for broadband (stills, which keep their
|
||||
// high-side sigma-clip). 0 = plain ring mean. Read by both the CPU and GPU engines.
|
||||
float bkg_trim = 0.0f;
|
||||
|
||||
DiffractionGeometry geom; // kept for the per-reflection polarization correction
|
||||
std::optional<float> polarization;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "../../common/CompressedImage.h"
|
||||
#include "../../common/JFJochException.h"
|
||||
@@ -56,6 +57,12 @@ std::vector<Reflection> BraggIntegrationEngineCPU::RunImpl(const Sampler &img,
|
||||
const int W = static_cast<int>(xpixel), H = static_cast<int>(ypixel);
|
||||
const bool do_clip = apply_bkg_clip && mode != IntegratorMode::BoxSum;
|
||||
|
||||
// Symmetric trimmed-mean background fraction (BraggIntegrationSettings, rugnux --background-trim):
|
||||
// replaces the r2..r3 ring MEAN with an f-trimmed mean, robust to the high-side contamination
|
||||
// (neighbour wings, tails) that biases the plain mean up and makes it over-subtract weak high-angle
|
||||
// reflections. Already forced to 0 for broadband/stills by the base ctor (they keep the clip below).
|
||||
const double bkg_trim_frac = bkg_trim;
|
||||
|
||||
auto grid_idx = [this](int dx, int dy) { return (dy + R) * G + (dx + R); };
|
||||
|
||||
// --- Reflection mask: mark the r2 signal disk of every predicted reflection so a neighbour's
|
||||
@@ -82,6 +89,7 @@ std::vector<Reflection> BraggIntegrationEngineCPU::RunImpl(const Sampler &img,
|
||||
};
|
||||
std::vector<Rough> rough(npredicted);
|
||||
double inv_d2_min = std::numeric_limits<double>::max(), inv_d2_max = 0.0;
|
||||
std::vector<int32_t> bkg_vals; // reused per reflection for the trimmed-mean background (idea 1)
|
||||
|
||||
for (size_t i = 0; i < npredicted; ++i) {
|
||||
const auto &r = predicted[i];
|
||||
@@ -94,6 +102,7 @@ std::vector<Reflection> BraggIntegrationEngineCPU::RunImpl(const Sampler &img,
|
||||
int64_t I_sum = 0, I_sum_x = 0, I_sum_y = 0, n_inner = 0, n_inner_valid = 0;
|
||||
double bkg_sum = 0.0;
|
||||
int n_bkg = 0;
|
||||
bkg_vals.clear();
|
||||
for (int y = y0; y <= y1; ++y)
|
||||
for (int x = x0; x <= x1; ++x) {
|
||||
const double d2 = (x - r.predicted_x) * (x - r.predicted_x) + (y - r.predicted_y) * (y - r.predicted_y);
|
||||
@@ -109,15 +118,28 @@ std::vector<Reflection> BraggIntegrationEngineCPU::RunImpl(const Sampler &img,
|
||||
if (refl_mask[y * W + x]) continue;
|
||||
if (!valid(px)) continue;
|
||||
bkg_sum += static_cast<double>(px);
|
||||
if (bkg_trim_frac > 0.0) bkg_vals.push_back(px);
|
||||
++n_bkg;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_inner_valid == n_inner && n_bkg > 5) {
|
||||
out.bkg = bkg_sum / n_bkg;
|
||||
// One high-outlier sigma-clip pass on the background ring (stills-only): reject pixels
|
||||
// above mean + 3*sqrt(mean) to strip a bandwidth-streaked neighbour that biases the mean.
|
||||
if (do_clip) {
|
||||
if (bkg_trim_frac > 0.0 && bkg_vals.size() > 5) {
|
||||
// Symmetric trimmed mean over the background ring (idea 1): drop the lowest and highest
|
||||
// bkg_trim_frac of the pixels, average the rest. Robust to the high-side contamination
|
||||
// that biases the plain ring mean and makes it over-subtract at high resolution.
|
||||
std::sort(bkg_vals.begin(), bkg_vals.end());
|
||||
const size_t lo = static_cast<size_t>(bkg_vals.size() * bkg_trim_frac);
|
||||
const size_t hi = bkg_vals.size() - lo;
|
||||
if (hi > lo) {
|
||||
double s = 0.0;
|
||||
for (size_t t = lo; t < hi; ++t) s += bkg_vals[t];
|
||||
out.bkg = s / static_cast<double>(hi - lo);
|
||||
}
|
||||
} else if (do_clip) {
|
||||
// One high-outlier sigma-clip pass on the background ring (stills-only): reject pixels
|
||||
// above mean + 3*sqrt(mean) to strip a bandwidth-streaked neighbour that biases the mean.
|
||||
const double thr = out.bkg + 3.0 * std::sqrt(std::max(out.bkg, 1.0));
|
||||
double s = 0.0;
|
||||
int n = 0;
|
||||
|
||||
@@ -26,10 +26,14 @@ struct BraggGpuParams {
|
||||
float c_radial;
|
||||
float F_px;
|
||||
float beam_x, beam_y;
|
||||
float bkg_trim; // idea 1: symmetric trimmed-mean background fraction (0 = plain ring mean)
|
||||
};
|
||||
|
||||
__device__ inline bool valid(int32_t v) { return v != INT32_MIN && v != INT32_MAX; }
|
||||
|
||||
// idea 1: max annulus pixels held in shared memory for the per-block trimmed-mean sort (else flat mean).
|
||||
constexpr int BKG_TRIM_MAX = 512;
|
||||
|
||||
// --- Mark the r2 signal disk of every predicted reflection (race-free: all writes are 1). ---
|
||||
__global__ void mark_mask(const float *px_x, const float *px_y, uint8_t *mask, BraggGpuParams p, int n) {
|
||||
const int i = blockIdx.x;
|
||||
@@ -111,8 +115,57 @@ __global__ void boxsum(const float *px_x, const float *px_y, const float *dd,
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Trimmed-mean background (idea 1): collect the annulus into shared memory, bitonic-sort the block, and
|
||||
// average the middle (1 - 2*bkg_trim) fraction - robust to the high-side contamination that biases the
|
||||
// plain ring mean. Falls back to the flat mean when the ring exceeds the shared buffer.
|
||||
__shared__ int s_bvals[BKG_TRIM_MAX];
|
||||
__shared__ int s_bn;
|
||||
if (threadIdx.x == 0) s_bn = 0;
|
||||
__syncthreads();
|
||||
const bool do_trim = s_accept && p.bkg_trim > 0.0f && s_nbkg > 5 && s_nbkg <= BKG_TRIM_MAX;
|
||||
if (do_trim) {
|
||||
for (int t = threadIdx.x; t < area; t += blockDim.x) {
|
||||
const int x = x0 + t % bw, y = y0 + t / bw;
|
||||
const float ddx = (float) x - cx, ddy = (float) y - cy;
|
||||
const float d2 = ddx * ddx + ddy * ddy;
|
||||
if (!(d2 >= p.r2_sq && d2 < p.r3_sq)) continue;
|
||||
if (mask[y * p.W + x]) continue;
|
||||
const int32_t px = img[y * p.W + x];
|
||||
if (!valid(px)) continue;
|
||||
const int slot = atomicAdd(&s_bn, 1);
|
||||
if (slot < BKG_TRIM_MAX) s_bvals[slot] = px;
|
||||
}
|
||||
__syncthreads();
|
||||
const int nb = min(s_bn, BKG_TRIM_MAX);
|
||||
int n2 = 1;
|
||||
while (n2 < nb) n2 <<= 1;
|
||||
for (int t = threadIdx.x + nb; t < n2; t += blockDim.x) s_bvals[t] = INT32_MAX; // pad to power of 2
|
||||
__syncthreads();
|
||||
for (int k = 2; k <= n2; k <<= 1) // ascending bitonic sort of s_bvals[0..n2)
|
||||
for (int j = k >> 1; j > 0; j >>= 1) {
|
||||
for (int idx = threadIdx.x; idx < n2; idx += blockDim.x) {
|
||||
const int ixj = idx ^ j;
|
||||
if (ixj > idx) {
|
||||
const bool up = ((idx & k) == 0);
|
||||
const int a = s_bvals[idx], b = s_bvals[ixj];
|
||||
if ((up && a > b) || (!up && a < b)) { s_bvals[idx] = b; s_bvals[ixj] = a; }
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
if (threadIdx.x == 0) {
|
||||
const int lo = (int) (nb * p.bkg_trim), hi = nb - lo;
|
||||
if (hi > lo) {
|
||||
double s = 0.0;
|
||||
for (int t = lo; t < hi; ++t) s += (double) s_bvals[t];
|
||||
s_bkg = s / (double) (hi - lo);
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Second ring pass for the stills sigma-clip (re-reads the annulus; avoids storing bkg values).
|
||||
if (s_accept && p.do_clip) {
|
||||
if (s_accept && p.do_clip && !do_trim) {
|
||||
double c_l = 0.0; int cn_l = 0;
|
||||
for (int t = threadIdx.x; t < area; t += blockDim.x) {
|
||||
const int x = x0 + t % bw, y = y0 + t / bw;
|
||||
@@ -437,6 +490,7 @@ std::vector<Reflection> BraggIntegrationEngineGPU::Run(const ImagePreprocessorBu
|
||||
.bw_sigma = static_cast<float>(bw_sigma), .c_radial = static_cast<float>(c_radial),
|
||||
.F_px = static_cast<float>(F_px),
|
||||
.beam_x = beam_x, .beam_y = beam_y,
|
||||
.bkg_trim = bkg_trim, // effective trim fraction (0 for stills), set by the base ctor from settings
|
||||
};
|
||||
|
||||
// Pass A: reset accumulators, mask, then box-sum.
|
||||
|
||||
@@ -111,6 +111,7 @@ void print_usage() {
|
||||
std::cout << " Integration" << std::endl;
|
||||
std::cout << " --bandwidth <num> Relative X-ray bandwidth FWHM (e.g. 0.01 for 1% DMM); default from file or 0" << std::endl;
|
||||
std::cout << " --integration-radius <r> Signal-box radius r1, or r1,r2,r3 (px). One value => r2=r1+2, r3=r1+4" << std::endl;
|
||||
std::cout << " --background-trim <f> Rotation: symmetric trimmed-mean fraction for the background ring (0<=f<0.5, default 0.10; 0 = plain mean). Removes the high-side bias that over-subtracts weak high-angle spots" << std::endl;
|
||||
std::cout << " --integrator <txt> Spot integrator boxsum|gaussian|empirical (default: gaussian profile-fit; boxsum is the classical fallback)" << std::endl;
|
||||
std::cout << " --still-partiality Experimental: weight stills reflections by a Gaussian excitation-error partiality exp(-dist_ewald^2/2sigma^2) instead of treating each as a full" << std::endl;
|
||||
std::cout << " -q, --azim-q-spacing <num> Azimuthal-integration Q bin spacing (1/A) (default: 0.01)" << std::endl;
|
||||
@@ -151,6 +152,7 @@ enum {
|
||||
OPT_REFINE_GEOMETRY,
|
||||
OPT_BANDWIDTH,
|
||||
OPT_INTEGRATION_RADIUS,
|
||||
OPT_BACKGROUND_TRIM,
|
||||
OPT_REJECT_OUTLIERS,
|
||||
OPT_REJECT_DELTA_CCHALF,
|
||||
OPT_REFERENCE_COLUMN,
|
||||
@@ -252,6 +254,7 @@ static option long_options[] = {
|
||||
{"resolution-shells", required_argument, nullptr, OPT_RESOLUTION_SHELLS},
|
||||
{"bandwidth", required_argument, nullptr, OPT_BANDWIDTH},
|
||||
{"integration-radius", required_argument, nullptr, OPT_INTEGRATION_RADIUS},
|
||||
{"background-trim", required_argument, nullptr, OPT_BACKGROUND_TRIM},
|
||||
{"integrator", required_argument, nullptr, OPT_INTEGRATOR},
|
||||
{"still-partiality", no_argument, nullptr, OPT_STILL_PARTIALITY},
|
||||
{"detect-ice-rings", optional_argument, nullptr, OPT_DETECT_ICE_RINGS},
|
||||
@@ -523,6 +526,7 @@ int main(int argc, char **argv) {
|
||||
std::optional<double> resolution_cc_target; // --resolution-cc-target
|
||||
std::optional<int> report_shell_count; // --resolution-shells
|
||||
std::optional<std::string> integration_radius_arg; // "r1" or "r1,r2,r3"
|
||||
std::optional<double> background_trim_arg; // --background-trim: background-ring trimmed-mean fraction
|
||||
std::optional<IntegratorMode> integrator_mode; // --integrator boxsum|gaussian|empirical
|
||||
bool still_partiality_flag = false; // --still-partiality (experimental stills partiality)
|
||||
std::optional<double> outlier_reject_nsigma; // merge per-observation outlier rejection
|
||||
@@ -786,6 +790,9 @@ int main(int argc, char **argv) {
|
||||
case OPT_INTEGRATION_RADIUS:
|
||||
integration_radius_arg = optarg;
|
||||
break;
|
||||
case OPT_BACKGROUND_TRIM:
|
||||
background_trim_arg = parse_double_arg(optarg, "--background-trim", logger);
|
||||
break;
|
||||
case OPT_INTEGRATOR:
|
||||
if (strcmp(optarg, "boxsum") == 0) integrator_mode = IntegratorMode::BoxSum;
|
||||
else if (strcmp(optarg, "gaussian") == 0) integrator_mode = IntegratorMode::ProfileGaussian;
|
||||
@@ -1398,6 +1405,14 @@ int main(int argc, char **argv) {
|
||||
: "profile (empirical)");
|
||||
}
|
||||
|
||||
if (background_trim_arg) {
|
||||
BraggIntegrationSettings bis = experiment.GetBraggIntegrationSettings();
|
||||
bis.BackgroundTrimFraction(static_cast<float>(*background_trim_arg));
|
||||
experiment.ImportBraggIntegrationSettings(bis);
|
||||
logger.Info("Background-ring trimmed-mean fraction set to {:.2f} (rotation; stills keep the sigma-clip)",
|
||||
*background_trim_arg);
|
||||
}
|
||||
|
||||
if (still_partiality_flag) {
|
||||
BraggIntegrationSettings bis = experiment.GetBraggIntegrationSettings();
|
||||
bis.StillPartiality(true);
|
||||
|
||||
Reference in New Issue
Block a user