mpdd-alignn 1.2.0__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/LICENSE.md +1 -1
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/PKG-INFO +3 -2
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/pretrained.py +180 -60
- mpdd_alignn-1.3.0/alignn/tests/test_mpddalignn.py +184 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/mpdd_alignn.egg-info/PKG-INFO +3 -2
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/mpdd_alignn.egg-info/SOURCES.txt +1 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/mpdd_alignn.egg-info/requires.txt +1 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/pyproject.toml +4 -2
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/.github/workflows/publishToPyPI.yaml +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/.gitignore +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/MANIFEST.in +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/README.md +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/__init__.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/cli.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/config.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/config.yaml +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/data.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/examples/sample_data/POSCAR-JVASP-10.vasp +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/ff/__init__.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/ff/ff.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/graphs.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/__init__.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/alignn.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/alignn_atomwise.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/alignn_cgcnn.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/alignn_layernorm.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/dense_alignn.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/densegcn.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/gcn.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/icgcnn.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/modified_cgcnn.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/models/utils.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/profile.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/run_alignn_ff.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/__init__.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/alignn_evac.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/all_train_cgcnn.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/compare_cfid.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/cubic_mat_relax.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/data_1.json +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/dataset_props.json +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/defect.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/early_stopping_checker.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/ev_curve.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/ev_curve_comp.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/final_model.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/graph_viz.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/make_test_split_cross_pred.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/plot_ff_results.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/plot_phonons_ff.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/predict.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/predict_db.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/predict_db_all.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_hmof.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_hpov.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_jv.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_jv_class.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_jv_dal.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_mp.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_oqmd.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_pdbbind.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_qetb.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_qm9_jctc.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_all_qmof.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_cgcnn_repo.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_edos_pdos.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/scripts/train_megnet.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/tests/test_alignn_ff.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/tests/test_force_reduction.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/tests/test_prop.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/train.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/train_folder.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/train_folder_ff.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/train_props.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/alignn/utils.py +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/0-Cr8Fe18Ni4.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/1-Cr16Fe8Ni6.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/10-Ce4Ti4O12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/11-Fe10Ni20.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/12-Gd4Cr4O12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/13-Fe16Ni14.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/14-Fe24Ni6.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/15-Ta4Tl4O12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/16-Fe18Ni12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/17-Pr4Ga4O12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/18-Fe28Ni2.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/19-Fe4Ni26.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/2-Fe8Ni22.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/20-Fe8Ni22.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/21-Fe10Ni20.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/22-Fe10Ni20.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/23-Fe12Ni18.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/24-Fe16Ni14.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/25-Fe12Ni18.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/26-Fe8Ni22.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/27-Cr28Fe2.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/28-Fe26Ni4.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/29-Fe12Ni18.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/3-Cr18Fe12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/30-Cr26Fe4.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/4-Fe30.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/5-Cr22Fe8.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/6-Fe2Ni28.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/7-Cr18Fe12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/8-Cr2Fe16Ni12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/example.SigmaPhase/9-Pb8O12.POSCAR +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/mpdd_alignn.egg-info/dependency_links.txt +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/mpdd_alignn.egg-info/top_level.txt +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/quickstart.ipynb +0 -0
- {mpdd_alignn-1.2.0 → mpdd_alignn-1.3.0}/setup.cfg +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mpdd-alignn
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Our version of the NIST-JARVIS ALIGNN optimized in terms of model performance and to some extent reliability, for large-scale deployments over the MPDD infrastructure by Phases Research Lab.
|
|
5
|
-
Author-email: "Adam M. Krajewski" <adam@phaseslab.org>
|
|
5
|
+
Author-email: "Adam M. Krajewski" <adam@phaseslab.org>, Ricardo Amaral <rna5137@psu.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/PhasesResearchLab/mpdd-alignn
|
|
7
7
|
Project-URL: Bug Tracker, https://github.com/PhasesResearchLab/mpdd-alignn/issues
|
|
8
8
|
Project-URL: Research Page, https://phaseslab.com/mpdd
|
|
@@ -34,6 +34,7 @@ Requires-Dist: dgl>=1.1.3
|
|
|
34
34
|
Requires-Dist: scikit-learn>=1.3.1
|
|
35
35
|
Requires-Dist: matplotlib>=3.4.1
|
|
36
36
|
Requires-Dist: tqdm>=4.60.0
|
|
37
|
+
Requires-Dist: natsort>=8.3.0
|
|
37
38
|
Requires-Dist: pandas>=1.2.3
|
|
38
39
|
Requires-Dist: pydantic_settings
|
|
39
40
|
Requires-Dist: ruamel.yaml
|
|
@@ -5,7 +5,7 @@ import os
|
|
|
5
5
|
import sys
|
|
6
6
|
import json
|
|
7
7
|
import time
|
|
8
|
-
from typing import List, Dict, Union
|
|
8
|
+
from typing import List, Dict, Union, Optional
|
|
9
9
|
from importlib import resources
|
|
10
10
|
|
|
11
11
|
# Extra utility imports
|
|
@@ -18,6 +18,7 @@ import pandas as pd
|
|
|
18
18
|
tqdm.pandas()
|
|
19
19
|
from pysmartdl2 import SmartDL
|
|
20
20
|
from dgl.data.utils import save_graphs, load_graphs
|
|
21
|
+
from natsort import natsorted
|
|
21
22
|
|
|
22
23
|
# ML imports
|
|
23
24
|
import torch
|
|
@@ -83,23 +84,46 @@ def unzip_default_models() -> None:
|
|
|
83
84
|
with zipfile.ZipFile(model['model'], 'r') as zip_ref:
|
|
84
85
|
zip_ref.extractall(model['model'].replace('.zip', ''))
|
|
85
86
|
|
|
86
|
-
def
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
87
|
+
def _run_pretrained_models(
|
|
88
|
+
atoms_array: List[Atoms],
|
|
89
|
+
outputs: List[Dict[str, Union[float, str]]],
|
|
90
|
+
mode: str = "serial",
|
|
91
|
+
saveGraphs: bool = False,
|
|
92
|
+
models: Optional[List[str]] = None,
|
|
93
|
+
):
|
|
94
|
+
"""Internal helper to run default models on already parsed structures.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
atoms_array : List[Atoms]
|
|
99
|
+
Array of atomic structures to run predictions on.
|
|
100
|
+
outputs : List[Dict[str, Union[float, str]]]
|
|
101
|
+
List of output dictionaries (typically containing file names).
|
|
102
|
+
mode : str, optional
|
|
103
|
+
Either "serial" or "parallel" for execution mode. Default is "serial".
|
|
104
|
+
saveGraphs : bool, optional
|
|
105
|
+
Whether to save computed graphs to disk. Default is False.
|
|
106
|
+
models : List[str], optional
|
|
107
|
+
List of model names to run. If None, all default models are run.
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
List[Dict[str, Union[float, str]]]
|
|
112
|
+
Output dictionaries with model predictions added as new keys.
|
|
113
|
+
"""
|
|
114
|
+
# Validate and filter models
|
|
115
|
+
if models is None:
|
|
116
|
+
active_models = default_models
|
|
117
|
+
else:
|
|
118
|
+
model_names_set = {m["name"] for m in default_models}
|
|
119
|
+
invalid_models = [m for m in models if m not in model_names_set]
|
|
120
|
+
if invalid_models:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
f"The following model names were not found in default models: {invalid_models}. "
|
|
123
|
+
f"Available models: {sorted(model_names_set)}"
|
|
124
|
+
)
|
|
125
|
+
active_models = [m for m in default_models if m["name"] in set(models)]
|
|
126
|
+
|
|
103
127
|
# Convert all Atoms to Graphs
|
|
104
128
|
print(f"Converting {len(atoms_array)} structures to graphs...", flush=True)
|
|
105
129
|
if mode == "serial":
|
|
@@ -115,86 +139,182 @@ def run_models_from_directory(
|
|
|
115
139
|
)
|
|
116
140
|
else:
|
|
117
141
|
raise ValueError(f"Mode {mode} not implemented!")
|
|
118
|
-
|
|
142
|
+
|
|
119
143
|
if saveGraphs:
|
|
120
144
|
print("Saving graphs to disk...", flush=True)
|
|
121
145
|
input_files = [i["name"] for i in outputs]
|
|
122
146
|
for g, name in zip(graph_array, input_files):
|
|
123
|
-
save_graphs(f"graphs/{name}.graph.bin", list(g), formats=
|
|
147
|
+
save_graphs(f"graphs/{name}.graph.bin", list(g), formats="coo")
|
|
124
148
|
print("Graphs saved!", flush=True)
|
|
125
149
|
|
|
126
150
|
modelArray = []
|
|
127
|
-
for model in
|
|
128
|
-
modelPath = str(resources.files(
|
|
129
|
-
zp = zipfile.ZipFile(modelPath,
|
|
130
|
-
|
|
131
|
-
# This is a workaround for the fact that some models (new ones) have a different naming convention
|
|
132
|
-
# for the ALIGNN checkpoint files.
|
|
151
|
+
for model in active_models:
|
|
152
|
+
modelPath = str(resources.files("alignn").joinpath(model["model"]))
|
|
153
|
+
zp = zipfile.ZipFile(modelPath, "r")
|
|
154
|
+
|
|
133
155
|
modelCheckpoint = [
|
|
134
|
-
i for i in zp.namelist()
|
|
156
|
+
i for i in zp.namelist()
|
|
135
157
|
if ("checkpoint_" in i and "pt" in i) or "best_model.pt" in i
|
|
136
|
-
|
|
158
|
+
]
|
|
137
159
|
if len(modelCheckpoint) == 0:
|
|
138
|
-
raise ValueError(
|
|
160
|
+
raise ValueError(
|
|
161
|
+
f"No model identifier found in {modelPath} for model {model['name']}!"
|
|
162
|
+
)
|
|
139
163
|
if len(modelCheckpoint) > 1:
|
|
140
|
-
print(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
164
|
+
print(
|
|
165
|
+
f"Multiple model identifiers ({len(modelCheckpoint)}) found in "
|
|
166
|
+
f"{modelPath} for model {model['name']}: {modelCheckpoint}. "
|
|
167
|
+
"Using the first one in the list.",
|
|
168
|
+
flush=True,
|
|
169
|
+
)
|
|
170
|
+
modelCheckpoint = modelCheckpoint[0]
|
|
171
|
+
|
|
172
|
+
config = json.loads(
|
|
173
|
+
zp.read([i for i in zp.namelist() if "config.json" in i][0])
|
|
174
|
+
)
|
|
145
175
|
data = zipfile.ZipFile(modelPath).read(modelCheckpoint)
|
|
146
|
-
|
|
147
|
-
# Create model based on the model name in config
|
|
176
|
+
|
|
148
177
|
model_type = config["model"]["name"]
|
|
149
178
|
if model_type == "alignn":
|
|
150
179
|
loadedModel = ALIGNN(ALIGNNConfig(**config["model"]))
|
|
151
180
|
elif model_type == "alignn_atomwise":
|
|
152
181
|
loadedModel = ALIGNNAtomWise(ALIGNNAtomWiseConfig(**config["model"]))
|
|
153
182
|
else:
|
|
154
|
-
raise ValueError(
|
|
183
|
+
raise ValueError(
|
|
184
|
+
f"Unknown model type: {model_type} for model {model['name']}"
|
|
185
|
+
)
|
|
155
186
|
|
|
156
|
-
|
|
187
|
+
fd, filename = tempfile.mkstemp()
|
|
188
|
+
os.close(fd)
|
|
157
189
|
with open(filename, "wb") as f:
|
|
158
190
|
f.write(data)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
checkpoint = torch.load(filename, map_location='cpu')
|
|
191
|
+
|
|
192
|
+
checkpoint = torch.load(filename, map_location="cpu")
|
|
162
193
|
if "model" in checkpoint:
|
|
163
194
|
loadedModel.load_state_dict(checkpoint["model"])
|
|
164
195
|
else:
|
|
165
|
-
# Some checkpoints store the model state dict directly
|
|
166
196
|
loadedModel.load_state_dict(checkpoint)
|
|
167
|
-
|
|
168
|
-
loadedModel.to(
|
|
197
|
+
|
|
198
|
+
loadedModel.to("cpu")
|
|
169
199
|
loadedModel.eval()
|
|
170
200
|
modelArray.append(loadedModel)
|
|
171
|
-
|
|
201
|
+
|
|
172
202
|
print(f"Model {model['name']} loaded!", flush=True)
|
|
173
|
-
|
|
174
|
-
print(
|
|
175
|
-
|
|
176
|
-
|
|
203
|
+
|
|
204
|
+
print(
|
|
205
|
+
f"Running {len(active_models)} models on {len(graph_array)} structures...",
|
|
206
|
+
flush=True,
|
|
207
|
+
)
|
|
208
|
+
for model, loaded_model in zip(active_models, modelArray):
|
|
177
209
|
for g, out in zip(graph_array, outputs):
|
|
178
210
|
model_output = loaded_model([g[0], g[1]])
|
|
179
|
-
|
|
180
|
-
# Handle different output formats
|
|
211
|
+
|
|
181
212
|
if isinstance(model_output, dict):
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
out_data = model_output['out'].item()
|
|
213
|
+
if "out" in model_output:
|
|
214
|
+
out_data = model_output["out"].item()
|
|
185
215
|
else:
|
|
186
|
-
# Take the first value if dict has other keys
|
|
187
216
|
out_data = list(model_output.values())[0].item()
|
|
188
217
|
else:
|
|
189
|
-
# For regular models that return tensor directly
|
|
190
218
|
out_data = model_output.item()
|
|
191
|
-
|
|
192
|
-
out[model['name']] = round(out_data, 6)
|
|
193
|
-
|
|
194
|
-
print("All models runs complete!", flush=True)
|
|
195
219
|
|
|
220
|
+
out[model["name"]] = round(out_data, 6)
|
|
221
|
+
|
|
222
|
+
print("All models runs complete!", flush=True)
|
|
196
223
|
return outputs
|
|
197
224
|
|
|
225
|
+
|
|
226
|
+
def run_models_from_directory(
|
|
227
|
+
directory: str,
|
|
228
|
+
mode: str = "serial",
|
|
229
|
+
saveGraphs: bool = False,
|
|
230
|
+
models: Optional[List[str]] = None,
|
|
231
|
+
):
|
|
232
|
+
"""Run default models on all structures in a directory that are either in POSCAR or CIF format.
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
directory : str
|
|
237
|
+
Path to directory containing structure files.
|
|
238
|
+
mode : str, optional
|
|
239
|
+
Either "serial" or "parallel" for execution mode. Default is "serial".
|
|
240
|
+
saveGraphs : bool, optional
|
|
241
|
+
Whether to save computed graphs to disk. Default is False.
|
|
242
|
+
models : List[str], optional
|
|
243
|
+
List of model names to run. If None, all default models are run.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
List[Dict[str, Union[float, str]]]
|
|
248
|
+
Output dictionaries with model predictions added as new keys.
|
|
249
|
+
"""
|
|
250
|
+
atoms_array = []
|
|
251
|
+
outputs: List[Dict[str, Union[float, str]]] = []
|
|
252
|
+
|
|
253
|
+
for file in natsorted(os.listdir(directory)):
|
|
254
|
+
if file.endswith(("poscar", "POSCAR", "vasp", "VASP")):
|
|
255
|
+
outputs.append({"name": file})
|
|
256
|
+
atoms_array.append(Atoms.from_poscar(os.path.join(directory, file)))
|
|
257
|
+
elif file.endswith(("cif", "CIF")):
|
|
258
|
+
outputs.append({"name": file})
|
|
259
|
+
atoms_array.append(Atoms.from_cif(os.path.join(directory, file)))
|
|
260
|
+
else:
|
|
261
|
+
print(f"Skipping file {file} as it is not a POSCAR or CIF file!", flush=True)
|
|
262
|
+
|
|
263
|
+
return _run_pretrained_models(
|
|
264
|
+
atoms_array=atoms_array,
|
|
265
|
+
outputs=outputs,
|
|
266
|
+
mode=mode,
|
|
267
|
+
saveGraphs=saveGraphs,
|
|
268
|
+
models=models,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def run_models_from_structure(
|
|
273
|
+
structure: str,
|
|
274
|
+
mode: str = "serial",
|
|
275
|
+
saveGraphs: bool = False,
|
|
276
|
+
models: Optional[List[str]] = None,
|
|
277
|
+
):
|
|
278
|
+
"""Run default models on a single structure file in POSCAR or CIF format.
|
|
279
|
+
|
|
280
|
+
Parameters
|
|
281
|
+
----------
|
|
282
|
+
structure : str
|
|
283
|
+
Path to structure file.
|
|
284
|
+
mode : str, optional
|
|
285
|
+
Either "serial" or "parallel" for execution mode. Default is "serial".
|
|
286
|
+
saveGraphs : bool, optional
|
|
287
|
+
Whether to save computed graphs to disk. Default is False.
|
|
288
|
+
models : List[str], optional
|
|
289
|
+
List of model names to run. If None, all default models are run.
|
|
290
|
+
|
|
291
|
+
Returns
|
|
292
|
+
-------
|
|
293
|
+
List[Dict[str, Union[float, str]]]
|
|
294
|
+
List containing one output dictionary for the input structure, with
|
|
295
|
+
model predictions added as new keys.
|
|
296
|
+
"""
|
|
297
|
+
atoms_array = []
|
|
298
|
+
outputs: List[Dict[str, Union[float, str]]] = []
|
|
299
|
+
|
|
300
|
+
file = os.path.basename(structure)
|
|
301
|
+
if file.endswith(("poscar", "POSCAR", "vasp", "VASP")):
|
|
302
|
+
outputs.append({"name": file})
|
|
303
|
+
atoms_array.append(Atoms.from_poscar(structure))
|
|
304
|
+
elif file.endswith(("cif", "CIF")):
|
|
305
|
+
outputs.append({"name": file})
|
|
306
|
+
atoms_array.append(Atoms.from_cif(structure))
|
|
307
|
+
else:
|
|
308
|
+
raise ValueError(f"File {structure} is not a POSCAR/VASP or CIF file!")
|
|
309
|
+
|
|
310
|
+
return _run_pretrained_models(
|
|
311
|
+
atoms_array=atoms_array,
|
|
312
|
+
outputs=outputs,
|
|
313
|
+
mode=mode,
|
|
314
|
+
saveGraphs=saveGraphs,
|
|
315
|
+
models=models,
|
|
316
|
+
)
|
|
317
|
+
|
|
198
318
|
# ******* Old method to download models *******
|
|
199
319
|
"""
|
|
200
320
|
Name of the model, figshare link, number of outputs,
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Standalone regression tests for pretrained MPDD ALIGNN wrappers."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest import TestCase, mock
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from alignn import pretrained
|
|
9
|
+
from alignn.pretrained import _run_pretrained_models
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_invalid_model_name_raises_error():
|
|
13
|
+
"""Regression test: passing invalid model names should raise a ValueError
|
|
14
|
+
from `_run_pretrained_models` with the expected message.
|
|
15
|
+
"""
|
|
16
|
+
invalid_models_list = ["fake_model_name_xyz", "another_bad_model"]
|
|
17
|
+
|
|
18
|
+
with pytest.raises(ValueError, match="The following model names were not found in default models"):
|
|
19
|
+
_run_pretrained_models(
|
|
20
|
+
atoms_array=[],
|
|
21
|
+
outputs=[],
|
|
22
|
+
models=invalid_models_list,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _fake_run_pretrained_models(
|
|
27
|
+
atoms_array, outputs, mode="serial", saveGraphs=False, models=None
|
|
28
|
+
):
|
|
29
|
+
"""Return deterministic payloads so the wrapper behavior can be compared.
|
|
30
|
+
|
|
31
|
+
Includes models information in response for validation.
|
|
32
|
+
"""
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
**out,
|
|
36
|
+
"mock_pred": f"{out['name']}|{mode}|{saveGraphs}",
|
|
37
|
+
"models_used": models, # Include for verification
|
|
38
|
+
}
|
|
39
|
+
for out in outputs
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Get model names from default models
|
|
44
|
+
_default_models = pretrained.get_default_models()
|
|
45
|
+
_model_names = [m["name"] for m in _default_models]
|
|
46
|
+
_first_model = _model_names[0] if _model_names else "model_1"
|
|
47
|
+
_second_model = _model_names[1] if len(_model_names) > 1 else "model_2"
|
|
48
|
+
_third_model = _model_names[2] if len(_model_names) > 2 else "model_3"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TestMPDDAlignNPretrainedWrappers(TestCase):
|
|
52
|
+
def test_run_models_from_directory_and_structure_agree_for_sigma_phase(self):
|
|
53
|
+
sigma_phase_dir = Path(__file__).resolve().parents[2] / "example.SigmaPhase"
|
|
54
|
+
poscar_files = sorted(
|
|
55
|
+
path
|
|
56
|
+
for path in sigma_phase_dir.iterdir()
|
|
57
|
+
if path.is_file() and path.suffix.lower() == ".poscar"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
self.assertGreater(
|
|
61
|
+
len(poscar_files),
|
|
62
|
+
0,
|
|
63
|
+
"Expected at least one POSCAR file under example.SigmaPhase.",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
with mock.patch.object(
|
|
67
|
+
pretrained,
|
|
68
|
+
"_run_pretrained_models",
|
|
69
|
+
side_effect=_fake_run_pretrained_models,
|
|
70
|
+
):
|
|
71
|
+
directory_results = pretrained.run_models_from_directory(
|
|
72
|
+
str(sigma_phase_dir),
|
|
73
|
+
mode="serial",
|
|
74
|
+
saveGraphs=False,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
directory_by_name = {result["name"]: result for result in directory_results}
|
|
78
|
+
expected_names = {path.name for path in poscar_files}
|
|
79
|
+
|
|
80
|
+
self.assertEqual(set(directory_by_name), expected_names)
|
|
81
|
+
|
|
82
|
+
for path in poscar_files:
|
|
83
|
+
with self.subTest(structure=path.name):
|
|
84
|
+
structure_result = pretrained.run_models_from_structure(
|
|
85
|
+
str(path),
|
|
86
|
+
mode="serial",
|
|
87
|
+
saveGraphs=False,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
self.assertEqual(len(structure_result), 1)
|
|
91
|
+
self.assertEqual(structure_result[0], directory_by_name[path.name])
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.mark.parametrize(
|
|
95
|
+
"models_selection,mode,saveGraphs",
|
|
96
|
+
[
|
|
97
|
+
(None, "serial", False), # Default: all models
|
|
98
|
+
([_first_model, _second_model], "serial", False), # Two models
|
|
99
|
+
([_first_model], "serial", True), # Single model with saveGraphs
|
|
100
|
+
([], "serial", False), # Empty list (no models)
|
|
101
|
+
([_third_model, _first_model], "parallel", False), # Multiple models, parallel mode
|
|
102
|
+
],
|
|
103
|
+
)
|
|
104
|
+
class TestModelFiltering:
|
|
105
|
+
"""Parametrized tests for model filtering functionality."""
|
|
106
|
+
|
|
107
|
+
def test_run_models_from_structure_with_model_filter(
|
|
108
|
+
self, models_selection, mode, saveGraphs
|
|
109
|
+
):
|
|
110
|
+
"""Test that run_models_from_structure correctly passes and validates model filtering."""
|
|
111
|
+
sigma_phase_dir = Path(__file__).resolve().parents[2] / "example.SigmaPhase"
|
|
112
|
+
poscar_files = sorted(
|
|
113
|
+
path
|
|
114
|
+
for path in sigma_phase_dir.iterdir()
|
|
115
|
+
if path.is_file() and path.suffix.lower() == ".poscar"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
assert len(poscar_files) > 0, "Expected at least one POSCAR file in example.SigmaPhase"
|
|
119
|
+
|
|
120
|
+
test_path = str(poscar_files[0])
|
|
121
|
+
|
|
122
|
+
with mock.patch.object(
|
|
123
|
+
pretrained,
|
|
124
|
+
"_run_pretrained_models",
|
|
125
|
+
side_effect=_fake_run_pretrained_models,
|
|
126
|
+
) as mock_run:
|
|
127
|
+
results = pretrained.run_models_from_structure(
|
|
128
|
+
test_path,
|
|
129
|
+
mode=mode,
|
|
130
|
+
saveGraphs=saveGraphs,
|
|
131
|
+
models=models_selection,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Verify the mock was called with correct parameters
|
|
135
|
+
mock_run.assert_called_once()
|
|
136
|
+
call_kwargs = mock_run.call_args.kwargs
|
|
137
|
+
|
|
138
|
+
# Verify models parameter was passed correctly
|
|
139
|
+
assert call_kwargs["models"] == models_selection, (
|
|
140
|
+
f"Expected models={models_selection}, got {call_kwargs['models']}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Verify other parameters
|
|
144
|
+
assert call_kwargs["mode"] == mode
|
|
145
|
+
assert call_kwargs["saveGraphs"] == saveGraphs
|
|
146
|
+
|
|
147
|
+
# Verify the models information is in the returned results
|
|
148
|
+
assert len(results) == 1
|
|
149
|
+
assert results[0]["models_used"] == models_selection
|
|
150
|
+
|
|
151
|
+
def test_run_models_from_directory_with_model_filter(
|
|
152
|
+
self, models_selection, mode, saveGraphs
|
|
153
|
+
):
|
|
154
|
+
"""Test that run_models_from_directory correctly passes and validates model filtering."""
|
|
155
|
+
sigma_phase_dir = Path(__file__).resolve().parents[2] / "example.SigmaPhase"
|
|
156
|
+
|
|
157
|
+
with mock.patch.object(
|
|
158
|
+
pretrained,
|
|
159
|
+
"_run_pretrained_models",
|
|
160
|
+
side_effect=_fake_run_pretrained_models,
|
|
161
|
+
) as mock_run:
|
|
162
|
+
results = pretrained.run_models_from_directory(
|
|
163
|
+
str(sigma_phase_dir),
|
|
164
|
+
mode=mode,
|
|
165
|
+
saveGraphs=saveGraphs,
|
|
166
|
+
models=models_selection,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Verify the mock was called with correct parameters
|
|
170
|
+
mock_run.assert_called_once()
|
|
171
|
+
call_kwargs = mock_run.call_args.kwargs
|
|
172
|
+
|
|
173
|
+
# Verify models parameter was passed correctly
|
|
174
|
+
assert call_kwargs["models"] == models_selection, (
|
|
175
|
+
f"Expected models={models_selection}, got {call_kwargs['models']}"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Verify other parameters
|
|
179
|
+
assert call_kwargs["mode"] == mode
|
|
180
|
+
assert call_kwargs["saveGraphs"] == saveGraphs
|
|
181
|
+
|
|
182
|
+
# Verify the models information is in all returned results
|
|
183
|
+
for result in results:
|
|
184
|
+
assert result["models_used"] == models_selection
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mpdd-alignn
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Our version of the NIST-JARVIS ALIGNN optimized in terms of model performance and to some extent reliability, for large-scale deployments over the MPDD infrastructure by Phases Research Lab.
|
|
5
|
-
Author-email: "Adam M. Krajewski" <adam@phaseslab.org>
|
|
5
|
+
Author-email: "Adam M. Krajewski" <adam@phaseslab.org>, Ricardo Amaral <rna5137@psu.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/PhasesResearchLab/mpdd-alignn
|
|
7
7
|
Project-URL: Bug Tracker, https://github.com/PhasesResearchLab/mpdd-alignn/issues
|
|
8
8
|
Project-URL: Research Page, https://phaseslab.com/mpdd
|
|
@@ -34,6 +34,7 @@ Requires-Dist: dgl>=1.1.3
|
|
|
34
34
|
Requires-Dist: scikit-learn>=1.3.1
|
|
35
35
|
Requires-Dist: matplotlib>=3.4.1
|
|
36
36
|
Requires-Dist: tqdm>=4.60.0
|
|
37
|
+
Requires-Dist: natsort>=8.3.0
|
|
37
38
|
Requires-Dist: pandas>=1.2.3
|
|
38
39
|
Requires-Dist: pydantic_settings
|
|
39
40
|
Requires-Dist: ruamel.yaml
|
|
@@ -68,6 +68,7 @@ alignn/scripts/train_edos_pdos.py
|
|
|
68
68
|
alignn/scripts/train_megnet.py
|
|
69
69
|
alignn/tests/test_alignn_ff.py
|
|
70
70
|
alignn/tests/test_force_reduction.py
|
|
71
|
+
alignn/tests/test_mpddalignn.py
|
|
71
72
|
alignn/tests/test_prop.py
|
|
72
73
|
example.SigmaPhase/0-Cr8Fe18Ni4.POSCAR
|
|
73
74
|
example.SigmaPhase/1-Cr16Fe8Ni6.POSCAR
|
|
@@ -4,9 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mpdd-alignn"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.3.0"
|
|
8
8
|
authors = [
|
|
9
|
-
{ name="Adam M. Krajewski", email="adam@phaseslab.org" }
|
|
9
|
+
{ name="Adam M. Krajewski", email="adam@phaseslab.org" },
|
|
10
|
+
{ name="Ricardo Amaral", email="rna5137@psu.edu" },
|
|
10
11
|
]
|
|
11
12
|
classifiers = [
|
|
12
13
|
"Programming Language :: Python :: 3",
|
|
@@ -37,6 +38,7 @@ dependencies = [
|
|
|
37
38
|
"scikit-learn>=1.3.1",
|
|
38
39
|
"matplotlib>=3.4.1",
|
|
39
40
|
"tqdm>=4.60.0",
|
|
41
|
+
"natsort>=8.3.0",
|
|
40
42
|
"pandas>=1.2.3",
|
|
41
43
|
"pydantic_settings",
|
|
42
44
|
"ruamel.yaml",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|