-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathcalc_UpU46.py
More file actions
147 lines (119 loc) · 3.99 KB
/
Copy pathcalc_UpU46.py
File metadata and controls
147 lines (119 loc) · 3.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""
Calculate the UpU46 benchmark dataset for RNA backbone conformations.
Journal of Chemical Theory and Computation,
2015 11 (10), 4972-4991.
DOI: 10.1021/acs.jctc.5b00515.
"""
from __future__ import annotations
from pathlib import Path
from ase import Atoms, units
from ase.io import read, write
import mlipx
from mlipx.abc import NodeWithCalculator
from tqdm import tqdm
import zntrack
from ml_peg.calcs.utils.utils import chdir, download_s3_data
from ml_peg.models.get_models import load_models
from ml_peg.models.models import current_models
MODELS = load_models(current_models)
KCAL_TO_EV = units.kcal / units.mol
OUT_PATH = Path(__file__).parent / "outputs"
class UpU46Benchmark(zntrack.Node):
"""Compute the benchmark."""
model: NodeWithCalculator = zntrack.deps()
model_name: str = zntrack.params()
@staticmethod
def get_atoms(atoms_path: Path) -> Atoms:
"""
Read atoms object and add charge and spin.
Parameters
----------
atoms_path
Path to atoms object.
Returns
-------
atoms
ASE atoms object.
"""
atoms = read(atoms_path)
atoms.info["charge"] = -1
atoms.info["spin"] = 1
if atoms.calc is not None:
if "energy" in atoms.calc.results:
del atoms.calc.results["energy"]
return atoms
def get_ref_energies(self, data_path: Path) -> None:
"""
Get reference conformer energies.
Parameters
----------
data_path
Path to the structure.
"""
self.ref_energies = {}
with open(data_path / "references") as lines:
for i, line in enumerate(lines):
# Skip the comment lines
if i < 5:
continue
items = line.strip().split()
label = items[2]
self.ref_energies[label] = float(items[7]) * KCAL_TO_EV
def run(self):
"""Run new benchmark."""
data_path = (
download_s3_data(
filename="UPU46.zip",
key="inputs/conformers/UpU46/UpU46.zip",
)
/ "UPU46"
)
zero_conf_label = "2p"
self.get_ref_energies(data_path)
# Read in data and attach calculator
calc = self.model.get_calculator()
# Add D3 calculator for this test
calc = self.model.add_d3_calculator(calc)
conf_lowest = self.get_atoms(data_path / f"{zero_conf_label}.xyz")
conf_lowest.calc = calc
e_conf_lowest_model = conf_lowest.get_potential_energy()
for label, e_ref in tqdm(self.ref_energies.items()):
# Skip the reference conformer for
# which the error is automatically zero
if label == zero_conf_label:
continue
atoms = self.get_atoms(data_path / f"{label}.xyz")
atoms.calc = calc
atoms.info["model_rel_energy"] = (
atoms.get_potential_energy() - e_conf_lowest_model
)
atoms.info["ref_energy"] = e_ref
atoms.calc = None
write_dir = OUT_PATH / self.model_name
write_dir.mkdir(parents=True, exist_ok=True)
write(write_dir / f"{label}.xyz", atoms)
def build_project(repro: bool = False) -> None:
"""
Build mlipx project.
Parameters
----------
repro
Whether to call dvc repro -f after building.
"""
project = mlipx.Project()
benchmark_node_dict = {}
for model_name, model in MODELS.items():
with project.group(model_name):
benchmark = UpU46Benchmark(
model=model,
model_name=model_name,
)
benchmark_node_dict[model_name] = benchmark
if repro:
with chdir(Path(__file__).parent):
project.repro(build=True, force=True)
else:
project.build()
def test_upu46():
"""Run UpU46 benchmark via pytest."""
build_project(repro=True)