diff --git a/docs/CPU_DATA_ANALYSIS_INTEGRATION.md b/docs/CPU_DATA_ANALYSIS_INTEGRATION.md index ea15fa94b..50fe3593c 100644 --- a/docs/CPU_DATA_ANALYSIS_INTEGRATION.md +++ b/docs/CPU_DATA_ANALYSIS_INTEGRATION.md @@ -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. diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index 9d5348647..98f8fe0c1 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -2474,7 +2474,15 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b const bool tied = static_cast(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