diff --git a/docs/rugnux_resolution_cutoff_design.md b/docs/rugnux_resolution_cutoff_design.md new file mode 100644 index 00000000..e3396305 --- /dev/null +++ b/docs/rugnux_resolution_cutoff_design.md @@ -0,0 +1,88 @@ + + +# Rugnux automatic resolution cutoff — design + experiments (WIP) + +Design notes and experiments for an automatic high-resolution cutoff in rugnux scaling/merging +(2026-07-09). **Not implemented** — this is the design to build from. Prototype and experiment data +live in the working scratchpad (`rescut_logistic.py`, `rescut.py`, run logs). + +## Problem + +Rugnux merges, reports, and writes to the **most optimistic resolution**. There is no +resolution-quality cutoff in the merge — the only limit is a manual `--scaling-high-resolution` +(default off). The reported high-res edge is set by the prediction/integration d_min, which is +**hardcoded to 1.0 Å** (`common/BraggIntegrationSettings.h`, no CLI setter), bounded only by the +detector edge. So a crystal that diffracts to 4 Å still gets shells and an MTZ/CIF written to +~1–1.5 Å, full of noise. (XDS's `CORRECT.LP` has the same raw behaviour; the difference is that XDS +users apply a cutoff downstream.) + +## Design brief + +- Do **not** try to answer "what *is* the resolution" (the ccp4bb debate). Pick a cutoff from the + **CC1/2 fall-off**, fit as a **logistic** curve (DIALS-style). +- Be **generous, not pessimistic** (XDS-style cuts are too tight): keep **one resolution shell too + far**, so maximum-likelihood refinement has a little weak data to work with. +- Leave the error model alone. + +## Why CC1/2, not ⟨I/σ⟩ + +⟨I/σ⟩ ≥ 1 is a fine limit *when the error model is healthy*, and on the test crystals it reproduces +XDS's chosen resolution well (lysoC 1.41 vs 1.43, Ins_I 1.46 vs 1.30, EP_cs_02-10 1.29 vs 1.31). But +it fails exactly where a cutoff is most needed: on **Benas** the weak-low-res error model blows up so +⟨I/σ⟩ < 1 in **every** shell — an I/σ rule would cut to nothing. **CC1/2 still shows the physical +fall-off** there (→ ~4–4.3 Å). CC1/2 is what the brief calls for and is robust to the (untouched) +error model. + +## Method + +1. Compute CC1/2 per shell vs `s = 1/d²`. Use **finer bins** than the 10 reported shells for a stable + fit (~20–30). +2. Fit a logistic `CC½(s) = 1 / (1 + exp(k·(s − s0)))` (low-res plateau ~1, high-res → 0; `s0` = + inflection, `k` = steepness). Bounded least-squares (`k > 0`), restricted to the + contiguous-from-low-res region so a high-res upward blip can't distort it. (Ceres is already linked; + the scratchpad prototype uses a grid+refine fit with no extra dependency.) +3. Fall-off limit = `s` where fitted CC½ = **cc_target (default 0.30)**: + `s_cut = s0 + ln(1/cc_target − 1) / k`. +4. **"One shell too far":** extend by one mean shell width in `s`: `s_final = s_cut + Δs`, + `d_final = 1/√s_final`. Clamp to `[d of highest-res reflection, d of lowest-res shell]`. + +## Experiment (logistic + 1 shell, on real rugnux merge tables) + +| crystal | XDS real res | ⟨I/σ⟩≥1 | **logistic + 1 shell** | +|----------------|--------------|---------|------------------------| +| EcwtCQ244-E3 | ~1.9 | 1.68 | 1.49 | +| EcwtCQ244-B1-1 | ~2.4 | 2.36 | 2.10 | +| cytC_2 | – | 1.88 | 1.63 | +| lysoC_14 | 1.43 | 1.41 | 1.23 | +| Ins_I_2 | 1.30 | 1.46 | 1.38 | +| EP_cs_02-10 | 1.31 | 1.29 | 1.29 | +| **Benas_3** | ~4.5 | none (I/σ<1 all) | **4.30** | +| **Benas_7** | ~4.1 | none | **4.10** | + +The logistic sits ~0.1–0.2 Å beyond the strict I/σ limit (the intended generosity), lands at the real +limit on the 4 Å Benas where I/σ can't, and never runs to 1.5 Å on a low-res crystal. + +## Implementation plan (in `image_analysis/scale_merge/`) + +- After the final merge computes per-shell CC1/2 (`MergeStatistics`), run the logistic fit + "one + shell" extension → `d_cut`. Apply `d_cut` as the high-resolution limit for the **written** MTZ/CIF + and the **reported** shell table (filter merged reflections by `d`). Do **not** trim the per-image + `_process.h5` (keep full data for re-scaling). +- CLI: `--resolution-cutoff cc-logistic|off` (default **cc-logistic**), `--resolution-cc-target ` + (default 0.30). `--scaling-high-resolution ` stays the explicit manual override (wins). +- Report the chosen limit + method, e.g. `Auto resolution cutoff: 4.3 A (CC1/2 logistic fall-off, + +1 shell; override with --scaling-high-resolution)`. + +## Robustness / edge cases + +- Degenerate fit (too few shells, flat or non-monotone CC1/2): fall back to the full range + warn. +- Enforce a low-res floor (never cut into good low-res data). +- The fit uses the merge CC1/2 consistently; a v2 could iterate (cut, re-fit), but one pass is enough. + +## Related + +- The broken weak-data error model that makes ⟨I/σ⟩ useless on Benas is also why Benas's cubic point + group (F432) is under-determined — see `docs/rugnux_denovo_symmetry_investigation.md`.