indexing: an axis harmonic is a small multiple, and the Wilson B is XDS's

Two unrelated notes, one on each side of what the merge reports.

The axis-harmonic precondition accepted any near-integer volume ratio. Its window
is absolute in a unit integer spacing, so a ratio between two UNRELATED lattices
passes it about a third of the time whatever the multiple is, and at 124x the
pair is not a cell and its harmonic in any sense. That branch was harmless while
the rule was "always take the smaller cell"; deciding the pair on the evidence
makes it reachable, so it is now bounded at 4.

Measured over the corpus: of 44 firings, all 36 at n <= 4 read 1.1 to 51.3
points BELOW the chance occupancy (n-1)/n, so an index-n sub-lattice really
exists in each of them; all 8 above it - two datasets, both decided by 8 to 34
sigma, so not a margin problem - read within 7.8 points OF chance, so none does.
The largest n at which a real sub-lattice was ever seen is 3, and no dataset
moves either way.

Separately, WILSON_B and _reflns.B_iso_Wilson_estimate are fitted to log<I>
directly, which is what XDS's Wilson line does and is not what TRUNCATE,
ctruncate or phenix.xtriage do - they divide out Sigma = sum f^2(s) first.
Leaving Sigma in the slope inflates B by 2-8 A^2 on our own merges, and against
those two programs on the same files this number runs 10-36 A^2 high, in the
same direction every time. The docs said "the analogue of XDS's Wilson-line B",
which is accurate but easy to read past; they now say the two conventions are
not comparable and which one this is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-02 13:03:25 +02:00
co-authored by Claude Opus 5
parent 476a849c0e
commit 5cc2f811b3
2 changed files with 11 additions and 2 deletions
+2 -1
View File
@@ -394,4 +394,5 @@ $
$
A linear regression of $\log\langle I\rangle$ vs $1/d^2$ provides an estimate of $B$, subject to basic quality checks (e.g. $R^2$ threshold).
A **dataset-wide** Wilson $B$ is also estimated over the merged reflections — restricted to the meaningful resolution range (skipping the low-resolution non-linear region below ~4 Å and shells past the signal limit $\langle I/\sigma\rangle < 1$, so it is insensitive to how far the merged data extend) — and written to the merged mmCIF as `_reflns.B_iso_Wilson_estimate` (and reported as `WILSON_B=`), the analogue of XDS's Wilson-line $B$. It is diagnostic only and is not fed back into scaling. It is also where the flux the fixed integration disk clips lands: that loss is degenerate with an overall $B$, so the reported number carries an $r_1$-dependent contribution and is not a property of the crystal alone (§9.1). The **per-image** estimate (used for the live radiation-damage plot) is accepted only when the fit is well-correlated and physically plausible ($0 < B < 200$ Ų); on a bad frame (an indexing glitch, too few reflections) the Wilson line runs wildly steep, so an implausible $B$ is reported as NaN rather than a spurious hundreds-of-Ų value.
A **dataset-wide** Wilson $B$ is also estimated over the merged reflections — restricted to the meaningful resolution range (skipping the low-resolution non-linear region below ~4 Å and shells past the signal limit $\langle I/\sigma\rangle < 1$, so it is insensitive to how far the merged data extend) — and written to the merged mmCIF as `_reflns.B_iso_Wilson_estimate` (and reported as `WILSON_B=`), the analogue of XDS's Wilson-line $B$. It is diagnostic only and is not fed back into scaling.
**It is the XDS convention, not the CCP4/Phenix one, and the two are not comparable.** The slope here is fitted to $\log\langle I\rangle$ directly; TRUNCATE, `ctruncate` and `phenix.xtriage` fit $\log(\langle I\rangle/\Sigma)$, dividing out $\Sigma=\sum_j f_j^2(s)$, the falloff of the atomic form factors for an assumed composition. Leaving $\Sigma$ in the slope inflates $B$ by roughly 2 to 8 Ų (measured across in-house merges; the arithmetic gives +6.9 Ų over 4.0-1.5 Å for a generic protein), and the fitted range accounts for more still: against `ctruncate` and `phenix.xtriage` on the same merged files this number runs 10 to 36 Ų high, in the same direction every time - though those two disagree with each other by 7 to 16 Ų, so there is a band rather than a right answer. Read it as a relative quantity, comparable between rugnux runs and against XDS, and do not compare it with a value quoted from a CCP4 or Phenix log. The same caveat applies to `_reflns.B_iso_Wilson_estimate` in the merged mmCIF, whose deposited values are conventionally the $\Sigma$-normalised kind. It is also where the flux the fixed integration disk clips lands: that loss is degenerate with an overall $B$, so the reported number carries an $r_1$-dependent contribution and is not a property of the crystal alone (§9.1). The **per-image** estimate (used for the live radiation-damage plot) is accepted only when the fit is well-correlated and physically plausible ($0 < B < 200$ Ų); on a bad frame (an indexing glitch, too few reflections) the Wilson line runs wildly steep, so an implausible $B$ is reported as NaN rather than a spurious hundreds-of-Ų value.
+9 -1
View File
@@ -2474,7 +2474,15 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
const bool tied = static_cast<float>(score) >= bp.score * 0.9f - 0.5f;
const double ratio = (vol < bp.vol) ? bp.vol / vol : vol / bp.vol;
const double nearest = std::round(ratio);
const bool integer_multiple = nearest >= 2.0 && std::abs(ratio - nearest) < 0.15;
// Bounded at 4. The window is absolute in a unit integer spacing, so a ratio
// between two UNRELATED lattices passes it about a third of the time whatever n
// is, and at n = 124 the pair is not a cell and its harmonic at all. Measured
// over the corpus: all 36 firings at n <= 4 read 1.1 to 51.3 points BELOW the
// chance occupancy (n-1)/n, so an index-n sub-lattice really exists in each; all
// 8 above it read within 7.8 points OF chance, so none does. The largest n at
// which a real sub-lattice was ever seen is 3.
const bool integer_multiple = nearest >= 2.0 && nearest <= 4.0
&& std::abs(ratio - nearest) < 0.15;
integer_subcell = tied && !clearly_more && integer_multiple && vol < bp.vol;
// Ask the data which of the pair is right instead of assuming the larger is