ML Interatomic Potentials (via fmlip-relay)
Using CREST with ML interatomic potentials via the fmlip-relay Python server.
Table of contents
- The fmlip-relay Server
- Installation
- Example 1: Geometry Optimization with Lennard-Jones
- Example 2: MACE-OFF for Organic Molecules
- Example 3: FairChem UMA
- Parallelization and CPU Threads
- Command-Line Shortcuts
- MLIP TOML Keywords Reference
- Adding a Custom Calculator
The fmlip-relay Server
CREST can use ML interatomic potentials (MLIPs) and classical potentials through fmlip-relay , a persistent Python server that ships as a subproject in subprojects/fmlip_relay/ of the CREST source tree. When CREST encounters an MLIP calculation, it automatically spawns the server, which loads the model once at startup and then communicates with CREST over a local TCP socket. This architecture avoids the overhead of repeated Python interpreter startup during iterative calculations such as geometry optimizations or molecular dynamics.
The following backends are available out of the box:
| Backend | Description | Requirements |
|---|---|---|
lj | Lennard-Jones potential with PBC support | numpy only |
mace_off | MACE-OFF organic foundation model (H, C, N, O, P, S, F, Cl, Br, I) | mace-torch, ase |
mace_mp | MACE-MP foundation model (89 elements, Materials Project) | mace-torch, ase |
mace | Custom MACE model from a .model file | mace-torch, ase |
uma | FairChem UMA universal foundation model (Meta FAIR), multi-task | fairchem-core, ase |
dummy | Random numbers (testing only) | numpy only |
Installation
The fmlip-relay Python package must be installed separately from the main CREST Fortran build. From the CREST source root directory, install it with:
pip install ./subprojects/fmlip_relayTo include the optional MACE dependencies (for mace, mace_mp, and mace_off backends):
pip install './subprojects/fmlip_relay[mace]'For the FairChem UMA backend, install the uma extra instead:
pip install './subprojects/fmlip_relay[uma]'huggingface-cli login (and request access to the model on Hugging Face) before the uma backend can download them.[mace] and [uma] extras pull in mace-torch / fairchem-core with default settings, which may not match your platform (CUDA version, PyTorch build). For a working GPU setup, follow the official installation instructions of MACE and fairchem/UMA.After installation, verify that the server executable and backends are available:
fmlip-relay-checkfmlip-relay-server command is on your $PATH.fmlip-relay-server executable must be found on $PATH for CREST to use it. If CREST cannot find the server, you will get an error at runtime.Example 1: Geometry Optimization with Lennard-Jones
As a minimal example, we optimize an Ar4 cluster using the Lennard-Jones potential. The LJ backend requires only numpy and is useful for testing the fmlip-relay setup before moving to ML potentials. The initial structure places four argon atoms in a slightly compressed tetrahedral arrangement; optimization should converge to the LJ minimum near ~3.82 Å.
crest input.toml
# Geometry optimisation of an Ar4 cluster with Lennard-Jones potential
input = "struc.xyz"
runtype = "optimize"
[calculation]
optlev = "normal"
[[calculation.level]]
method = "mlip" # use fmlip-relay socket server
mlip_backend = "lj" # Lennard-Jones (default Ar parameters)
4
Ar4 cluster (compressed, for LJ optimization)
Ar 0.000 0.000 0.000
Ar 3.300 0.000 0.000
Ar 1.650 2.860 0.000
Ar 1.650 0.953 2.694
The key TOML settings here are:
method = "mlip"tells CREST to use the fmlip-relay calculator instead of a semiempirical or ab initio method.mlip_backend = "lj"selects the Lennard-Jones backend with default argon parameters (ε = 0.0104 eV, σ = 3.40 Å).
The optimized structure is written to crestopt.xyz.
Example 2: MACE-OFF for Organic Molecules
The MACE-OFF backend provides a pre-trained foundation model for organic molecules (MACE-OFF23), covering H, C, N, O, P, S, F, Cl, Br, and I. It requires mace-torch and ase to be installed. In the following, MACE-OFF is used to optimize the alanineglycine molecule.
crest input.toml
# Geometry optimisation of alanineglycine with MACE-OFF
input = "struc.xyz"
runtype = "optimize"
[calculation]
optlev = "tight"
[[calculation.level]]
method = "mlip" # use fmlip-relay socket server
mlip_backend = "mace_off" # MACE-OFF organic foundation model
mlip_modelsize = "medium" # small / medium / large
20
C 2.081440 0.615100 -0.508430
C 2.742230 1.824030 -1.200820
N 4.117790 1.799870 -1.190410
C 4.943570 2.827040 -1.822060
C 6.440080 2.569360 -1.637600
O 7.351600 3.252270 -2.069090
N 0.610100 0.695090 -0.538780
O 2.095560 2.724940 -1.739670
O 6.705220 1.463410 -0.897460
H 0.303080 1.426060 0.103770
H 0.338420 1.050680 -1.460480
C 2.488753 -0.593400 -1.198448
H 2.416500 0.557400 0.532050
H 4.614100 1.081980 -0.670550
H 4.699850 3.794460 -1.373720
H 4.722890 2.844690 -2.894180
H 7.687400 1.448620 -0.860340
H 2.029201 -1.457008 -0.719999
H 2.170233 -0.542411 -2.238576
H 3.572730 -0.688405 -1.154998
The mlip_modelsize keyword controls the trade-off between accuracy and speed. The "small" model is fastest, while "large" is most accurate. The default is "medium".
If you have a custom-trained MACE model (a .model file), use the mace backend with mlip_modelpath instead:
[[calculation.level]]
method = "mlip"
mlip_backend = "mace"
mlip_modelpath = "/path/to/my_model.model"
Example 3: FairChem UMA
The uma backend serves the FairChem UMA universal foundation model from Meta FAIR. It is a multi-task model: the mlip_uma_task keyword selects the domain head, and mlip_uma_model selects the checkpoint. The uma backend requires fairchem-core and ase (install with the [uma] extra) and a one-time huggingface-cli login.
For molecular systems, use the omol task, which consumes the total charge and spin multiplicity passed through from CREST.
crest input.toml
# Geometry optimisation with the FairChem UMA foundation model
input = "struc.xyz"
runtype = "optimize"
[calculation]
optlev = "tight"
[[calculation.level]]
method = "mlip" # use fmlip-relay socket server
mlip_backend = "uma" # FairChem UMA universal model
mlip_uma_task = "omol" # domain head: omol / omat / omc / oc20 / odac
mlip_uma_model = "uma-s-1p2" # checkpoint (default; also uma-s-1, uma-s-1p1, uma-m-1)
mlip_device = "cuda" # torch device: cpu (default) / cuda / cuda:0
The available task heads are omol (molecules), omat (materials), omc (molecular crystals), oc20 (catalysis), and odac (MOFs/direct air capture). Only the omol task uses the per-structure charge and spin multiplicity.
mlip_device = "cuda" to run on the GPU; the default is "cpu".Parallelization and CPU Threads
In parallel workflows (ensemble optimizations, ensemble refinement, metadynamics) CREST spawns one server instance per parallel job, each holding its own copy of the model. On the CPU this is a problem: torch and the underlying BLAS libraries grab all available cores by default, so several server instances running at the same time would heavily oversubscribe the machine and each of them would become slower than if it ran alone.
To avoid this, the threads key (alias ncores) can be set inside the [[calculation.level]] block. It is passed on to every server instance as --max-threads, which caps the inference thread pools (OMP_NUM_THREADS, MKL_NUM_THREADS, torch.set_num_threads, …), and at the same time tells CREST to run only as many concurrent jobs as fit into the global thread budget:
# Optimization of an existing ensemble with MACE-OFF on 16 cores
input = "struc.xyz"
ensemble_input = "crest_conformers.xyz"
runtype = "optimize_ensemble"
threads = 16 # total cores available to CREST
[[calculation.level]]
method = "mlip"
mlip_backend = "mace_off"
mlip_modelsize = "medium"
threads = 4 # cores per server -> 4 servers x 4 threads
Here CREST runs 4 parallel jobs, each with its own fmlip-relay server that is limited to 4 CPU threads, instead of 16 servers all fighting over the same 16 cores. The setting is echoed in the calculation summary printout as Server thread cap. The same applies when the MLIP is used as the refinement level of a composite setup, which is the recommended way to combine it with a conformational search.
threads, CREST keeps its historical behavior: one job per core, and each server inherits OMP_NUM_THREADS from the environment. For the cheap lj or dummy backends this is perfectly fine; for torch-based models it is usually not.The general rules for the per-level threads reservation (idle-core warnings, interaction with the global threads) are described in the Input File Documentation.
mlip_device = "cuda") the CPU thread cap matters much less; there the limit is GPU memory, since every parallel server loads its own copy of the model onto the device. Reducing the number of parallel jobs with a larger threads value is also the simplest way to keep the GPU memory footprint in check.Command-Line Shortcuts
For quick setups without writing a TOML file, two ML potentials are available directly on the command line via the -mlip flag:
crest struc.xyz -mlip umaselects the UMA foundation model with the omol task, and
crest struc.xyz -mlip maceoffselects the MACE-OFF23 (medium) organic force field. For finer control (model size, task, device, custom checkpoints), use the TOML [[calculation.level]] block as shown in the examples above.
MLIP TOML Keywords Reference
The following keywords can be set within a [[calculation.level]] block when using the fmlip-relay interface:
| Keyword | Description | Values |
|---|---|---|
method | Calculator selection, must be "mlip" | "mlip" |
mlip_backend | Backend to use | "lj", "mace", "mace_mp", "mace_off", "uma", "dummy" |
mlip_modelpath | Path to a custom MACE .model file | file path string |
mlip_modelsize | Size variant for MACE foundation models | "small", "medium", "large" |
mlip_device | Torch device for NN backends (mace*, uma) | "cpu" (default), "cuda", "cuda:0" |
mlip_uma_task | UMA domain head (uma backend) | "omol", "omat", "omc", "oc20", "odac" |
mlip_uma_model | UMA checkpoint (uma backend) | "uma-s-1p2" (default), "uma-s-1", "uma-s-1p1", "uma-m-1" |
mlip_port | Base TCP port for the socket server | integer (default 54320) |
mlip_timeout | Server startup timeout in seconds | integer (default 120) |
threads (ncores) | CPU threads per server instance (--max-threads), also caps the number of parallel jobs | integer (default unset) |
Adding a Custom Calculator
The fmlip-relay subproject is designed to be extensible. Any potential can be wrapped as a standalone or an ASE calculator and plugged into CREST through fmlip-relay. This requires editing the subproject source and re-installing with pip.
Step 1: Create a backend file
Create a new file in subprojects/fmlip_relay/src/python/fmlip_relay/backends/, for example mybackend.py. A ready-to-use template is provided as _template_ase_backend.py in the backends directory. Copy it and adapt to your needs. The simplest approach is to inherit from _ASEComputeMixin, which implements the compute() method on top of any ASE calculator stored as self._calc:
from ._ase_base import _ASEComputeMixin
class MyBackend(_ASEComputeMixin):
def __init__(self, model_path: str, device: str = "cpu"):
from my_package import MyCalculator
self._calc = MyCalculator(model=model_path, device=device)
self._model_path = model_path
@property
def name(self) -> str:
return f"mybackend({self._model_path})"
Alternatively, you can inherit directly from BackendBase and implement the compute() method yourself. This is useful when no ASE calculator is available:
from .base import BackendBase
import numpy as np
class MyBackend(BackendBase):
def compute(self, atomic_numbers, positions, cell, pbc,
compute_stress, charge, spin):
energy = 0.0 # your energy evaluation
forces = np.zeros((len(atomic_numbers), 3), dtype=np.float64)
stress = np.zeros((3, 3), dtype=np.float64)
return energy, forces, stress
@property
def name(self) -> str:
return "mybackend"
Step 2: Register the backend
In subprojects/fmlip_relay/src/python/fmlip_relay/backends/__init__.py, add your backend to the registry inside the _register_optional() function:
try:
from .mybackend import MyBackend
_REGISTRY["mybackend"] = MyBackend
except ImportError:
pass
Wrapping the import in a try/except ensures that missing optional dependencies do not break the entire package.
Step 3: Re-install and test
After editing the source, re-install the package so that the changes take effect:
pip install ./subprojects/fmlip_relayTo test the backend with a single-point calculation (without starting a full server), use the --test flag with a geometry file:
fmlip-relay-server --backend mybackend --test struc.xyzThis loads the backend, evaluates energy, forces, and stress for the given geometry, prints a summary, and exits. Once the test passes, the new backend can be used in CREST input files via mlip_backend = "mybackend".
Optionally, you can also make fmlip-relay-check aware of the new backend by adding an entry to the _BACKENDS list in check.py:
# In fmlip_relay/check.py, add to the _BACKENDS list:
("mybackend", "fmlip_relay.backends.mybackend", "MyBackend", "Short description", None),
_template_ase_backend.py as a starting point for ASE-based backends. The existing lj.py backend is a good reference for implementing compute() directly, while mace_off.py shows the ASE calculator pattern via _ASEComputeMixin.pip install for CREST to pick up the changes.