Remove non-helping stills merge/scaling knobs
Trims three opt-in stills parameters that did not improve data quality on the external-reference (PDB R-free) battery and only added code: - --partiality-uncertainty: the (1-p)/p merge-sigma term was null on all four serial-stills datasets of the battery vs their reference structures (and neutral-to-harmful at higher coefficients); removed the flag, setting and CorrectedSigma term. - --stills-modulation: the detector-plane flat-field surface was net-negative on flooded data; removed the flag, setting and MergeOnTheFly::RefineModulation (the rotation modulation in RotationScaleMerge is unaffected). - --min-indexed-fraction: every value other than the 0.20 default collapsed CC1/2; removed the override flag/setter, keeping the fixed 0.20 acceptance floor. Default behaviour is unchanged (all three were off / at their default). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -28,11 +28,6 @@ int64_t IndexingSettings::GetViableCellMinSpots() const {
|
|||||||
return viable_cell_min_spots;
|
return viable_cell_min_spots;
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexingSettings &IndexingSettings::MinIndexedSpotFraction(float input) {
|
|
||||||
min_indexed_spot_fraction = input;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
float IndexingSettings::GetMinIndexedSpotFraction() const {
|
float IndexingSettings::GetMinIndexedSpotFraction() const {
|
||||||
return min_indexed_spot_fraction;
|
return min_indexed_spot_fraction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ public:
|
|||||||
IndexingSettings();
|
IndexingSettings();
|
||||||
|
|
||||||
IndexingSettings& ViableCellMinSpots(int64_t input);
|
IndexingSettings& ViableCellMinSpots(int64_t input);
|
||||||
IndexingSettings& MinIndexedSpotFraction(float input);
|
|
||||||
IndexingSettings& Algorithm(IndexingAlgorithmEnum input);
|
IndexingSettings& Algorithm(IndexingAlgorithmEnum input);
|
||||||
IndexingSettings& FFT_MaxUnitCell_A(float input);
|
IndexingSettings& FFT_MaxUnitCell_A(float input);
|
||||||
IndexingSettings& FFT_MinUnitCell_A(float input);
|
IndexingSettings& FFT_MinUnitCell_A(float input);
|
||||||
|
|||||||
@@ -151,15 +151,6 @@ bool ScalingSettings::GetCorrectionSurfaces() const {
|
|||||||
return correction_surfaces;
|
return correction_surfaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScalingSettings &ScalingSettings::StillsModulation(bool input) {
|
|
||||||
stills_modulation = input;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScalingSettings::GetStillsModulation() const {
|
|
||||||
return stills_modulation;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScalingSettings &ScalingSettings::StillsPartialityRefine(bool input) {
|
ScalingSettings &ScalingSettings::StillsPartialityRefine(bool input) {
|
||||||
stills_partiality_refine = input;
|
stills_partiality_refine = input;
|
||||||
return *this;
|
return *this;
|
||||||
@@ -229,15 +220,6 @@ double ScalingSettings::GetCaptureUncertaintyCoeff() const {
|
|||||||
return capture_uncertainty_coeff;
|
return capture_uncertainty_coeff;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScalingSettings &ScalingSettings::PartialityUncertaintyCoeff(double input) {
|
|
||||||
partiality_uncertainty_coeff = input;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
double ScalingSettings::GetPartialityUncertaintyCoeff() const {
|
|
||||||
return partiality_uncertainty_coeff;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScalingSettings &ScalingSettings::MinCapturedFraction(double input) {
|
ScalingSettings &ScalingSettings::MinCapturedFraction(double input) {
|
||||||
if (input < 0.0)
|
if (input < 0.0)
|
||||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ class ScalingSettings {
|
|||||||
// a fraction f<1 of its rocking curve is extrapolated, and the unobserved (1-f) carries a
|
// a fraction f<1 of its rocking curve is extrapolated, and the unobserved (1-f) carries a
|
||||||
// systematic error ~coeff*(1-f)*I that plain counting sigma misses. 0 = off (baseline).
|
// systematic error ~coeff*(1-f)*I that plain counting sigma misses. 0 = off (baseline).
|
||||||
double capture_uncertainty_coeff = 0.0;
|
double capture_uncertainty_coeff = 0.0;
|
||||||
double partiality_uncertainty_coeff = 0.0;
|
|
||||||
// Full-level captured-fraction floor for the rot3d combine: drop a reconstructed full whose rocking
|
// Full-level captured-fraction floor for the rot3d combine: drop a reconstructed full whose rocking
|
||||||
// curve was only fractionally captured (sum of its partials' partiality < this). Distinct from
|
// curve was only fractionally captured (sum of its partials' partiality < this). Distinct from
|
||||||
// min_partiality, which gates individual partials; this gates the assembled full. 0 = off (baseline).
|
// min_partiality, which gates individual partials; this gates the assembled full. 0 = off (baseline).
|
||||||
@@ -46,10 +45,6 @@ class ScalingSettings {
|
|||||||
bool correction_surfaces = true;
|
bool correction_surfaces = true;
|
||||||
// Absorption-surface refinement iteration count (used when correction_surfaces is on).
|
// Absorption-surface refinement iteration count (used when correction_surfaces is on).
|
||||||
int absorption_iter = 3;
|
int absorption_iter = 3;
|
||||||
// Detector-plane modulation (flat-field) correction for the STILLS merge path (ScaleOnTheFly +
|
|
||||||
// MergeOnTheFly), which otherwise has no correction surfaces. Off by default (experimental; the
|
|
||||||
// rotation path fits its own modulation via RotationScaleMerge). Enabled by rugnux --stills-modulation.
|
|
||||||
bool stills_modulation = false;
|
|
||||||
|
|
||||||
// Physical partiality post-refinement for the STILLS merge (StillsPartialityRefine): refine a per-crystal
|
// Physical partiality post-refinement for the STILLS merge (StillsPartialityRefine): refine a per-crystal
|
||||||
// orientation tilt against the running merge, recompute each reflection's partiality from the refined
|
// orientation tilt against the running merge, recompute each reflection's partiality from the refined
|
||||||
@@ -100,14 +95,12 @@ public:
|
|||||||
ScalingSettings& MinPartiality(double min_partiality);
|
ScalingSettings& MinPartiality(double min_partiality);
|
||||||
ScalingSettings& ForcedMosaicity(std::optional<double> input);
|
ScalingSettings& ForcedMosaicity(std::optional<double> input);
|
||||||
ScalingSettings& CaptureUncertaintyCoeff(double input);
|
ScalingSettings& CaptureUncertaintyCoeff(double input);
|
||||||
ScalingSettings& PartialityUncertaintyCoeff(double input);
|
|
||||||
ScalingSettings& MinCapturedFraction(double input);
|
ScalingSettings& MinCapturedFraction(double input);
|
||||||
ScalingSettings& MinCCForImage(double min_cc_for_image);
|
ScalingSettings& MinCCForImage(double min_cc_for_image);
|
||||||
ScalingSettings& OutlierRejectNsigma(double input);
|
ScalingSettings& OutlierRejectNsigma(double input);
|
||||||
ScalingSettings& ScaleFulls(bool input);
|
ScalingSettings& ScaleFulls(bool input);
|
||||||
ScalingSettings& AbsorptionIter(int input);
|
ScalingSettings& AbsorptionIter(int input);
|
||||||
ScalingSettings& CorrectionSurfaces(bool input);
|
ScalingSettings& CorrectionSurfaces(bool input);
|
||||||
ScalingSettings& StillsModulation(bool input);
|
|
||||||
ScalingSettings& StillsPartialityRefine(bool input);
|
ScalingSettings& StillsPartialityRefine(bool input);
|
||||||
ScalingSettings& ExpectedVarianceMerge(bool input);
|
ScalingSettings& ExpectedVarianceMerge(bool input);
|
||||||
ScalingSettings& SmoothGDegrees(double input);
|
ScalingSettings& SmoothGDegrees(double input);
|
||||||
@@ -141,14 +134,12 @@ public:
|
|||||||
[[nodiscard]] double GetMinPartiality() const;
|
[[nodiscard]] double GetMinPartiality() const;
|
||||||
[[nodiscard]] std::optional<double> GetForcedMosaicity() const;
|
[[nodiscard]] std::optional<double> GetForcedMosaicity() const;
|
||||||
[[nodiscard]] double GetCaptureUncertaintyCoeff() const;
|
[[nodiscard]] double GetCaptureUncertaintyCoeff() const;
|
||||||
[[nodiscard]] double GetPartialityUncertaintyCoeff() const;
|
|
||||||
[[nodiscard]] double GetMinCapturedFraction() const;
|
[[nodiscard]] double GetMinCapturedFraction() const;
|
||||||
[[nodiscard]] double GetMinCCForImage() const;
|
[[nodiscard]] double GetMinCCForImage() const;
|
||||||
[[nodiscard]] double GetOutlierRejectNsigma() const;
|
[[nodiscard]] double GetOutlierRejectNsigma() const;
|
||||||
[[nodiscard]] bool GetScaleFulls() const;
|
[[nodiscard]] bool GetScaleFulls() const;
|
||||||
[[nodiscard]] int GetAbsorptionIter() const;
|
[[nodiscard]] int GetAbsorptionIter() const;
|
||||||
[[nodiscard]] bool GetCorrectionSurfaces() const;
|
[[nodiscard]] bool GetCorrectionSurfaces() const;
|
||||||
[[nodiscard]] bool GetStillsModulation() const;
|
|
||||||
[[nodiscard]] bool GetStillsPartialityRefine() const;
|
[[nodiscard]] bool GetStillsPartialityRefine() const;
|
||||||
[[nodiscard]] bool GetExpectedVarianceMerge() const;
|
[[nodiscard]] bool GetExpectedVarianceMerge() const;
|
||||||
[[nodiscard]] double GetSmoothGDegrees() const;
|
[[nodiscard]] double GetSmoothGDegrees() const;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ void MergeOnTheFly::AddImage(const IntegrationOutcome &outcome, int64_t image_id
|
|||||||
continue;
|
continue;
|
||||||
auto hkl = generator(r);
|
auto hkl = generator(r);
|
||||||
auto hkl_key = hkl.pack();
|
auto hkl_key = hkl.pack();
|
||||||
sigma_corr = CorrectedSigma(I_corr, sigma_corr, r.image_scale_corr, hkl_key, r.partiality);
|
sigma_corr = CorrectedSigma(I_corr, sigma_corr, r.image_scale_corr, hkl_key);
|
||||||
|
|
||||||
// Robust outlier rejection: drop this observation if it sits more than
|
// Robust outlier rejection: drop this observation if it sits more than
|
||||||
// reject_nsigma error-model sigmas from the reflection's median. Needs the active
|
// reject_nsigma error-model sigmas from the reflection's median. Needs the active
|
||||||
@@ -131,139 +131,8 @@ void MergeOnTheFly::AddImage(const IntegrationOutcome &outcome, int64_t image_id
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double MergeOnTheFly::RefineModulation(std::vector<IntegrationOutcome> &outcomes) {
|
|
||||||
// A minimum held-out generalizing gain (fraction of the held-out scatter) before the surface is
|
|
||||||
// applied - a margin, so a noise-level "improvement" never engages the correction. Matches the
|
|
||||||
// rotation ApplyCellSurface gate.
|
|
||||||
constexpr double CV_MIN_RELATIVE_GAIN = 0.02;
|
|
||||||
constexpr int NB = 16;
|
|
||||||
const int ncell = NB * NB;
|
|
||||||
|
|
||||||
// One scaled observation reduced to what the surface fit needs. `parity` (image index & 1) drives the
|
|
||||||
// even/odd cross-validation split - the stills analogue of the rotation frame parity.
|
|
||||||
struct MObs { double I, sigma, corr; float px, py; int32_t group; int parity; int cell; };
|
|
||||||
std::vector<MObs> obs;
|
|
||||||
|
|
||||||
// Accept exactly what AddImage merges (systematic absence, scale/resolution/ice/partiality filters),
|
|
||||||
// and additionally require a finite detector position. Returns the dense ASU-group id or -1.
|
|
||||||
std::unordered_map<uint64_t, int> group_of;
|
|
||||||
auto accept = [&](const Reflection &r, MObs &out) -> bool {
|
|
||||||
if (generator.IsSystematicallyAbsent(r)) return false;
|
|
||||||
if (r.image_scale_corr <= 0.0f || !std::isfinite(r.image_scale_corr)) return false;
|
|
||||||
if (!AcceptReflection(r, high_resolution_limit)) return false;
|
|
||||||
if (exclude_ice_rings && r.on_ice_ring) return false;
|
|
||||||
if (IsMaskedRing(r)) return false;
|
|
||||||
if (r.partiality < min_partiality) return false;
|
|
||||||
if (!std::isfinite(r.predicted_x) || !std::isfinite(r.predicted_y)) return false;
|
|
||||||
const float I_corr = r.I * r.image_scale_corr, sigma_corr = r.sigma * r.image_scale_corr;
|
|
||||||
if (!std::isfinite(I_corr) || !std::isfinite(sigma_corr) || sigma_corr <= 0.0f) return false;
|
|
||||||
const uint64_t key = generator(r).pack();
|
|
||||||
auto [it, inserted] = group_of.try_emplace(key, static_cast<int>(group_of.size()));
|
|
||||||
out.I = r.I; out.sigma = r.sigma; out.corr = r.image_scale_corr;
|
|
||||||
out.px = r.predicted_x; out.py = r.predicted_y; out.group = it->second;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
float pxmin = std::numeric_limits<float>::infinity(), pxmax = -pxmin, pymin = pxmin, pymax = -pxmin;
|
|
||||||
for (size_t i = 0; i < outcomes.size(); ++i) {
|
|
||||||
const int parity = static_cast<int>(i & 1);
|
|
||||||
for (const auto &r : outcomes[i].reflections) {
|
|
||||||
MObs m{};
|
|
||||||
if (!accept(r, m)) continue;
|
|
||||||
m.parity = parity;
|
|
||||||
pxmin = std::min(pxmin, m.px); pxmax = std::max(pxmax, m.px);
|
|
||||||
pymin = std::min(pymin, m.py); pymax = std::max(pymax, m.py);
|
|
||||||
obs.push_back(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const int n_groups = static_cast<int>(group_of.size());
|
|
||||||
if (obs.size() < static_cast<size_t>(8 * ncell) || !(pxmax > pxmin) || !(pymax > pymin))
|
|
||||||
return 0.0; // too sparse to over-determine a 16x16 surface, or degenerate detector footprint
|
|
||||||
|
|
||||||
const float sx = NB / (pxmax - pxmin), sy = NB / (pymax - pymin);
|
|
||||||
for (auto &m : obs) {
|
|
||||||
const int ix = std::clamp(static_cast<int>((m.px - pxmin) * sx), 0, NB - 1);
|
|
||||||
const int iy = std::clamp(static_cast<int>((m.py - pymin) * sy), 0, NB - 1);
|
|
||||||
m.cell = ix * NB + iy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fit the per-cell factor over {parity subset} (parity < 0 = all obs), n_iter alternating rounds against
|
|
||||||
// that subset's own inverse-variance reference: Tikhonov pull to 1, gauge-fixed to a den-weighted
|
|
||||||
// geometric mean of 1 so it never drifts the overall scale. Mirrors RotationScaleMerge::ApplyCellSurface.
|
|
||||||
constexpr int N_ITER = 3;
|
|
||||||
auto fit_surface = [&](int parity) -> std::vector<double> {
|
|
||||||
std::vector<double> A(ncell, 1.0);
|
|
||||||
for (int it = 0; it < N_ITER; ++it) {
|
|
||||||
std::vector<double> sw(n_groups, 0.0), swI(n_groups, 0.0);
|
|
||||||
for (const auto &o : obs) {
|
|
||||||
if (parity >= 0 && o.parity != parity) continue;
|
|
||||||
const double a = A[o.cell], sc = o.sigma * o.corr * a, w = 1.0 / (sc * sc);
|
|
||||||
sw[o.group] += w; swI[o.group] += w * o.I * o.corr * a;
|
|
||||||
}
|
|
||||||
std::vector<double> num(ncell, 0.0), den(ncell, 0.0);
|
|
||||||
for (const auto &o : obs) {
|
|
||||||
if ((parity >= 0 && o.parity != parity) || sw[o.group] <= 0.0) continue;
|
|
||||||
const double Iref = swI[o.group] / sw[o.group], a = A[o.cell];
|
|
||||||
const double Is = o.I * o.corr * a, sc = o.sigma * o.corr * a;
|
|
||||||
if (!std::isfinite(Iref) || Iref <= 0.0 || !(Is > 0.0) || !(sc > 0.0)) continue;
|
|
||||||
const double w = 1.0 / (sc * sc);
|
|
||||||
num[o.cell] += w * Is * Iref; den[o.cell] += w * Is * Is;
|
|
||||||
}
|
|
||||||
std::vector<double> dsorted = den;
|
|
||||||
std::nth_element(dsorted.begin(), dsorted.begin() + dsorted.size() / 2, dsorted.end());
|
|
||||||
const double lambda = 0.1 * std::max(1e-30, dsorted[dsorted.size() / 2]);
|
|
||||||
double logsum = 0.0, wsum = 0.0;
|
|
||||||
std::vector<double> upd(ncell, 1.0);
|
|
||||||
for (int c = 0; c < ncell; ++c) upd[c] = (num[c] + lambda) / (den[c] + lambda);
|
|
||||||
for (int c = 0; c < ncell; ++c) if (den[c] > 0.0) { logsum += den[c] * std::log(upd[c]); wsum += den[c]; }
|
|
||||||
const double gm = wsum > 0.0 ? std::exp(logsum / wsum) : 1.0;
|
|
||||||
for (int c = 0; c < ncell; ++c) A[c] = std::clamp(A[c] * upd[c] / gm, 0.25, 4.0);
|
|
||||||
}
|
|
||||||
return A;
|
|
||||||
};
|
|
||||||
// Sigma-independent (R-meas-like) agreement of the held-out equivalents: sum|Is - Iref| / sum|Iref|.
|
|
||||||
// A fractional metric cannot be gamed by a surface that merely reshapes sigma via corr.
|
|
||||||
auto score = [&](int parity, const std::vector<double> &A) -> double {
|
|
||||||
std::vector<double> sw(n_groups, 0.0), swI(n_groups, 0.0);
|
|
||||||
for (const auto &o : obs) {
|
|
||||||
if (o.parity != parity) continue;
|
|
||||||
const double a = A[o.cell], Is = o.I * o.corr * a, sc = o.sigma * o.corr * a, w = 1.0 / (sc * sc);
|
|
||||||
sw[o.group] += w; swI[o.group] += w * Is;
|
|
||||||
}
|
|
||||||
double num = 0.0, den = 0.0;
|
|
||||||
for (const auto &o : obs) {
|
|
||||||
if (o.parity != parity || sw[o.group] <= 0.0) continue;
|
|
||||||
const double a = A[o.cell], Is = o.I * o.corr * a, Iref = swI[o.group] / sw[o.group];
|
|
||||||
if (!std::isfinite(Iref) || Iref <= 0.0) continue;
|
|
||||||
num += std::abs(Is - Iref); den += Iref;
|
|
||||||
}
|
|
||||||
return den > 0.0 ? num / den : 0.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cross-validate: fit on even images, score the held-out odd equivalents (and vice versa). Apply the
|
|
||||||
// full-data surface only if the held-out agreement improves by a clear margin.
|
|
||||||
const std::vector<double> ident(ncell, 1.0);
|
|
||||||
const std::vector<double> A_even = fit_surface(0), A_odd = fit_surface(1);
|
|
||||||
const double base = score(1, ident) + score(0, ident);
|
|
||||||
const double gain = base - (score(1, A_even) + score(0, A_odd));
|
|
||||||
if (!(gain > CV_MIN_RELATIVE_GAIN * base))
|
|
||||||
return 0.0; // not cross-validated: the correction stays a no-op (caller logs)
|
|
||||||
const std::vector<double> A = fit_surface(-1);
|
|
||||||
|
|
||||||
// Fold the surface into each accepted reflection's image_scale_corr (recompute its cell deterministically).
|
|
||||||
for (auto &outcome : outcomes)
|
|
||||||
for (auto &r : outcome.reflections) {
|
|
||||||
MObs m{};
|
|
||||||
if (!accept(r, m)) continue;
|
|
||||||
const int ix = std::clamp(static_cast<int>((m.px - pxmin) * sx), 0, NB - 1);
|
|
||||||
const int iy = std::clamp(static_cast<int>((m.py - pymin) * sy), 0, NB - 1);
|
|
||||||
r.image_scale_corr = static_cast<float>(r.image_scale_corr * A[ix * NB + iy]);
|
|
||||||
}
|
|
||||||
return gain / std::max(base, 1e-30); // held-out gain fraction (caller logs)
|
|
||||||
}
|
|
||||||
|
|
||||||
float MergeOnTheFly::CorrectedSigma(float I_corr, float sigma_corr, float image_scale_corr,
|
float MergeOnTheFly::CorrectedSigma(float I_corr, float sigma_corr, float image_scale_corr,
|
||||||
uint64_t hkl_key, float partiality) const {
|
uint64_t hkl_key) const {
|
||||||
if (!error_model_active)
|
if (!error_model_active)
|
||||||
return sigma_corr;
|
return sigma_corr;
|
||||||
|
|
||||||
@@ -288,21 +157,8 @@ float MergeOnTheFly::CorrectedSigma(float I_corr, float sigma_corr, float image_
|
|||||||
a_var = base;
|
a_var = base;
|
||||||
}
|
}
|
||||||
|
|
||||||
double v = error_model_a * a_var
|
const double v = error_model_a * a_var
|
||||||
+ (error_model_b * I_for_b) * (error_model_b * I_for_b);
|
+ (error_model_b * I_for_b) * (error_model_b * I_for_b);
|
||||||
|
|
||||||
// Partiality-model uncertainty: a reflection recorded at fraction p carries a systematic intensity
|
|
||||||
// error ~ (dp/p) that is proportional to <I> and grows as p falls - plain counting sigma misses it,
|
|
||||||
// so strong low-p partials would otherwise be over-trusted. This is the stills-partiality analog of
|
|
||||||
// the rotation --capture-uncertainty term ((1-captured_fraction)*I in RotationScaleMerge). Inert when
|
|
||||||
// partiality == 1 (no stills partiality model). Gated on a real systematic (error_model_b > 1, i.e.
|
|
||||||
// ISa < 1): on weak counting-limited data (small b) it would only over-concentrate the merge and hurt.
|
|
||||||
const double c = scaling_settings.GetPartialityUncertaintyCoeff();
|
|
||||||
if (c > 0.0 && error_model_b > 1.0) {
|
|
||||||
const double one_minus_p = std::max(0.0, std::min(1.0, 1.0 - static_cast<double>(partiality)));
|
|
||||||
const double t = c * I_for_b * one_minus_p;
|
|
||||||
v += t * t;
|
|
||||||
}
|
|
||||||
return (v > 0.0) ? static_cast<float>(std::sqrt(v)) : sigma_corr;
|
return (v > 0.0) ? static_cast<float>(std::sqrt(v)) : sigma_corr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class MergeOnTheFly {
|
|||||||
// using the per-observation I_i instead would over-weight down-fluctuated points.
|
// using the per-observation I_i instead would over-weight down-fluctuated points.
|
||||||
std::unordered_map<uint64_t, float> error_model_mean_I;
|
std::unordered_map<uint64_t, float> error_model_mean_I;
|
||||||
[[nodiscard]] float CorrectedSigma(float I_corr, float sigma_corr, float image_scale_corr,
|
[[nodiscard]] float CorrectedSigma(float I_corr, float sigma_corr, float image_scale_corr,
|
||||||
uint64_t hkl_key, float partiality) const;
|
uint64_t hkl_key) const;
|
||||||
|
|
||||||
// Optional per-observation outlier rejection: drop observations whose corrected
|
// Optional per-observation outlier rejection: drop observations whose corrected
|
||||||
// intensity lies more than reject_nsigma error-model sigmas from the reflection's
|
// intensity lies more than reject_nsigma error-model sigmas from the reflection's
|
||||||
@@ -168,16 +168,6 @@ public:
|
|||||||
// order (or threading) of AddImage calls - not a draw from a shared RNG in call order.
|
// order (or threading) of AddImage calls - not a draw from a shared RNG in call order.
|
||||||
void AddImage(const IntegrationOutcome& outcome, int64_t image_id, bool cc_mask = false);
|
void AddImage(const IntegrationOutcome& outcome, int64_t image_id, bool cc_mask = false);
|
||||||
|
|
||||||
// Detector-plane modulation (flat-field) correction for the STILLS path: fit a smooth multiplicative
|
|
||||||
// factor over where a reflection lands on the detector (predicted x,y) against the merged reference and
|
|
||||||
// fold it into each reflection's image_scale_corr, so the following AddImage merge (and the error model)
|
|
||||||
// see the corrected scale. A 16x16 grid, cross-validated (fit even images, score the held-out odd
|
|
||||||
// equivalents by a sigma-independent R-meas-like metric) so it is a no-op when the systematic is absent
|
|
||||||
// or the data too sparse. The stills analogue of RotationScaleMerge::RefineModulation - the stills
|
|
||||||
// ScaleOnTheFly/MergeOnTheFly path otherwise has no correction surfaces. Mutates `outcomes`; call after
|
|
||||||
// scaling and before RefineErrorModel/AddImage. Returns the applied held-out gain fraction (0 = no-op).
|
|
||||||
double RefineModulation(std::vector<IntegrationOutcome> &outcomes);
|
|
||||||
|
|
||||||
// d_min_override, when set, is the effective high-resolution limit for the shell table (used for
|
// d_min_override, when set, is the effective high-resolution limit for the shell table (used for
|
||||||
// the automatic resolution cutoff computed by the caller); otherwise the manual
|
// the automatic resolution cutoff computed by the caller); otherwise the manual
|
||||||
// ScalingSettings high-resolution limit stands. The number of shells is ScalingSettings::ReportShellCount.
|
// ScalingSettings high-resolution limit stands. The number of shells is ScalingSettings::ReportShellCount.
|
||||||
|
|||||||
@@ -991,17 +991,6 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
|
|||||||
merge_engine.MaskIceRings(masked_ice_rings, config_.spot_finding.ice_ring_width_Q_recipA);
|
merge_engine.MaskIceRings(masked_ice_rings, config_.spot_finding.ice_ring_width_Q_recipA);
|
||||||
if (result.consensus_cell.has_value())
|
if (result.consensus_cell.has_value())
|
||||||
merge_engine.ReferenceCell(*result.consensus_cell);
|
merge_engine.ReferenceCell(*result.consensus_cell);
|
||||||
// Optional detector-plane modulation (flat-field) correction for stills, folded into each
|
|
||||||
// reflection's scale before the error model and merge (never for the P1 search pass). The
|
|
||||||
// rotation path fits its own modulation inside RotationScaleMerge.
|
|
||||||
if (experiment_.GetScalingSettings().GetStillsModulation() && !for_search) {
|
|
||||||
const double mod_gain = merge_engine.RefineModulation(indexer->GetIntegrationOutcome());
|
|
||||||
if (mod_gain > 0.0)
|
|
||||||
logger.Info("Stills modulation: detector-frame 16x16 surface applied "
|
|
||||||
"(cross-validated, held-out gain {:.1f}%)", 100.0 * mod_gain);
|
|
||||||
else
|
|
||||||
logger.Info("Stills modulation: no cross-validated gain (skipped)");
|
|
||||||
}
|
|
||||||
merge_engine.RefineErrorModel(merge_input);
|
merge_engine.RefineErrorModel(merge_input);
|
||||||
if (merge_engine.ErrorModelActive())
|
if (merge_engine.ErrorModelActive())
|
||||||
logger.Info("Error model: a={:.3f} b={:.3f} ISa={:.1f} chi2={:.2f}", merge_engine.ErrorModelA(),
|
logger.Info("Error model: a={:.3f} b={:.3f} ISa={:.1f} chi2={:.2f}", merge_engine.ErrorModelA(),
|
||||||
|
|||||||
@@ -149,10 +149,6 @@ std::string RugnuxCommandLine(const ProcessConfig &config,
|
|||||||
args.emplace_back("-A");
|
args.emplace_back("-A");
|
||||||
if (sc.GetRefineB())
|
if (sc.GetRefineB())
|
||||||
args.emplace_back("-B");
|
args.emplace_back("-B");
|
||||||
if (sc.GetPartialityUncertaintyCoeff() > 0.0)
|
|
||||||
add("--partiality-uncertainty", num(sc.GetPartialityUncertaintyCoeff()));
|
|
||||||
if (sc.GetStillsModulation())
|
|
||||||
args.emplace_back("--stills-modulation");
|
|
||||||
if (!sc.GetStillsPartialityRefine())
|
if (!sc.GetStillsPartialityRefine())
|
||||||
args.emplace_back("--simple-stills");
|
args.emplace_back("--simple-stills");
|
||||||
if (!sc.GetExpectedVarianceMerge())
|
if (!sc.GetExpectedVarianceMerge())
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ void print_usage() {
|
|||||||
std::cout << " --min-pix-per-spot <num> Minimum connected strong pixels per spot (default: 2; serial data can index better with 1 + a higher --spot-threshold)" << std::endl;
|
std::cout << " --min-pix-per-spot <num> Minimum connected strong pixels per spot (default: 2; serial data can index better with 1 + a higher --spot-threshold)" << std::endl;
|
||||||
std::cout << " --adaptive-spots Self-calibrating detection: replace the fixed --spot-threshold with a per-resolution-ring threshold set from each image's own noise, so one setting adapts across datasets (no per-dataset --spot-threshold/--spot-sigma tuning)" << std::endl;
|
std::cout << " --adaptive-spots Self-calibrating detection: replace the fixed --spot-threshold with a per-resolution-ring threshold set from each image's own noise, so one setting adapts across datasets (no per-dataset --spot-threshold/--spot-sigma tuning)" << std::endl;
|
||||||
std::cout << " --spot-false-pixels <num> Adaptive detection operating point: expected noise pixels tolerated per frame (default: 100; implies --adaptive-spots)" << std::endl;
|
std::cout << " --spot-false-pixels <num> Adaptive detection operating point: expected noise pixels tolerated per frame (default: 100; implies --adaptive-spots)" << std::endl;
|
||||||
std::cout << " --min-indexed-fraction <f> Minimum fraction of in-resolution spots a lattice must index to be accepted (default 0.20); lower to admit weaker/sparser crystals (pair with --min-image-cc to gate the extra spurious ones)" << std::endl;
|
|
||||||
std::cout << " --spot-high-resolution <num> High resolution limit for spot finding (default: 1.5)" << std::endl;
|
std::cout << " --spot-high-resolution <num> High resolution limit for spot finding (default: 1.5)" << std::endl;
|
||||||
std::cout << " --spot-low-resolution <num> Low resolution limit for spot finding, in A (default: 50; lower it, e.g. 24, to exclude the direct-beam halo on weakly-diffracting serial data)" << std::endl;
|
std::cout << " --spot-low-resolution <num> Low resolution limit for spot finding, in A (default: 50; lower it, e.g. 24, to exclude the direct-beam halo on weakly-diffracting serial data)" << std::endl;
|
||||||
std::cout << " --max-spots <num> Max spot count (default: 250)" << std::endl;
|
std::cout << " --max-spots <num> Max spot count (default: 250)" << std::endl;
|
||||||
@@ -97,7 +96,6 @@ void print_usage() {
|
|||||||
std::cout << " --smooth-g[=deg] rot3d: smooth per-frame scale G over a deg-degree rotation range (XDS DELPHI-like) before the combine (default: 5 for rot3d; 0 = off)" << std::endl;
|
std::cout << " --smooth-g[=deg] rot3d: smooth per-frame scale G over a deg-degree rotation range (XDS DELPHI-like) before the combine (default: 5 for rot3d; 0 = off)" << std::endl;
|
||||||
std::cout << " --relative-b[=deg] rot3d: fit a per-batch relative-B (beyond the single decay slope) over deg-degree batches; cross-validated (default: 10 deg when bare; off otherwise)" << std::endl;
|
std::cout << " --relative-b[=deg] rot3d: fit a per-batch relative-B (beyond the single decay slope) over deg-degree batches; cross-validated (default: 10 deg when bare; off otherwise)" << std::endl;
|
||||||
std::cout << " --no-scaling-corrections rot3d: disable the (default-on) decay + absorption correction surfaces fitted on the fulls after scale-fulls" << std::endl;
|
std::cout << " --no-scaling-corrections rot3d: disable the (default-on) decay + absorption correction surfaces fitted on the fulls after scale-fulls" << std::endl;
|
||||||
std::cout << " --stills-modulation stills: fit a detector-plane modulation (flat-field) surface over the merged reflections (cross-validated; experimental, default off)" << std::endl;
|
|
||||||
std::cout << " --no-expected-variance-merge stills: disable the default expected-variance merge weighting (which rebuilds each weak observation's signal variance at the reflection mean to de-bias the inverse-variance merge); restores observed-sigma weighting" << std::endl;
|
std::cout << " --no-expected-variance-merge stills: disable the default expected-variance merge weighting (which rebuilds each weak observation's signal variance at the reflection mean to de-bias the inverse-variance merge); restores observed-sigma weighting" << std::endl;
|
||||||
std::cout << " -A, --anomalous Anomalous mode (don't merge Friedel pairs)" << std::endl;
|
std::cout << " -A, --anomalous Anomalous mode (don't merge Friedel pairs)" << std::endl;
|
||||||
std::cout << " -B, --refine-bfactor Refine per image B-factor (stills only)" << std::endl;
|
std::cout << " -B, --refine-bfactor Refine per image B-factor (stills only)" << std::endl;
|
||||||
@@ -107,7 +105,6 @@ void print_usage() {
|
|||||||
std::cout << " --resolution-shells <num> Number of resolution shells in the reported statistics table (default: 10)" << std::endl;
|
std::cout << " --resolution-shells <num> Number of resolution shells in the reported statistics table (default: 10)" << std::endl;
|
||||||
std::cout << " --min-partiality <num> Minimum partiality to accept reflection (default: 0.02)" << std::endl;
|
std::cout << " --min-partiality <num> Minimum partiality to accept reflection (default: 0.02)" << std::endl;
|
||||||
std::cout << " --capture-uncertainty <num> rot3d: systematic sigma ~num*(1-captured_fraction)*I on under-captured fulls (default: 1.0 for rot3d, 0 otherwise)" << std::endl;
|
std::cout << " --capture-uncertainty <num> rot3d: systematic sigma ~num*(1-captured_fraction)*I on under-captured fulls (default: 1.0 for rot3d, 0 otherwise)" << std::endl;
|
||||||
std::cout << " --partiality-uncertainty <num> stills: extra merge sigma ~num*(1-partiality)*<I> on partials (auto-gated to error-model b>1 / ISa<1; default 0, ~2.5 recommended)" << std::endl;
|
|
||||||
std::cout << " --min-captured-fraction <num> rot3d: drop a combined full whose rocking curve was captured below this fraction (edge-of-sweep truncated fulls) (default: 0.7 for rotation, 0 otherwise; 0 = off)" << std::endl;
|
std::cout << " --min-captured-fraction <num> rot3d: drop a combined full whose rocking curve was captured below this fraction (edge-of-sweep truncated fulls) (default: 0.7 for rotation, 0 otherwise; 0 = off)" << std::endl;
|
||||||
std::cout << " --mosaicity <num> Diagnostic: fix the scaling mosaicity (deg) instead of the per-image seed" << std::endl;
|
std::cout << " --mosaicity <num> Diagnostic: fix the scaling mosaicity (deg) instead of the per-image seed" << std::endl;
|
||||||
std::cout << " --reject-outliers <num> Per-observation merge outlier rejection, N sigma from the per-reflection median (default: 6 for rot3d, XDS/DIALS-style; 0 = off)" << std::endl;
|
std::cout << " --reject-outliers <num> Per-observation merge outlier rejection, N sigma from the per-reflection median (default: 6 for rot3d, XDS/DIALS-style; 0 = off)" << std::endl;
|
||||||
@@ -148,7 +145,6 @@ enum {
|
|||||||
OPT_MIN_PIX_PER_SPOT,
|
OPT_MIN_PIX_PER_SPOT,
|
||||||
OPT_ADAPTIVE_SPOTS,
|
OPT_ADAPTIVE_SPOTS,
|
||||||
OPT_SPOT_FALSE_PIXELS,
|
OPT_SPOT_FALSE_PIXELS,
|
||||||
OPT_MIN_INDEXED_FRACTION,
|
|
||||||
OPT_SPOT_RESOLUTION,
|
OPT_SPOT_RESOLUTION,
|
||||||
OPT_SPOT_LOW_RESOLUTION,
|
OPT_SPOT_LOW_RESOLUTION,
|
||||||
OPT_MAX_SPOTS,
|
OPT_MAX_SPOTS,
|
||||||
@@ -175,13 +171,11 @@ enum {
|
|||||||
OPT_SIMPLE_STILLS,
|
OPT_SIMPLE_STILLS,
|
||||||
OPT_SCALE_FULLS,
|
OPT_SCALE_FULLS,
|
||||||
OPT_CAPTURE_UNCERTAINTY,
|
OPT_CAPTURE_UNCERTAINTY,
|
||||||
OPT_PARTIALITY_UNCERTAINTY,
|
|
||||||
OPT_MIN_CAPTURED_FRACTION,
|
OPT_MIN_CAPTURED_FRACTION,
|
||||||
OPT_MOSAICITY,
|
OPT_MOSAICITY,
|
||||||
OPT_SMOOTH_G,
|
OPT_SMOOTH_G,
|
||||||
OPT_RELATIVE_B,
|
OPT_RELATIVE_B,
|
||||||
OPT_NO_SCALING_CORRECTIONS,
|
OPT_NO_SCALING_CORRECTIONS,
|
||||||
OPT_STILLS_MODULATION,
|
|
||||||
OPT_NO_EXPECTED_VARIANCE_MERGE,
|
OPT_NO_EXPECTED_VARIANCE_MERGE,
|
||||||
OPT_DETECT_ICE_RINGS,
|
OPT_DETECT_ICE_RINGS,
|
||||||
OPT_NO_SCALE_FULLS,
|
OPT_NO_SCALE_FULLS,
|
||||||
@@ -229,7 +223,6 @@ static option long_options[] = {
|
|||||||
{"smooth-g", optional_argument, nullptr, OPT_SMOOTH_G},
|
{"smooth-g", optional_argument, nullptr, OPT_SMOOTH_G},
|
||||||
{"relative-b", optional_argument, nullptr, OPT_RELATIVE_B},
|
{"relative-b", optional_argument, nullptr, OPT_RELATIVE_B},
|
||||||
{"no-scaling-corrections", no_argument, nullptr, OPT_NO_SCALING_CORRECTIONS},
|
{"no-scaling-corrections", no_argument, nullptr, OPT_NO_SCALING_CORRECTIONS},
|
||||||
{"stills-modulation", no_argument, nullptr, OPT_STILLS_MODULATION},
|
|
||||||
{"no-expected-variance-merge", no_argument, nullptr, OPT_NO_EXPECTED_VARIANCE_MERGE},
|
{"no-expected-variance-merge", no_argument, nullptr, OPT_NO_EXPECTED_VARIANCE_MERGE},
|
||||||
{"refine", required_argument, nullptr, 'r'},
|
{"refine", required_argument, nullptr, 'r'},
|
||||||
|
|
||||||
@@ -260,13 +253,11 @@ static option long_options[] = {
|
|||||||
{"min-pix-per-spot", required_argument, nullptr, OPT_MIN_PIX_PER_SPOT},
|
{"min-pix-per-spot", required_argument, nullptr, OPT_MIN_PIX_PER_SPOT},
|
||||||
{"adaptive-spots", no_argument, nullptr, OPT_ADAPTIVE_SPOTS},
|
{"adaptive-spots", no_argument, nullptr, OPT_ADAPTIVE_SPOTS},
|
||||||
{"spot-false-pixels", required_argument, nullptr, OPT_SPOT_FALSE_PIXELS},
|
{"spot-false-pixels", required_argument, nullptr, OPT_SPOT_FALSE_PIXELS},
|
||||||
{"min-indexed-fraction", required_argument, nullptr, OPT_MIN_INDEXED_FRACTION},
|
|
||||||
{"spot-high-resolution", required_argument, nullptr, OPT_SPOT_RESOLUTION},
|
{"spot-high-resolution", required_argument, nullptr, OPT_SPOT_RESOLUTION},
|
||||||
{"spot-low-resolution", required_argument, nullptr, OPT_SPOT_LOW_RESOLUTION},
|
{"spot-low-resolution", required_argument, nullptr, OPT_SPOT_LOW_RESOLUTION},
|
||||||
{"max-spots", required_argument, nullptr, OPT_MAX_SPOTS},
|
{"max-spots", required_argument, nullptr, OPT_MAX_SPOTS},
|
||||||
{"min-partiality", required_argument, nullptr, OPT_MIN_PARTIALITY},
|
{"min-partiality", required_argument, nullptr, OPT_MIN_PARTIALITY},
|
||||||
{"capture-uncertainty", required_argument, nullptr, OPT_CAPTURE_UNCERTAINTY},
|
{"capture-uncertainty", required_argument, nullptr, OPT_CAPTURE_UNCERTAINTY},
|
||||||
{"partiality-uncertainty", required_argument, nullptr, OPT_PARTIALITY_UNCERTAINTY},
|
|
||||||
{"min-captured-fraction", required_argument, nullptr, OPT_MIN_CAPTURED_FRACTION},
|
{"min-captured-fraction", required_argument, nullptr, OPT_MIN_CAPTURED_FRACTION},
|
||||||
{"mosaicity", required_argument, nullptr, OPT_MOSAICITY},
|
{"mosaicity", required_argument, nullptr, OPT_MOSAICITY},
|
||||||
{"min-image-cc", required_argument, nullptr, OPT_MIN_IMAGE_CC},
|
{"min-image-cc", required_argument, nullptr, OPT_MIN_IMAGE_CC},
|
||||||
@@ -515,7 +506,6 @@ int main(int argc, char **argv) {
|
|||||||
std::optional<double> smooth_g_deg_arg; // --smooth-g[=deg]; default 5 deg for rot3d, 0 (off) otherwise
|
std::optional<double> smooth_g_deg_arg; // --smooth-g[=deg]; default 5 deg for rot3d, 0 (off) otherwise
|
||||||
std::optional<double> relative_b_deg_arg; // --relative-b[=deg]; per-batch relative-B width, 0 (off) unless given
|
std::optional<double> relative_b_deg_arg; // --relative-b[=deg]; per-batch relative-B width, 0 (off) unless given
|
||||||
bool no_scaling_corrections = false; // --no-scaling-corrections: disable rot3d decay+absorption surfaces
|
bool no_scaling_corrections = false; // --no-scaling-corrections: disable rot3d decay+absorption surfaces
|
||||||
bool stills_modulation_flag = false; // --stills-modulation: detector-plane flat-field surface for stills
|
|
||||||
bool no_expected_variance_merge = false; // --no-expected-variance-merge: restore observed-sigma stills merge weighting
|
bool no_expected_variance_merge = false; // --no-expected-variance-merge: restore observed-sigma stills merge weighting
|
||||||
bool anomalous_mode = false;
|
bool anomalous_mode = false;
|
||||||
std::optional<int64_t> space_group_number;
|
std::optional<int64_t> space_group_number;
|
||||||
@@ -526,7 +516,6 @@ int main(int argc, char **argv) {
|
|||||||
int64_t min_pix_per_spot = 2;
|
int64_t min_pix_per_spot = 2;
|
||||||
bool adaptive_spots = false;
|
bool adaptive_spots = false;
|
||||||
float false_pixels_per_frame = 100.0f;
|
float false_pixels_per_frame = 100.0f;
|
||||||
std::optional<float> min_indexed_fraction;
|
|
||||||
bool refine_bfactor = false;
|
bool refine_bfactor = false;
|
||||||
std::string ref_mtz;
|
std::string ref_mtz;
|
||||||
std::string ref_column;
|
std::string ref_column;
|
||||||
@@ -535,7 +524,6 @@ int main(int argc, char **argv) {
|
|||||||
double min_partiality = 0.02;
|
double min_partiality = 0.02;
|
||||||
std::optional<double> min_captured_fraction_arg; // explicit --min-captured-fraction; default depends on rotation
|
std::optional<double> min_captured_fraction_arg; // explicit --min-captured-fraction; default depends on rotation
|
||||||
std::optional<double> capture_uncertainty_arg; // explicit --capture-uncertainty; default depends on rot3d
|
std::optional<double> capture_uncertainty_arg; // explicit --capture-uncertainty; default depends on rot3d
|
||||||
std::optional<double> partiality_uncertainty_arg; // --partiality-uncertainty (stills partiality merge sigma)
|
|
||||||
std::optional<double> forced_mosaicity_arg; // diagnostic: fix the scaling mosaicity (deg) instead of the per-image seed
|
std::optional<double> forced_mosaicity_arg; // diagnostic: fix the scaling mosaicity (deg) instead of the per-image seed
|
||||||
double min_image_cc = 0.0;
|
double min_image_cc = 0.0;
|
||||||
int64_t scaling_iter = 3;
|
int64_t scaling_iter = 3;
|
||||||
@@ -765,10 +753,6 @@ int main(int argc, char **argv) {
|
|||||||
adaptive_spots = true;
|
adaptive_spots = true;
|
||||||
logger.Info("Adaptive spot detection: expected false pixels/frame set to {:.0f}", false_pixels_per_frame);
|
logger.Info("Adaptive spot detection: expected false pixels/frame set to {:.0f}", false_pixels_per_frame);
|
||||||
break;
|
break;
|
||||||
case OPT_MIN_INDEXED_FRACTION:
|
|
||||||
min_indexed_fraction = parse_number_arg<float>(optarg, "--min-indexed-fraction", logger, 0.0f);
|
|
||||||
logger.Info("Minimum indexed-spot fraction for acceptance set to {:.2f}", min_indexed_fraction.value());
|
|
||||||
break;
|
|
||||||
case OPT_SPOT_LOW_RESOLUTION:
|
case OPT_SPOT_LOW_RESOLUTION:
|
||||||
d_max_spot_finding = parse_number_arg<float>(optarg, "--spot-low-resolution", logger, 0.0f);
|
d_max_spot_finding = parse_number_arg<float>(optarg, "--spot-low-resolution", logger, 0.0f);
|
||||||
logger.Info("Low resolution limit for spot finding set to {:.1f} A", d_max_spot_finding);
|
logger.Info("Low resolution limit for spot finding set to {:.1f} A", d_max_spot_finding);
|
||||||
@@ -815,9 +799,6 @@ int main(int argc, char **argv) {
|
|||||||
case OPT_RELATIVE_B:
|
case OPT_RELATIVE_B:
|
||||||
relative_b_deg_arg = optarg ? parse_double_arg(optarg, "--relative-b", logger) : RELATIVE_B_DEFAULT_DEG;
|
relative_b_deg_arg = optarg ? parse_double_arg(optarg, "--relative-b", logger) : RELATIVE_B_DEFAULT_DEG;
|
||||||
break;
|
break;
|
||||||
case OPT_STILLS_MODULATION:
|
|
||||||
stills_modulation_flag = true;
|
|
||||||
break;
|
|
||||||
case OPT_NO_EXPECTED_VARIANCE_MERGE:
|
case OPT_NO_EXPECTED_VARIANCE_MERGE:
|
||||||
no_expected_variance_merge = true;
|
no_expected_variance_merge = true;
|
||||||
break;
|
break;
|
||||||
@@ -830,9 +811,6 @@ int main(int argc, char **argv) {
|
|||||||
case OPT_CAPTURE_UNCERTAINTY:
|
case OPT_CAPTURE_UNCERTAINTY:
|
||||||
capture_uncertainty_arg = parse_double_arg(optarg, "--capture-uncertainty", logger);
|
capture_uncertainty_arg = parse_double_arg(optarg, "--capture-uncertainty", logger);
|
||||||
break;
|
break;
|
||||||
case OPT_PARTIALITY_UNCERTAINTY:
|
|
||||||
partiality_uncertainty_arg = parse_double_arg(optarg, "--partiality-uncertainty", logger);
|
|
||||||
break;
|
|
||||||
case OPT_MIN_CAPTURED_FRACTION:
|
case OPT_MIN_CAPTURED_FRACTION:
|
||||||
min_captured_fraction_arg = parse_double_arg(optarg, "--min-captured-fraction", logger);
|
min_captured_fraction_arg = parse_double_arg(optarg, "--min-captured-fraction", logger);
|
||||||
break;
|
break;
|
||||||
@@ -1099,7 +1077,6 @@ int main(int argc, char **argv) {
|
|||||||
scaling_settings.MinCapturedFraction(min_captured_fraction_arg.value_or(
|
scaling_settings.MinCapturedFraction(min_captured_fraction_arg.value_or(
|
||||||
(experiment.GetGoniometer().has_value() && !force_still) ? 0.7 : 0.0));
|
(experiment.GetGoniometer().has_value() && !force_still) ? 0.7 : 0.0));
|
||||||
scaling_settings.MinCCForImage(min_image_cc / 100.0); // --min-image-cc is percent; the setting is a fraction
|
scaling_settings.MinCCForImage(min_image_cc / 100.0); // --min-image-cc is percent; the setting is a fraction
|
||||||
scaling_settings.StillsModulation(stills_modulation_flag);
|
|
||||||
scaling_settings.StillsPartialityRefine(!simple_stills_flag);
|
scaling_settings.StillsPartialityRefine(!simple_stills_flag);
|
||||||
scaling_settings.ExpectedVarianceMerge(!no_expected_variance_merge);
|
scaling_settings.ExpectedVarianceMerge(!no_expected_variance_merge);
|
||||||
scaling_settings.OutlierRejectNsigma(
|
scaling_settings.OutlierRejectNsigma(
|
||||||
@@ -1167,16 +1144,6 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
MergeOnTheFly merge_engine(experiment);
|
MergeOnTheFly merge_engine(experiment);
|
||||||
merge_engine.ReferenceCell(experiment.GetUnitCell());
|
merge_engine.ReferenceCell(experiment.GetUnitCell());
|
||||||
// Optional detector-plane modulation (flat-field) correction, folded into each reflection's
|
|
||||||
// scale before the error model + merge (mirrors the full-analysis stills path in Rugnux.cpp).
|
|
||||||
if (experiment.GetScalingSettings().GetStillsModulation()) {
|
|
||||||
const double mod_gain = merge_engine.RefineModulation(reflections);
|
|
||||||
if (mod_gain > 0.0)
|
|
||||||
logger.Info("Stills modulation: detector-frame 16x16 surface applied "
|
|
||||||
"(cross-validated, held-out gain {:.1f}%)", 100.0 * mod_gain);
|
|
||||||
else
|
|
||||||
logger.Info("Stills modulation: no cross-validated gain (skipped)");
|
|
||||||
}
|
|
||||||
// Fit the (a, b) error model from symmetry-mate scatter before merging, exactly as the full
|
// Fit the (a, b) error model from symmetry-mate scatter before merging, exactly as the full
|
||||||
// pipeline does (Rugnux.cpp). Without this the offline --scale merge would use the identity
|
// pipeline does (Rugnux.cpp). Without this the offline --scale merge would use the identity
|
||||||
// model and produce much worse stills intensities (no (b*I)^2 systematic term, no sigma floor).
|
// model and produce much worse stills intensities (no (b*I)^2 systematic term, no sigma floor).
|
||||||
@@ -1404,8 +1371,6 @@ int main(int argc, char **argv) {
|
|||||||
if (rotation_indexing_range.has_value())
|
if (rotation_indexing_range.has_value())
|
||||||
indexing_settings.RotationIndexingMinAngularRange_deg(rotation_indexing_range.value());
|
indexing_settings.RotationIndexingMinAngularRange_deg(rotation_indexing_range.value());
|
||||||
indexing_settings.GeomRefinementAlgorithm(refinement_algorithm);
|
indexing_settings.GeomRefinementAlgorithm(refinement_algorithm);
|
||||||
if (min_indexed_fraction.has_value())
|
|
||||||
indexing_settings.MinIndexedSpotFraction(min_indexed_fraction.value());
|
|
||||||
experiment.ImportIndexingSettings(indexing_settings);
|
experiment.ImportIndexingSettings(indexing_settings);
|
||||||
|
|
||||||
// --detect-ice-rings[=on|off] overrides the value carried in from the dataset (HDF5MetadataSource
|
// --detect-ice-rings[=on|off] overrides the value carried in from the dataset (HDF5MetadataSource
|
||||||
@@ -1423,7 +1388,6 @@ int main(int argc, char **argv) {
|
|||||||
scaling_settings.RelativeBDegrees(relative_b_deg_arg.value_or(0.0)); // opt-in only; default off
|
scaling_settings.RelativeBDegrees(relative_b_deg_arg.value_or(0.0)); // opt-in only; default off
|
||||||
if (no_scaling_corrections)
|
if (no_scaling_corrections)
|
||||||
scaling_settings.CorrectionSurfaces(false);
|
scaling_settings.CorrectionSurfaces(false);
|
||||||
scaling_settings.StillsModulation(stills_modulation_flag);
|
|
||||||
scaling_settings.StillsPartialityRefine(!simple_stills_flag);
|
scaling_settings.StillsPartialityRefine(!simple_stills_flag);
|
||||||
scaling_settings.ExpectedVarianceMerge(!no_expected_variance_merge);
|
scaling_settings.ExpectedVarianceMerge(!no_expected_variance_merge);
|
||||||
if (d_min_scale_merge)
|
if (d_min_scale_merge)
|
||||||
@@ -1446,7 +1410,6 @@ int main(int argc, char **argv) {
|
|||||||
// over-extrapolated under-captured fulls and, with the mosaicity fix, lifts rotation ISa/anomalous
|
// over-extrapolated under-captured fulls and, with the mosaicity fix, lifts rotation ISa/anomalous
|
||||||
// substantially. Off for non-rot3d (no combine). An explicit --capture-uncertainty always wins.
|
// substantially. Off for non-rot3d (no combine). An explicit --capture-uncertainty always wins.
|
||||||
scaling_settings.CaptureUncertaintyCoeff(capture_uncertainty_arg.value_or(rotation_indexing ? 1.0 : 0.0));
|
scaling_settings.CaptureUncertaintyCoeff(capture_uncertainty_arg.value_or(rotation_indexing ? 1.0 : 0.0));
|
||||||
scaling_settings.PartialityUncertaintyCoeff(partiality_uncertainty_arg.value_or(0.0));
|
|
||||||
scaling_settings.ForcedMosaicity(forced_mosaicity_arg);
|
scaling_settings.ForcedMosaicity(forced_mosaicity_arg);
|
||||||
scaling_settings.MinCCForImage(min_image_cc / 100.0); // --min-image-cc is in percent; the setting is a fraction
|
scaling_settings.MinCCForImage(min_image_cc / 100.0); // --min-image-cc is in percent; the setting is a fraction
|
||||||
scaling_settings.OutlierRejectNsigma(
|
scaling_settings.OutlierRejectNsigma(
|
||||||
|
|||||||
@@ -499,11 +499,6 @@ QWidget *JFJochViewerSettingsDock::BuildScalingSection() {
|
|||||||
corrections->setToolTip("Rotation only: fit a radiation-damage decay and a goniometer-frame absorption "
|
corrections->setToolTip("Rotation only: fit a radiation-damage decay and a goniometer-frame absorption "
|
||||||
"surface on the fulls. Cross-validated, so they no-op when their systematic is "
|
"surface on the fulls. Cross-validated, so they no-op when their systematic is "
|
||||||
"absent. On by default.");
|
"absent. On by default.");
|
||||||
auto *modulation = new QCheckBox("Detector-plane modulation (stills)", this);
|
|
||||||
modulation->setChecked(scaling_.GetStillsModulation());
|
|
||||||
modulation->setToolTip("Stills: fit a detector-plane modulation (flat-field) surface over where each "
|
|
||||||
"reflection lands, cross-validated so it no-ops when the systematic is absent. "
|
|
||||||
"For rotation, modulation is part of \"Correction surfaces\" above.");
|
|
||||||
auto *partRefine = new QCheckBox("Partiality post-refinement (stills)", this);
|
auto *partRefine = new QCheckBox("Partiality post-refinement (stills)", this);
|
||||||
partRefine->setChecked(scaling_.GetStillsPartialityRefine());
|
partRefine->setChecked(scaling_.GetStillsPartialityRefine());
|
||||||
partRefine->setToolTip("Stills: refine a per-crystal orientation tilt against the running merge and "
|
partRefine->setToolTip("Stills: refine a per-crystal orientation tilt against the running merge and "
|
||||||
@@ -515,23 +510,9 @@ QWidget *JFJochViewerSettingsDock::BuildScalingSection() {
|
|||||||
auto *highRes = new NumberLineEdit(0.3f, 5.0f, scaling_.GetHighResolutionLimit_A().value_or(2.0), 1, "Å", this);
|
auto *highRes = new NumberLineEdit(0.3f, 5.0f, scaling_.GetHighResolutionLimit_A().value_or(2.0), 1, "Å", this);
|
||||||
highRes->setEnabled(limitRes->isChecked());
|
highRes->setEnabled(limitRes->isChecked());
|
||||||
|
|
||||||
// Stills partiality-uncertainty merge term: adds a systematic sigma ~c*(1-partiality)*<I> on partials,
|
|
||||||
// so strong low-partiality partials are not over-trusted. Relevant once partials exist (partiality
|
|
||||||
// post-refinement on, the default); the library auto-gates it to strong/medium data. 0 = off; ~2.5 rec.
|
|
||||||
const double part_unc = scaling_.GetPartialityUncertaintyCoeff();
|
|
||||||
auto *partUncertain = new QCheckBox("Partiality uncertainty", this);
|
|
||||||
partUncertain->setChecked(part_unc > 0.0);
|
|
||||||
partUncertain->setToolTip("Stills: add a systematic merge σ ~c·(1−partiality)·⟨I⟩ to partials so strong "
|
|
||||||
"low-partiality partials are not over-trusted. Relevant with partiality "
|
|
||||||
"post-refinement (the default); auto-gated to strong/medium data. ~2.5 "
|
|
||||||
"recommended; unchecked = off.");
|
|
||||||
auto *partUncertainCoeff = new NumberLineEdit(0.1f, 10.0f, part_unc > 0.0 ? part_unc : 2.5, 1, "", this);
|
|
||||||
partUncertainCoeff->setEnabled(partUncertain->isChecked());
|
|
||||||
|
|
||||||
form->addRow("", friedel);
|
form->addRow("", friedel);
|
||||||
form->addRow("", refineB);
|
form->addRow("", refineB);
|
||||||
form->addRow("", corrections);
|
form->addRow("", corrections);
|
||||||
form->addRow("", modulation);
|
|
||||||
form->addRow("", partRefine);
|
form->addRow("", partRefine);
|
||||||
// Compact, and aligned with the checkboxes above: the limit checkbox + value sit together in the
|
// Compact, and aligned with the checkboxes above: the limit checkbox + value sit together in the
|
||||||
// field column (not as a row label, which would indent it differently).
|
// field column (not as a row label, which would indent it differently).
|
||||||
@@ -539,10 +520,6 @@ QWidget *JFJochViewerSettingsDock::BuildScalingSection() {
|
|||||||
resRow->addWidget(limitRes);
|
resRow->addWidget(limitRes);
|
||||||
resRow->addWidget(highRes, 1);
|
resRow->addWidget(highRes, 1);
|
||||||
form->addRow("", resRow);
|
form->addRow("", resRow);
|
||||||
auto *puRow = new QHBoxLayout();
|
|
||||||
puRow->addWidget(partUncertain);
|
|
||||||
puRow->addWidget(partUncertainCoeff, 1);
|
|
||||||
form->addRow("", puRow);
|
|
||||||
section->setContentLayout(form);
|
section->setContentLayout(form);
|
||||||
section->setExpanded(false); // folded on start (only geometry + unit cell start open)
|
section->setExpanded(false); // folded on start (only geometry + unit cell start open)
|
||||||
|
|
||||||
@@ -550,24 +527,18 @@ QWidget *JFJochViewerSettingsDock::BuildScalingSection() {
|
|||||||
scaling_.MergeFriedel(friedel->isChecked());
|
scaling_.MergeFriedel(friedel->isChecked());
|
||||||
scaling_.RefineB(refineB->isChecked());
|
scaling_.RefineB(refineB->isChecked());
|
||||||
scaling_.CorrectionSurfaces(corrections->isChecked());
|
scaling_.CorrectionSurfaces(corrections->isChecked());
|
||||||
scaling_.StillsModulation(modulation->isChecked());
|
|
||||||
scaling_.StillsPartialityRefine(partRefine->isChecked());
|
scaling_.StillsPartialityRefine(partRefine->isChecked());
|
||||||
scaling_.HighResolutionLimit_A(limitRes->isChecked()
|
scaling_.HighResolutionLimit_A(limitRes->isChecked()
|
||||||
? std::optional<double>(highRes->value()) : std::nullopt);
|
? std::optional<double>(highRes->value()) : std::nullopt);
|
||||||
scaling_.PartialityUncertaintyCoeff(partUncertain->isChecked() ? partUncertainCoeff->value() : 0.0);
|
|
||||||
emit scalingChanged(scaling_);
|
emit scalingChanged(scaling_);
|
||||||
};
|
};
|
||||||
connect(friedel, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
connect(friedel, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
||||||
connect(refineB, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
connect(refineB, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
||||||
connect(corrections, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
connect(corrections, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
||||||
connect(modulation, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
|
||||||
connect(partRefine, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
connect(partRefine, &QCheckBox::toggled, this, [emitScaling] { emitScaling(); });
|
||||||
connect(limitRes, &QCheckBox::toggled, this, [emitScaling, highRes](bool on) {
|
connect(limitRes, &QCheckBox::toggled, this, [emitScaling, highRes](bool on) {
|
||||||
highRes->setEnabled(on); emitScaling(); });
|
highRes->setEnabled(on); emitScaling(); });
|
||||||
connect(highRes, &NumberLineEdit::newValue, this, [emitScaling] { emitScaling(); });
|
connect(highRes, &NumberLineEdit::newValue, this, [emitScaling] { emitScaling(); });
|
||||||
connect(partUncertain, &QCheckBox::toggled, this, [emitScaling, partUncertainCoeff](bool on) {
|
|
||||||
partUncertainCoeff->setEnabled(on); emitScaling(); });
|
|
||||||
connect(partUncertainCoeff, &NumberLineEdit::newValue, this, [emitScaling] { emitScaling(); });
|
|
||||||
return section;
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user