A2 Orb-v3, MatGL and dispersion¶
Objectives
Run the screening workflow with another universal model.
Test whether a PBE-trained model binds graphite layers.
Add D3 dispersion and judge against the right reference.
The same screening runs with Orb-v3 and MatGL TensorNet; graphite then shows what PBE-trained models miss.
PBE has no dispersion (From bespoke to foundation models).
Graphite layers are held together by van der Waals (dispersion) attraction.
Orb-v3¶
Orb-v3 (Rhodes et al., 2025): universal potentials from Orbital Materials, Apache-2.0, in
orb-models.Not equivariant by construction (MACE is); it learns the symmetry approximately from data. Its developers report accurate properties at much lower latency and memory use.

Comparison with other fast models: Choosing and trusting a model.
Model names encode three choices:
Part of the name |
Options |
Meaning |
|---|---|---|
|
forces from the energy gradient, or predicted directly |
conservative is slower but gives a consistent energy surface; use it for relaxations, elastic constants, phonons and NVE MD |
|
up to 120 neighbours, or at most 20 |
the cap of 20 makes the energy surface discontinuous |
|
trained on OMat24, or on MPtrj and Alexandria |
the Orb authors advise |
The example uses orb-v3-conservative-inf-omat: trained on PBE and PBE+U
without dispersion, so it has learnt none, despite its 6 Å cutoff.
Switch the model¶
The Orb loader returns a model and an atoms adapter, used by both the ASE calculator (serial) and the TorchSim wrapper (batch):
models.py
80def load_orb(name: str, device: torch.device, dtype: torch.dtype,
81 checkpoint: str | None = None, d3: bool = False) -> ModelPair:
82 from orb_models.forcefield import pretrained
83 from orb_models.forcefield.inference.d3_model import AlchemiDFTD3, D3SumModel
84 from torch_sim.models.orb import OrbModel
85
86 loader = getattr(pretrained, ORB_MODELS[name])
87 options = {"device": device, "precision": ORB_PRECISION[dtype], "compile": False}
88 if checkpoint:
89 options["weights_path"] = checkpoint
90 with default_dtype():
91 orbff, adapter = loader(**options)
92 if d3:
93 orbff = D3SumModel(orbff, AlchemiDFTD3(functional="PBE", damping="BJ"))
94 return ModelPair(lambda: OrbModel(orbff, adapter, device=device, dtype=dtype),
95 lambda: orb_ase_calc(orbff, adapter, device, dtype), checkpoint)
96
97
98def load_matgl(name: str, device: torch.device, dtype: torch.dtype) -> ModelPair:
99 """ASE only: TorchSim has no MatGL model, and MatGL runs in float32."""
100 import matgl
101 from matgl.ext.ase import PESCalculator
102
103 if dtype != torch.float32:
104 raise ValueError("MatGL models run in float32; pass --dtype float32")
105 with default_dtype():
106 potential = matgl.load_model(MATGL_MODELS[name]).to(device)
107 return ModelPair(None, lambda: PESCalculator(potential, stress_unit="eV/A3"))
108
109
Laptop CPU check: change only --model. First run downloads about 100 MB:
cd examples
pixi run --manifest-path torchsim/pixi.toml \
python -m torchsim --model orb-v3-conservative-inf-omat --device cpu \
--n-variants 1 --baseline-n 1 --max-steps 20 --outdir orb_cpu
ASE and TorchSim energies differed by 4 × 10⁻⁴ eV after at most 20 steps. This is the optimiser paths, not the model: single points agree to about 10⁻¹² eV.
Implementation details that affect accuracy:
Set precision in the loader (
precision="float32-highest"or"float64"), not withtorch.set_float32_matmul_precision: the loader resets it.The Orb and MACE loaders change torch’s global default dtype; the example restores it after each load, or results depend on load order.
The Orb ASE calculator builds its graph in the global default dtype; the example sets the dtype per call, or a float64 model gets float32 positions.
compile=Falseturns offtorch.compile, which would add a one-off cost to the timed serial baseline.
MatGL¶
MatGL (BSD-3-Clause): TensorNet, CHGNet, M3GNet, QET and others; pretrained on MatPES (PBE or r2SCAN), distributed through Hugging Face.
Since 4.0: PyTorch Geometric only; DGL removed.
Example:
TensorNet-PES-MatPES-PBE-2025.2(0.84 M parameters, 5 Å cutoff) through MatGL’s ASE calculator:
models.py
98def load_matgl(name: str, device: torch.device, dtype: torch.dtype) -> ModelPair:
99 """ASE only: TorchSim has no MatGL model, and MatGL runs in float32."""
100 import matgl
101 from matgl.ext.ase import PESCalculator
102
103 if dtype != torch.float32:
104 raise ValueError("MatGL models run in float32; pass --dtype float32")
105 with default_dtype():
106 potential = matgl.load_model(MATGL_MODELS[name]).to(device)
107 return ModelPair(None, lambda: PESCalculator(potential, stress_unit="eV/A3"))
108
109
TorchSim 0.6 has no MatGL interface: serial ASE only, in float32. First
run downloads to MATGL_CACHE:
cd examples
pixi run --manifest-path torchsim/pixi.toml \
python -m torchsim --model matgl-tensornet-pbe --device cpu --dtype float32 \
--n-variants 1 --baseline-n 1 --max-steps 20 --outdir matgl_cpu
Output on a laptop CPU (matgl 4.0.3, torch 2.9.1):
model=matgl-tensornet-pbe device=cpu dtype=float32 structures=4 serial subset=1
matgl-tensornet-pbe has no TorchSim interface; ran the ASE baseline only
serial, measured: 0.9 s, 0.86 s/structure (1 structures)
Relaxed 32-atom copper cell: −119.344 eV.
Set
stress_unit="eV/A3"onPESCalculator(as the loader does): the default is GPa; ASE expects eV/ų.
Graphite interlayer spacing¶

Bernal (AB) graphite, four atoms per cell; layers bound almost entirely by dispersion.
Plain PBE: 4.40 Å spacing, binding energy only 1 meV per carbon atom (Hazrati et al., 2014); experiment 3.34 Å (Baskin and Meyer, 1955, as tabulated by Hazrati et al.).
A PBE-trained model inherits this error whatever its cutoff; dispersion must come from an added term such as D3.
examples/torchsim/layered.py relaxes
cell and positions with ASE (FrechetCellFilter, FIRE, 0.002 eV/Å) for
MACE-MP-0b small, Orb-v3, and Orb-v3 plus Grimme’s D3(BJ) with PBE
parameters (default variants).
--variants tensornet runs MatGL instead, in float32. D3 comes from
orb-models:
orbff = D3SumModel(orbff, AlchemiDFTD3(functional="PBE", damping="BJ"))
PBE is the right D3 functional: it matches the training data.
Never add D3 to a model already trained with dispersion (such as
orb-d3-v2): it counts twice. This page adds D3 to Orb only.Each model starts from 3.34 Å (experiment) and 4.4 Å (PBE); different results mean the surface is too flat to define a spacing.
graphite.jsongets one row per run: versions, steps, convergence.
cd examples/torchsim
pixi run graphite-cpu
Output on a laptop CPU (one run, float64, orb-models 0.7.0):
variant start d a (A) d (A) d err steps converged
mace-small 3.34 2.467 4.101 22.8% 58 True
mace-small 4.40 2.467 4.099 22.7% 34 True
orb-v3 3.34 2.468 4.310 29.0% 526 True
orb-v3 4.40 2.468 4.365 30.7% 135 True
orb-v3+d3 3.34 2.466 3.443 3.1% 103 True
orb-v3+d3 4.40 2.466 3.448 3.2% 229 True
tensornet 3.34 2.464 4.884 46.2% 588 True
tensornet 4.40 2.464 4.883 46.2% 429 True
PBE 2.470 4.400
experiment 2.460 3.340
d = c/2 is the interlayer spacing; d err is against experiment.
In-plane
a: within 0.4 % of experiment for all models.Without D3, the reference is PBE: plain Orb-v3 (4.31 to 4.37 Å) is near PBE’s 4.40 Å; MACE stops at 4.10 Å. Its smaller error is not better binding: on a nearly unbound surface, small fitting differences shift the spacing a lot.
Plain Orb-v3 is poorly defined: starts end 0.05 Å apart after more than 500 steps, a nearly flat surface.
With D3: clear minimum within about 3 % of experiment; starts agree to 0.005 Å. At the looser 0.01 eV/Å they differed by about 0.04 Å, so check convergence before quoting a spacing.
Step counts can change by a few between runs; spacings do not.
TensorNet (
matgl4.0.3, float32): energy falls monotonically, 36 meV per atom from 3.34 to 4.88 Å, constant beyond its 5 Å cutoff. Both starts stop at 4.88 Å where the force vanishes: no interlayer minimum.
Note
Experiment is low-temperature; relaxations are static (no zero-point motion or temperature).
In
orb-models0.7.0 the D3 neighbour list assumes atoms inside the cell. The graphite cell is built that way and atoms stay inside. A fix for unwrapped positions is only on the main branch.
Leonardo run (one A100)¶
Set the variables from A1. On a login node (compute nodes have no internet):
Update pixi (this page adds
orb-models).Download the Orb checkpoint next to the MACE one; check its SHA-256.
Submit the job: graphite study plus Orb screening batch.
Commands
cd "$MLIP_LESSON_ROOT/examples/torchsim"
pixi install
cd "$MLIP_LESSON_ROOT"
curl -L -o <SCRATCH>/models/orb-v3-conservative-inf-omat.ckpt \
https://orbitalmaterials-public-models.s3.us-west-1.amazonaws.com/forcefields/orb-v3/orb-v3-conservative-inf-omat-20250404.ckpt
printf '%s %s\n' \
0a41ef1132ad9c41ee0c7b9d855fccabfefb97abe04625c97ec0d07930b6c0f0 \
<SCRATCH>/models/orb-v3-conservative-inf-omat.ckpt | sha256sum -c -
export MLIP_ORB_CHECKPOINT=<SCRATCH>/models/orb-v3-conservative-inf-omat.ckpt
sbatch --account=<PROJECT> \
--export=ALL,MLIP_LESSON_ROOT,MLIP_TORCHSIM_CHECKPOINT,MLIP_ORB_CHECKPOINT,MLIP_RESULTS_DIR \
scripts/test-leonardo-orb.sbatch
This script has not yet been qualified. The laptop results above are the reference.
Reading the results¶
Switching model needs only
--model(plus float32 for TensorNet).Without D3, no model gets the graphite spacing: Orb-v3 stays near PBE’s 4.40 Å; TensorNet (5 Å cutoff) has no minimum.
With D3, Orb-v3 is within about 3 % of experiment. Judge uncorrected models against PBE, not experiment.
How this fits into choosing a model for other systems: Choosing an MLIP.
Keypoints
Other universal models plug in; TensorNet runs serial ASE only.
PBE-trained models miss dispersion; add D3 once, never twice.
Start from two spacings and check convergence before quoting a result.
References¶
B. Rhodes et al., Orb-v3: atomistic simulation at scale, arXiv:2504.06231 (2025).
Y. Baskin and L. Meyer, Lattice constants of graphite at low temperatures, Phys. Rev. 100, 544 (1955).
E. Hazrati, G. A. de Wijs and G. Brocks, Li intercalation in graphite: a van der Waals density-functional study, Phys. Rev. B 90, 155448 (2014).
S. Grimme, S. Ehrlich and L. Goerigk, Effect of the damping function in dispersion corrected density functional theory, J. Comput. Chem. 32, 1456 (2011).