c5cfdcef7ea537a988d90d8b3921ca03d8f0367e
1187
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c5cfdcef7e |
rugnux: per-batch relative-B correction + radiation-damage B monitor
Rotation scaling modelled dose-dependent scattering power with a single global relative-B decay slope only (RefineDecay); per-frame refinement was scale-only. Add a per-batch relative Debye-Waller B, both as an opt-in correction and as an always-on measurement. - FitRelativeBCurve / SolveCurvatureSmoothedB: per rotation-range batch, fit the s^2 slope of ln(Iref/Iobs) that the resolution-flat per-frame G leaves, regularised by a second-difference (curvature) penalty so a genuine smooth relative-B is kept while resolution-correlated noise (which an unconstrained per-batch fit would chase into an oscillating curve) is suppressed. - Correction (--relative-b[=deg], default off): applied after RefineDecay, gated by a physical peak-to-peak floor and cross-validated by ASU-group parity (a per-batch parameter owns whole frames, so it is scored on held-out equivalents, not held-out frames). On a battery re-scaling identical stored reflections it is a no-op on well-behaved data and a small gain on radiation-damaged rotation data; never a regression. - Radiation-damage monitor (always on for rotation, report-only): measure the relative-B change from the pristine low-dose start of the run to its end (a whole-run-mean reference inverts on damaged data) plus the per-batch curve, and report them on the command line (in the radiation-damage report and the merge statistics) and in the mmCIF (_reflns.jfjoch_radiation_damage_ relative_B and a per-batch loop). A large magnitude flags radiation damage, complementing the existing per-image CC / mosaicity read. - --scale now reproduces the de-novo rotation merge (it previously omitted scale-fulls and smooth-G, silently using a weaker model), and honours --relative-b, so an offline re-scale matches the full pipeline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e97a610091 |
rugnux: reindex the de-novo primitive cell to the group's conventional setting (two-pass)
When the two-pass second pass indexes de novo and the pseudo-symmetry guard keeps a triclinic (primitive) cell - a huge oblique cell whose constrained centred refine is ill-posed - the reflections are left in the primitive frame while the reused merge space group expects its conventional setting, so the merge folds the wrong equivalents (a centred group's half-set CC1/2 collapses). Reindex the integrated reflections into the group's conventional setting first, using the same exact-integer primitive->conventional change of basis the space-group search applies to a demoted result, gated on LatticeSearch recovering the group's own metric (crystal system + centring). Fires only for a demoted-triclinic indexing under a higher-symmetry reused group; every other dataset is bit-identical. Closes the last centred-space-group case where the second pass's merge was frame-mismatched (CC1/2 72 -> 98). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
290c30b9be |
rugnux: post-refine commits detector/cell geometry only for a small (< 1 %) move
The rotation geometry post-refine committed the detector distance and cell scale on any cross-validated improvement within a loose 3 % / 2 % bound. On a multi-lattice crystal the second lattice's spots bias every cross-validation fold the same way, so the "it improved" test cannot catch it and a spurious distance<->cell pair (their radial degeneracy) is committed far off the true geometry, losing reflections in the re-integration. Tighten the commit bound to < 1 % (a calibrated header needs < ~0.6 %). The move SIZE discriminates a genuine header correction from the multi-lattice failure far better than the fit residual, which genuinely marginal (noisy or ice-ring) data shares with the multi-lattice case - so a real ~0.3 % correction is kept while the ~1.7 % multi-lattice one is rejected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0ebf90a1ad |
rugnux: two-pass second pass re-indexes de novo, reuses the group for the merge only
The two-pass second pass reused pass-1's space group for both the indexer and the merge. Reusing it in the INDEXER disables the de-novo pseudo-symmetry safeguards, so build_sr adopts a wrong cell: a metrically pseudo-centred crystal doubles into a super-cell (every real reflection plus phantom half-integer ones - so the merge keeps CC1/2 but halves <I/sigma>), and a huge oblique cell's constrained monoclinic refine is ill-posed, diverges, and the re-indexing collapses. Decouple the two: the second pass now indexes DE NOVO (the group is cleared before it, safeguards on) so it recovers the true cell, and pass-1's group is reinstated for the MERGE ONLY, just before RotationScaleMerge, so the symmetry stays fixed exactly as a user-fixed group would - no re-search, no flip. A reindex-derived group is still left to re-search. Neutral for genuine-symmetry data (its de-novo cell already matches the group); it recovers the pseudo-symmetric cases that previously collapsed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1089b75906 |
rugnux: two-pass writes both passes (_01 header geometry, _02 refined)
The two-pass geometry post-refinement previously wrote only the second
(refined-geometry) pass. Write both instead, so the header-geometry and
post-refined results can be compared directly and the better one kept:
<prefix>_01_* first pass, at the header geometry (the plain single-pass
baseline)
<prefix>_02_* second pass, at the post-refined geometry
Each is a complete standalone result - its own merged reflections
(.mtz/.cif), per-image _process.h5 (with --write-process-h5), ice-ring
flags and mmCIF detector geometry, all consistent with the geometry that
pass actually used.
Mechanics: the pre-pass now stores the post-refined detector geometry
instead of applying it mid-pass (so the first pass finishes and writes at
the header geometry), and no longer returns early - it continues through the
full write. Run() then applies the stored geometry, lets the second pass
re-search a reindex-derived space group, and routes the two passes to the
_01 / _02 prefixes. The second pass's result is unchanged from before.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
c140b3567d |
rugnux: restrain the gauge-weak beam centre toward the header in geometry refinement
Single-axis rotation has a gauge degeneracy: rotating the whole experiment about the spindle leaves every spot position unchanged, so the beam-centre component parallel to the spindle is a null / under-determined direction. Refining it freely lets it wander a few pixels and absorb centroid systematics into a wrong beam that the co-refined orientation keeps position-consistent. Add a soft header prior on that one component in the shared XtalOptimizer: residual = w*(beam[parallel] - header), with w set so the restraint behaves like a sigma_px-pixel prior competing with the positional residuals. The gauge direction has ~zero data sensitivity, so the prior pins it near the LaB6-monitored header, while a real, well-supported drift can still overcome it (the beam does drift). One restraint covers all three refinement sites at once: the noisy accumulated-spot primary, the per-frame filter, and post-refine. Validated against independent XDS processing: neutral on well-determined data (the gauge direction has no effect there), and a real low-resolution accuracy gain where the free refinement had otherwise wandered off the header - the merged intensities move back into agreement with XDS while the internal CC1/2, which is blind to this, barely changes. No space-group determinations change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c9ed9c3776 |
rugnux: two-pass reuses pass-1 space group; floor the fed mosaicity
The second pass re-searching the space group at the slightly changed geometry could flip a borderline determination (cubic <-> orthorhombic) and mix the two passes, collapsing the multiplicity. Fix it: the pre-pass now continues through the space-group search, so the determined group is already fixed on experiment_ and the second pass reuses it instead of re-searching. The lattice is NOT forced - the second pass re-indexes at the refined geometry so the cell stays self-consistent with it (forcing the pre-pass lattice would pin the nominal cell and undo the refinement). A reindex-derived group (the centred-lattice test) is in a conventional setting the indexer's primitive frame does not share, so it is cleared for the second pass to re-search (else its indexing fails outright). Also floor the mosaicity fed to the second pass's prediction at each frame's own estimate (max), so a too-narrow smoothed value can never make prediction drop reflections. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1b91823eed |
rugnux: --rotation-post-refine two-pass geometry post-refinement (opt-in)
A first pass integrates and post-refines the detector geometry from the observed
diffraction, applies it to experiment_, and a second pass re-indexes and
re-integrates with it - only the second pass is written, so the _process.h5 / mtz
(and the ice-ring flags, which move with the geometry) reflect the refined values.
Default off; enable with --rotation-post-refine.
PostRefineRotationGeometry refines in two SEPARATE cross-validated steps (a joint
fit of everything is fragile - the same lesson as integration, where refining the
profile width and the scale jointly fails but separately works):
* Step A: cell scale + rotation axis from the observed rocking centroids phi_obs
(a distance-independent excitation residual, so it pins the absolute cell scale
that the positional residual leaves degenerate with distance).
* Step B: detector distance + beam centre from the observed spot positions, with
the cell fixed at step A.
Each step commits only if it lowers a held-out (deterministic split-half) residual,
else that part of the geometry is left at nominal.
Rugnux::Run wraps RunPipeline(write_output, geometry_prepass); the pre-pass runs
the first scale/merge so RotationScaleMerge fits a frame-order-smoothed mosaicity,
which is captured and fed to the second pass's Bragg prediction (via
IndexAndRefine::SetPredictionMosaicityOverride) rather than re-derived per image.
observed_x/y were plumbed through the profile integrator earlier for the positional
residual. rugnux_vs_xds.py gains --extra-args to sweep the option over the battery.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
f396385e11 |
rugnux: record the detector distance and beam centre in the merged mmCIF
Add _diffrn_detector.jfjoch_{distance_mm,beam_center_x_pxl,beam_center_y_pxl} to
the merged mmCIF header, as feedback of the geometry the data was actually
reduced with. When geometry refinement ran (the rotation two-pass or the stills
global refinement, which update experiment_ before the written pass) these are
the refined values; otherwise they are the nominal header geometry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
9bfecaadf5 |
beam_stop: add ShadowFinder detection class (unwired)
Add image_analysis/beam_stop/ShadowFinder.{h,cpp} and SHADOW_FINDER.md: a
self-contained beam-stop shadow detector that accumulates images via AddImage()
and returns a mask from GetMask(), mirroring the shape of DarkMaskAnalysis.
It detects the beam-stop shadow (central disk + holder arm) as an azimuthal
anomaly: an iterated radial-median background baseline, a ratio threshold, a
connectivity-to-beam-centre anchor with module-gap bridging, a central low-res
disk guard capped just inside the innermost reflection, and a reflection guard
that never masks a pixel that recorded real signal.
Not yet wired: not added to image_analysis/CMakeLists.txt and no PixelMask /
Rugnux / viewer changes. SHADOW_FINDER.md documents the algorithm and the
deferred offline (Rugnux bit 9 + viewer user-mask) integration plan.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
1911875e1c |
integration: carry the box-sum observed centroid through the profile path
Pass A (the box-sum) always computes an intensity-weighted centroid (the observed spot position), but it was only emitted in BoxSum mode. Rotation integration runs the profile path, so observed_x/y were left NAN there and no positional (detector<->reciprocal) residual was possible downstream. Emit the Pass-A centroid in the profile path too (CPU) and copy d_obs_x/d_obs_y back in all modes (GPU) - Pass A fills them regardless of integrator mode. observed_x/y are consumed only by the diagnostic HDF5 reflection table and the viewer read-back; nothing in scaling/merge reads them, so merged output is unchanged - the _process.h5 observed_x/y columns simply go from NAN to the real centroid. This makes the observed position available to post-refinement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7ceffde5a4 |
rugnux: rank space groups by net absences, not gross (fix C222 over-centering)
Build Packages / Unit tests (pull_request) Successful in 1h1m38s
Build Packages / build:windows:nocuda (pull_request) Successful in 15m24s
Build Packages / build:windows:cuda (pull_request) Successful in 17m21s
Build Packages / build:viewer-tgz:cpu (pull_request) Successful in 8m51s
Build Packages / build:viewer-tgz:cuda (pull_request) Successful in 8m21s
Build Packages / build:rpm (ubuntu2404_nocuda) (pull_request) Successful in 10m28s
Build Packages / build:rpm (rocky8) (pull_request) Successful in 12m54s
Build Packages / build:rpm (rocky9) (pull_request) Successful in 13m33s
Build Packages / build:rpm (ubuntu2204) (pull_request) Successful in 13m8s
Build Packages / build:rpm (ubuntu2404) (pull_request) Successful in 11m45s
Build Packages / DIALS test (pull_request) Successful in 14m34s
Build Packages / XDS test (durin plugin) (pull_request) Successful in 7m39s
Build Packages / XDS test (JFJoch plugin) (pull_request) Successful in 7m48s
Build Packages / XDS test (neggia plugin) (pull_request) Successful in 5m56s
Build Packages / Generate python client (pull_request) Successful in 12s
Build Packages / Build documentation (pull_request) Successful in 52s
Build Packages / Create release (pull_request) Skipped
Build Packages / build:rpm (ubuntu2204_nocuda) (pull_request) Successful in 16m25s
Build Packages / build:rpm (rocky9_nocuda) (pull_request) Successful in 16m38s
Build Packages / build:rpm (rocky8_nocuda) (pull_request) Successful in 16m52s
Build Packages / build:rpm (rocky8_sls9) (pull_request) Successful in 16m46s
Build Packages / build:rpm (rocky9_sls9) (pull_request) Successful in 17m40s
Build Packages / Unit tests (push) Successful in 1h11m44s
Build Packages / build:windows:nocuda (push) Successful in 10m57s
Build Packages / build:viewer-tgz:cpu (push) Successful in 6m47s
Build Packages / build:viewer-tgz:cuda (push) Successful in 8m11s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 11m5s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 11m8s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 16m3s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 9m13s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 16m31s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m22s
Build Packages / build:rpm (rocky8) (push) Successful in 12m4s
Build Packages / build:rpm (rocky9) (push) Successful in 15m43s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 15m39s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 14m28s
Build Packages / DIALS test (push) Successful in 15m19s
Build Packages / XDS test (durin plugin) (push) Successful in 8m42s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m41s
Build Packages / XDS test (neggia plugin) (push) Successful in 8m19s
Build Packages / Generate python client (push) Successful in 17s
Build Packages / Build documentation (push) Successful in 1m4s
Build Packages / Create release (push) Skipped
Build Packages / build:windows:cuda (push) Successful in 12m46s
Stage B of SearchSpaceGroup chose the largest centering that explained the most absences and was self-consistent, ranking candidates by the gross observed-absent count. A false super-centering over-claims: on a C222 crystal, F222 predicts every C absence (all genuinely weak) PLUS a block of C-present reflections it wrongly calls absent. Its absent class is then bimodal - many true zeros diluting a strong block - so its mean <I/sigma>abs (and the violation-rate arm) slip under the centering strength gate, which cannot see the over-claim in isolation. With the gross-count ranking F222 then beat the true C222, and the strength gate is scale-invariant here, so real C-centered data trips it identically (the rotation battery just has no C-centered crystal on this path; the synthetic unit test does). Rank instead by the absences GENUINELY explained, absent_observed - absent_violations, then by fewer violations, then lower space-group number. C222 and F222 net to the same count (F222's genuine absences ARE C222's), so the fewer-violations tie-break keeps the honest, less-centred C222. Symmetric: on a real F222 crystal F explains strictly more weak absences and still wins. Also tighten the "indistinguishable alternatives" grouping to require the same absent AND violation counts, so a super-centering is never reported as an equal alternative. No tuned threshold is changed; the ranking is a no-op for any 0-violation winner. Fixes the SearchSpaceGroup C222 synthetic-space-group test. Rotation battery (32 crystals, de-novo) is space-group-identical to before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
742a1c1b94 |
rugnux: identify the producing software in the merged mmCIF and MTZ
Build Packages / build:windows:nocuda (pull_request) Successful in 15m47s
Build Packages / build:windows:cuda (pull_request) Successful in 17m52s
Build Packages / build:viewer-tgz:cpu (pull_request) Successful in 8m17s
Build Packages / build:viewer-tgz:cuda (pull_request) Successful in 6m44s
Build Packages / build:rpm (rocky8_nocuda) (pull_request) Successful in 9m25s
Build Packages / build:rpm (rocky9_nocuda) (pull_request) Successful in 10m11s
Build Packages / build:rpm (ubuntu2204_nocuda) (pull_request) Successful in 9m33s
Build Packages / build:rpm (ubuntu2404_nocuda) (pull_request) Successful in 9m19s
Build Packages / build:rpm (rocky8_sls9) (pull_request) Successful in 10m1s
Build Packages / build:rpm (rocky9_sls9) (pull_request) Successful in 11m27s
Build Packages / build:rpm (rocky8) (pull_request) Successful in 10m15s
Build Packages / build:rpm (ubuntu2204) (pull_request) Successful in 10m33s
Build Packages / DIALS test (pull_request) Successful in 10m33s
Build Packages / XDS test (durin plugin) (pull_request) Successful in 7m24s
Build Packages / XDS test (JFJoch plugin) (pull_request) Successful in 7m47s
Build Packages / XDS test (neggia plugin) (pull_request) Successful in 7m10s
Build Packages / Generate python client (pull_request) Successful in 24s
Build Packages / Build documentation (pull_request) Successful in 1m4s
Build Packages / Create release (pull_request) Skipped
Build Packages / build:viewer-tgz:cpu (push) Successful in 6m51s
Build Packages / build:viewer-tgz:cuda (push) Successful in 7m43s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 12m17s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 12m54s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 12m28s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 11m57s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 10m58s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 10m48s
Build Packages / DIALS test (push) Successful in 13m12s
Build Packages / XDS test (durin plugin) (push) Successful in 9m10s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m40s
Build Packages / XDS test (neggia plugin) (push) Successful in 8m25s
Build Packages / Generate python client (push) Successful in 16s
Build Packages / Build documentation (push) Successful in 1m7s
Build Packages / Create release (push) Skipped
Build Packages / build:windows:nocuda (push) Failing after 2m35s
Build Packages / build:windows:cuda (push) Successful in 18m20s
Build Packages / build:rpm (ubuntu2404) (pull_request) Successful in 13m16s
Build Packages / build:rpm (rocky9) (pull_request) Successful in 15m14s
Build Packages / build:rpm (rocky8) (push) Successful in 12m6s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 11m47s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 12m13s
Build Packages / build:rpm (rocky9) (push) Successful in 12m32s
Build Packages / Unit tests (pull_request) Failing after 59m4s
Build Packages / Unit tests (push) Failing after 51m23s
mmCIF: set _software.name to 'Rugnux' (was 'Jungfraujoch') and correct
_software.classification to the CIF-dictionary value 'data reduction'.
MTZ: add the producing software to the header via the title and a HISTORY
record ("From Rugnux <version>, data reduction"), the CCP4 provenance channel,
which was previously empty.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
e209069ccd |
docs: compress the rc.160 changelog entries to one-line summaries
The rc.160 entries were multi-sentence explanations; rewrite each as a short statement of what changed (feature / flag / behaviour), roughly under 200 characters, matching a normal changelog. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3a23c79ea4 |
docs: document the asymptotic ISa reporting and the azint Q-field precision
CHANGELOG: describe reporting ISa at its asymptotic definition (read from the strong equivalents, relaxed threshold on weak/damaged data) and the frontend azimuthal-integration Q fields gaining 5-decimal precision. CPU_DATA_ANALYSIS: update the error-model section - ISa is the counting- subtracted strong-reflection asymptote (and drives the systematic floor), not 1/b of the whole-range fit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
59e260876a |
rugnux: recover the asymptotic ISa on weak / radiation-damaged data
Build Packages / build:viewer-tgz:cpu (push) Successful in 7m2s
Build Packages / build:viewer-tgz:cuda (push) Successful in 7m47s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 13m54s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m13s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 13m44s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 13m17s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 14m14s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 13m28s
Build Packages / build:rpm (rocky8) (push) Successful in 12m14s
Build Packages / build:rpm (rocky9) (push) Successful in 13m16s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 13m20s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 12m28s
Build Packages / DIALS test (push) Successful in 13m47s
Build Packages / XDS test (durin plugin) (push) Successful in 8m18s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m54s
Build Packages / XDS test (neggia plugin) (push) Successful in 8m11s
Build Packages / Generate python client (push) Successful in 35s
Build Packages / Build documentation (push) Successful in 1m6s
Build Packages / Create release (push) Skipped
Build Packages / build:windows:nocuda (push) Successful in 15m47s
Build Packages / build:windows:cuda (push) Failing after 3m45s
Build Packages / Unit tests (push) Failing after 49m43s
The asymptote was read only from groups with I/sigma > 20 and required at least 100 of them, so weak or radiation-damaged data (few strong reflections) fell back to the higher whole-range b and reported an over-conservative ISa. Relax the I/sigma threshold in a second tier (>= 50 groups above I/sigma 10) when the tight one is unmet; the per-group counting-subtracted estimate is stable down to that threshold. Data with many strong groups is unchanged (it keeps the tight-threshold asymptote); weak / damaged data now reports the asymptote its own reflections support instead of falling back. Battery (32 crystals): no space-group, R_meas or CC1/2 change; strong sets unchanged; e.g. a room-temperature radiation-damage series recovers ISa 5.4 -> 11.2 (its data already exceeds the reference on CC1/2 and R_meas). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
88359a57ee |
rugnux: report ISa at its asymptotic definition (strong-reflection limit)
ISa is the I->infinity limit of the signal-to-noise (Diederichs, Acta Cryst. D66 (2010), 733-740), i.e. the reproducibility of the strongest reflections. The reported value was taken as 1/b of the whole-intensity-range error-model fit, whose systematic term b is raised by a mild excess of scatter at intermediate intensity, so it understated that limit. Read the asymptote directly instead: for each well-measured reflection group the counting-subtracted fractional scatter of its symmetry mates estimates the systematic term, and the robust median over strong groups is the asymptote. It falls back to the whole-range b when too few strong groups exist (weak or low-multiplicity data). The reported ISa and the merged-sigma systematic floor use this value, so merged I/sigma approaches ISa. The (a, b) fit and the per-observation error model are unchanged, so the merge weights, anomalous statistics and chi^2 are untouched, and CC1/2 and R_meas (which do not depend on sigma) are bit-identical. Battery (32 crystals): no space-group or indexing change, R_meas and CC1/2 unchanged, ISa rises to the true asymptote of the data. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
174b9585c5 |
docs: document SigAno and the low-energy centering fix
CHANGELOG (rc.160): add the centering rate-gate recovery on weak/low-energy data and the SigAno anomalous signal-to-noise statistic. CPU_DATA_ANALYSIS: describe SigAno in the merging-statistics section - its definition, the standard PDBx mmCIF items, and why a half-set anomalous CC is not used (complementary halves drive it negative on weak shells) - and note the floor-independent centering-acceptance test in the space-group-determination notes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7fe12773de |
rugnux: report anomalous signal-to-noise SigAno (stats table + mmCIF)
Compute SigAno = <|I(+)-I(-)|>/<sigma(I(+)-I(-))> per report shell and overall from the full-multiplicity I(+)/I(-) Bijvoet split, store it on MergeStatisticsShell, and surface it in the console merge-statistics table (new SigAno column) and the mmCIF via the standard wwPDB PDBx items _reflns.pdbx_absDiff_over_sigma_anomalous (overall) and _reflns_shell.pdbx_absDiff_over_sigma_anomalous (per shell) - the dictionary's home for this quantity, so no jfjoch_ local item is needed (unlike ISa). SigAno approaches ~0.8 for pure noise and rises above 1 with real anomalous signal. A half-set anomalous CC is deliberately not used: its two half estimates are complementary partitions of one observation pool (dI0 + dI1 = 2*dI_full), so differencing the two Bijvoet hands cancels the large common intensity that keeps CC1/2 non-negative and, once the anomalous SNR per half drops below 1, drives the correlation towards -1 rather than 0 - misrepresenting a weak-but-real signal. Emitted only when an anomalous split was made (has_anom guard), so a non-anomalous merge keeps its stats block and shell-loop columns byte-for-byte. Export-neutral: the I(+)/I(-)/IMEAN accumulation is unchanged. On low-energy S-SAD test data SigAno tracks XDS (13 keV null ~0.89, 6 keV ~1.44, 5 keV ~1.5-1.7) and never goes spuriously negative. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1b5eb80ba4 |
rugnux: recover I-centering on weak / low-energy data (centering rate gate)
The de-novo centering gate promoted to a centred lattice only when the mean signed <I/sigma> of the centering-absent class was <= 0.5x that of the present class. Weak / low-energy data carry a positive intensity floor (background / profile leakage) that lifts <I/sigma> of the genuinely-extinct class to ~1.5-2.3 instead of ~0; when the present class is itself weak that additive floor inflates the ratio past the bound and the true centering is missed. An I-centred cubic crystal recorded at 5 and 13 keV sat at ratio ~0.57 and stayed primitive P2_1 3, while the same crystal form at 6 keV (stronger data) correctly merged as I23 - an energy-dependent space group for one physical crystal. Add a second, floor-independent acceptance path OR'd with the mean-ratio: the centering-absent class's strong-reflection rate (violations / absent) versus the present class's own strong rate. A false centering (absent as strong as present) fails both arms; a true weak centering passes the rate arm. Battery SG sweep vs XDS: the low-energy datasets now adopt I23, and no crystal is newly over-centred. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
32c7f9f59f |
rugnux: don't veto genuine high symmetry on excellent data (b-veto floor)
The de-novo space-group search's systematic-b veto - which keeps a merohedral twin in its true lower symmetry - is a RATIO test: reject a point-group promotion whose merge error-model b balloons past 2x the parent subgroup's. That ratio is meaningless when the parent b is near zero, on excellent data where symmetry equivalents already agree almost perfectly. A genuine tetragonal 422 whose 222 parent sits at b=0.008 and 422 at b=0.049 (both tiny, ISa ~20) reads as a 6x balloon and is wrongly demoted to 222. Floor the parent b (min_systematic_b_for_veto = 0.05) before forming the ratio, so the veto only judges a promotion once its b is a non-negligible fraction of I. A real merohedral twin drives b to ~0.19 regardless of how clean the parent is, so it is still caught (the insulin R3 + ~20% twin veto still fires, b=0.19 > 0.15). A P41212 58/58/150 crystal at 16 keV goes P212121 -> P41212; full 26-crystal rotation battery: only that crystal changes (its space group now matches XDS, SG match 22->23/26), every other space group bit-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
afae1dec2f |
rugnux: index long finely-spaced axes (de-novo two-pass long-axis rescue)
De-novo two-pass rotation indexing collapsed on a crystal with a long, finely spaced axis (a ~150 A c-axis at 16 keV): the unconstrained FFT either collapsed the long axis to a short sub-multiple or let a denser supercell over-fit the accumulated cloud - a small global-orientation error throws the many high-order reflections off along the fine axis, so the true cell scores worst on the raw cloud, and propagating that inaccurate global orientation to each frame fails. Add a long-axis rescue that only runs when the standard pass indexes few validation frames (<50%), so well-indexing crystals are untouched and keep the fast path: re-run the first pass at a COARSE resolution (low-order reflections only, where the fine axis stays robust) to recover the true metric, take the recovered cell with the longest axis directly, then RE-INDEX at full resolution with that cell as a reference (the -C path) - the reference-cell filter drops the collapsed/supercell candidates and refines an accurate global lattice. The scheme feed/index/validate loop is factored into a pick_best() lambda shared by the standard and constrained passes (the standard pass is behaviour-preserved). A P41212 58/58/150 test crystal goes 15% -> 99.89% indexed. Full 25-crystal rotation battery: every other crystal's space group and cell BIT-IDENTICAL, and the rescue fires only on the failing crystal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
799e1e15ec |
frontend: configurable decimal precision, 5 places for azint Q fields
Build Packages / build:viewer-tgz:cpu (push) Successful in 7m45s
Build Packages / build:viewer-tgz:cuda (push) Successful in 8m26s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 12m26s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 13m46s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 13m49s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m1s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 14m6s
Build Packages / build:windows:nocuda (push) Successful in 16m37s
Build Packages / build:windows:cuda (push) Successful in 18m27s
Build Packages / build:rpm (rocky8) (push) Successful in 12m13s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 13m21s
Build Packages / XDS test (durin plugin) (push) Successful in 8m22s
Build Packages / Generate python client (push) Successful in 29s
Build Packages / Build documentation (push) Successful in 1m5s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky9) (push) Successful in 12m41s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 12m24s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 13m1s
Build Packages / DIALS test (push) Successful in 14m15s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m30s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m20s
Build Packages / Unit tests (push) Failing after 58m14s
NumberTextField quantized every float input to 3 decimals, so the finest q_spacing reachable through the UI was 0.001 - capping the azimuthal-integration q-bin count around 200 regardless of the CPU/FPGA backend. Add an optional `decimals` prop (default 3, unchanged elsewhere) and set it to 5 on the Q spacing / Low Q / High Q fields, matching the q_spacing minimum of 1e-5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
65bc40e7c7 |
merge: deltaCChalf uses the deterministic HalfForImage split, not an RNG
Build Packages / build:viewer-tgz:cpu (push) Successful in 7m54s
Build Packages / build:viewer-tgz:cuda (push) Successful in 8m37s
Build Packages / build:windows:cuda (push) Failing after 9m13s
Build Packages / build:windows:nocuda (push) Successful in 10m57s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 13m8s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 14m0s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 13m56s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m6s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 14m6s
Build Packages / build:rpm (rocky8) (push) Successful in 11m48s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m43s
Build Packages / XDS test (durin plugin) (push) Successful in 7m58s
Build Packages / Generate python client (push) Successful in 31s
Build Packages / Build documentation (push) Successful in 58s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky9) (push) Successful in 12m19s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 13m27s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 13m30s
Build Packages / DIALS test (push) Successful in 14m23s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m58s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m43s
Build Packages / Unit tests (push) Failing after 1h0m21s
MergeOnTheFly::DeltaCChalfReject assigned its CC1/2 half-sets from a seeded std::mt19937 drawn in image (call) order, whereas the actual merge, the rotation merge, the GPU path and the R-free flags all split with the deterministic HalfForImage(image_id) splitmix64 hash. So the deltaCChalf was measured on a different half-partition than the reported CC1/2, and was order-dependent (a reordered outcomes vector gave different halves). Use HalfForImage(i) - i is the image's stable index, the same image_id AddImage merges with - so deltaCChalf now reflects the exact CC1/2 split the statistics report, order-independently. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0c33139983 |
rugnux: radiation-damage report for rotation (per-image CC vs dose)
Summarise the per-image scale the rotation merge already fits - the CC-to-merge and mosaicity binned by frame (= dose) across the sweep - and flag a falling per-image CC (or rising mosaicity) as the radiation-damage / crystal-decay signature. A data-quality-vs-dose read complementary to the fitted decay correction; the full per-image table is still written to <prefix>_scaling.txt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b647cf454c |
rugnux: write I(+)/I(-) and F(+)/F(-) by default (rotation)
A rotation merge now keeps each acentric reflection's Bijvoet split - the inverse-variance I(+)/I(-) from the same scaled fulls - even when the merge is Friedel-averaged (the default, no -A). IMEAN stays the primary intensity and is bit-identical to before; the split is purely additive and scaled non-anomalously, so a weak anomalous signal is preserved in the output without having to reprocess with -A. French-Wilson now also fills F(+)/F(-) from the two hands (one pass, shared Wilson prior). Writers: the default (Friedel-merged) MTZ and mmCIF now carry IMEAN plus I(+)/I(-) and F/F(+)/F(-) whenever a reflection has an anomalous split (the stills path, which computes none, keeps the previous columns). A missing mate or a centric is emitted as the CCP4 missing-value flag (NaN). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b945444372 |
rugnux_vs_xds.py: find the current rugnux binary (build*/rugnux/rugnux)
The rugnux CLI target builds to build*/rugnux/rugnux; the old build*/tools/ rugnux paths are stale leftovers from a previous layout that cmake no longer updates, so the harness could silently run weeks-old code. Pick the most recently built binary among the candidates instead of a fixed order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
67a18357b5 |
rugnux: fix de-novo space-group centering and weak-data high symmetry
De-novo space-group search: - Determine lattice centering from the strength of the systematically-absent class (its mean I/sigma vs the present class) instead of a per-reflection violation count. The count gate was brittle on noisy / obverse-reverse- twinned data, where enough genuinely-absent reflections cross I/sigma>3 to trip the 10% bound although the class is several-fold weaker, and wrongly kept a genuine R-centered lattice primitive. False centerings, whose absent class is as strong as the present one, stay rejected. Screws keep the count test (too few axial reflections to average). - Recover a genuine high-symmetry point group when the merged error model is badly miscalibrated (weak, low-resolution data whose sigmas are far too small). The fixed-sigma chi^2 ratio then grows with point-group order for real symmetry too and wrongly rejects it (a true cubic group reached a chi^2 ratio ~14); the self-normalising systematic-b test stays valid, so once chi2_ref shows the ratio is untrustworthy the promotion is confirmed on the b-test alone. The balloon veto still rejects a twin. This is the gap left when the earlier log10(chi2_ref) widening was removed for the b-veto. Also print the adopted space group and unit cell as a clean one-line summary at the end of the run (de-novo or user-fixed -S), not only in the mmCIF. Full rotation-test battery: exactly the two intended crystals change (both to the correct answer, with lower R-meas and higher ISa), every other crystal unchanged; space-group match 21->22/25. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d1f6b382a6 |
docs: rc.160 changelog (trim + scrub) and CPU_DATA_ANALYSIS updates
CHANGELOG: add the Wilson B, decay-CV and space-group b-veto entries; shorten the verbose modulation / stills-modulation / trimmed-background entries and remove the dataset identities from them. CPU_DATA_ANALYSIS: the correction surfaces are now three (add modulation), on by default, cross-validated on the sigma-independent metric; the background trimmed mean applies to monochromatic stills as well as rotation (broadband keeps the sigma-clip); document the dataset-wide Wilson B, the still-partiality model, and the twin b-veto in the space-group search. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7d1fb8db77 |
bragg integration: clarify that the trimmed background applies to stills too
The background trimmed mean is gated on the beam (monochromatic vs broadband), not on the acquisition mode: a monochromatic still (zero bandwidth) already gets the trim by default, only broadband (non-zero bandwidth: pink-beam / DMM) data keep the high-side sigma-clip. The comments, the settings doc and the --background-trim help wrongly implied the trim was rotation-only / that stills keep the sigma-clip. Wording only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7a9d25850c |
rugnux: veto a b-ballooning merohedral-twin over-promotion (space-group search)
The systematic-b test (a genuine symmetry step barely moves the merge error model's b; a merohedral twin, forcing non-equivalent reflections together, balloons it) was only ever a RESCUE - it could promote a chi^2-borderline genuine step but never demote a chi^2-passing one. So a partial twin whose within-orbit scatter looks self-consistent (merged chi^2 below the ratio bound) slipped through on chi^2 alone and was over-promoted to the holohedral group, ballooning b and collapsing the merged ISa. Make the same test a VETO: a chi^2-passing high-symmetry promotion whose b, relative to the largest confirmed subgroup, exceeds max_systematic_b_veto is kept in its true lower symmetry. Calibrated on the rotation-test battery, where the largest genuine step (a P422 tetragonal) sits at b-ratio ~1.8 and a merohedral R3->R32 twin at ~2.6 - an empty gap - so 2.0 catches the twin while never demoting a genuine high-symmetry merge. Battery space-group match 20->21/25 (an R3 case previously merged as R32 is now correct), every other crystal unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e9a913f84b |
scale_merge: remove sample identities from SearchSpaceGroup comments
The point-group self-consistency comments named specific datasets when illustrating the chi^2-ratio / systematic-b calibration. Describe the crystallographic situation instead (a merohedral R3->R32 twin, a genuine cubic step, a tetragonal P41212 case), per the repository's no-sample-names rule. Comment-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3bfce24faa |
rugnux: score the decay cross-validation on the sigma-independent metric
RefineDecay's cross-validation still scored the held-out equivalents on a studentized chi^2, while the absorption/modulation surfaces were switched to a sigma-independent Rmeas-like agreement (sum|Is-Iref|/sum|Iref|) precisely because a correction can "pass" a studentized gate by reshaping sigma without tightening the intensities. The decay slope scales sigma up on late / high-angle frames, so it is exposed to the same loophole. Score it the same way. On the rotation battery this rejects a spurious slope on a mis-indexed monoclinic (P2_1) case: the studentized gate had accepted a physically absurd -74 A^2 "decay" that cross-validated yet collapsed the error model (ISa 7.1 -> 1.1) and CC1/2 (to ~2%); the fractional gate scores it a -50% held-out loss and skips it. A small genuine ~3 A^2 decay on another crystal now cross-validates and engages, with CC1/2 unchanged. Every other crystal is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8bd0977833 |
rugnux_vs_xds.py: drop the removed --scaling-output flag
rugnux always writes both MTZ and CIF now (the --scaling-output option was removed), so passing it made every crystal in the battery fail against the current binary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
85cf826d2e |
rugnux: dataset Wilson B-factor estimate + robust per-image Wilson B
Add a dataset-wide isotropic Wilson B-factor estimate, the analogue of XDS's "WILSON LINE ... B=" which we did not export. CalcGlobalWilsonB fits ln<I> vs 1/d^2 over the merged reflections (B = -2*slope), skipping the low-resolution non-linear region (d > 4 A) and shells past the signal limit (<I/sigma> < 1) so the estimate is insensitive to how far the merged data were carried. It is diagnostic only - not fed back into scaling - and is written to the mmCIF (_reflns.B_iso_Wilson_estimate), the printed merge statistics, and the log. Also harden the per-image Wilson B (CalcWilsonBFactor): accept the fit only when it is well-correlated and physically plausible (0 < B < 200 A^2), else leave b_factor unset. A bad frame (an indexing glitch, too few reflections) otherwise produced a wildly steep Wilson line and a B of several hundred A^2 that polluted the per-image plot; NaN is preferable to garbage. Diagnostic-only: the merged intensities and every merge statistic are byte-identical (verified baseline vs modified on the rotation battery). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3b168a1a46 |
viewer: expose detector-plane modulation and background trim in the settings dock
Two new controls in the interactive settings dock: - Scaling section: a "Detector-plane modulation (stills)" checkbox that toggles the stills flat-field modulation surface (ScalingSettings::StillsModulation), alongside the existing decay/absorption "Correction surfaces" checkbox (rotation modulation is fitted there). Off by default. - Bragg integration section: a "Background trim" checkbox + fraction that sets the symmetric trimmed-mean background fraction (BraggIntegrationSettings::BackgroundTrimFraction; 0 = plain ring mean). Defaults on at 0.10, matching the integrator default, so the viewer requests trimming for every dataset (the integration engine still applies its own per-mode gating). Viewer-only change; compiles against the current settings API. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
74d7589abe |
stills: optional detector-plane modulation (flat-field) correction
The stills scaling/merging path (ScaleOnTheFly + MergeOnTheFly) had no correction surfaces, whereas rotation fits decay/absorption/modulation inside RotationScaleMerge. Serial stills hammer the same detector regions shot after shot, so a detector-plane (flat-field) systematic is at least as well determined there as in rotation - it just had no home. Add MergeOnTheFly::RefineModulation: a 16x16 multiplicative surface over the predicted detector position, fit against the merged reference and folded into each reflection's image_scale_corr before the error model and the merge. It mirrors RotationScaleMerge::ApplyCellSurface - alternating per-cell fit with a den-weighted geometric-mean gauge (never drifts the overall scale) and Tikhonov pull to 1 - and is cross-validated the same way: fit the surface on even images, score the held-out odd equivalents by a sigma-independent R-meas-like agreement (a fractional metric a sigma-reshaping surface cannot game), and apply the full-data surface only if the held-out gain clears a margin. A no-op when the systematic is absent or the data too sparse (< 8 obs per cell), so it is safe to leave off by default and opt in. Wired through ScalingSettings::StillsModulation and rugnux --stills-modulation (default off), in both the full-analysis and --scale paths. On a 17 MP JUNGFRAU serial set the surface cross-validates with a large held-out gain, confirming a real detector-plane systematic; whether it improves the merged data quality is under evaluation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c3e877d5ec |
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
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> |
||
|
|
118f0833dc |
common: remove dead Reflection.completeness field
Reflection.completeness (spot-footprint fraction) was only ever set to its 1.0f default and never read anywhere - not in scaling/merge, CBOR, HDF5, or the viewer. The box-sum integrator already rejects any partial-footprint reflection outright (n_inner_valid == n_inner), so the intended "down-weight clipped spots" role is already covered more strictly. Removed to reduce confusion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
818ec8fcbf |
rugnux: add detector-plane modulation (flat-field) correction surface
Rotation scaling gained a third cross-validated correction surface alongside decay and absorption: a smooth multiplicative factor over the predicted detector position (px, py) where each full lands. It absorbs detector-response and geometric flat-field systematics that vary across the detector plane and inflate R-meas; symmetry-equivalents of one reflection land at different detector positions as the crystal rotates, over-determining the surface. Because it lives in the detector frame (not the rotation), the same correction concept applies to stills. The surface fit/cross-validate/apply machinery shared with the absorption surface is factored into ApplyCellSurface; RefineAbsorption and the new RefineModulation just build their per-full cell assignment (goniometer-frame vs detector-frame) and call it. The cross-validation now scores a sigma-INDEPENDENT, R-meas-like fractional agreement of the held-out equivalents rather than a studentized chi^2, so a surface can no longer pass CV by reshaping sigma without tightening the intensities - this removes an over-fit regression on mis-indexed data and hardens the absorption surface too. On the /data/rotation_test rotation battery (A/B, modulation off vs on): 17 of 22 processed crystals improve R-meas, 0 regress, e.g. lysoC 23.2->16.3%, lyso_2 47.4->28.5%, EcwtAL500 53.2->27.6%, EP_cs_01-17 CC1/2 60->93%; CC1/2 held or improved everywhere and the anomalous S signal preserved/improved (lyso_ref SD_MET 4.37->4.46, lysoC 3.07->3.25). On by default with the other surfaces. Also: --dump-observations now writes the per-full centroid frame and predicted px/py columns, so the dumped combined fulls are a complete unmerged export. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
eae7ea90dc |
rugnux: always write both MTZ and CIF; drop --scaling-output
The merged reflections were written in one of mtz/cif/txt selected by --scaling-output. Write both an MTZ and an mmCIF unconditionally instead - each has its uses downstream (MTZ for the CCP4/phenix tools, mmCIF for deposition) - and remove the format selector, the plain-text .hkl writer, and the now-unused IntensityFormat enum / ScalingSettings::FileFormat plumbing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ce12d69751 |
rugnux: write standard CCP4 anomalous MTZ (IMEAN + I(+)/I(-))
In anomalous mode the merge keeps the two Friedel mates as separate rows, which WriteMtzReflections emitted verbatim - two IMEAN rows per Bijvoet pair that downstream tools had to re-collapse. Pair the mates into one row per reflection with the standard CCP4 anomalous layout: IMEAN + I(+)/I(-) and the matching F/F(+)/F(-) amplitudes. A single HKLKeyGenerator(merge_friedel=false) yields both the shared ASU group key and which mate a row is (.plus); IMEAN/F are the inverse-variance Friedel mean, centrics/unpaired keep one hand with the other missing. aimless/mtz2sca/ANODE now read the file directly. The non-anomalous MTZ path is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ce15900471 |
changelog: add the 1.0.0-rc.160 section
Build Packages / build:viewer-tgz:cpu (push) Successful in 7m50s
Build Packages / build:viewer-tgz:cuda (push) Successful in 9m25s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 12m18s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 12m12s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 12m24s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 12m32s
Build Packages / build:windows:nocuda (push) Successful in 16m14s
Build Packages / build:windows:cuda (push) Successful in 17m46s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 11m38s
Build Packages / build:rpm (ubuntu2404) (push) Failing after 6m54s
Build Packages / build:rpm (ubuntu2204) (push) Failing after 7m9s
Build Packages / build:rpm (rocky8) (push) Failing after 7m19s
Build Packages / build:rpm (rocky9_sls9) (push) Failing after 10m17s
Build Packages / build:rpm (rocky9) (push) Failing after 7m25s
Build Packages / Generate python client (push) Successful in 17s
Build Packages / Create release (push) Skipped
Build Packages / Build documentation (push) Successful in 48s
Build Packages / XDS test (neggia plugin) (push) Successful in 6m57s
Build Packages / XDS test (durin plugin) (push) Successful in 7m34s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 7m30s
Build Packages / DIALS test (push) Successful in 10m14s
Build Packages / Unit tests (push) Successful in 56m43s
VERSION was bumped to rc.160 but the new items sat under the rc.159 heading, which released (origin/main) does not contain. Add a proper rc.160 section, move the three misfiled items (supercell fix, -S symbol, FFTW-on-GPU error) into it, and document the rest of the branch (stills geometry refinement, -r flex, stills partiality, r=6 box default, single-pass self-referenced scaling, reference-MTZ-never-a-scale-anchor, E^2 second-lattice cap, --scale on _process.h5 fix, new spot options, CBF/TIFF writer removal, negative total_flux, viewer, packaging). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0e4f91b219 |
rugnux: validate offline spot-finding settings and bound numeric options
The offline rugnux path never ran CheckDataProcessingSettings (only the online broker/receiver paths did), so the new --min-pix-per-spot and --spot-low-resolution knobs were unbounded: --min-pix-per-spot 0 silently disabled the per-spot filter, and a low-resolution limit finer than the high-resolution limit made spot finding reject every pixel with no diagnostic. Add a parse_number_arg<T> helper (integral or floating, with optional inclusive bounds) that rejects non-numeric input, trailing garbage, and out-of-range values, replacing the raw atoi/atof in the spot-finding options; parse_double_arg/parse_float_arg become thin wrappers over it. After assembling the settings, validate them with the same CheckDataProcessingSettings the online receivers use, which also enforces the cross-field low>=high-resolution constraint. Also scrubs a residual sample-name comment (the r=6 box-default note) in this file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
8fea7f195a |
docs: stop advertising the removed CBF/TIFF writers
The CBF/TIFF writer removal left two docs describing them as available output: JFJOCH_WRITER.md's "Other formats" section and an aside in HDF5.md. A user following them would issue a now-rejected request. State that only NXmx HDF5 is written and that the CBF/TIFF enum values are retained for wire back-compatibility but rejected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
d393ec814c |
Remove residual sample identities missed by the earlier scrub
The rc.160 sample-identity cleanup left protein names, internal dataset codes, and a measured cell angle in several source comments and one test. Most sensitive: a measured monoclinic beta tied to an internal code in Rugnux.cpp and IndexAndRefine.cpp. Rewrite each to describe the crystallographic situation only (space group / metric relation), keeping the technical reasoning intact. Comment- and string-only; no behaviour change. (rugnux_cli.cpp's remaining name is scrubbed in the following commit, alongside its other change to that file.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
68272a96e1 | VERSION: 1.0.0-rc.160 | ||
|
|
b450140816 |
rugnux: warn when the cell/space group has an indexing ambiguity
Build Packages / build:viewer-tgz:cpu (push) Successful in 6m38s
Build Packages / build:viewer-tgz:cuda (push) Successful in 8m2s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 13m26s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 13m59s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 13m58s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m20s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 14m25s
Build Packages / build:windows:nocuda (push) Successful in 16m39s
Build Packages / build:windows:cuda (push) Successful in 18m37s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m50s
Build Packages / build:rpm (rocky8) (push) Successful in 12m18s
Build Packages / XDS test (durin plugin) (push) Successful in 7m45s
Build Packages / Generate python client (push) Successful in 28s
Build Packages / Build documentation (push) Successful in 1m7s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky9) (push) Successful in 12m46s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 12m28s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 12m52s
Build Packages / DIALS test (push) Successful in 13m40s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m45s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m53s
Build Packages / Unit tests (push) Successful in 1h13m13s
At the twinning-analysis print, detect alternative-indexing (twin-law) operators for the final cell + space group via ReindexAmbiguityOperators(). When any exist and no reference was supplied, emit a Warning (also written into the merge stats) that serial-stills crystals are indexed in a random hand and the ambiguity can only be broken against a reference (-z / --model). For an obvious merohedral case (P3/P4/P6...) users expect this, but a PSEUDO-merohedral metric is easy to miss -- e.g. NmHR / 7O8F, where C2 beta=131.8 is pseudo-F-orthorhombic and gemmi reports the reindex operator h-l,-k,-l. Advisory only; no change to processing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3e3545770f |
indexing: check candidate angles against the reference cell in Refine()
When a reference cell is supplied, PostIndexingRefinement::Refine() vetted candidates on sorted edge lengths only; a cell with the right edges but a wrong angle (a pseudo-symmetric near-metric, e.g. a monoclinic beta refined to the wrong value) passed. Add a sorted-angle check against the reference, folding each angle to its acute complement min(x,180-x) so the obtuse/acute setting choice is irrelevant (tolerance 10 deg). Reference-cell path only (no effect de novo, where reference_unit_cell is null): the /data/rotation_test battery is byte-identical (22/25), and NmHR with the correct C2 cell is unchanged (9.1%, beta~131.7). A latent guard against wrong-angle cells slipping through on -C / reference-MTZ runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0f07b7a499 |
rotation: widen candidate-refine angle bound to [30,150] deg
RotationIndexer's per-candidate XtalOptimizer relied on the struct-default monoclinic angle bound [60,120], the same latent clamp fixed for the stills path: a rotation crystal with beta>120 (or <60) would have its cell clamped to the boundary. Set [30,150] explicitly, matching the indexers. No change on the /data/rotation_test battery (22/25 SG match, byte-identical) -- no battery crystal has beta outside [60,120]; this is a latent-correctness fix for future high-beta monoclinic rotation data. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |