Build Packages / Unit tests (push) Skipped
Build Packages / build:windows:cuda (push) Successful in 18m44s
Build Packages / build:viewer-tgz:cpu (push) Successful in 6m11s
Build Packages / build:viewer-tgz:cuda (push) Successful in 6m54s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 9m40s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 10m41s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 10m10s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 10m4s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 11m5s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m23s
Build Packages / build:rpm (rocky8) (push) Successful in 11m30s
Build Packages / build:rpm (rocky9) (push) Successful in 12m51s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 12m8s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 11m21s
Build Packages / DIALS test (push) Successful in 13m22s
Build Packages / XDS test (durin plugin) (push) Successful in 9m2s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 7m55s
Build Packages / XDS test (neggia plugin) (push) Successful in 5m57s
Build Packages / Generate python client (push) Successful in 23s
Build Packages / Build documentation (push) Successful in 57s
Build Packages / Create release (push) Skipped
Build Packages / build:windows:nocuda (push) Successful in 10m24s
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: Add `--model model.pdb` - score the merged data against an atomic model and compute initial maps. It reports R-work/R-free (scaling the model to the observed amplitudes with an overall scale, an anisotropic B and a flat bulk solvent - the standard few-parameter model, so a batch of maps stays directly comparable) and writes 2Fo-Fc / Fo-Fc electron-density maps (CCP4) plus a map-coefficient MTZ. The structure itself is not refined; the model is only re-fractionalised into the data cell. * rugnux: The merged reflection output now carries French-Wilson amplitudes (|F| and its sigma) next to the intensities - MTZ `F`/`SIGF`, mmCIF `_refln.F_meas_au`, and the text HKL - computed with the correct centric/acentric Wilson prior and epsilon multiplicity, so a downstream program (e.g. phenix.refine) can refine against amplitudes. The intensity columns are unchanged. * rugnux: R-free test-set flags are now assigned deterministically and consistently across symmetry - a Bijvoet pair I(+)/I(-) is never split between the work and free sets, and the assignment is a reproducible per-hkl hash that depends only on the reflection index, so every dataset of one crystal form gets the same ~5% free set (what a multi-dataset campaign such as PanDDA needs). On small data the fraction is floored so the test set stays large enough for a stable R-free (~500 reflections, capped at 10%); it stays flat at 5% on ordinary data. When a reference MTZ carries a `FreeR_flag` column its test set is imported instead, letting a whole campaign inherit one shared free set. * rugnux: A reference MTZ (`--reference-mtz`) can now fix the space group and cell for rotation data too (previously rejected), without being used to scale - the rotation merge stays self-consistent. When the crystal has an indexing (merohedral) ambiguity - a lattice symmetry higher than its Laue symmetry, e.g. P3/P4/P6/C2 - the reference also resolves it: each candidate reindexing (identity plus the twin-law cosets of the metric symmetry) is scored by its intensity correlation against the reference and the data are re-merged in the best-correlating one. This is a metric-preserving relabelling of hkl (the cell is unchanged) and a no-op for a holohedral crystal such as lysozyme. * rugnux: `--model` validation now aligns the data to the model before scoring - the observed reflections are reindexed into the model's enantiomorph when the two differ only by hand (indistinguishable from merged intensities). A merohedral indexing ambiguity is resolved against the reference MTZ when one is given (so a whole campaign shares one indexing convention); only with a model and no reference does validation fall back to fitting each candidate reindexing and keeping the lowest R-free. * rugnux: De-novo symmetry - recover a genuine high-symmetry group whose data are imperfectly scaled. Such a merge's within-orbit chi² lands just past the self-consistency bound (each real symmetry step adds a little systematic scatter), right where a merohedral twin also lands, so the chi² ratio alone cannot separate them. The candidate is now rescued when the extra intensity-proportional systematic error it invokes stays small relative to the confirmed subgroup - a genuine symmetry step gains multiplicity without inflating the merge error model's b, whereas a twin forces non-equivalent reflections together and b balloons. Fixes cubic insulin (I23 instead of I222) with no change to any other crystal in the test battery, including the twins that must stay in their lower symmetry. * Docs: Document the French-Wilson amplitude estimation, R-free flagging, reference-based space-group/ambiguity resolution, and model-based validation/maps in CPU_DATA_ANALYSIS.md. * Frontend: The status-bar pill now shows a progress bar during detector calibration (previously only during measurement), and the calibration state and its button are labelled "Calibration"/"CALIBRATE" (the internal `Pedestal` state name is unchanged for back-compatibility).Reviewed-on: #70 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
144 lines
7.5 KiB
C++
144 lines
7.5 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <optional>
|
|
#include "JFJochException.h"
|
|
|
|
// How the high-resolution cutoff for the written reflections and the reported shell table is chosen
|
|
// when no explicit --scaling-high-resolution is given. Off = keep the full (detector-edge) range;
|
|
// CCHalfLogistic = fit the CC1/2 fall-off and cut one shell past cc_target (DIALS-style, generous).
|
|
enum class ResolutionCutoffMethod { Off, CCHalfLogistic };
|
|
|
|
class ScalingSettings {
|
|
bool refine_b = false;
|
|
double max_b = 200.0;
|
|
double min_b = -50.0;
|
|
|
|
bool refine_wedge = false;
|
|
|
|
bool merge_friedel = true;
|
|
std::optional<double> high_resolution_limit_A;
|
|
std::optional<double> wedge_for_scaling;
|
|
std::optional<double> forced_mosaicity; // diagnostic: fix the scaling mosaicity (deg) instead of the per-image seed
|
|
double min_partiality = 0.02;
|
|
// Capture-aware systematic uncertainty for the rot3d combine: a full reconstructed from only
|
|
// 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).
|
|
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
|
|
// 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).
|
|
double min_captured_fraction = 0.0;
|
|
double min_cc_for_image = 0.0;
|
|
double outlier_reject_nsigma = 0.0; // per-observation merge outlier rejection (XDS/DIALS-style); 0 = off, e.g. 6 enables
|
|
|
|
// Scale fulls: after the rotation 3D combine, refit a per-frame scale on the combined fulls (XDS
|
|
// order). Only used by the rotation path (RotationScaleMerge).
|
|
bool scale_fulls = false;
|
|
|
|
// Correction surfaces fitted on the rot3d fulls after scale-fulls: a decay (per-run Debye-Waller B)
|
|
// and an absorption surface (over the diffracted-beam direction in the goniometer frame). Both are
|
|
// cross-validated, so they no-op when their systematic is absent - hence ON by default (they only ever
|
|
// help or do nothing). No-op without rot3d. Set false to disable both.
|
|
bool correction_surfaces = true;
|
|
// Absorption-surface refinement iteration count (used when correction_surfaces is on).
|
|
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;
|
|
|
|
// Smooth the per-frame scale G across frames (centered moving average of log G) before the rot3d
|
|
// combine, so a rocking event's partials share a consistent scale. Given as a ROTATION RANGE in
|
|
// degrees (like XDS DELPHI), converted to an odd frame window from the oscillation step; this keeps
|
|
// the smoothing physical (independent of frame slicing). 0 = off. A no-op without rot3d.
|
|
double smooth_g_deg = 0.0;
|
|
|
|
// Per-batch relative-B on the rot3d fulls (beyond the single global decay slope): bin frames into
|
|
// rotation-range batches of this width in degrees and refine one relative Debye-Waller B per batch, so
|
|
// NON-monotonic changes in scattering power across the run (absorption path, crystal slippage, dose
|
|
// bursts) are corrected the resolution-flat per-frame G and the single decay slope both miss. Cross-
|
|
// validated + zero-mean-anchored, so a no-op when absent. 0 = off. A no-op without rot3d.
|
|
double relative_b_deg = 0.0;
|
|
|
|
double rfree_fraction = 0.05;
|
|
|
|
// Automatic high-resolution cutoff for the written reflections + reported shells (not the scaling
|
|
// or the error model, and not the per-image _process.h5). Applied only when no explicit
|
|
// high_resolution_limit_A is set - that manual limit always wins and disables the auto-cut.
|
|
ResolutionCutoffMethod resolution_cutoff = ResolutionCutoffMethod::CCHalfLogistic;
|
|
double resolution_cc_target = 0.30; // CC1/2 value defining the fall-off limit before the +1 shell
|
|
int report_shell_count = 10; // number of resolution shells in the reported statistics table
|
|
|
|
bool scaling_regularize = false;
|
|
public:
|
|
ScalingSettings& RefineB(bool input);
|
|
ScalingSettings& RefineRotationWedge(bool input);
|
|
ScalingSettings& RotationWedgeForScaling(std::optional<double> input);
|
|
ScalingSettings& MergeFriedel(bool input);
|
|
ScalingSettings& HighResolutionLimit_A(double limit);
|
|
ScalingSettings& HighResolutionLimit_A(std::optional<double> limit); // nullopt clears the limit
|
|
ScalingSettings& MinPartiality(double min_partiality);
|
|
ScalingSettings& ForcedMosaicity(std::optional<double> input);
|
|
ScalingSettings& CaptureUncertaintyCoeff(double input);
|
|
ScalingSettings& PartialityUncertaintyCoeff(double input);
|
|
ScalingSettings& MinCapturedFraction(double input);
|
|
ScalingSettings& MinCCForImage(double min_cc_for_image);
|
|
ScalingSettings& OutlierRejectNsigma(double input);
|
|
ScalingSettings& ScaleFulls(bool input);
|
|
ScalingSettings& AbsorptionIter(int input);
|
|
ScalingSettings& CorrectionSurfaces(bool input);
|
|
ScalingSettings& StillsModulation(bool input);
|
|
ScalingSettings& SmoothGDegrees(double input);
|
|
ScalingSettings& RelativeBDegrees(double input);
|
|
|
|
ScalingSettings& RfreeFraction(double input);
|
|
ScalingSettings& ScalingRegularize(bool input);
|
|
|
|
ScalingSettings& ResolutionCutoff(ResolutionCutoffMethod input);
|
|
ScalingSettings& ResolutionCCTarget(double input);
|
|
ScalingSettings& ReportShellCount(int input);
|
|
|
|
[[nodiscard]] bool GetRefineB() const;
|
|
[[nodiscard]] bool GetRefineWedge() const;
|
|
|
|
[[nodiscard]] double GetMinB() const;
|
|
[[nodiscard]] double GetMaxB() const;
|
|
|
|
[[nodiscard]] double GetMinMosaicity() const;
|
|
[[nodiscard]] double GetDefaultMosaicity() const;
|
|
[[nodiscard]] double GetMaxMosaicity() const;
|
|
|
|
[[nodiscard]] double GetMinWedge() const;
|
|
[[nodiscard]] std::optional<double> GetRotationWedgeForScaling() const;
|
|
[[nodiscard]] double GetMaxWedge() const;
|
|
|
|
[[nodiscard]] bool GetMergeFriedel() const;
|
|
|
|
[[nodiscard]] std::optional<double> GetHighResolutionLimit_A() const;
|
|
|
|
[[nodiscard]] double GetMinPartiality() const;
|
|
[[nodiscard]] std::optional<double> GetForcedMosaicity() const;
|
|
[[nodiscard]] double GetCaptureUncertaintyCoeff() const;
|
|
[[nodiscard]] double GetPartialityUncertaintyCoeff() const;
|
|
[[nodiscard]] double GetMinCapturedFraction() const;
|
|
[[nodiscard]] double GetMinCCForImage() const;
|
|
[[nodiscard]] double GetOutlierRejectNsigma() const;
|
|
[[nodiscard]] bool GetScaleFulls() const;
|
|
[[nodiscard]] int GetAbsorptionIter() const;
|
|
[[nodiscard]] bool GetCorrectionSurfaces() const;
|
|
[[nodiscard]] bool GetStillsModulation() const;
|
|
[[nodiscard]] double GetSmoothGDegrees() const;
|
|
[[nodiscard]] double GetRelativeBDegrees() const;
|
|
|
|
[[nodiscard]] double GetRfreeFraction() const;
|
|
[[nodiscard]] bool GetScalingRegularize() const;
|
|
|
|
[[nodiscard]] ResolutionCutoffMethod GetResolutionCutoff() const;
|
|
[[nodiscard]] double GetResolutionCCTarget() const;
|
|
[[nodiscard]] int GetReportShellCount() const;
|
|
};
|