Spaces:
Running
Running
Yuan (Cyrus) Chiang
commited on
Add eSEN and update dependency versions (#58)
Browse files* add prefect task flow script
* add esen diatomics
* separate matgl
* update installation script
* adjust test ci installation
* enforce `dgl==2.5.0`
* rollback torch to 2.4
* test first
* dgl 2.2.1
* dgl 2.2.1
* dgl 2.2.1
* no cache; cu124
* remove pin for dgl
* bump matgl version and update calculator api
* rollback orb to 0.4.0
* specify fairchem github repo as dep
* update uv version on ci
* fairchem
- .github/README.md +18 -5
- .github/workflows/test.yaml +3 -10
- examples/mof/classification/classification.py +4 -4
- mlip_arena/models/__init__.py +1 -1
- mlip_arena/models/externals/fairchem.py +27 -0
- mlip_arena/models/externals/matgl.py +4 -1
- mlip_arena/models/registry.yaml +23 -0
- mlip_arena/tasks/diatomics/analysis.py +198 -0
- mlip_arena/tasks/diatomics/fairchem/homonuclear-diatomics.json +2 -2
- mlip_arena/tasks/diatomics/run.py +131 -0
- pyproject.toml +15 -9
- scripts/install-linux.sh +6 -4
- serve/tasks/homonuclear-diatomics.py +3 -12
.github/README.md
CHANGED
@@ -34,28 +34,41 @@ pip install mlip-arena
|
|
34 |
> [!CAUTION]
|
35 |
> We recommend clean build in a new virtual environment due to the compatibility issues between multiple popular MLIPs. We provide a single installation script using `uv` for minimal package conflicts and fast installation!
|
36 |
|
|
|
|
|
|
|
37 |
**Linux**
|
38 |
|
39 |
```bash
|
40 |
# (Optional) Install uv
|
41 |
curl -LsSf https://astral.sh/uv/install.sh | sh
|
42 |
source $HOME/.local/bin/env
|
|
|
|
|
|
|
|
|
43 |
# One script uv pip installation
|
44 |
bash scripts/install-linux.sh
|
45 |
```
|
46 |
|
47 |
-
```bash
|
48 |
# Or from command line
|
49 |
git clone https://github.com/atomind-ai/mlip-arena.git
|
50 |
cd mlip-arena
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
pip install -e .[test]
|
55 |
pip install -e .[mace]
|
56 |
# DeePMD
|
57 |
DP_ENABLE_TENSORFLOW=0 pip install -e .[deepmd]
|
58 |
-
```
|
59 |
|
60 |
**Mac**
|
61 |
|
|
|
34 |
> [!CAUTION]
|
35 |
> We recommend clean build in a new virtual environment due to the compatibility issues between multiple popular MLIPs. We provide a single installation script using `uv` for minimal package conflicts and fast installation!
|
36 |
|
37 |
+
> [!CAUTION]
|
38 |
+
> To automatically download farichem OMat24 checkpoint, please make sure you have gained downloading access to their HuggingFace [***model repo***](https://huggingface.co/facebook/OMAT24) (not dataset repo), and login locally on your machine through `huggginface-cli login` (see [HF hub authentication](https://huggingface.co/docs/huggingface_hub/en/quick-start#authentication))
|
39 |
+
|
40 |
**Linux**
|
41 |
|
42 |
```bash
|
43 |
# (Optional) Install uv
|
44 |
curl -LsSf https://astral.sh/uv/install.sh | sh
|
45 |
source $HOME/.local/bin/env
|
46 |
+
|
47 |
+
git clone https://github.com/atomind-ai/mlip-arena.git
|
48 |
+
cd mlip-arena
|
49 |
+
|
50 |
# One script uv pip installation
|
51 |
bash scripts/install-linux.sh
|
52 |
```
|
53 |
|
54 |
+
<!-- ```bash
|
55 |
# Or from command line
|
56 |
git clone https://github.com/atomind-ai/mlip-arena.git
|
57 |
cd mlip-arena
|
58 |
+
|
59 |
+
TORCH=2.4
|
60 |
+
CUDA=cu124
|
61 |
+
pip install torch==${TORCH}.0
|
62 |
+
pip install torch-scatter torch-sparse -f https://data.pyg.org/whl/torch-${TORCH}.0+${CUDA}.html
|
63 |
+
pip install dgl -f https://data.dgl.ai/wheels/torch-${TORCH}/${CUDA}/repo.html
|
64 |
+
pip install -e .[fairchem] --no-deps
|
65 |
+
pip install -e .[orb]
|
66 |
+
pip install -e .[matgl]
|
67 |
pip install -e .[test]
|
68 |
pip install -e .[mace]
|
69 |
# DeePMD
|
70 |
DP_ENABLE_TENSORFLOW=0 pip install -e .[deepmd]
|
71 |
+
``` -->
|
72 |
|
73 |
**Mac**
|
74 |
|
.github/workflows/test.yaml
CHANGED
@@ -24,8 +24,9 @@ jobs:
|
|
24 |
uses: actions/checkout@v4
|
25 |
|
26 |
- name: Install uv
|
27 |
-
uses: astral-sh/setup-uv@
|
28 |
with:
|
|
|
29 |
enable-cache: true
|
30 |
cache-dependency-glob: "pyproject.toml"
|
31 |
|
@@ -36,15 +37,7 @@ jobs:
|
|
36 |
|
37 |
- name: Install dependencies
|
38 |
run: |
|
39 |
-
|
40 |
-
CUDA=cu121
|
41 |
-
uv pip install torch==${TORCH}.0
|
42 |
-
uv pip install torch-scatter -f https://data.pyg.org/whl/torch-${TORCH}.0+${CUDA}.html
|
43 |
-
uv pip install torch-sparse -f https://data.pyg.org/whl/torch-${TORCH}.0+${CUDA}.html
|
44 |
-
uv pip install dgl -f https://data.dgl.ai/wheels/torch-${TORCH}/${CUDA}/repo.html
|
45 |
-
uv pip install -e .[test]
|
46 |
-
uv pip install -e .[mace]
|
47 |
-
uv pip install -e .[deepmd]
|
48 |
|
49 |
- name: List dependencies
|
50 |
run: pip list
|
|
|
24 |
uses: actions/checkout@v4
|
25 |
|
26 |
- name: Install uv
|
27 |
+
uses: astral-sh/setup-uv@v6
|
28 |
with:
|
29 |
+
version: "latest"
|
30 |
enable-cache: true
|
31 |
cache-dependency-glob: "pyproject.toml"
|
32 |
|
|
|
37 |
|
38 |
- name: Install dependencies
|
39 |
run: |
|
40 |
+
bash scripts/install-linux.sh
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
- name: List dependencies
|
43 |
run: pip list
|
examples/mof/classification/classification.py
CHANGED
@@ -60,7 +60,7 @@ def save_result(
|
|
60 |
# Orchestrate your awesome dask workflow runner
|
61 |
|
62 |
nodes_per_alloc = 1
|
63 |
-
gpus_per_alloc =
|
64 |
ntasks = 1
|
65 |
|
66 |
cluster_kwargs = dict(
|
@@ -68,7 +68,7 @@ cluster_kwargs = dict(
|
|
68 |
memory="64 GB",
|
69 |
shebang="#!/bin/bash",
|
70 |
account="m3828",
|
71 |
-
walltime="
|
72 |
job_mem="0",
|
73 |
job_script_prologue=[
|
74 |
"source ~/.bashrc",
|
@@ -82,7 +82,7 @@ cluster_kwargs = dict(
|
|
82 |
f"-N {nodes_per_alloc}",
|
83 |
"-C gpu",
|
84 |
f"-G {gpus_per_alloc}",
|
85 |
-
"--exclusive",
|
86 |
],
|
87 |
)
|
88 |
|
@@ -122,7 +122,7 @@ def run_all():
|
|
122 |
|
123 |
for model, row in tqdm(itertools.product(MLIPEnum, load_row_from_df("input.pkl"))):
|
124 |
|
125 |
-
if model.name not in ["MACE-MPA", "MatterSim", "SevenNet", "M3GNet", "
|
126 |
continue
|
127 |
|
128 |
fpath = Path(f"{model.name}.pkl")
|
|
|
60 |
# Orchestrate your awesome dask workflow runner
|
61 |
|
62 |
nodes_per_alloc = 1
|
63 |
+
gpus_per_alloc = 1
|
64 |
ntasks = 1
|
65 |
|
66 |
cluster_kwargs = dict(
|
|
|
68 |
memory="64 GB",
|
69 |
shebang="#!/bin/bash",
|
70 |
account="m3828",
|
71 |
+
walltime="04:00:00",
|
72 |
job_mem="0",
|
73 |
job_script_prologue=[
|
74 |
"source ~/.bashrc",
|
|
|
82 |
f"-N {nodes_per_alloc}",
|
83 |
"-C gpu",
|
84 |
f"-G {gpus_per_alloc}",
|
85 |
+
# "--exclusive",
|
86 |
],
|
87 |
)
|
88 |
|
|
|
122 |
|
123 |
for model, row in tqdm(itertools.product(MLIPEnum, load_row_from_df("input.pkl"))):
|
124 |
|
125 |
+
if model.name not in ["MACE-MPA", "MatterSim", "SevenNet", "M3GNet", "ORBv2"]:
|
126 |
continue
|
127 |
|
128 |
fpath = Path(f"{model.name}.pkl")
|
mlip_arena/models/__init__.py
CHANGED
@@ -42,7 +42,7 @@ for model, metadata in REGISTRY.items():
|
|
42 |
f"{__package__}.{metadata['module']}.{metadata['family']}"
|
43 |
)
|
44 |
MLIPMap[model] = getattr(module, metadata["class"])
|
45 |
-
except (ModuleNotFoundError, AttributeError, ValueError) as e:
|
46 |
logger.warning(e)
|
47 |
continue
|
48 |
|
|
|
42 |
f"{__package__}.{metadata['module']}.{metadata['family']}"
|
43 |
)
|
44 |
MLIPMap[model] = getattr(module, metadata["class"])
|
45 |
+
except (ModuleNotFoundError, AttributeError, ValueError, ImportError) as e:
|
46 |
logger.warning(e)
|
47 |
continue
|
48 |
|
mlip_arena/models/externals/fairchem.py
CHANGED
@@ -10,6 +10,33 @@ from huggingface_hub import hf_hub_download
|
|
10 |
with open(Path(__file__).parents[1] / "registry.yaml", encoding="utf-8") as f:
|
11 |
REGISTRY = yaml.safe_load(f)
|
12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
class eqV2(OCPCalculator):
|
14 |
def __init__(
|
15 |
self,
|
|
|
10 |
with open(Path(__file__).parents[1] / "registry.yaml", encoding="utf-8") as f:
|
11 |
REGISTRY = yaml.safe_load(f)
|
12 |
|
13 |
+
|
14 |
+
class eSEN(OCPCalculator):
|
15 |
+
def __init__(
|
16 |
+
self,
|
17 |
+
checkpoint=REGISTRY["eSEN"]["checkpoint"],
|
18 |
+
cache_dir=None,
|
19 |
+
cpu=False, # TODO: cannot assign device
|
20 |
+
seed=0,
|
21 |
+
**kwargs,
|
22 |
+
) -> None:
|
23 |
+
|
24 |
+
# https://huggingface.co/facebook/OMAT24/resolve/main/esen_30m_oam.pt
|
25 |
+
|
26 |
+
checkpoint_path = hf_hub_download(
|
27 |
+
"fairchem/OMAT24",
|
28 |
+
filename=checkpoint,
|
29 |
+
revision="13ab5b8d71af67bd1c83fbbf53250c82cd87f506",
|
30 |
+
cache_dir=cache_dir
|
31 |
+
)
|
32 |
+
kwargs.pop("device", None)
|
33 |
+
super().__init__(
|
34 |
+
checkpoint_path=checkpoint_path,
|
35 |
+
cpu=cpu,
|
36 |
+
seed=seed,
|
37 |
+
**kwargs,
|
38 |
+
)
|
39 |
+
|
40 |
class eqV2(OCPCalculator):
|
41 |
def __init__(
|
42 |
self,
|
mlip_arena/models/externals/matgl.py
CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3 |
import matgl
|
4 |
import torch
|
5 |
from matgl.ext.ase import PESCalculator
|
|
|
6 |
|
7 |
|
8 |
class M3GNet(PESCalculator):
|
@@ -11,8 +12,10 @@ class M3GNet(PESCalculator):
|
|
11 |
checkpoint="M3GNet-MP-2021.2.8-PES",
|
12 |
# TODO: cannot assign device
|
13 |
state_attr: torch.Tensor | None = None,
|
|
|
14 |
stress_weight: float = 1.0,
|
|
|
15 |
**kwargs,
|
16 |
) -> None:
|
17 |
potential = matgl.load_model(checkpoint)
|
18 |
-
super().__init__(potential, state_attr, stress_weight, **kwargs)
|
|
|
3 |
import matgl
|
4 |
import torch
|
5 |
from matgl.ext.ase import PESCalculator
|
6 |
+
from typing import Literal
|
7 |
|
8 |
|
9 |
class M3GNet(PESCalculator):
|
|
|
12 |
checkpoint="M3GNet-MP-2021.2.8-PES",
|
13 |
# TODO: cannot assign device
|
14 |
state_attr: torch.Tensor | None = None,
|
15 |
+
stress_unit: Literal["eV/A3", "GPa"] = "GPa",
|
16 |
stress_weight: float = 1.0,
|
17 |
+
use_voigt: bool = False,
|
18 |
**kwargs,
|
19 |
) -> None:
|
20 |
potential = matgl.load_model(checkpoint)
|
21 |
+
super().__init__(potential, state_attr, stress_unit, stress_weight, use_voigt, **kwargs)
|
mlip_arena/models/registry.yaml
CHANGED
@@ -192,6 +192,29 @@ MACE-MPA:
|
|
192 |
npt: true
|
193 |
license: MIT
|
194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
195 |
EquiformerV2(OC22):
|
196 |
module: externals
|
197 |
class: EquiformerV2
|
|
|
192 |
npt: true
|
193 |
license: MIT
|
194 |
|
195 |
+
eSEN:
|
196 |
+
module: externals
|
197 |
+
class: eSEN
|
198 |
+
family: fairchem
|
199 |
+
package: fairchem-core==1.9.0
|
200 |
+
checkpoint: esen_30m_oam.pt
|
201 |
+
username: fairchem # HF handle
|
202 |
+
last-update: 2025-04-21
|
203 |
+
datetime: 2025-04-21
|
204 |
+
datasets:
|
205 |
+
- OMat
|
206 |
+
- MPTrj
|
207 |
+
- Alexandria
|
208 |
+
gpu-tasks:
|
209 |
+
- homonuclear-diatomics
|
210 |
+
prediction: EFS
|
211 |
+
nvt: true
|
212 |
+
npt: false # https://github.com/FAIR-Chem/fairchem/issues/888, https://github.com/atomind-ai/mlip-arena/issues/17
|
213 |
+
date: 2025-04-14
|
214 |
+
github: https://github.com/FAIR-Chem/fairchem
|
215 |
+
doi: https://arxiv.org/abs/2502.12147
|
216 |
+
license: Modified Apache-2.0 (Meta)
|
217 |
+
|
218 |
EquiformerV2(OC22):
|
219 |
module: externals
|
220 |
class: EquiformerV2
|
mlip_arena/tasks/diatomics/analysis.py
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pathlib import Path
|
2 |
+
|
3 |
+
import numpy as np
|
4 |
+
import pandas as pd
|
5 |
+
from ase.data import chemical_symbols
|
6 |
+
from ase.io import read
|
7 |
+
from scipy import stats
|
8 |
+
from scipy.interpolate import UnivariateSpline
|
9 |
+
from tqdm.auto import tqdm
|
10 |
+
|
11 |
+
from mlip_arena.models import REGISTRY, MLIPEnum
|
12 |
+
|
13 |
+
for model in MLIPEnum:
|
14 |
+
|
15 |
+
df = pd.DataFrame(
|
16 |
+
columns=[
|
17 |
+
"name",
|
18 |
+
"method",
|
19 |
+
"R",
|
20 |
+
"E",
|
21 |
+
"F",
|
22 |
+
"S^2",
|
23 |
+
"force-flip-times",
|
24 |
+
"force-total-variation",
|
25 |
+
"force-jump",
|
26 |
+
"energy-diff-flip-times",
|
27 |
+
"energy-grad-norm-max",
|
28 |
+
"energy-jump",
|
29 |
+
"energy-total-variation",
|
30 |
+
"tortuosity",
|
31 |
+
"conservation-deviation",
|
32 |
+
"spearman-descending-force",
|
33 |
+
"spearman-ascending-force",
|
34 |
+
"spearman-repulsion-energy",
|
35 |
+
"spearman-attraction-energy",
|
36 |
+
"pbe-energy-mae",
|
37 |
+
"pbe-force-mae",
|
38 |
+
]
|
39 |
+
)
|
40 |
+
|
41 |
+
for symbol in tqdm(chemical_symbols[1:]):
|
42 |
+
da = symbol + symbol
|
43 |
+
|
44 |
+
out_dir = Path(model.name)
|
45 |
+
|
46 |
+
traj_fpath = out_dir / f"{str(da)}.extxyz"
|
47 |
+
|
48 |
+
if traj_fpath.exists():
|
49 |
+
traj = read(traj_fpath, index=":")
|
50 |
+
else:
|
51 |
+
continue
|
52 |
+
|
53 |
+
Rs, Es, Fs, S2s = [], [], [], []
|
54 |
+
for atoms in traj:
|
55 |
+
vec = atoms.positions[1] - atoms.positions[0]
|
56 |
+
r = np.linalg.norm(vec)
|
57 |
+
e = atoms.get_potential_energy()
|
58 |
+
f = np.inner(vec / r, atoms.get_forces()[1])
|
59 |
+
# s2 = np.mean(np.power(atoms.get_magnetic_moments(), 2))
|
60 |
+
|
61 |
+
Rs.append(r)
|
62 |
+
Es.append(e)
|
63 |
+
Fs.append(f)
|
64 |
+
# S2s.append(s2)
|
65 |
+
|
66 |
+
rs = np.array(Rs)
|
67 |
+
es = np.array(Es)
|
68 |
+
fs = np.array(Fs)
|
69 |
+
|
70 |
+
# sort interatomic distances and align to zero at far field
|
71 |
+
indices = np.argsort(rs)[::-1]
|
72 |
+
rs = rs[indices]
|
73 |
+
es = es[indices]
|
74 |
+
eshift = es[0]
|
75 |
+
es -= eshift
|
76 |
+
fs = fs[indices]
|
77 |
+
|
78 |
+
iminf = np.argmin(fs)
|
79 |
+
imine = np.argmin(es)
|
80 |
+
|
81 |
+
de_dr = np.gradient(es, rs)
|
82 |
+
d2e_dr2 = np.gradient(de_dr, rs)
|
83 |
+
|
84 |
+
# avoid numerical sensitity close to zero
|
85 |
+
rounded_fs = np.copy(fs)
|
86 |
+
rounded_fs[np.abs(rounded_fs) < 1e-2] = 0 # 10meV/A
|
87 |
+
fs_sign = np.sign(rounded_fs)
|
88 |
+
mask = fs_sign != 0
|
89 |
+
rounded_fs = rounded_fs[mask]
|
90 |
+
fs_sign = fs_sign[mask]
|
91 |
+
f_flip = np.diff(fs_sign) != 0
|
92 |
+
|
93 |
+
fdiff = np.diff(fs)
|
94 |
+
fdiff_sign = np.sign(fdiff)
|
95 |
+
mask = fdiff_sign != 0
|
96 |
+
fdiff = fdiff[mask]
|
97 |
+
fdiff_sign = fdiff_sign[mask]
|
98 |
+
fdiff_flip = np.diff(fdiff_sign) != 0
|
99 |
+
fjump = (
|
100 |
+
np.abs(fdiff[:-1][fdiff_flip]).sum() + np.abs(fdiff[1:][fdiff_flip]).sum()
|
101 |
+
)
|
102 |
+
|
103 |
+
ediff = np.diff(es)
|
104 |
+
ediff[np.abs(ediff) < 1e-3] = 0 # 1meV
|
105 |
+
ediff_sign = np.sign(ediff)
|
106 |
+
mask = ediff_sign != 0
|
107 |
+
ediff = ediff[mask]
|
108 |
+
ediff_sign = ediff_sign[mask]
|
109 |
+
ediff_flip = np.diff(ediff_sign) != 0
|
110 |
+
ejump = (
|
111 |
+
np.abs(ediff[:-1][ediff_flip]).sum() + np.abs(ediff[1:][ediff_flip]).sum()
|
112 |
+
)
|
113 |
+
|
114 |
+
try:
|
115 |
+
pbe_traj = read(f"./vasp/{da}/PBE.extxyz", index=":")
|
116 |
+
|
117 |
+
pbe_rs, pbe_es, pbe_fs = [], [], []
|
118 |
+
|
119 |
+
for atoms in pbe_traj:
|
120 |
+
vec = atoms.positions[1] - atoms.positions[0]
|
121 |
+
r = np.linalg.norm(vec)
|
122 |
+
pbe_rs.append(r)
|
123 |
+
pbe_es.append(atoms.get_potential_energy())
|
124 |
+
pbe_fs.append(np.inner(vec / r, atoms.get_forces()[1]))
|
125 |
+
|
126 |
+
pbe_rs = np.array(pbe_rs)
|
127 |
+
pbe_es = np.array(pbe_es)
|
128 |
+
pbe_fs = np.array(pbe_fs)
|
129 |
+
|
130 |
+
indices = np.argsort(pbe_rs)
|
131 |
+
pbe_rs = pbe_rs[indices]
|
132 |
+
pbe_es = pbe_es[indices]
|
133 |
+
pbe_fs = pbe_fs[indices]
|
134 |
+
|
135 |
+
pbe_es -= pbe_es[-1]
|
136 |
+
|
137 |
+
xs = np.linspace(pbe_rs.min(), pbe_rs.max(), int(1e3))
|
138 |
+
|
139 |
+
cs = UnivariateSpline(pbe_rs, pbe_es, s=0)
|
140 |
+
pbe_energy_mae = np.mean(np.abs(es - cs(rs)))
|
141 |
+
|
142 |
+
cs = UnivariateSpline(pbe_rs, pbe_fs, s=0)
|
143 |
+
pbe_force_mae = np.mean(np.abs(fs - cs(rs)))
|
144 |
+
except Exception as e:
|
145 |
+
print(e)
|
146 |
+
pbe_energy_mae = None
|
147 |
+
pbe_force_mae = None
|
148 |
+
|
149 |
+
conservation_deviation = np.mean(np.abs(fs + de_dr))
|
150 |
+
|
151 |
+
etv = np.sum(np.abs(np.diff(es)))
|
152 |
+
|
153 |
+
data = {
|
154 |
+
"name": da,
|
155 |
+
"method": model.name,
|
156 |
+
"R": rs,
|
157 |
+
"E": es + eshift,
|
158 |
+
"F": fs,
|
159 |
+
"S^2": S2s,
|
160 |
+
"force-flip-times": np.sum(f_flip),
|
161 |
+
"force-total-variation": np.sum(np.abs(np.diff(fs))),
|
162 |
+
"force-jump": fjump,
|
163 |
+
"energy-diff-flip-times": np.sum(ediff_flip),
|
164 |
+
"energy-grad-norm-max": np.max(np.abs(de_dr)),
|
165 |
+
"energy-jump": ejump,
|
166 |
+
# "energy-grad-norm-mean": np.mean(de_dr_abs),
|
167 |
+
"energy-total-variation": etv,
|
168 |
+
"tortuosity": etv / (abs(es[0] - es.min()) + (es[-1] - es.min())),
|
169 |
+
"conservation-deviation": conservation_deviation,
|
170 |
+
"spearman-descending-force": stats.spearmanr(
|
171 |
+
rs[iminf:], fs[iminf:]
|
172 |
+
).statistic,
|
173 |
+
"spearman-ascending-force": stats.spearmanr(
|
174 |
+
rs[:iminf], fs[:iminf]
|
175 |
+
).statistic,
|
176 |
+
"spearman-repulsion-energy": stats.spearmanr(
|
177 |
+
rs[imine:], es[imine:]
|
178 |
+
).statistic,
|
179 |
+
"spearman-attraction-energy": stats.spearmanr(
|
180 |
+
rs[:imine], es[:imine]
|
181 |
+
).statistic,
|
182 |
+
"pbe-energy-mae": pbe_energy_mae,
|
183 |
+
"pbe-force-mae": pbe_force_mae,
|
184 |
+
}
|
185 |
+
|
186 |
+
df = pd.concat([df, pd.DataFrame([data])], ignore_index=True)
|
187 |
+
|
188 |
+
json_fpath = Path(REGISTRY[model.name]["family"]) / "homonuclear-diatomics.json"
|
189 |
+
|
190 |
+
if json_fpath.exists():
|
191 |
+
df0 = pd.read_json(json_fpath)
|
192 |
+
df = pd.concat([df0, df], ignore_index=True)
|
193 |
+
df.drop_duplicates(inplace=True, subset=["name", "method"], keep="last")
|
194 |
+
|
195 |
+
df.to_json(json_fpath, orient="records")
|
196 |
+
|
197 |
+
json_fpath = Path(model.name) / "homonuclear-diatomics.json"
|
198 |
+
df.to_json(json_fpath, orient="records")
|
mlip_arena/tasks/diatomics/fairchem/homonuclear-diatomics.json
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5f955a5e08dbb9be3d0a04f06bd4cf93ac63a6a401132a7e74e971365133472a
|
3 |
+
size 4286019
|
mlip_arena/tasks/diatomics/run.py
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import itertools
|
2 |
+
from pathlib import Path
|
3 |
+
|
4 |
+
import numpy as np
|
5 |
+
from ase import Atom, Atoms
|
6 |
+
from ase.calculators.calculator import BaseCalculator
|
7 |
+
from ase.data import chemical_symbols, covalent_radii, vdw_alvarez
|
8 |
+
from ase.io import read, write
|
9 |
+
from prefect import flow, task
|
10 |
+
from tqdm.auto import tqdm
|
11 |
+
|
12 |
+
from mlip_arena.models import REGISTRY, MLIPEnum
|
13 |
+
from mlip_arena.tasks.utils import get_calculator
|
14 |
+
|
15 |
+
|
16 |
+
@task
|
17 |
+
def homonuclear_diatomics(symbol: str, calculator: BaseCalculator, out_dir: Path):
|
18 |
+
"""
|
19 |
+
Calculate potential energy curves for homonuclear diatomic molecules.
|
20 |
+
|
21 |
+
This function computes the potential energy of a diatomic molecule (two atoms of
|
22 |
+
the same element) across a range of interatomic distances. The distance range is
|
23 |
+
automatically determined from the covalent and van der Waals radii of the element.
|
24 |
+
|
25 |
+
Args:
|
26 |
+
symbol: Chemical symbol of the atom (e.g., 'H', 'O', 'Fe')
|
27 |
+
calculator: ASE calculator object used to compute the potential energies. Could be VASP, MLIP, etc.
|
28 |
+
|
29 |
+
Returns:
|
30 |
+
None: Results are saved as trajectory files in a directory structure:
|
31 |
+
/{model_family}/{element_pair}/{model_name}.extxyz
|
32 |
+
|
33 |
+
Note:
|
34 |
+
- Minimum distance is set to 0.9× the covalent radius
|
35 |
+
- Maximum distance is set to 3.1× the van der Waals radius (or 6 Å if unknown)
|
36 |
+
- Distance step size is fixed at 0.01 Å
|
37 |
+
- If an existing trajectory file is found, the calculation will resume from where it left off
|
38 |
+
- The atoms are placed in a periodic box large enough to avoid self-interaction
|
39 |
+
"""
|
40 |
+
|
41 |
+
atom = Atom(symbol)
|
42 |
+
rmin = 0.9 * covalent_radii[atom.number]
|
43 |
+
rvdw = (
|
44 |
+
vdw_alvarez.vdw_radii[atom.number]
|
45 |
+
if atom.number < len(vdw_alvarez.vdw_radii)
|
46 |
+
else np.nan
|
47 |
+
)
|
48 |
+
rmax = 3.1 * rvdw if not np.isnan(rvdw) else 6
|
49 |
+
rstep = 0.01
|
50 |
+
npts = int((rmax - rmin) / rstep)
|
51 |
+
|
52 |
+
rs = np.linspace(rmin, rmax, npts)
|
53 |
+
es = np.zeros_like(rs)
|
54 |
+
|
55 |
+
da = symbol + symbol
|
56 |
+
|
57 |
+
out_dir.mkdir(parents=True, exist_ok=True)
|
58 |
+
|
59 |
+
skip = 0
|
60 |
+
|
61 |
+
a = 5 * rmax
|
62 |
+
r = rs[0]
|
63 |
+
|
64 |
+
positions = [
|
65 |
+
[a / 2 - r / 2, a / 2, a / 2],
|
66 |
+
[a / 2 + r / 2, a / 2, a / 2],
|
67 |
+
]
|
68 |
+
|
69 |
+
traj_fpath = out_dir / f"{da!s}.extxyz"
|
70 |
+
|
71 |
+
if traj_fpath.exists():
|
72 |
+
traj = read(traj_fpath, index=":")
|
73 |
+
skip = len(traj)
|
74 |
+
atoms = traj[-1]
|
75 |
+
else:
|
76 |
+
# Create the unit cell with two atoms
|
77 |
+
atoms = Atoms(
|
78 |
+
da,
|
79 |
+
positions=positions,
|
80 |
+
# magmoms=magmoms,
|
81 |
+
cell=[a, a + 0.001, a + 0.002],
|
82 |
+
pbc=False,
|
83 |
+
)
|
84 |
+
|
85 |
+
atoms.calc = calculator
|
86 |
+
|
87 |
+
for i, r in enumerate(tqdm(rs)):
|
88 |
+
if i < skip:
|
89 |
+
continue
|
90 |
+
|
91 |
+
positions = [
|
92 |
+
[a / 2 - r / 2, a / 2, a / 2],
|
93 |
+
[a / 2 + r / 2, a / 2, a / 2],
|
94 |
+
]
|
95 |
+
|
96 |
+
# atoms.set_initial_magnetic_moments(magmoms)
|
97 |
+
atoms.set_positions(positions)
|
98 |
+
es[i] = atoms.get_potential_energy()
|
99 |
+
write(traj_fpath, atoms, append="a")
|
100 |
+
|
101 |
+
|
102 |
+
@flow
|
103 |
+
def submit_homonuclear_diatomics():
|
104 |
+
futures = []
|
105 |
+
for symbol, model in itertools.product(
|
106 |
+
chemical_symbols[1:],
|
107 |
+
MLIPEnum,
|
108 |
+
):
|
109 |
+
if "homonuclear-diatomics" not in REGISTRY[model.name].get("gpu-tasks", []):
|
110 |
+
continue
|
111 |
+
|
112 |
+
out_dir = Path(__file__).parent / model.name
|
113 |
+
|
114 |
+
calculator = get_calculator(model)
|
115 |
+
|
116 |
+
# if not (out_dir / "homonuclear-diatomics.json").exists():
|
117 |
+
future = homonuclear_diatomics.submit(
|
118 |
+
symbol,
|
119 |
+
calculator,
|
120 |
+
out_dir=out_dir,
|
121 |
+
)
|
122 |
+
futures.append(future)
|
123 |
+
|
124 |
+
return [f.result(raise_on_failure=False) for f in futures]
|
125 |
+
|
126 |
+
|
127 |
+
if __name__ == "__main__":
|
128 |
+
submit_homonuclear_diatomics.with_options(
|
129 |
+
# task_runner=DaskTaskRunner(address=client.scheduler.address),
|
130 |
+
log_prints=True,
|
131 |
+
)()
|
pyproject.toml
CHANGED
@@ -47,19 +47,14 @@ app = [
|
|
47 |
"plotly",
|
48 |
]
|
49 |
test = [
|
50 |
-
"torch==2.
|
51 |
"torch_dftd==0.4.0",
|
52 |
-
"nvidia-ml-py==12.560.30",
|
53 |
"e3nn==0.5.0",
|
54 |
-
"
|
55 |
-
# "dgl==2.4.0",
|
56 |
"chgnet==0.3.8",
|
57 |
-
"fairchem-core==1.2.0",
|
58 |
"sevenn==0.9.3.post1",
|
59 |
-
"orb-models==0.4.0",
|
60 |
-
"pynanoflann@git+https://github.com/dwastberg/pynanoflann#egg=af434039ae14bedcbb838a7808924d6689274168",
|
61 |
"alignn==2024.5.27",
|
62 |
-
"mattersim==1.
|
63 |
"torchani==2.2.4",
|
64 |
"pytest",
|
65 |
"pytest-xdist",
|
@@ -69,7 +64,18 @@ test = [
|
|
69 |
"streamlit==1.43.2"
|
70 |
]
|
71 |
mace = [
|
72 |
-
"mace-torch==0.3.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
]
|
74 |
deepmd = [
|
75 |
"torch==2.2.0",
|
|
|
47 |
"plotly",
|
48 |
]
|
49 |
test = [
|
50 |
+
"torch==2.4.0",
|
51 |
"torch_dftd==0.4.0",
|
|
|
52 |
"e3nn==0.5.0",
|
53 |
+
"dgl",
|
|
|
54 |
"chgnet==0.3.8",
|
|
|
55 |
"sevenn==0.9.3.post1",
|
|
|
|
|
56 |
"alignn==2024.5.27",
|
57 |
+
"mattersim==1.1.2",
|
58 |
"torchani==2.2.4",
|
59 |
"pytest",
|
60 |
"pytest-xdist",
|
|
|
64 |
"streamlit==1.43.2"
|
65 |
]
|
66 |
mace = [
|
67 |
+
"mace-torch==0.3.12",
|
68 |
+
]
|
69 |
+
matgl = [
|
70 |
+
"matgl==1.2.6",
|
71 |
+
]
|
72 |
+
fairchem = [
|
73 |
+
"hydra-core",
|
74 |
+
"fairchem-core@git+https://github.com/facebookresearch/fairchem.git#subdirectory=packages/fairchem-core",
|
75 |
+
]
|
76 |
+
orb = [
|
77 |
+
"orb-models==0.4.0",
|
78 |
+
"pynanoflann@git+https://github.com/dwastberg/pynanoflann#egg=af434039ae14bedcbb838a7808924d6689274168",
|
79 |
]
|
80 |
deepmd = [
|
81 |
"torch==2.2.0",
|
scripts/install-linux.sh
CHANGED
@@ -1,8 +1,10 @@
|
|
1 |
-
TORCH=2.
|
2 |
-
CUDA=
|
3 |
uv pip install torch==${TORCH}.0
|
4 |
-
uv pip install torch-scatter -f https://data.pyg.org/whl/torch-${TORCH}.0+${CUDA}.html
|
5 |
-
uv pip install torch-sparse -f https://data.pyg.org/whl/torch-${TORCH}.0+${CUDA}.html
|
6 |
uv pip install dgl -f https://data.dgl.ai/wheels/torch-${TORCH}/${CUDA}/repo.html
|
|
|
|
|
|
|
7 |
uv pip install -e .[test]
|
8 |
uv pip install -e .[mace]
|
|
|
1 |
+
TORCH=2.4
|
2 |
+
CUDA=cu124
|
3 |
uv pip install torch==${TORCH}.0
|
4 |
+
uv pip install torch-scatter torch-sparse -f https://data.pyg.org/whl/torch-${TORCH}.0+${CUDA}.html
|
|
|
5 |
uv pip install dgl -f https://data.dgl.ai/wheels/torch-${TORCH}/${CUDA}/repo.html
|
6 |
+
uv pip install -e .[fairchem]
|
7 |
+
uv pip install -e .[orb]
|
8 |
+
uv pip install -e .[matgl]
|
9 |
uv pip install -e .[test]
|
10 |
uv pip install -e .[mace]
|
serve/tasks/homonuclear-diatomics.py
CHANGED
@@ -5,10 +5,10 @@ import pandas as pd
|
|
5 |
import plotly.colors as pcolors
|
6 |
import plotly.graph_objects as go
|
7 |
import streamlit as st
|
8 |
-
from
|
9 |
from plotly.subplots import make_subplots
|
10 |
|
11 |
-
from
|
12 |
|
13 |
st.markdown(
|
14 |
"""
|
@@ -29,16 +29,7 @@ valid_models = [
|
|
29 |
mlip_methods = container.multiselect(
|
30 |
"MLIPs",
|
31 |
valid_models,
|
32 |
-
[
|
33 |
-
"MACE-MP(M)",
|
34 |
-
"CHGNet",
|
35 |
-
"M3GNet",
|
36 |
-
"MatterSim",
|
37 |
-
"SevenNet",
|
38 |
-
"ORBv2",
|
39 |
-
"eqV2(OMat)",
|
40 |
-
"ANI2x",
|
41 |
-
],
|
42 |
)
|
43 |
dft_methods = container.multiselect("DFT Methods", ["PBE"], ["PBE"])
|
44 |
|
|
|
5 |
import plotly.colors as pcolors
|
6 |
import plotly.graph_objects as go
|
7 |
import streamlit as st
|
8 |
+
from ase.data import chemical_symbols
|
9 |
from plotly.subplots import make_subplots
|
10 |
|
11 |
+
from mlip_arena.models import REGISTRY
|
12 |
|
13 |
st.markdown(
|
14 |
"""
|
|
|
29 |
mlip_methods = container.multiselect(
|
30 |
"MLIPs",
|
31 |
valid_models,
|
32 |
+
["MACE-MP(M)", "MatterSim", "SevenNet", "ORBv2", "eqV2(OMat)", "ANI2x", "eSEN"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
)
|
34 |
dft_methods = container.multiselect("DFT Methods", ["PBE"], ["PBE"])
|
35 |
|