The pre-scan's spot-shape estimate (spot_width::EstimateBandwidth) becomes the run's bandwidth where it
is significant (z > 3) and neither the file nor --bandwidth states one; an explicit value, including
--bandwidth 0, still wins. Everything that already reads GetBandwidthFWHM() - prediction, partiality,
the profile's radial width, the stencil growth, scaling - takes it from there. Below z 3 the bandwidth
stays unset, so monochromatic runs are bit-identical (12 mono sets checked on p.hkl md5).
The width tiers stop as soon as r80 settles, often at 15 frames and a few hundred spots, which is
too few for the slope. The pre-scan now keeps measuring spot shapes for the bandwidth alone until the
pool holds 1000 spots or the sample ends. Those extra frames feed neither r80 nor the powder
measurement, so r1..r3, powder and the spot-resolution quantiles are unchanged. Over the 68-set
battery plus the known mono controls (47 sets re-probed) no monochromatic set crosses z 3 (highest
lyso_x06da_5keV 2.0, insu 5-6 keV 1.7); the one new positive is 9z44 (ALS 8.2.1, multilayer
beamline, 0.38% at z 3.1 from 382 spots instead of z 1.0 from 94).
Two hidden b > 0 mode switches go, so the bandwidth acts continuously:
- the background clip default dropped from 4 to 3 sigma when a bandwidth was set. With the measured
bandwidth, clip 3 vs 4 on the MicroMAX pink sets: REFRES R_meas 0.0507/0.0507 (lyso),
0.0842/0.0844 (thau), REFRES ISa 28.83/28.77, 14.12/14.12 - no bandwidth-specific effect.
- the "--integration-stencil has no effect without --bandwidth" message, which is decided before the
pre-scan and would now be wrong.
The one set where clip 3 helps (9z44, 7 A: R_meas 0.252 -> 0.201) gains the same at zero bandwidth, so
that is a property of the clip, not of the beam.
Effect of the measured bandwidth (stencil 0, clip 4), against hq-integ 9b6736dbd at the same
resolution range (REFRES; open sets rerun with --report-resolution at the base d_min): R_meas up on
all six consumers - MicroMAX pink lyso 0.0499 -> 0.0507, thau 0.0824 -> 0.0844, 9q41 0.250 -> 0.265,
8u0i 0.0796 -> 0.0816, 9sl0 0.0758 -> 0.0773, 9z44 0.252 -> 0.255; CC1/2 unchanged to 1e-4; CC_MODEL
overall +0.001/+0.0004/+0.0005/-0.0025 (9q41/8u0i/9sl0/9z44); no space-group change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1G8gJVAy6gp1K5Dz3NE5C
4.3 KiB
What Rugnux does
The map of a run, in the order it happens — one paragraph per stage, each linking into the data-analysis reference where the algorithm lives. The walk-through is a rotation run with the defaults; stills differences are at the end.
Open the dataset. The geometry, wavelength and goniometer come from the file (What Rugnux reads). A goniometer axis makes it a rotation run, none makes it serial stills — nothing is asked of the user.
Pre-scan. A projection of the first frames (60 by default) finds the beam-stop shadow and masks it (§1.5), masks pixels its frames show to be defective (§1.6), measures the beam centre from the isotropy of the scattered background and compares it with the file's (§1.4), and reads how wide this crystal's spots are, which sets the integration radius (§9.5), and how much longer they are along their radius than across it, which is the beam's bandwidth where the file does not state one (§9.6).
Spots. Every image is decoded — on the GPU straight from the compressed chunk (§0) — and one fused pass computes the azimuthal profile and finds the spots against each image's own per-resolution-ring noise (§2–§3). The ice-ring score is read off the same profile.
Indexing. The spots of a sample of frames are rotated back to a common crystal frame and the FFT search looks for periodicity over thousands of directions; candidate cells are Niggli-reduced, classified by Bravais lattice, refined both constrained and triclinic, and decided on how many validation frames each actually indexes (§4–§7). A failed pass triggers the discrete rescues — the rotation-axis sign, the beam-centre search — before anything is given up on.
First integration pass. At the geometry in the file, every frame is predicted (§8) and profile-fit integrated (§9); partials are combined into fulls, scaled and merged (§10).
Geometry post-refinement. From those reflections the detector distance, beam centre and the cell scale / rotation axis are refined over all frames at once, each step committed only if it improves a held-out residual (§7.5).
Second pass. The sweep is re-indexed de novo and re-integrated at the refined geometry; this
pass is the canonical output, and a guard compares the two passes and keeps the better one
(reported as PASS= / PASS_DECISION= in the report).
Space group. On the P1 merge of the final pass, the point group is scored operator by operator
on resolution-normalised intensities and the screw axes, glide planes and centring are read from the
systematic absences (§13.1); twinning and translational
pseudo-symmetry are checked beside it (§13.2). CANNOT_DETERMINE and an enantiomorphic pair are real
answers here, not evasions.
Scale and merge. In the determined group: per-frame scales, the cross-validated correction surfaces (decay, absorption, modulation), the error model and ISa, outlier rejection, the CC1/2-based resolution cut, the anisotropy description, French–Wilson amplitudes and the R-free flags (§10, §13.3–§13.5).
Write. The merged .mtz / .cif / .hkl, the unmerged MTZ, the P1 cross-check and the
results report land next to each other (Output files); with
--model, validation runs first and the maps and the placed model are written too
(§14).
Stills instead. Serial data skip the two-pass machinery: each image is indexed independently
(with the known-cell ffbidx indexer where a cell is given), partiality comes from a per-crystal
orientation-tilt post-refinement rather than a rocking curve, and a merohedral indexing ambiguity
has to be broken per image, at integration time, against a reference or a model
(Advanced ▸ the indexing ambiguity).