Skip to content

Commit 4a4bfd9

Browse files
Add MatPES forcefields (#1158)
* add matpes models / bump matgl * ensure MD ensemble is correctly rehydrated during fireworks style runs * pin mdanalysis, add matpes tests * modify schemas slightly to accommodate single ase calculator addition; pin openmdanalysis version * precommit * resolve torch conflict * pin chgnet version to slightly older release for torch interop * fix ase utils test and pin typing to see if that fixes docs * pin upper bound on setuptools from major breaking changes * pin upper bound on setuptools from major breaking changes * pin upper bound on setuptools from major breaking changes * keep experimenting to see how to mitigate setuptools breakage * bump ase version for newer md calc * fix num threads in forcefields tests * modify test tolerance * change ref data for matpes pbe stress
1 parent b435eab commit 4a4bfd9

File tree

13 files changed

+321
-129
lines changed

13 files changed

+321
-129
lines changed

.github/workflows/docs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
- name: Install dependencies
3434
run: |
3535
python -m pip install --upgrade pip
36+
pip install "setuptools==77.0.3"
3637
pip install .[strict,docs]
3738
3839
- name: Copy tutorials

pyproject.toml

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["setuptools >= 42", "versioningit >= 1,< 4", "wheel"]
2+
requires = ["setuptools >= 42, <78", "versioningit >= 1,< 4", "wheel"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
@@ -55,7 +55,7 @@ forcefields = [
5555
"calorine>=3.0",
5656
"chgnet>=0.2.2",
5757
"mace-torch>=0.3.3",
58-
"matgl>=1.1.3",
58+
"matgl>=1.2.1",
5959
"torchdata<=0.7.1",
6060
# quippy-ase support for py3.12 tracked in https://github.com/libAtoms/QUIP/issues/645
6161
"quippy-ase>=0.9.14; python_version < '3.12'",
@@ -95,7 +95,7 @@ tests = [
9595
]
9696
strict = [
9797
"PyYAML==6.0.2",
98-
"ase==3.23.0",
98+
"ase==3.24.0",
9999
"cclib==1.8.1",
100100
"click==8.1.7",
101101
"custodian==2024.10.16",
@@ -119,6 +119,7 @@ strict = [
119119
"typing-extensions==4.12.2",
120120
]
121121
strict-openff = [
122+
"mdanalysis==2.7.0",
122123
"monty==2025.3.3",
123124
"openmm-mdanalysis-reporter==0.1.0",
124125
"openmm==8.1.1",
@@ -127,12 +128,12 @@ strict-openff = [
127128
]
128129
strict-forcefields = [
129130
"calorine==3.0",
130-
"chgnet==0.4.0",
131+
"chgnet==0.3.8",
131132
"mace-torch==0.3.10",
132-
"matgl==1.1.3",
133+
"matgl==1.2.1",
133134
"quippy-ase==0.9.14; python_version < '3.12'",
134135
"sevenn==0.10.3",
135-
"torch==2.6.0",
136+
"torch==2.2.0",
136137
"torchdata==0.7.1", # TODO: remove when issue fixed
137138
]
138139

src/atomate2/ase/jobs.py

+67-12
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
from __future__ import annotations
44

55
import logging
6+
import time
67
from abc import ABCMeta, abstractmethod
78
from dataclasses import dataclass, field
89
from typing import TYPE_CHECKING
910

1011
from ase.io import Trajectory as AseTrajectory
1112
from emmet.core.vasp.calculation import StoreTrajectoryOption
1213
from jobflow import Maker, job
14+
from pymatgen.core import Molecule, Structure
1315
from pymatgen.core.trajectory import Trajectory as PmgTrajectory
16+
from pymatgen.io.ase import AseAtomsAdaptor
1417

1518
from atomate2.ase.schemas import AseResult, AseTaskDoc
1619
from atomate2.ase.utils import AseRelaxer
@@ -21,7 +24,6 @@
2124
from pathlib import Path
2225

2326
from ase.calculators.calculator import Calculator
24-
from pymatgen.core import Molecule, Structure
2527

2628
from atomate2.ase.schemas import AseMoleculeTaskDoc, AseStructureTaskDoc
2729

@@ -33,11 +35,33 @@ class AseMaker(Maker, metaclass=ABCMeta):
3335
"""
3436
Define basic template of ASE-based jobs.
3537
36-
This class defines two functions relevant attributes
37-
for the ASE TaskDoc schemas, as well as two methods
38-
that must be implemented in subclasses:
39-
1. `calculator`: the ASE .Calculator object
40-
2. `run_ase`: which actually makes the call to ASE.
38+
This class defines relevant attributes for the ASE TaskDoc
39+
schemas, and one method that must be implemented in subclasses:
40+
`calculator`: the ASE .Calculator object
41+
42+
The intent of this class is twofold: if users wish to have a
43+
high-throughput way to access a calculator, they need only
44+
subclass this class with a calculator defined, e.g., the following
45+
is sufficient to define an EMT static calculator with basic I/O:
46+
47+
```python
48+
from ase.calculators.emt import EMT
49+
50+
51+
@dataclass
52+
class EMTStaticMaker(AseMaker):
53+
name: str = "EMT static maker"
54+
55+
@property
56+
def calculator(self):
57+
return EMT()
58+
```
59+
60+
Note that the user should adapt `run_ase`, which is not a job
61+
and makes a call to ASE, and `make`, which is a job, to their uses.
62+
63+
`run_ase` should return an `AseResult` which has basic calculation info.
64+
`make` should return a pydantic-based document model with more details.
4165
4266
Parameters
4367
----------
@@ -69,17 +93,35 @@ class AseMaker(Maker, metaclass=ABCMeta):
6993
store_trajectory: StoreTrajectoryOption = StoreTrajectoryOption.NO
7094
tags: list[str] | None = None
7195

72-
@abstractmethod
96+
@job(data=_ASE_DATA_OBJECTS)
97+
def make(
98+
self,
99+
mol_or_struct: Molecule | Structure,
100+
prev_dir: str | Path | None = None,
101+
) -> AseStructureTaskDoc | AseMoleculeTaskDoc:
102+
"""
103+
Run ASE as job, can be re-implemented in subclasses.
104+
105+
Parameters
106+
----------
107+
mol_or_struct: .Molecule or .Structure
108+
pymatgen molecule or structure
109+
prev_dir : str or Path or None
110+
A previous calculation directory to copy output files from. Unused, just
111+
added to match the method signature of other makers.
112+
"""
113+
return AseTaskDoc.to_mol_or_struct_metadata_doc(
114+
getattr(self.calculator, "name", type(self.calculator).__name__),
115+
self.run_ase(mol_or_struct, prev_dir=prev_dir),
116+
)
117+
73118
def run_ase(
74119
self,
75120
mol_or_struct: Structure | Molecule,
76121
prev_dir: str | Path | None = None,
77122
) -> AseResult:
78123
"""
79-
Run ASE, method to be implemented in subclasses.
80-
81-
This method exists to permit subclasses to redefine `make`
82-
for different output schemas.
124+
Run ASE, can be re-implemented in subclasses.
83125
84126
Parameters
85127
----------
@@ -89,7 +131,20 @@ def run_ase(
89131
A previous calculation directory to copy output files from. Unused, just
90132
added to match the method signature of other makers.
91133
"""
92-
raise NotImplementedError
134+
is_mol = isinstance(mol_or_struct, Molecule)
135+
adaptor = AseAtomsAdaptor()
136+
atoms = adaptor.get_atoms(mol_or_struct)
137+
atoms.calc = self.calculator
138+
t_i = time.perf_counter()
139+
final_energy = atoms.get_potential_energy()
140+
t_f = time.perf_counter()
141+
return AseResult(
142+
final_mol_or_struct=getattr(
143+
adaptor, f"get_{'molecule' if is_mol else 'structure'}"
144+
)(atoms),
145+
final_energy=final_energy,
146+
elapsed_time=t_f - t_i,
147+
)
93148

94149
@property
95150
@abstractmethod

src/atomate2/ase/md.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class AseMDMaker(AseMaker, metaclass=ABCMeta):
180180
def __post_init__(self) -> None:
181181
"""Ensure that ensemble is an enum."""
182182
if isinstance(self.ensemble, str):
183-
self.ensemble = MDEnsemble(self.ensemble)
183+
self.ensemble = MDEnsemble(self.ensemble.split("MDEnsemble.")[-1])
184184

185185
@staticmethod
186186
def _interpolate_quantity(values: Sequence | np.ndarray, n_pts: int) -> np.ndarray:

0 commit comments

Comments
 (0)