gpuma 0.5.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.
- gpuma/__init__.py +65 -0
- gpuma/api.py +280 -0
- gpuma/cli.py +629 -0
- gpuma/config.py +341 -0
- gpuma/decorators.py +33 -0
- gpuma/io_handler.py +390 -0
- gpuma/logging_utils.py +30 -0
- gpuma/models.py +164 -0
- gpuma/mol_utils.py +165 -0
- gpuma/optimizer.py +338 -0
- gpuma/structure.py +55 -0
- gpuma-0.5.0.dist-info/METADATA +146 -0
- gpuma-0.5.0.dist-info/RECORD +17 -0
- gpuma-0.5.0.dist-info/WHEEL +5 -0
- gpuma-0.5.0.dist-info/entry_points.txt +2 -0
- gpuma-0.5.0.dist-info/licenses/LICENSE.md +21 -0
- gpuma-0.5.0.dist-info/top_level.txt +1 -0
gpuma/__init__.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""GPUMA: A minimal package for molecular geometry optimization using Fairchem's UMA models.
|
|
2
|
+
|
|
3
|
+
This package provides essential tools for:
|
|
4
|
+
- Single molecule optimization from SMILES strings or XYZ files
|
|
5
|
+
- Batch optimization of multiple structures (e.g., conformer ensembles)
|
|
6
|
+
- Format conversion between SMILES and XYZ coordinates
|
|
7
|
+
- Configurable optimization parameters through JSON/YAML configuration files
|
|
8
|
+
|
|
9
|
+
The package is designed to be simple and focused, providing only the functionality
|
|
10
|
+
that is actually implemented and tested.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .api import (
|
|
14
|
+
optimize_batch_multi_xyz_file,
|
|
15
|
+
optimize_batch_xyz_directory,
|
|
16
|
+
optimize_ensemble_smiles,
|
|
17
|
+
optimize_single_smiles,
|
|
18
|
+
optimize_single_xyz_file,
|
|
19
|
+
)
|
|
20
|
+
from .config import Config, default_config, load_config_from_file, save_config_to_file
|
|
21
|
+
from .decorators import time_it
|
|
22
|
+
from .io_handler import (
|
|
23
|
+
read_multi_xyz,
|
|
24
|
+
read_xyz,
|
|
25
|
+
read_xyz_directory,
|
|
26
|
+
save_multi_xyz,
|
|
27
|
+
save_xyz_file,
|
|
28
|
+
smiles_to_ensemble,
|
|
29
|
+
smiles_to_xyz,
|
|
30
|
+
)
|
|
31
|
+
from .models import load_model_fairchem, load_model_torchsim
|
|
32
|
+
from .optimizer import optimize_single_structure, optimize_structure_batch
|
|
33
|
+
from .structure import Structure
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
# Data types
|
|
37
|
+
"Structure",
|
|
38
|
+
# I/O functions
|
|
39
|
+
"read_xyz",
|
|
40
|
+
"read_multi_xyz",
|
|
41
|
+
"read_xyz_directory",
|
|
42
|
+
"smiles_to_xyz",
|
|
43
|
+
"smiles_to_ensemble",
|
|
44
|
+
"save_xyz_file",
|
|
45
|
+
"save_multi_xyz",
|
|
46
|
+
# Optimization functions
|
|
47
|
+
"optimize_single_structure",
|
|
48
|
+
"optimize_structure_batch",
|
|
49
|
+
# Convenience functions (re-exported from ``api``)
|
|
50
|
+
"optimize_single_smiles",
|
|
51
|
+
"optimize_single_xyz_file",
|
|
52
|
+
"optimize_ensemble_smiles",
|
|
53
|
+
"optimize_batch_multi_xyz_file",
|
|
54
|
+
"optimize_batch_xyz_directory",
|
|
55
|
+
# Model functions
|
|
56
|
+
"load_model_torchsim",
|
|
57
|
+
"load_model_fairchem",
|
|
58
|
+
# Configuration
|
|
59
|
+
"Config",
|
|
60
|
+
"default_config",
|
|
61
|
+
"load_config_from_file",
|
|
62
|
+
"save_config_to_file",
|
|
63
|
+
# Decorators
|
|
64
|
+
"time_it",
|
|
65
|
+
]
|
gpuma/api.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Public high-level Python API for common geometry optimization workflows.
|
|
2
|
+
|
|
3
|
+
This module provides convenience functions built on top of the lower-level
|
|
4
|
+
I/O and optimization utilities. It allows users to easily optimize molecular
|
|
5
|
+
structures starting from SMILES strings or XYZ files, as well as optimizing
|
|
6
|
+
ensembles of conformers.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import cast
|
|
13
|
+
|
|
14
|
+
from .config import Config, load_config_from_file
|
|
15
|
+
from .io_handler import (
|
|
16
|
+
file_exists,
|
|
17
|
+
read_multi_xyz,
|
|
18
|
+
read_xyz,
|
|
19
|
+
read_xyz_directory,
|
|
20
|
+
save_multi_xyz,
|
|
21
|
+
save_xyz_file,
|
|
22
|
+
smiles_to_ensemble,
|
|
23
|
+
smiles_to_xyz,
|
|
24
|
+
)
|
|
25
|
+
from .optimizer import optimize_single_structure, optimize_structure_batch
|
|
26
|
+
from .structure import Structure
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def optimize_single_smiles(
|
|
30
|
+
smiles: str,
|
|
31
|
+
output_file: str | None = None,
|
|
32
|
+
config: Config | None = None,
|
|
33
|
+
) -> Structure:
|
|
34
|
+
"""Optimize a single molecule from a SMILES string.
|
|
35
|
+
|
|
36
|
+
This function uses the provided SMILES string to generate an initial 3D structure
|
|
37
|
+
using the Morfeus library. It then optimizes the structure using the specified
|
|
38
|
+
optimization pipeline.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
smiles (str): SMILES string of the molecule to optimize.
|
|
42
|
+
output_file (str): Path to an output XYZ file where the optimized structure
|
|
43
|
+
will be written. If None, the optimized structure is not saved to a file.
|
|
44
|
+
config (Config, optional): Config object to control the optimization pipeline.
|
|
45
|
+
Highly recommended to specify. If None, the configuration will be loaded
|
|
46
|
+
from the default file.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Structure: The optimized molecular structure as a Structure object.
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
ValueError: If the generated structure is not valid.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
if config is None:
|
|
56
|
+
config = load_config_from_file()
|
|
57
|
+
|
|
58
|
+
multiplicity = getattr(config.optimization, "multiplicity", 1)
|
|
59
|
+
structure = smiles_to_xyz(smiles, multiplicity=multiplicity)
|
|
60
|
+
|
|
61
|
+
if not isinstance(structure, Structure):
|
|
62
|
+
raise ValueError("smiles_to_xyz did not return a Structure")
|
|
63
|
+
structure.comment = f"Optimized from SMILES: {smiles}"
|
|
64
|
+
result = optimize_single_structure(structure, config)
|
|
65
|
+
|
|
66
|
+
if output_file:
|
|
67
|
+
save_xyz_file(result, output_file)
|
|
68
|
+
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def optimize_single_xyz_file(
|
|
73
|
+
input_file: str,
|
|
74
|
+
output_file: str | None = None,
|
|
75
|
+
config: Config | None = None,
|
|
76
|
+
) -> Structure:
|
|
77
|
+
"""Optimize a single structure from an XYZ file.
|
|
78
|
+
|
|
79
|
+
This function reads a molecular structure from the specified XYZ file,
|
|
80
|
+
optimizes it using the provided optimization pipeline, and optionally
|
|
81
|
+
writes the optimized structure to an output XYZ file.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
input_file (str): Path to an input XYZ file from which to read the initial structure.
|
|
85
|
+
output_file (str): Path to an output XYZ file where the optimized structure will be written.
|
|
86
|
+
If None, the optimized structure will not be saved to a file.
|
|
87
|
+
config (Config, optional): Config object to control the optimization pipeline.
|
|
88
|
+
Highly recommended to specify. If None, the configuration will be loaded from
|
|
89
|
+
the default file.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Structure: The optimized molecular structure as a Structure object.
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
ValueError: If the input file does not exist or if the read structure is not valid.
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
if not file_exists(input_file):
|
|
99
|
+
raise ValueError(f"Input file {input_file} does not exist.")
|
|
100
|
+
if config is None:
|
|
101
|
+
config = load_config_from_file()
|
|
102
|
+
|
|
103
|
+
eff_charge = int(getattr(config.optimization, "charge", 0))
|
|
104
|
+
eff_mult = int(getattr(config.optimization, "multiplicity", 1))
|
|
105
|
+
structure = read_xyz(input_file, charge=eff_charge, multiplicity=eff_mult)
|
|
106
|
+
if not isinstance(structure, Structure):
|
|
107
|
+
raise ValueError("read_xyz did not return a Structure")
|
|
108
|
+
structure.comment = f"Optimized from: {input_file}"
|
|
109
|
+
result = optimize_single_structure(structure, config)
|
|
110
|
+
|
|
111
|
+
if output_file:
|
|
112
|
+
save_xyz_file(result, output_file)
|
|
113
|
+
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def optimize_ensemble_smiles(
|
|
118
|
+
smiles: str,
|
|
119
|
+
output_file: str | None = None,
|
|
120
|
+
config: Config | None = None,
|
|
121
|
+
) -> list[Structure]:
|
|
122
|
+
"""Optimize a conformer ensemble generated from a SMILES string.
|
|
123
|
+
|
|
124
|
+
This function generates a specified number of conformers from the provided
|
|
125
|
+
SMILES string using the Morfeus library. It then optimizes each conformer
|
|
126
|
+
using the specified optimization pipeline. Optionally, the optimized ensemble
|
|
127
|
+
can be saved to a multi-structure XYZ file.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
smiles (str): SMILES string of the molecule for which to generate conformers.
|
|
131
|
+
output_file (str, optional): Path to an output multi-structure XYZ file where
|
|
132
|
+
the optimized ensemble will be written. If None, the ensemble is not saved to a file.
|
|
133
|
+
config (Config, optional): Config object to control the optimization pipeline.
|
|
134
|
+
Highly recommended to specify. If None, the configuration will be loaded from
|
|
135
|
+
the default file.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
list[Structure]: A list of optimized molecular structures as Structure objects.
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
ValueError: If output_file is not specified when required or if the generated
|
|
142
|
+
conformers are not valid.
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
if config is None:
|
|
146
|
+
config = load_config_from_file()
|
|
147
|
+
multiplicity = int(getattr(config.optimization, "multiplicity", 1))
|
|
148
|
+
num_conformers = int(getattr(config.optimization, "max_num_conformers", 10))
|
|
149
|
+
conformers = smiles_to_ensemble(smiles, num_conformers, multiplicity)
|
|
150
|
+
if not isinstance(conformers, list) or (
|
|
151
|
+
len(conformers) and not isinstance(conformers[0], Structure)
|
|
152
|
+
):
|
|
153
|
+
raise ValueError("smiles_to_ensemble did not return a list of Structure")
|
|
154
|
+
for s in conformers:
|
|
155
|
+
s.multiplicity = multiplicity
|
|
156
|
+
results = optimize_structure_batch(conformers, config)
|
|
157
|
+
|
|
158
|
+
if output_file:
|
|
159
|
+
comments = [
|
|
160
|
+
f"Optimized conformer {i + 1} from SMILES: {smiles}" for i in range(len(results))
|
|
161
|
+
]
|
|
162
|
+
save_multi_xyz(results, output_file, comments)
|
|
163
|
+
|
|
164
|
+
return results
|
|
165
|
+
|
|
166
|
+
def optimize_batch_multi_xyz_file(
|
|
167
|
+
input_file: str,
|
|
168
|
+
output_file: str | None = None,
|
|
169
|
+
config: Config | None = None,
|
|
170
|
+
) -> list[Structure]:
|
|
171
|
+
"""Optimize a batch of structures from a multi-structure XYZ file.
|
|
172
|
+
|
|
173
|
+
This function reads multiple molecular structures from the specified multi-structure
|
|
174
|
+
XYZ file, optimizes each structure using the provided optimization pipeline, and writes
|
|
175
|
+
the optimized structures to an output multi-structure XYZ file.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
input_file (str): Path to an input multi-structure XYZ file from which to
|
|
179
|
+
read the initial structures.
|
|
180
|
+
output_file (str): Path to an output multi-structure XYZ file where the optimized
|
|
181
|
+
structures will be written.
|
|
182
|
+
If None, the optimized structures will not be saved to a file.
|
|
183
|
+
config (Config, optional): Config object to control the optimization pipeline.
|
|
184
|
+
Highly recommended to specify. If None, the configuration will be loaded from
|
|
185
|
+
the default file.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
list[Structure]: A list of optimized molecular structures as Structure objects.
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
ValueError: If the input file does not exist or if the read structures are not valid.
|
|
192
|
+
|
|
193
|
+
"""
|
|
194
|
+
if not file_exists(input_file):
|
|
195
|
+
raise ValueError(f"Input file {input_file} does not exist.")
|
|
196
|
+
|
|
197
|
+
if config is None:
|
|
198
|
+
config = load_config_from_file()
|
|
199
|
+
|
|
200
|
+
eff_charge = int(getattr(config.optimization, "charge", 0))
|
|
201
|
+
eff_mult = int(getattr(config.optimization, "multiplicity", 1))
|
|
202
|
+
|
|
203
|
+
structures = cast(
|
|
204
|
+
list[Structure],
|
|
205
|
+
read_multi_xyz(input_file, charge=eff_charge, multiplicity=eff_mult),
|
|
206
|
+
)
|
|
207
|
+
if not isinstance(structures, list) or (
|
|
208
|
+
len(structures) and not isinstance(structures[0], Structure)
|
|
209
|
+
):
|
|
210
|
+
raise ValueError("read_multi_xyz did not return a list of Structure")
|
|
211
|
+
|
|
212
|
+
results = optimize_structure_batch(structures, config)
|
|
213
|
+
|
|
214
|
+
if output_file:
|
|
215
|
+
comments = [
|
|
216
|
+
f"Optimized structure {i + 1} from: {input_file}" for i in range(len(results))
|
|
217
|
+
]
|
|
218
|
+
save_multi_xyz(results, output_file, comments)
|
|
219
|
+
|
|
220
|
+
return results
|
|
221
|
+
|
|
222
|
+
def optimize_batch_xyz_directory(
|
|
223
|
+
input_directory: str,
|
|
224
|
+
output_file: str,
|
|
225
|
+
config: Config | None = None,
|
|
226
|
+
) -> list[Structure]:
|
|
227
|
+
"""Optimize a batch of structures from XYZ files in a directory.
|
|
228
|
+
|
|
229
|
+
This function reads multiple molecular structures from XYZ files in the specified input
|
|
230
|
+
directory, optimizes each structure using the provided optimization pipeline,
|
|
231
|
+
and writes the optimized structures to XYZ files in the specified output directory.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
input_directory (str): Path to an input directory containing XYZ files.
|
|
235
|
+
output_file (str): Path to an output multi-structure XYZ file where the
|
|
236
|
+
optimized structures will be written.
|
|
237
|
+
config (Config, optional): Config object to control the optimization pipeline.
|
|
238
|
+
Highly recommended to specify. If None, the configuration will be loaded from the
|
|
239
|
+
default file.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
list[Structure]: A list of optimized molecular structures as Structure objects.
|
|
243
|
+
|
|
244
|
+
Raises:
|
|
245
|
+
ValueError: If the input directory does not exist or contains no valid XYZ files.
|
|
246
|
+
|
|
247
|
+
"""
|
|
248
|
+
if config is None:
|
|
249
|
+
config = load_config_from_file()
|
|
250
|
+
|
|
251
|
+
eff_charge = int(getattr(config.optimization, "charge", 0))
|
|
252
|
+
eff_mult = int(getattr(config.optimization, "multiplicity", 1))
|
|
253
|
+
|
|
254
|
+
structures = cast(
|
|
255
|
+
list[Structure],
|
|
256
|
+
read_xyz_directory(
|
|
257
|
+
input_directory,
|
|
258
|
+
charge=eff_charge,
|
|
259
|
+
multiplicity=eff_mult,
|
|
260
|
+
),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
results = optimize_structure_batch(structures, config)
|
|
264
|
+
|
|
265
|
+
if output_file:
|
|
266
|
+
comments = [
|
|
267
|
+
f"Optimized structure {i + 1} from batch input"
|
|
268
|
+
for i in range(len(results))
|
|
269
|
+
]
|
|
270
|
+
save_multi_xyz(results, output_file, comments)
|
|
271
|
+
|
|
272
|
+
return results
|
|
273
|
+
|
|
274
|
+
__all__ = [
|
|
275
|
+
"optimize_single_smiles",
|
|
276
|
+
"optimize_single_xyz_file",
|
|
277
|
+
"optimize_ensemble_smiles",
|
|
278
|
+
"optimize_batch_multi_xyz_file",
|
|
279
|
+
"optimize_batch_xyz_directory",
|
|
280
|
+
]
|