Single Point¶
janus-core contains various machine learnt interatomic potentials (MLIPs), including MACE based models (MACE-MP, MACE-OFF, MACE-OMOL, MACE-POLAR), CHGNet, Orb and more, full list on https://github.com/stfc/janus-core.
Other will be added as their utility is proven beyond a specific material.
Set up environment (optional)¶
These steps are required to run this tutorial with Google Colab. To do so, uncomment and run the cell below.
These instructions but may work for other systems too, but it is typically preferable to prepare a virtual environment separately before running this notebook if possible.
[1]:
# ! uv pip install janus-core[mace,orb,chgnet,visualise] data-tutorials --system # Install janus-core with MACE, Orb, CHGNet, and WeasWidget, and data-tutorials
To ensure you have the latest version of janus-core installed, compare the output of the following cell to the latest version available at https://pypi.org/project/janus-core/
[2]:
from janus_core import __version__
print(__version__)
0.9.4
Prepare data¶
Use data_tutorials to get the data required for this tutorial:
[3]:
from data_tutorials.data import get_data
get_data(
url="https://raw.githubusercontent.com/stfc/janus-core/main/docs/source/tutorials/data/",
filename=["sucrose.xyz", "NaCl-set.xyz"],
folder="data",
)
try to download sucrose.xyz from https://raw.githubusercontent.com/stfc/janus-core/main/docs/source/tutorials/data/ and save it in data/sucrose.xyz
saved in data/sucrose.xyz
try to download NaCl-set.xyz from https://raw.githubusercontent.com/stfc/janus-core/main/docs/source/tutorials/data/ and save it in data/NaCl-set.xyz
saved in data/NaCl-set.xyz
Single point calculations for periodic system¶
[4]:
from ase.build import bulk
from janus_core.calculations.single_point import SinglePoint
# Change to cuda if you have a gpu or mps for apple silicon
device = "cpu"
NaCl = bulk("NaCl", "rocksalt", a=5.63, cubic=True)
sp = SinglePoint(
struct=NaCl,
arch="mace_mp",
device=device,
model="small",
calc_kwargs={"default_dtype": "float64"},
)
res_mace = sp.run()
NaCl = bulk("NaCl", "rocksalt", a=5.63, cubic=True)
sp = SinglePoint(
struct=NaCl,
arch="orb",
device=device,
)
res_orb = sp.run()
NaCl = bulk("NaCl", "rocksalt", a=5.63, cubic=True)
sp = SinglePoint(
struct=NaCl,
arch="chgnet",
device=device,
)
res_chgnet = sp.run()
print(f" MACE[eV]: {res_mace['energy']}")
print(f"Orb[eV]: {res_orb['energy']}")
print(f" CHGNeT[eV]: {res_chgnet['energy']}")
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/e3nn/o3/_wigner.py:10: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
_Jd, _W3j_flat, _W3j_indices = torch.load(os.path.join(os.path.dirname(__file__), 'constants.pt'))
cuequivariance or cuequivariance_torch is not available. Cuequivariance acceleration will be disabled.
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/20231210mace128L0_energy_epoch249model
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:226: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
torch.load(f=model_path, map_location=device)
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/orb_models/utils.py:30: UserWarning: Setting global torch default dtype to torch.float32.
warnings.warn(f"Setting global torch default dtype to {torch_dtype}.")
CHGNet v0.3.0 initialized with 412,525 parameters
CHGNet will run on cpu
MACE[eV]: -27.0300815358917
Orb[eV]: -27.077098846435547
CHGNeT[eV]: -29.32662010192871
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/chgnet/model/model.py:898: UserWarning: Converting a tensor with requires_grad=True to a scalar may lead to unexpected behavior.
Consider using tensor.detach() first. (Triggered internally at /pytorch/torch/csrc/autograd/generated/python_variable_methods.cpp:835.)
volumes = torch.tensor(volumes, dtype=TORCH_DTYPE, device=atomic_numbers.device)
Simple Molecules¶
[5]:
from ase.build import molecule
from weas_widget import WeasWidget
from janus_core.calculations.single_point import SinglePoint
sp = SinglePoint(
struct=molecule("H2O"),
arch="mace_off",
device=device,
model="medium",
)
res = sp.run()
print(res)
v=WeasWidget()
v.from_ase(sp.struct)
v.avr.model_style = 1
v.avr.show_hydrogen_bonds = True
v
Downloading MACE model from 'https://raw.githubusercontent.com/ACEsuit/mace-off/main/mace_off23/MACE-OFF23_medium.model'
The model is distributed under the Academic Software License (ASL) license, see https://github.com/gabor1/ASL
To use the model you accept the terms of the license.
ASL is based on the Gnu Public License, but does not permit commercial use
Downloading: 100.0% (17.5 MB / 17.5 MB)
Cached MACE model to /home/runner/.cache/mace/MACE-OFF23_medium.model
Using MACE-OFF23 MODEL for MACECalculator with /home/runner/.cache/mace/MACE-OFF23_medium.model
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:226: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
torch.load(f=model_path, map_location=device)
{'energy': -2081.11639716893, 'forces': array([[ 0.00000000e+00, -6.10622664e-16, -6.47486826e-01],
[ 0.00000000e+00, -3.43765601e-01, 3.23743413e-01],
[ 0.00000000e+00, 3.43765601e-01, 3.23743413e-01]]), 'stress': array([0.00000000e+00, 6.12631596e-06, 4.50763482e-06, 3.72644318e-21,
0.00000000e+00, 0.00000000e+00])}
[5]:
similarly you can use MACE-POLAR, see docs for more info
[6]:
benzene = molecule("C6H6")
sp = SinglePoint(
struct=benzene,
arch="mace_polar",
device=device,
model="polar-1-s",
)
res = sp.run()
print(res)
v=WeasWidget()
v.from_ase(sp.struct)
v.avr.model_style = 1
v.avr.show_hydrogen_bonds = True
v
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:226: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
torch.load(f=model_path, map_location=device)
WARNING:root:Default dtype float64 does not match model dtype float32, converting models to float64.
Using MACE-Polar model for MACECalculator with /home/runner/.cache/mace/MACEPOLAR1Smodel
{'energy': -6319.211270420166, 'forces': array([[-2.04937928e-16, -1.98828974e-01, 0.00000000e+00],
[-1.72178835e-01, -9.94156277e-02, 0.00000000e+00],
[-1.72178835e-01, 9.94156277e-02, 0.00000000e+00],
[-2.48958230e-16, 1.98828974e-01, 0.00000000e+00],
[ 1.72178835e-01, 9.94156277e-02, 0.00000000e+00],
[ 1.72178835e-01, -9.94156277e-02, 0.00000000e+00],
[-4.16723269e-17, -1.68395598e-01, 0.00000000e+00],
[-1.45845017e-01, -8.42030924e-02, 0.00000000e+00],
[-1.45845017e-01, 8.42030924e-02, 0.00000000e+00],
[ 6.16004711e-17, 1.68395598e-01, 0.00000000e+00],
[ 1.45845017e-01, 8.42030924e-02, 0.00000000e+00],
[ 1.45845017e-01, -8.42030924e-02, 0.00000000e+00]]), 'stress': array([ 1.78621848e-06, 1.78621906e-06, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, -5.59877885e-22])}
[6]:
you can get the full results that mace-polar provides by inspecting the results stored in the calculator:
[7]:
print(sp.struct.calc.results)
print(sp.struct.calc.results['dipole'])
{'energy': -6319.211270420166, 'node_energy': array([-0.35921942, -0.35921936, -0.35921936, -0.35921942, -0.35921936,
-0.35921936, 0.09283821, 0.09283819, 0.09283819, 0.09283821,
0.09283819, 0.09283819]), 'forces': array([[-2.04937928e-16, -1.98828974e-01, 0.00000000e+00],
[-1.72178835e-01, -9.94156277e-02, 0.00000000e+00],
[-1.72178835e-01, 9.94156277e-02, 0.00000000e+00],
[-2.48958230e-16, 1.98828974e-01, 0.00000000e+00],
[ 1.72178835e-01, 9.94156277e-02, 0.00000000e+00],
[ 1.72178835e-01, -9.94156277e-02, 0.00000000e+00],
[-4.16723269e-17, -1.68395598e-01, 0.00000000e+00],
[-1.45845017e-01, -8.42030924e-02, 0.00000000e+00],
[-1.45845017e-01, 8.42030924e-02, 0.00000000e+00],
[ 6.16004711e-17, 1.68395598e-01, 0.00000000e+00],
[ 1.45845017e-01, 8.42030924e-02, 0.00000000e+00],
[ 1.45845017e-01, -8.42030924e-02, 0.00000000e+00]]), 'stress': array([ 1.78621848e-06, 1.78621906e-06, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, -5.59877885e-22]), 'dipole': array([-1.11022302e-16, 1.38777878e-16, 0.00000000e+00]), 'charges': array([-0.08329323, -0.08329334, -0.08329334, -0.08329323, -0.08329334,
-0.08329334, 0.08329332, 0.08329329, 0.08329329, 0.08329332,
0.08329329, 0.08329329]), 'interaction_energy': np.float64(-1.5982871061611228), 'electrostatic_energy': np.float64(0.025256362904242408), 'electron_energy': np.float64(1.3088619161084094), 'spins': array([ 0.13361648, 0.13361667, 0.13361667, 0.13361648, 0.13361667,
0.13361667, -0.13361662, -0.1336166 , -0.1336166 , -0.13361662,
-0.1336166 , -0.1336166 ]), 'density_coefficients': array([[-0.08329323, 0. , 0. , 0. ],
[-0.08329334, 0. , 0. , 0. ],
[-0.08329334, 0. , 0. , 0. ],
[-0.08329323, 0. , 0. , 0. ],
[-0.08329334, 0. , 0. , 0. ],
[-0.08329334, 0. , 0. , 0. ],
[ 0.08329332, 0. , 0. , 0. ],
[ 0.08329329, 0. , 0. , 0. ],
[ 0.08329329, 0. , 0. , 0. ],
[ 0.08329332, 0. , 0. , 0. ],
[ 0.08329329, 0. , 0. , 0. ],
[ 0.08329329, 0. , 0. , 0. ]]), 'spin_charge_density': array([[[ 0.02516162, 0. , 0. , 0. ],
[-0.10845485, 0. , 0. , 0. ]],
[[ 0.02516167, 0. , 0. , 0. ],
[-0.108455 , 0. , 0. , 0. ]],
[[ 0.02516167, 0. , 0. , 0. ],
[-0.108455 , 0. , 0. , 0. ]],
[[ 0.02516162, 0. , 0. , 0. ],
[-0.10845485, 0. , 0. , 0. ]],
[[ 0.02516167, 0. , 0. , 0. ],
[-0.108455 , 0. , 0. , 0. ]],
[[ 0.02516167, 0. , 0. , 0. ],
[-0.108455 , 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845497, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845495, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845495, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845497, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845495, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845495, 0. , 0. , 0. ]]]), 'free_energy': -6319.211270420166, 'energies': array([-1037.52706609, -1037.52706604, -1037.52706604, -1037.52706609,
-1037.52706604, -1037.52706604, -15.89716538, -15.8971654 ,
-15.8971654 , -15.89716538, -15.8971654 , -15.8971654 ])}
[-1.11022302e-16 1.38777878e-16 0.00000000e+00]
mace-polar allows specifying charge and spin between other properties, check documentation for the complete list.
[8]:
benzene.info["charge"] = 0
benzene.info["spin"] = 1
sp = SinglePoint(
struct=benzene,
arch="mace_polar",
device=device,
model="polar-1-s",
)
res = sp.run()
print(sp.struct.calc.results)
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:226: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
torch.load(f=model_path, map_location=device)
WARNING:root:Default dtype float64 does not match model dtype float32, converting models to float64.
Using MACE-Polar model for MACECalculator with /home/runner/.cache/mace/MACEPOLAR1Smodel
{'energy': -6319.211270420166, 'node_energy': array([-0.35921942, -0.35921936, -0.35921936, -0.35921942, -0.35921936,
-0.35921936, 0.09283821, 0.09283819, 0.09283819, 0.09283821,
0.09283819, 0.09283819]), 'forces': array([[-2.04937928e-16, -1.98828974e-01, 0.00000000e+00],
[-1.72178835e-01, -9.94156277e-02, 0.00000000e+00],
[-1.72178835e-01, 9.94156277e-02, 0.00000000e+00],
[-2.48958230e-16, 1.98828974e-01, 0.00000000e+00],
[ 1.72178835e-01, 9.94156277e-02, 0.00000000e+00],
[ 1.72178835e-01, -9.94156277e-02, 0.00000000e+00],
[-4.16723269e-17, -1.68395598e-01, 0.00000000e+00],
[-1.45845017e-01, -8.42030924e-02, 0.00000000e+00],
[-1.45845017e-01, 8.42030924e-02, 0.00000000e+00],
[ 6.16004711e-17, 1.68395598e-01, 0.00000000e+00],
[ 1.45845017e-01, 8.42030924e-02, 0.00000000e+00],
[ 1.45845017e-01, -8.42030924e-02, 0.00000000e+00]]), 'stress': array([ 1.78621848e-06, 1.78621906e-06, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, -5.59877885e-22]), 'dipole': array([-1.11022302e-16, 1.38777878e-16, 0.00000000e+00]), 'charges': array([-0.08329323, -0.08329334, -0.08329334, -0.08329323, -0.08329334,
-0.08329334, 0.08329332, 0.08329329, 0.08329329, 0.08329332,
0.08329329, 0.08329329]), 'interaction_energy': np.float64(-1.5982871061611228), 'electrostatic_energy': np.float64(0.025256362904242408), 'electron_energy': np.float64(1.3088619161084094), 'spins': array([ 0.13361648, 0.13361667, 0.13361667, 0.13361648, 0.13361667,
0.13361667, -0.13361662, -0.1336166 , -0.1336166 , -0.13361662,
-0.1336166 , -0.1336166 ]), 'density_coefficients': array([[-0.08329323, 0. , 0. , 0. ],
[-0.08329334, 0. , 0. , 0. ],
[-0.08329334, 0. , 0. , 0. ],
[-0.08329323, 0. , 0. , 0. ],
[-0.08329334, 0. , 0. , 0. ],
[-0.08329334, 0. , 0. , 0. ],
[ 0.08329332, 0. , 0. , 0. ],
[ 0.08329329, 0. , 0. , 0. ],
[ 0.08329329, 0. , 0. , 0. ],
[ 0.08329332, 0. , 0. , 0. ],
[ 0.08329329, 0. , 0. , 0. ],
[ 0.08329329, 0. , 0. , 0. ]]), 'spin_charge_density': array([[[ 0.02516162, 0. , 0. , 0. ],
[-0.10845485, 0. , 0. , 0. ]],
[[ 0.02516167, 0. , 0. , 0. ],
[-0.108455 , 0. , 0. , 0. ]],
[[ 0.02516167, 0. , 0. , 0. ],
[-0.108455 , 0. , 0. , 0. ]],
[[ 0.02516162, 0. , 0. , 0. ],
[-0.10845485, 0. , 0. , 0. ]],
[[ 0.02516167, 0. , 0. , 0. ],
[-0.108455 , 0. , 0. , 0. ]],
[[ 0.02516167, 0. , 0. , 0. ],
[-0.108455 , 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845497, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845495, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845495, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845497, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845495, 0. , 0. , 0. ]],
[[-0.02516165, 0. , 0. , 0. ],
[ 0.10845495, 0. , 0. , 0. ]]]), 'free_energy': -6319.211270420166, 'energies': array([-1037.52706609, -1037.52706604, -1037.52706604, -1037.52706609,
-1037.52706604, -1037.52706604, -15.89716538, -15.8971654 ,
-15.8971654 , -15.89716538, -15.8971654 , -15.8971654 ])}
Sugar on salt¶
[9]:
from ase.build import add_adsorbate, bulk
from ase.io import read, write
a = 5.63
NaCl = bulk(
"NaCl", crystalstructure="rocksalt", cubic=True, orthorhombic=True, a=5.63
) * (6, 6, 3)
NaCl.center(vacuum=20.0, axis=2)
sugar = read("data/sucrose.xyz")
add_adsorbate(slab=NaCl, adsorbate=sugar, height=4.5, position=(10, 10))
write("slab.xyz", NaCl)
sp = SinglePoint(
struct="slab.xyz",
arch="mace_mp",
device=device,
model="small",
)
res = sp.run()
print(res)
v = WeasWidget()
v.avr.model_style = 1
v.avr.show_hydrogen_bonds = True
v.from_ase(NaCl)
v
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/20231210mace128L0_energy_epoch249model
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/ase/io/extxyz.py:320: UserWarning: Skipping unhashable information adsorbate_info
warnings.warn('Skipping unhashable information '
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:226: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
torch.load(f=model_path, map_location=device)
{'energy': -1706.3958128381037, 'forces': array([[ 1.52358983e-09, 1.52359056e-09, 1.60311833e-01],
[ 5.89516275e-10, 5.89516520e-10, -2.49849858e-01],
[-3.71422141e-08, -4.76124785e-09, 2.55549362e-03],
...,
[-1.81414029e-01, 1.43043590e-01, -7.13535637e-02],
[ 1.37346116e-01, -1.33239451e-01, -1.05307659e-01],
[ 5.18585386e-02, 1.62407401e-01, -8.67263312e-02]],
shape=(477, 3)), 'stress': array([-4.33386986e-05, 7.45245471e-07, -1.69529958e-03, -5.32726537e-05,
-3.41297966e-05, -4.53043161e-05])}
[9]:
Calculate an entire collection of data frames¶
[10]:
from ase.io import read
import matplotlib.pyplot as plt
import numpy as np
frames = read("data/NaCl-set.xyz", format="extxyz", index=":")
dft_energy = np.array([s.info["dft_energy"] / len(s) for s in frames])
sp = SinglePoint(
struct="data/NaCl-set.xyz",
arch="mace_mp",
device=device,
model="medium-0b3",
)
sp.run()
mace_mp_energy = np.array([s.info["mace_mp_energy"] / len(s) for s in sp.struct])
rmse_mace = np.linalg.norm(mace_mp_energy - dft_energy) / np.sqrt(len(dft_energy))
sp = SinglePoint(struct="data/NaCl-set.xyz", arch="chgnet", device=device)
sp.run()
chgnet_energy = np.array([s.info["chgnet_energy"] / len(s) for s in sp.struct])
rmse_chgnet = np.linalg.norm(chgnet_energy - dft_energy) / np.sqrt(len(dft_energy))
sp = SinglePoint(struct="data/NaCl-set.xyz", arch="orb", device=device)
sp.run()
orb_energy = np.array([s.info["orb_energy"] / len(s) for s in sp.struct])
rmse_orb = np.linalg.norm(orb_energy - dft_energy) / np.sqrt(len(dft_energy))
print(
f"rmse: mace_mp = {rmse_mace}, chgnet = {rmse_chgnet}, "
f"orb = {rmse_orb} eV/atom"
)
fig, ax = plt.subplots()
ax.scatter(dft_energy, mace_mp_energy, marker="o", label="mace-mp-0")
ax.scatter(dft_energy, orb_energy, marker="x", label="orb")
ax.scatter(dft_energy, chgnet_energy, marker="+", label="chgnet")
ax.legend()
plt.xlabel("MLIP [eV/atom]")
plt.ylabel("DFT [eV/atom]")
plt.show()
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/macemp0b3mediummodel
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:226: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
torch.load(f=model_path, map_location=device)
CHGNet v0.3.0 initialized with 412,525 parameters
CHGNet will run on cpu
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/orb_models/utils.py:30: UserWarning: Setting global torch default dtype to torch.float32.
warnings.warn(f"Setting global torch default dtype to {torch_dtype}.")
rmse: mace_mp = 0.06372546949516093, chgnet = 0.41196170963560735, orb = 0.049681582155335004 eV/atom