upet 0.1.0__py3-none-any.whl
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.
- upet/__init__.py +1 -0
- upet/_metadata.py +61 -0
- upet/_models.py +256 -0
- upet/_version.py +28 -0
- upet/calculator.py +462 -0
- upet/explore/__init__.py +4 -0
- upet/explore/_explorer.py +163 -0
- upet/explore/_featurizer.py +200 -0
- upet/explore/_modules.py +56 -0
- upet/modules.py +48 -0
- upet/utils.py +178 -0
- upet-0.1.0.dist-info/METADATA +586 -0
- upet-0.1.0.dist-info/RECORD +16 -0
- upet-0.1.0.dist-info/WHEEL +5 -0
- upet-0.1.0.dist-info/licenses/LICENSE +28 -0
- upet-0.1.0.dist-info/top_level.txt +1 -0
upet/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
upet/_metadata.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from metatomic.torch import ModelMetadata
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_upet_metadata(model: str, size: str, version: str):
|
|
5
|
+
description = (
|
|
6
|
+
r"A universal interatomic potential for advanced materials modeling "
|
|
7
|
+
r"based on a Point-Edge Transformer (PET) architecture, and trained on "
|
|
8
|
+
r"the {} dataset. Model size: {}"
|
|
9
|
+
)
|
|
10
|
+
dataset = model.split("-")[1].upper()
|
|
11
|
+
|
|
12
|
+
if "mad" in model.lower():
|
|
13
|
+
authors = [
|
|
14
|
+
"Arslan Mazitov (arslan.mazitov@epfl.ch)",
|
|
15
|
+
"Filippo Bigi",
|
|
16
|
+
"Matthias Kellner",
|
|
17
|
+
"Paolo Pegolo",
|
|
18
|
+
"Davide Tisi",
|
|
19
|
+
"Guillaume Fraux",
|
|
20
|
+
"Sergey Pozdnyakov",
|
|
21
|
+
"Philip Loche",
|
|
22
|
+
"Michele Ceriotti (michele.ceriotti@epfl.ch)",
|
|
23
|
+
]
|
|
24
|
+
else:
|
|
25
|
+
authors = [
|
|
26
|
+
"Filippo Bigi (filippo.bigi@epfl.ch)",
|
|
27
|
+
"Arslan Mazitov (arslan.mazitov@epfl.ch)",
|
|
28
|
+
"Paolo Pegolo",
|
|
29
|
+
"Michele Ceriotti (michele.ceriotti@epfl.ch)",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
return ModelMetadata(
|
|
33
|
+
name=f"{model.upper()}-{size.upper()} v{version}",
|
|
34
|
+
description=description.format(dataset, size),
|
|
35
|
+
authors=authors,
|
|
36
|
+
references={
|
|
37
|
+
"architecture": ["https://arxiv.org/abs/2305.19302v3"],
|
|
38
|
+
"model": ["https://doi.org/10.1038/s41467-025-65662-7"],
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_pet_mad_dos_metadata(version: str):
|
|
44
|
+
return ModelMetadata(
|
|
45
|
+
name=f"PET-MAD-DOS v{version}",
|
|
46
|
+
description="A universal machine learning model for the electronic density of states", # noqa: E501
|
|
47
|
+
authors=[
|
|
48
|
+
"Wei Bin How (weibin.how@epfl.ch)",
|
|
49
|
+
"Pol Febrer",
|
|
50
|
+
"Sanggyu Chong",
|
|
51
|
+
"Arslan Mazitov",
|
|
52
|
+
"Filippo Bigi",
|
|
53
|
+
"Matthias Kellner",
|
|
54
|
+
"Sergey Pozdnyakov",
|
|
55
|
+
"Michele Ceriotti (michele.ceriotti@epfl.ch)",
|
|
56
|
+
],
|
|
57
|
+
references={
|
|
58
|
+
"architecture": ["https://arxiv.org/abs/2508.09000"],
|
|
59
|
+
"model": [],
|
|
60
|
+
},
|
|
61
|
+
)
|
upet/_models.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
from urllib.request import urlretrieve
|
|
6
|
+
|
|
7
|
+
import torch
|
|
8
|
+
from huggingface_hub import HfApi, hf_hub_download
|
|
9
|
+
from metatomic.torch import AtomisticModel
|
|
10
|
+
from metatrain.utils.io import load_model as load_metatrain_model
|
|
11
|
+
from packaging.version import Version
|
|
12
|
+
|
|
13
|
+
from ._metadata import get_pet_mad_dos_metadata, get_upet_metadata
|
|
14
|
+
from ._version import (
|
|
15
|
+
PET_MAD_DOS_AVAILABLE_VERSIONS,
|
|
16
|
+
PET_MAD_DOS_LATEST_STABLE_VERSION,
|
|
17
|
+
)
|
|
18
|
+
from .modules import BandgapModel
|
|
19
|
+
from .utils import hf_hub_download_url
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upet_get_size_to_load(model: str, requested_size: Optional[str] = None) -> str:
|
|
23
|
+
"""
|
|
24
|
+
Get the size of a UPET model.
|
|
25
|
+
|
|
26
|
+
:param model: name of the model.
|
|
27
|
+
:param requested_size: a requested size of the model.
|
|
28
|
+
:return: If the model has multiple sizes available, the
|
|
29
|
+
sizes will be chosen based on the following priority: s > m > xs > l > xl,
|
|
30
|
+
depending on availability.
|
|
31
|
+
"""
|
|
32
|
+
# We need to inspect the models in https://huggingface.co/lab-cosmo/upet/tree/main/models
|
|
33
|
+
# and get the available sizes for each model.
|
|
34
|
+
hf_api = HfApi()
|
|
35
|
+
repo_files = hf_api.list_repo_files("lab-cosmo/upet")
|
|
36
|
+
files_in_models_folder = [f[7:] for f in repo_files if f.startswith("models/")]
|
|
37
|
+
all_model_files = [
|
|
38
|
+
f
|
|
39
|
+
for f in files_in_models_folder
|
|
40
|
+
if f.startswith(f"{model}-") and f.endswith(".ckpt")
|
|
41
|
+
]
|
|
42
|
+
all_model_sizes = [f.split(f"{model}-")[1].split("-")[0] for f in all_model_files]
|
|
43
|
+
all_model_sizes = sorted(set(all_model_sizes))
|
|
44
|
+
|
|
45
|
+
if requested_size is not None:
|
|
46
|
+
if requested_size in all_model_sizes:
|
|
47
|
+
return requested_size
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
f"Requested size {requested_size} not available for model {model}. "
|
|
51
|
+
f"Available sizes are: {all_model_sizes}"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if "s" in all_model_sizes:
|
|
55
|
+
return "s"
|
|
56
|
+
elif "m" in all_model_sizes:
|
|
57
|
+
return "m"
|
|
58
|
+
elif "xs" in all_model_sizes:
|
|
59
|
+
return "xs"
|
|
60
|
+
elif "l" in all_model_sizes:
|
|
61
|
+
return "l"
|
|
62
|
+
elif "xl" in all_model_sizes:
|
|
63
|
+
return "xl"
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError(f"No sizes found for model {model}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def upet_get_version_to_load(
|
|
69
|
+
model: str, size: str, requested_version: Optional[Version] = None
|
|
70
|
+
) -> Version:
|
|
71
|
+
"""
|
|
72
|
+
Get the version of a UPET model.
|
|
73
|
+
|
|
74
|
+
:param model: name of the model.
|
|
75
|
+
:param size: size of the model.
|
|
76
|
+
:param requested_version: a requested version of the model.
|
|
77
|
+
:return: the version to load.
|
|
78
|
+
"""
|
|
79
|
+
if requested_version == "latest":
|
|
80
|
+
requested_version = None
|
|
81
|
+
|
|
82
|
+
hf_api = HfApi()
|
|
83
|
+
repo_files = hf_api.list_repo_files("lab-cosmo/upet")
|
|
84
|
+
files_in_models_folder = [f[7:] for f in repo_files if f.startswith("models/")]
|
|
85
|
+
all_model_files = [
|
|
86
|
+
f
|
|
87
|
+
for f in files_in_models_folder
|
|
88
|
+
if f.startswith(f"{model}-{size}-") and f.endswith(".ckpt")
|
|
89
|
+
]
|
|
90
|
+
all_model_versions = [
|
|
91
|
+
Version(f.split(f"{model}-{size}-")[1].split(".ckpt")[0])
|
|
92
|
+
for f in all_model_files
|
|
93
|
+
]
|
|
94
|
+
all_model_versions = sorted(set(all_model_versions))
|
|
95
|
+
|
|
96
|
+
if requested_version is not None:
|
|
97
|
+
if not isinstance(requested_version, Version):
|
|
98
|
+
requested_version = Version(requested_version)
|
|
99
|
+
if requested_version in all_model_versions:
|
|
100
|
+
return requested_version
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"Requested version {requested_version} not available for model "
|
|
104
|
+
f"{model} size {size}. Available versions are: "
|
|
105
|
+
f"{list(str(v) for v in all_model_versions)}"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return max(all_model_versions)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_upet(
|
|
112
|
+
*, model: str, size: str, version: str, checkpoint_path: Optional[str] = None
|
|
113
|
+
) -> AtomisticModel:
|
|
114
|
+
"""Get a metatomic ``AtomisticModel`` for a UPET MLIP.
|
|
115
|
+
|
|
116
|
+
:param model: name of the UPET model.
|
|
117
|
+
:param size: size of the UPET model.
|
|
118
|
+
:param version: version of the UPET model.
|
|
119
|
+
:param checkpoint_path: path to a checkpoint file to load the model from. If
|
|
120
|
+
provided, the `version` parameter is ignored.
|
|
121
|
+
"""
|
|
122
|
+
if checkpoint_path is not None:
|
|
123
|
+
logging.info(f"Loading model from checkpoint: {checkpoint_path}")
|
|
124
|
+
path = checkpoint_path
|
|
125
|
+
else:
|
|
126
|
+
model_string = f"{model}-{size}-v{version}.ckpt"
|
|
127
|
+
logging.info(f"Loading pre-trained model: {model_string}")
|
|
128
|
+
path = hf_hub_download(
|
|
129
|
+
repo_id="lab-cosmo/upet",
|
|
130
|
+
filename=model_string,
|
|
131
|
+
subfolder="models",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
with warnings.catch_warnings():
|
|
135
|
+
warnings.filterwarnings(
|
|
136
|
+
action="ignore",
|
|
137
|
+
message="PET assumes that Cartesian tensors of rank 2 are stress-like",
|
|
138
|
+
)
|
|
139
|
+
loaded_model = load_metatrain_model(path)
|
|
140
|
+
|
|
141
|
+
metadata = get_upet_metadata(model, size, version)
|
|
142
|
+
return loaded_model.export(metadata)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def save_upet(
|
|
146
|
+
*,
|
|
147
|
+
model: str,
|
|
148
|
+
size: str,
|
|
149
|
+
version: str,
|
|
150
|
+
checkpoint_path: Optional[str] = None,
|
|
151
|
+
output=None,
|
|
152
|
+
):
|
|
153
|
+
"""
|
|
154
|
+
Save the UPET model to a TorchScript file (``pet-xxx.pt``). These files can
|
|
155
|
+
be used with LAMMPS and other tools to run simulations without Python.
|
|
156
|
+
|
|
157
|
+
:param model: name of the UPET model.
|
|
158
|
+
:param size: size of the UPET model.
|
|
159
|
+
:param version: UPET version to use. Defaults to the latest stable version.
|
|
160
|
+
:param checkpoint_path: path to a checkpoint file to load the model from. If
|
|
161
|
+
provided, the `version` parameter is ignored.
|
|
162
|
+
:param output: path to use for the output model, defaults to
|
|
163
|
+
``pet-{version}.pt`` when using a version, or the checkpoint path when using
|
|
164
|
+
a checkpoint.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
loaded_model = get_upet(
|
|
168
|
+
model=model, size=size, version=version, checkpoint_path=checkpoint_path
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if output is None:
|
|
172
|
+
if checkpoint_path is None:
|
|
173
|
+
output = "-".join([model, size, f"v{version}"]) + ".pt"
|
|
174
|
+
else:
|
|
175
|
+
raise
|
|
176
|
+
|
|
177
|
+
loaded_model.save(output)
|
|
178
|
+
logging.info(f"Saved UPET model to {output}")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
BASE_URL_PET_MAD_DOS = "https://huggingface.co/lab-cosmo/pet-mad-dos/resolve/{tag}/models/pet-mad-dos-{version}.pt"
|
|
182
|
+
BASE_URL_BANDGAP_MODEL = (
|
|
183
|
+
"https://huggingface.co/lab-cosmo/pet-mad-dos/resolve/{tag}/models/bandgap-model.pt"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def get_pet_mad_dos(
|
|
188
|
+
*, version: str = "latest", model_path: Optional[str] = None
|
|
189
|
+
) -> AtomisticModel:
|
|
190
|
+
"""Get a metatomic ``AtomisticModel`` for PET-MAD-DOS.
|
|
191
|
+
|
|
192
|
+
:param version: PET-MAD-DOS version to use. Defaults to latest available version.
|
|
193
|
+
:param model_path: path to a Torch-Scripted metatomic ``AtomisticModel``. If
|
|
194
|
+
provided, the `version` parameter is ignored.
|
|
195
|
+
"""
|
|
196
|
+
if version == "latest":
|
|
197
|
+
version = Version(PET_MAD_DOS_LATEST_STABLE_VERSION)
|
|
198
|
+
if not isinstance(version, Version):
|
|
199
|
+
version = Version(version)
|
|
200
|
+
|
|
201
|
+
if version not in [Version(v) for v in PET_MAD_DOS_AVAILABLE_VERSIONS]:
|
|
202
|
+
raise ValueError(
|
|
203
|
+
f"Version {version} is not supported. Supported versions are "
|
|
204
|
+
f"{PET_MAD_DOS_AVAILABLE_VERSIONS}"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
if model_path is not None:
|
|
208
|
+
logging.info(f"Loading PET-MAD-DOS model from checkpoint: {model_path}")
|
|
209
|
+
path = model_path
|
|
210
|
+
else:
|
|
211
|
+
logging.info(f"Downloading PET-MAD-DOS model version: {version}")
|
|
212
|
+
path = BASE_URL_PET_MAD_DOS.format(tag=f"v{version}", version=f"v{version}")
|
|
213
|
+
|
|
214
|
+
model = load_metatrain_model(path)
|
|
215
|
+
metadata = get_pet_mad_dos_metadata(version)
|
|
216
|
+
model._metadata = metadata
|
|
217
|
+
return model
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _get_bandgap_model(version: str = "latest", model_path: Optional[str] = None):
|
|
221
|
+
"""
|
|
222
|
+
Get a bandgap model for PET-MAD-DOS
|
|
223
|
+
"""
|
|
224
|
+
if version == "latest":
|
|
225
|
+
version = Version(PET_MAD_DOS_LATEST_STABLE_VERSION)
|
|
226
|
+
if not isinstance(version, Version):
|
|
227
|
+
version = Version(version)
|
|
228
|
+
|
|
229
|
+
if version not in [Version(v) for v in PET_MAD_DOS_AVAILABLE_VERSIONS]:
|
|
230
|
+
raise ValueError(
|
|
231
|
+
f"Version {version} is not supported. Supported versions are "
|
|
232
|
+
f"{PET_MAD_DOS_AVAILABLE_VERSIONS}"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
if model_path is not None:
|
|
236
|
+
logging.info(
|
|
237
|
+
f"Loading the PET-MAD-DOS bandgap model from checkpoint: {model_path}"
|
|
238
|
+
)
|
|
239
|
+
path = model_path
|
|
240
|
+
else:
|
|
241
|
+
logging.info(f"Downloading bandgap model version: {version}")
|
|
242
|
+
path = BASE_URL_BANDGAP_MODEL.format(tag=f"v{version}")
|
|
243
|
+
path = str(path)
|
|
244
|
+
url = urlparse(path)
|
|
245
|
+
|
|
246
|
+
if url.scheme:
|
|
247
|
+
if url.netloc == "huggingface.co":
|
|
248
|
+
path = hf_hub_download_url(url=url.geturl(), hf_token=None)
|
|
249
|
+
else:
|
|
250
|
+
# Avoid caching generic URLs due to lack of a model hash for proper
|
|
251
|
+
# cache invalidation
|
|
252
|
+
path, _ = urlretrieve(url=url.geturl())
|
|
253
|
+
|
|
254
|
+
model = BandgapModel()
|
|
255
|
+
model.load_state_dict(torch.load(path, weights_only=False, map_location="cpu"))
|
|
256
|
+
return model
|
upet/_version.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
UPET_AVAILABLE_MODELS = [
|
|
2
|
+
"pet-mad-s",
|
|
3
|
+
"pet-omat-xs",
|
|
4
|
+
"pet-omat-s",
|
|
5
|
+
"pet-omat-m",
|
|
6
|
+
"pet-omat-l",
|
|
7
|
+
"pet-omat-xl",
|
|
8
|
+
"pet-oam-l",
|
|
9
|
+
"pet-oam-xl",
|
|
10
|
+
"pet-omad-l",
|
|
11
|
+
"pet-omatpes-l",
|
|
12
|
+
"pet-spice-s",
|
|
13
|
+
"pet-spice-l",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
UPET_NO_NC_SUPPORT_MODELS = [
|
|
17
|
+
"pet-mad-s-v1.0.2",
|
|
18
|
+
"pet-spice-s-v0.2.0",
|
|
19
|
+
"pet-spice-l-v0.2.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
UPET_UQ_SUPPORTED_MODELS = [
|
|
23
|
+
"pet-mad-s-v1.0.2",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
# PET-MAD DOS
|
|
27
|
+
PET_MAD_DOS_LATEST_STABLE_VERSION = "1.0"
|
|
28
|
+
PET_MAD_DOS_AVAILABLE_VERSIONS = ["1.0"]
|