---
myst:
html_meta:
"description": "Fat Metatomic, RGPOT dlopen engine, and ASE MetatomicCalculator backends in eOn / pyeonclient — same science, different packaging paths."
"keywords": "eOn metatomic, RGPOT, ASE, PET-MAD, pyeonclient backends, benchmark"
---
# Metatomic backends (fat · RGPOT · ASE)
Three supported ways to evaluate the same Metatomic model (for example PET-MAD)
from eOn / [pyeonclient](project:pyeonclient.md). **All three stay supported.**
| Path | How | Torch linked into host? | Use when |
|------|-----|-------------------------|----------|
| **Fat / native** | `potential = Metatomic` / `make_backend("metatomic")` | Yes (`-Dwith_metatomic`) | Default, conda-forge, lowest overhead |
| **RGPOT / engine** | `potential = RGPOT`, `backend = metatomic` / `make_backend("rgpot_metatomic")` | No on thin hosts; torch lives in `libmetatomic_engine.so` | Optional plugin on base wheels |
| **ASE wrap** | `metatomic_ase.MetatomicCalculator` → `make_backend("ase")` or `make_backend("ase_metatomic")` | Python `metatomic-torch` only | Cookbook / ASE workflows; same energies |
rgpot is not on conda-forge, so the fat native path is the packaging default.
The RGPOT engine is the thin-host path. ASE is the Python calculator path.
## Benchmark (PET-MAD)
Single-point energy and force on a 14-atom PET-MAD geometry
(`pet-mad-s-v1.5.0.pt`, CPU). Fat and RGPOT-dlopen match to machine precision;
ASE agrees within ~10 µeV. Fat and RGPOT are ~10× faster per call than the ASE
wrap on this workload (the ASE path pays Python / neighbor-list setup each
evaluation).
Numbers and figure are **generated at docs build time** from the committed
JSON SSoT ({file}`../fig/data/metatomic_backend_bench.json`). Do not commit the
SVG/PNG; ``sphinx-build`` runs ``scripts/plot_metatomic_backend_bench.py``.
```{figure} ../fig/generated/metatomic_backend_bench.svg
:alt: Bar charts of single-point wall time and energy difference for fat, RGPOT dlopen, and ASE metatomic backends
:width: 100%
pyeonclient metatomic backends on PET-MAD s v1.5 (CPU, 14 atoms). Left: wall
time per force call. Right: energy relative to fat (µeV).
```
```{include} ../fig/generated/metatomic_backend_bench_table.md
```
Treat timings as workload-specific (host CPU, torch build, warm vs cold
neighbor lists). The plot is meant to show **order-of-magnitude packaging
cost**, not a universal ranking.
```{note}
Fat C++ Metatomic and the Python ``metatomic-torch`` extension both register
``TORCH_LIBRARY(metatomic)``. Loading both in one process aborts. Compare them
in separate processes (the compare script does this), or use a base
pyeonclient build (no ``-Dwith_metatomic``) when wrapping ASE calculators in
the same interpreter as ``make_potential_from_ase``.
```
## pyeonclient key → factory
```{code-block} python
import pyeonclient as pyec
from pyeonclient.backends import make_backend, list_backends
print(list_backends())
# ['ase', 'ase_metatomic', 'lj', 'metatomic', 'metatomic_dlopen', ...]
# Fat (needs -Dwith_metatomic=true)
pot = make_backend("metatomic", model_path="pet-mad.pt", device="cpu")
# RGPOT + dlopen engine
pot = make_backend(
"rgpot_metatomic",
model_path="pet-mad.pt",
engine_path="libmetatomic_engine.so",
device="cpu",
)
# ASE MetatomicCalculator (PET-MAD-safe load)
pot = make_backend("ase_metatomic", model_path="pet-mad.pt", device="cpu")
# equivalent:
# from pyeonclient.backends import make_metatomic_ase_calculator
# pot = make_backend("ase", calculator=make_metatomic_ase_calculator("pet-mad.pt"))
m = pyec.Matter(pot, pyec.Parameters())
```
`make_backend("ase_metatomic", …)` applies a small load compatibility shim for
exported models (including PET-MAD) where upstream
`load_atomistic_model` re-wraps a scripted `AtomisticModel` and misses
`_model_capabilities_outputs_names`. Prefer that helper over raw
`MetatomicCalculator(load_atomistic_model(path))` on PET-MAD.
## Reproducible bench (pixi)
One-shot path from a clean tree (builds fat + ASE-safe pyeonclient, runs the
three-way compare, writes the JSON SSoT and regenerates the figure):
```{code-block} bash
# PET-MAD + d016_pos.con live under subprojects/gpr_optim/bench_data/petmad/
# (rsynced from ../gpr_optim when missing). Override with EON_PET_MAD_*.
pixi run -e mta-bench mta-backend-bench
```
| Task | What it does |
|------|----------------|
| `mta-backend-bench` | meson fat + ASE cores, compare, write JSON + SVG |
| `mta-backend-bench-skip-build` | re-run compare/plot only (`EON_MTA_BENCH_SKIP_BUILD=1`) |
| `mta-backend-plot` | plot from committed JSON only (no C++ build) |
Build trees: `bbdir-mta-bench/` (fat Metatomic + RGPOT engine) and
`bbdir-mta-bench-ase/` (pyeonclient without C++ metatomic so ASE
`metatomic-torch` does not double-register `TORCH_LIBRARY(metatomic)`).
```{code-block} bash
# optional overrides
export EON_PET_MAD_MODEL=/path/to/pet-mad-s-v1.5.0.pt
export EON_PET_MAD_POS=/path/to/pos.con
export RGPOT_METATOMIC_ENGINE=/path/to/libmetatomic_engine.so
pixi run -e mta-bench mta-backend-bench-skip-build
# figure alone (docs build also regenerates from JSON)
pixi run -e mta-bench mta-backend-plot
# or: sphinx-build docs/source docs/build/html
```
Driver: {file}`scripts/run_metatomic_backend_bench.sh`. Compare:
{file}`scripts/compare_metatomic_backends.py`. Plot:
{file}`scripts/plot_metatomic_backend_bench.py`.
## INI configuration
### Fat / native
```{code-block} ini
[Potential]
potential = Metatomic
[Metatomic]
model_path = /path/to/model.pt
device = cpu
```
Full Metatomic keys (variants, non-conservative forces, determinism) are in
[Metatomic Interface](project:metatomic_pot.md).
### RGPOT / engine
```{code-block} ini
[Potential]
potential = RGPOT
[RgpotPot]
backend = metatomic
engine_path = /path/to/libmetatomic_engine.so
model_path = /path/to/model.pt
device = cpu
```
Or set `RGPOT_METATOMIC_ENGINE` / `METATOMIC_ENGINE` and put the model under
`[Metatomic] model_path` (also accepted when backend is metatomic).
## Build
```{code-block} bash
# Fat host: native Metatomic pot + engine for optional RGPOT consumers
meson setup build-mta -Dwith_metatomic=true -Dwith_rgpot=true
meson compile -C build-mta
# -> client/libmetatomic_pot.so (potential = Metatomic)
# -> client/libmetatomic_engine.so (RGPOT backend=metatomic)
# Thin host: RGPOT only; no torch at link time
meson setup build-thin -Dwith_metatomic=false -Dwith_rgpot=true
meson compile -C build-thin
# engine still comes from a fat build or a packager that ships libmetatomic_engine.so
```
Do **not** remove native Metatomic from eOn: it is the conda-forge path.
## Runtime notes
- Prefer **build-tree pot plugins** over any env that also installs
`librgpot_pot.so` (e.g. cookbook `.nox/.../lib`). Putting a fat install prefix
first on `LD_LIBRARY_PATH` can interpose a pre-metatomic `librgpot_pot` and
yield `unknown backend 'metatomic'`.
- The engine needs torch **when dlopened** (`LD_LIBRARY_PATH` / rpath of the
engine and its metatensor stack). Thin host DT_NEEDED stays free of torch.
- The engine wraps eOn `MetatomicPotential` through the C ABI
(`rgpot_mta_create` / `rgpot_mta_force`); energies match fat
`potential = Metatomic` on the same model (verified on PET-MAD; see figure).