Skip to content

ASE calculator

With ASE installed (pip install torchnep[ase]), a trained model drives any ASE workflow — relaxations, molecular dynamics, equations of state, phonons.

from ase.io import read
from torchnep.ase_calculator import NEP

atoms = read("POSCAR")
atoms.calc = NEP("nep.txt", dtype="float32", device="cuda")

atoms.get_potential_energy()   # eV
atoms.get_forces()             # (N, 3) eV/Å
atoms.get_stress()             # Voigt 6-vector, eV/ų (periodic cells)

Non-periodic structures (molecules, clusters) work too: the calculator puts them in a box wide enough for the cutoff.

Example: vacancy formation energy in nickel

The calculator is reused for every structure, so the model is loaded once. This example finds the equilibrium lattice constant of fcc Ni, then relaxes a 3×3×3 supercell with one atom removed:

import numpy as np
from ase.build import bulk
from ase.optimize import BFGS
from torchnep.ase_calculator import NEP

calc = NEP("nep.txt", device="cuda")

# equilibrium lattice constant of this model
best_a, best_e = None, None
for a in np.arange(3.44, 3.60, 0.005):
    cell = bulk("Ni", "fcc", a=a, cubic=True)
    cell.calc = calc
    e = cell.get_potential_energy() / len(cell)
    if best_e is None or e < best_e:
        best_a, best_e = a, e

perfect = bulk("Ni", "fcc", a=best_a, cubic=True).repeat(3)
perfect.calc = calc
e_perfect = perfect.get_potential_energy()

atoms = perfect.copy()
del atoms[0]                       # one vacancy
atoms.calc = calc
n = len(atoms)
e_unrelaxed = atoms.get_potential_energy()
BFGS(atoms).run(fmax=0.01)

print(f"a0 = {best_a:.3f} A, E/atom = {best_e:.4f} eV")
print(f"unrelaxed E_vac = {e_unrelaxed - n / (n + 1) * e_perfect:.3f} eV")
print(f"relaxed   E_vac = {atoms.get_potential_energy() - n / (n + 1) * e_perfect:.3f} eV")

With a Cr-Co-Ni model trained on 3030 frames this prints:

      Step     Time          Energy          fmax
BFGS:    0 21:10:33     -583.710611        0.243060
BFGS:    1 21:10:33     -583.721211        0.211663
BFGS:    2 21:10:33     -583.754787        0.047080
BFGS:    3 21:10:33     -583.755732        0.041422
BFGS:    4 21:10:33     -583.759054        0.014542
BFGS:    5 21:10:33     -583.759159        0.012571
BFGS:    6 21:10:33     -583.759458        0.004755
a0 = 3.515 A, E/atom = -5.4695 eV
unrelaxed E_vac = 1.523 eV
relaxed   E_vac = 1.474 eV

An equilibrium lattice constant of 3.515 Å (experiment: 3.524 Å) and a relaxed vacancy formation energy of 1.47 eV, both in the range expected for nickel.

NEP and ZBL parts

parts = atoms.calc.get_components()
parts["nep"]["energy"], parts["zbl"]["energy"], parts["total"]["energy"]

Each entry holds the energy, forces and, for periodic cells, the stress of that part; the NEP and ZBL parts add up to the total. get_energy_components() returns only the energies. For a model without ZBL, and for any structure whose atoms all sit beyond the ZBL cutoff, the ZBL part is zero.