Metadata-Version: 2.5
Name: facet-nmr
Version: 0.4.0
Summary: Retrieval-augmented prediction of backbone torsion angles, secondary structure, and Ramachandran basin populations from NMR chemical shifts
Project-URL: Homepage, https://github.com/maxzinke/facet-nmr
Project-URL: Documentation, https://github.com/maxzinke/facet-nmr/tree/main/docs
Project-URL: Repository, https://github.com/maxzinke/facet-nmr
Project-URL: Issues, https://github.com/maxzinke/facet-nmr/issues
Project-URL: Changelog, https://github.com/maxzinke/facet-nmr/blob/main/CHANGELOG.md
Project-URL: Web app, https://huggingface.co/spaces/SiXa18/facet
Project-URL: Model weights, https://huggingface.co/SiXa18/facet-weights
Author-email: Maximilian Zinke <maximilian.zinke@gmail.com>
License-Expression: MIT
License-File: LICENSE
License-File: LICENSE-WEIGHTS
Keywords: NMR,Ramachandran,backbone,chemical shifts,dihedral restraints,protein,secondary structure,torsion angles
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Classifier: Topic :: Scientific/Engineering :: Chemistry
Requires-Python: >=3.10
Requires-Dist: numpy>=1.24
Requires-Dist: torch>=2.0
Provides-Extra: all
Requires-Dist: httpx>=0.24; extra == 'all'
Requires-Dist: matplotlib>=3.6; extra == 'all'
Requires-Dist: onnxruntime>=1.16; extra == 'all'
Provides-Extra: bmrb
Requires-Dist: httpx>=0.24; extra == 'bmrb'
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: httpx>=0.24; extra == 'dev'
Requires-Dist: matplotlib>=3.6; extra == 'dev'
Requires-Dist: onnxruntime>=1.16; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Provides-Extra: onnx
Requires-Dist: onnxruntime>=1.16; extra == 'onnx'
Provides-Extra: plot
Requires-Dist: matplotlib>=3.6; extra == 'plot'
Description-Content-Type: text/markdown

# FACET — backbone torsion angles from NMR chemical shifts

[![CI](https://github.com/maxzinke/facet-nmr/actions/workflows/ci.yml/badge.svg)](https://github.com/maxzinke/facet-nmr/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/facet-nmr.svg)](https://pypi.org/project/facet-nmr/)
[![Try it](https://img.shields.io/badge/HuggingFace-Space-yellow)](https://huggingface.co/spaces/SiXa18/facet)
<!-- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.XXXXXXX.svg)](https://doi.org/10.5281/zenodo.XXXXXXX) -->

**FACET** (Fold And Conformation Estimation Tool) predicts per-residue backbone
**φ/ψ torsion angles** with a confidence tier, **secondary structure**, **χ1
rotamers** and **Ramachandran basin populations** from backbone chemical shifts
(H, HA, N, CA, CB, C), and writes dihedral restraints for XPLOR-NIH / CNS / HADDOCK /
ARIA, CYANA and NEF.

A 1.29 M-parameter local-biased transformer embeds each residue from a pentapeptide
window of secondary shifts, masking whatever is unassigned; φ/ψ are inferred by
kNN + DBSCAN retrieval over a 250 K-residue reference index.

## Install and run

```bash
pip install facet-nmr
facet predict shifts.tab            # writes shifts_facet.tbl, .aco, .predtab
facet predict shifts.tab --all      # + .nef, .csv, .json
facet predict --bmrb 4493           # straight from a BMRB entry
facet predict shifts.tab --include-medium   # broaden restraints to the Medium tier
```

Inputs: NMRPipe/TALOS `.tab`, CSV (`ResID, AA, H, HA, N, CA, CB, C`), NEF, NMR-STAR —
auto-detected. Weights and the retrieval index (~155 MB) are downloaded on first use
into `~/.facet` (override with `$FACET_HOME`; `python -m facet.assets` pre-fetches;
`FACET_NO_DOWNLOAD=1` never fetches). Every file is verified against a pinned SHA-256.

```python
from facet import predict

result = predict("shifts.tab")
for r in result.residues:
    print(r.seq_id, r.comp_id, f"{r.phi:.0f} {r.psi:.0f}", r.ss, r.confidence_class)
result.to_tbl("restraints.tbl")     # High tier only, by default
result.to_nef("restraints.nef")
print(result.summary())
```

Or use the web app: <https://huggingface.co/spaces/SiXa18/facet>.

## Accuracy

Leak-safe benchmark of 745 proteins held out by sequence-similarity clustering,
scored against the deposited structures with a validated correction to the ground
truth of the 63 X-ray entries. FACET emits a prediction for every one of the 55,032
scored residues; TALOS-N for 98.6 %; figures are over the **54,260 residues both
methods predict** (724 proteins). Differences are FACET − TALOS-N with a 95 %
protein-level paired-bootstrap interval.

| | TALOS-N | **FACET** | difference |
|---|---|---|---|
| Median φ/ψ error | 11.58° | **10.91°** | −0.67° [−0.78, −0.56] |
| Residues with error > 25° | 20.3 % | **18.0 %** | −2.4 pt [−2.7, −2.0] |
| Mean | 21.3° | **19.9°** | |
| p90 | 46.1° | **40.3°** | |
| Helix median (n = 24,157) | 7.4° | **7.0°** | −0.35° [−0.44, −0.26] |
| Strand median (n = 11,882) | 13.6° | **12.8°** | −0.81° [−0.99, −0.56] |
| Coil median (n = 18,221) | 19.3° | **18.0°** | −1.30° [−1.57, −0.98] |
| Head-to-head win rate | 46.2 % | **53.8 %** | [53.3, 54.4] |

Error is `sqrt((Δφ² + Δψ²) / 2)`, each difference wrapped to [0°, 180°], against the
circular-mean angles over the models of the deposited structure. The table **is** what
the released package produces: it was generated by running `facet.predict()` with
default settings on the 745 published input files, and every number regenerates from
the published per-residue table with `python benchmarks/rescore.py --bootstrap`.

**Confidence tiers** (same benchmark):

| Tier | Share | Median error | > 25° | Written to restraints |
|---|---|---|---|---|
| **High** | 76.3 % | 9.3° | 9.9 % | yes (±20°) |
| **Medium** | 15.8 % | 19.9° | 40.2 % | with `--include-medium` (±35°) |
| **Low** | 3.2 % | 59.8° | 67.8 % | no — multi-modal |
| **Flexible** | 4.6 % | 20.8° | 43.0 % | no — no coherent cluster |

*Flexible* is not a failure state: it flags residues whose retrieved neighbours do
not agree, which is what conformational averaging looks like in shift space. Its
median error sits between Medium and Low, but without a coherent cluster the error
bar is not trustworthy, so those residues stay out of restraint files; the basin
populations still describe them.

**Missing shifts.** 75 of the benchmark proteins have no HA shifts at all
(perdeuterated-style assignments); on their 7,824 residues FACET scores 9.6° median
against TALOS-N's 10.3°, so incomplete assignments are handled by the default path.
An opt-in shift-space fallback (`--mask-safe-fallback`) exists for inputs where the
encoder path fails; it was the default in 0.3.1 and measurably hurt on real data
(11.0° on the same residues, half the High-tier coverage) — see
[docs/BENCHMARKS.md](docs/BENCHMARKS.md) §6.

> **Provenance of these numbers.** While preparing this release we found that every
> single-model (X-ray) structure in the data pipeline had its φ/ψ converted to radians
> twice — including 11.7 % of the training targets and the benchmark truth of 63
> entries. For 0.4.0 the truth was repaired and validated against the PDB files
> (median deviation 0.02°), the model was retrained on the fixed pipeline, and the
> retrieval index and shift reference were rebuilt; the table above is measured with
> the released package on the corrected truth. The defect, the validation and the
> before/after numbers are in [docs/BENCHMARKS.md](docs/BENCHMARKS.md) §7.

## Where the details are

| Question | Read |
|---|---|
| How does it work? | [docs/METHOD.md](docs/METHOD.md) — featurisation, architecture, training, retrieval, tiers, the mask-aware fallback, the population readouts |
| What was it trained on? | [docs/DATA.md](docs/DATA.md) — BMRB + PDB sources, curation, labels, the homology split, exact sizes |
| How was it benchmarked? | [docs/BENCHMARKS.md](docs/BENCHMARKS.md) and [benchmarks/README.md](benchmarks/README.md) — a ten-minute read, then the files |
| When should I not trust it? | [docs/LIMITATIONS.md](docs/LIMITATIONS.md) |
| What ships and under which licence? | [DATA_PROVENANCE.md](DATA_PROVENANCE.md) |
| What changed between versions? | [CHANGELOG.md](CHANGELOG.md) |

## Things to know before you use it

- **Referencing.** FACET expects DSS-referenced shifts (the BMRB convention). A
  composition-adaptive per-nucleus check warns about systematic offsets and suggests
  a correction (`auto_reference=True` applies it). It is weakest on CA-only offsets;
  for publication-quality restraints run LACS first.
- **Perdeuterated samples.** Pass `--deuteration perdeut-exchanged` (or
  `perdeut-unexchanged`, `ilv-methyl`) to apply the analytical ¹³C isotope correction.
  Missing HA shifts are masked; `--mask-safe-fallback` switches on the shift-space
  fallback if the default path misbehaves on your sample.
- **Disordered proteins.** FACET reports three population readouts that answer
  different questions: *geometric* basin populations (`basin_populations`, share of
  retrieved neighbours per Ramachandran basin), *structural* populations
  (`structural_populations`, kernel-weighted posterior over the reference structures'
  helix/strand/loop states — over-estimates helix on
  pure IDPs), and the retrieval-free d2D-style engine (`facet.predict_ss_populations`,
  the one to report for IDPs and to compare with d2D / CheSPI). See
  [docs/METHOD.md](docs/METHOD.md) (§7–8) and
  [docs/LIMITATIONS.md](docs/LIMITATIONS.md).
- **Ensemble seeds.** The 25 retrieved neighbours per residue can be exported
  (`result.to_ensemble_csv()` / `to_ensemble_json()`) as seed conformers for BME or
  ENSEMBLE-style reweighting.

## Output formats

| Format | Used by |
|---|---|
| `.tbl` | XPLOR-NIH, CNS, HADDOCK, ARIA dihedral restraints |
| `.aco` | CYANA angle constraints |
| `.nef` | NEF 1.1 dihedral restraints (wwPDB, CCPN) |
| `.predtab` | per-residue summary (TALOS-style) |
| `.csv`, `.json` | everything, including basin populations, alternative clusters, RCI S², retrieved neighbours |

Every output records the package version and the retrieval-index version in its
header.

## Licence

Code: [MIT](LICENSE). Model weights, retrieval index, shift reference and fitted
parameters: [CC BY 4.0](LICENSE-WEIGHTS). They derive from the PDB and the BMRB, both CC0 —
please also cite BMRB: Hoch *et al.*, *Nucleic Acids Res.* **51**, D368 (2023),
[10.1093/nar/gkac1050](https://doi.org/10.1093/nar/gkac1050).

## Citation

```
Zinke, M. FACET: backbone torsion angle prediction from NMR chemical shifts.
Software, version 0.4.0 (2026). https://github.com/maxzinke/facet-nmr
```

A machine-readable citation is in [CITATION.cff](CITATION.cff) (GitHub's "Cite this
repository" button). The archived-release DOI and the preprint reference will be
added there on release.

FACET builds on published methods; if you use the corresponding outputs, cite them
too:

- **RCI S²** (`rci_s2`): Berjanskii & Wishart, *J. Biomol. NMR* 40, 31 (2008),
  [10.1007/s10858-007-9208-0](https://doi.org/10.1007/s10858-007-9208-0). Our
  implementation follows Eq. 2 and 3 without the per-subset weight optimisation,
  neighbour corrections, REFCOR or end-effect steps.
- **Random-coil shifts**: Wishart *et al.*, *J. Biomol. NMR* 5, 67 (1995);
  Schwarzinger *et al.*, *JACS* 123, 2970 (2001).
- **Referencing check**: adapted from LACS — Wang & Markley, *J. Biomol. NMR* 44, 95
  (2009).
- **d2D likelihood** (`predict_ss_populations`): Camilloni, De Simone, Vranken &
  Vendruscolo, *Biochemistry* 51, 2224 (2012) — same per-residue-type Gaussian
  likelihood, all parameters refit on our own data.
- **CheSPI** (recommended IDP cross-check): Nielsen & Mulder, *J. Biomol. NMR* 75,
  273 (2021), [10.1007/s10858-021-00374-w](https://doi.org/10.1007/s10858-021-00374-w).
- **Deuterium isotope corrections**: Venters *et al.*, *JACS* 118, 8985 (1996);
  Hansen, *Prog. NMR Spectrosc.* 20, 207 (1988).
- **TALOS-N** (benchmark reference): Shen & Bax, *J. Biomol. NMR* 56, 227 (2013).
