pack-mm 0.0.14__py3-none-any.whl → 0.0.19__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.
- pack_mm/cli/packmm.py +21 -14
- pack_mm/core/core.py +76 -59
- {pack_mm-0.0.14.dist-info → pack_mm-0.0.19.dist-info}/METADATA +1 -1
- pack_mm-0.0.19.dist-info/RECORD +8 -0
- pack_mm-0.0.14.dist-info/RECORD +0 -8
- {pack_mm-0.0.14.dist-info → pack_mm-0.0.19.dist-info}/WHEEL +0 -0
- {pack_mm-0.0.14.dist-info → pack_mm-0.0.19.dist-info}/entry_points.txt +0 -0
- {pack_mm-0.0.14.dist-info → pack_mm-0.0.19.dist-info}/licenses/LICENSE +0 -0
pack_mm/cli/packmm.py
CHANGED
@@ -52,30 +52,28 @@ def packmm(
|
|
52
52
|
),
|
53
53
|
centre: str | None = typer.Option(
|
54
54
|
None,
|
55
|
-
help="""Centre of the insertion zone in
|
56
|
-
e.g., '0.
|
55
|
+
help="""Centre of the insertion zone, coordinates in Å,
|
56
|
+
e.g., '5.0, 5.0, 5.0'.""",
|
57
57
|
),
|
58
58
|
radius: float | None = typer.Option(
|
59
59
|
None,
|
60
60
|
help="""Radius of the sphere or cylinder in Å,
|
61
61
|
depending on the insertion volume.""",
|
62
62
|
),
|
63
|
-
height: float | None = typer.Option(
|
64
|
-
None, help="Height of the cylinder in fractional coordinates."
|
65
|
-
),
|
63
|
+
height: float | None = typer.Option(None, help="Height of the cylinder in Å."),
|
66
64
|
a: float | None = typer.Option(
|
67
65
|
None,
|
68
|
-
help="""Side of the box or semi-axis of the ellipsoid,
|
66
|
+
help="""Side of the box or semi-axis of the ellipsoid, in Å,
|
69
67
|
depends on the insertion method.""",
|
70
68
|
),
|
71
69
|
b: float | None = typer.Option(
|
72
70
|
None,
|
73
|
-
help="""Side of the box or semi-axis of the ellipsoid,
|
71
|
+
help="""Side of the box or semi-axis of the ellipsoid, in Å,
|
74
72
|
depends on the insertion method.""",
|
75
73
|
),
|
76
74
|
c: float | None = typer.Option(
|
77
75
|
None,
|
78
|
-
help="""Side of the box or semi-axis of the ellipsoid,
|
76
|
+
help="""Side of the box or semi-axis of the ellipsoid, in Å,
|
79
77
|
depends on the insertion method.""",
|
80
78
|
),
|
81
79
|
device: str = typer.Option(
|
@@ -86,15 +84,22 @@ def packmm(
|
|
86
84
|
temperature: float = typer.Option(
|
87
85
|
300.0, help="Temperature for the Monte Carlo acceptance rule."
|
88
86
|
),
|
89
|
-
cell_a: float = typer.Option(
|
90
|
-
|
91
|
-
|
87
|
+
cell_a: float = typer.Option(
|
88
|
+
20.0, help="Side of the empty box along the x-axis in Å."
|
89
|
+
),
|
90
|
+
cell_b: float = typer.Option(
|
91
|
+
20.0, help="Side of the empty box along the y-axis in Å."
|
92
|
+
),
|
93
|
+
cell_c: float = typer.Option(
|
94
|
+
20.0, help="Side of the empty box along the z-axis in Å."
|
95
|
+
),
|
92
96
|
fmax: float = typer.Option(
|
93
97
|
0.1, help="force tollerance for optimisation if needed."
|
94
98
|
),
|
95
99
|
geometry: bool = typer.Option(
|
96
100
|
True, help="Perform geometry optimization at the end."
|
97
101
|
),
|
102
|
+
out_path: str = typer.Option(".", help="path to save various outputs."),
|
98
103
|
):
|
99
104
|
"""Pack molecules into a system based on the specified parameters."""
|
100
105
|
print("Script called with following input")
|
@@ -119,6 +124,7 @@ def packmm(
|
|
119
124
|
print(f"{temperature=}")
|
120
125
|
print(f"{fmax=}")
|
121
126
|
print(f"{geometry=}")
|
127
|
+
print(f"{out_path=}")
|
122
128
|
if nmols == -1:
|
123
129
|
print("nothing to do, no molecule to insert")
|
124
130
|
raise typer.Exit(0)
|
@@ -151,9 +157,10 @@ def packmm(
|
|
151
157
|
ntries=ntries,
|
152
158
|
fmax=fmax,
|
153
159
|
geometry=geometry,
|
154
|
-
|
155
|
-
|
156
|
-
|
160
|
+
cell_a=cell_a,
|
161
|
+
cell_b=cell_b,
|
162
|
+
cell_c=cell_c,
|
163
|
+
out_path=out_path,
|
157
164
|
)
|
158
165
|
|
159
166
|
|
pack_mm/core/core.py
CHANGED
@@ -138,28 +138,29 @@ def validate_value(label, x):
|
|
138
138
|
|
139
139
|
|
140
140
|
def pack_molecules(
|
141
|
-
system: str
|
142
|
-
molecule: str,
|
143
|
-
nmols: int,
|
144
|
-
arch: str,
|
145
|
-
model: str,
|
146
|
-
device: str,
|
147
|
-
where: str,
|
148
|
-
center: tuple[float, float, float]
|
149
|
-
radius: float
|
150
|
-
height: float
|
151
|
-
a: float
|
152
|
-
b: float
|
153
|
-
c: float
|
154
|
-
seed: int,
|
155
|
-
temperature: float,
|
156
|
-
ntries: int,
|
157
|
-
geometry: bool,
|
158
|
-
fmax: float,
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
141
|
+
system: str = None,
|
142
|
+
molecule: str = "H2O",
|
143
|
+
nmols: int = -1,
|
144
|
+
arch: str = "cpu",
|
145
|
+
model: str = "mace_mp",
|
146
|
+
device: str = "medium-omat-0",
|
147
|
+
where: str = "anywhere",
|
148
|
+
center: tuple[float, float, float] = None,
|
149
|
+
radius: float = None,
|
150
|
+
height: float = None,
|
151
|
+
a: float = None,
|
152
|
+
b: float = None,
|
153
|
+
c: float = None,
|
154
|
+
seed: int = 2025,
|
155
|
+
temperature: float = 300.0,
|
156
|
+
ntries: int = 50,
|
157
|
+
geometry: bool = False,
|
158
|
+
fmax: float = 0.1,
|
159
|
+
cell_a: float = None,
|
160
|
+
cell_b: float = None,
|
161
|
+
cell_c: float = None,
|
162
|
+
out_path: str = ".",
|
163
|
+
) -> float:
|
163
164
|
"""
|
164
165
|
Pack molecules into a system based on the specified parameters.
|
165
166
|
|
@@ -181,7 +182,8 @@ def pack_molecules(
|
|
181
182
|
temperature (float): Temperature in Kelvin for acceptance probability.
|
182
183
|
ntries (int): Maximum number of attempts to insert each molecule.
|
183
184
|
geometry (bool): Whether to perform geometry optimization after insertion.
|
184
|
-
|
185
|
+
cell_a, cell_b, cell_c (float): Cell dimensions if system is empty.
|
186
|
+
out_path (str): path to save various outputs
|
185
187
|
"""
|
186
188
|
kbt = temperature * 8.6173303e-5 # Boltzmann constant in eV/K
|
187
189
|
validate_value("temperature", temperature)
|
@@ -193,10 +195,10 @@ def pack_molecules(
|
|
193
195
|
validate_value("box b", b)
|
194
196
|
validate_value("box c", c)
|
195
197
|
validate_value("ntries", ntries)
|
196
|
-
validate_value("cell box a",
|
197
|
-
validate_value("cell box b",
|
198
|
+
validate_value("cell box cell a", cell_a)
|
199
|
+
validate_value("cell box cell b", cell_b)
|
198
200
|
validate_value("nmols", nmols)
|
199
|
-
validate_value("cell box c",
|
201
|
+
validate_value("cell box cell c", cell_c)
|
200
202
|
|
201
203
|
random.seed(seed)
|
202
204
|
|
@@ -204,25 +206,21 @@ def pack_molecules(
|
|
204
206
|
sys = read(system)
|
205
207
|
sysname = Path(system).stem
|
206
208
|
except Exception:
|
207
|
-
sys = Atoms(cell=[
|
209
|
+
sys = Atoms(cell=[cell_a, cell_b, cell_c], pbc=[True, True, True])
|
208
210
|
sysname = "empty"
|
209
211
|
|
210
212
|
cell = sys.cell.lengths()
|
211
213
|
|
212
|
-
# Print
|
214
|
+
# Print summary
|
213
215
|
print(f"Inserting {nmols} {molecule} molecules in {sysname}.")
|
214
216
|
print(f"Using {arch} model {model} on {device}.")
|
215
217
|
print(f"Insert in {where}.")
|
216
218
|
|
217
|
-
# Set center of insertion region
|
218
219
|
if center is None:
|
219
220
|
center = (cell[0] * 0.5, cell[1] * 0.5, cell[2] * 0.5)
|
220
|
-
else:
|
221
|
-
center = tuple(ci * cell[i] for i, ci in enumerate(center))
|
222
221
|
|
223
|
-
# Set parameters based on insertion region
|
224
222
|
if where == "anywhere":
|
225
|
-
a, b, c =
|
223
|
+
a, b, c = cell[0], cell[1], cell[2]
|
226
224
|
elif where == "sphere":
|
227
225
|
if radius is None:
|
228
226
|
radius = min(cell) * 0.5
|
@@ -230,20 +228,22 @@ def pack_molecules(
|
|
230
228
|
if radius is None:
|
231
229
|
if where == "cylinderZ":
|
232
230
|
radius = min(cell[0], cell[1]) * 0.5
|
231
|
+
if height is None:
|
232
|
+
height = 0.5 * cell[2]
|
233
233
|
elif where == "cylinderY":
|
234
234
|
radius = min(cell[0], cell[2]) * 0.5
|
235
|
+
if height is None:
|
236
|
+
height = 0.5 * cell[1]
|
235
237
|
elif where == "cylinderX":
|
236
238
|
radius = min(cell[2], cell[1]) * 0.5
|
237
|
-
|
238
|
-
|
239
|
+
if height is None:
|
240
|
+
height = 0.5 * cell[0]
|
239
241
|
elif where == "box":
|
240
|
-
a, b, c = a or
|
242
|
+
a, b, c = a or cell[0], b or cell[1], c or cell[2]
|
241
243
|
elif where == "ellipsoid":
|
242
|
-
a, b, c = a or 0
|
244
|
+
a, b, c = a or cell[0], b or cell[1], c or cell[2]
|
243
245
|
|
244
|
-
calc = choose_calculator(
|
245
|
-
arch=arch, model_path=model, device=device, default_dtype="float64"
|
246
|
-
)
|
246
|
+
calc = choose_calculator(arch=arch, model_path=model, device=device)
|
247
247
|
sys.calc = calc
|
248
248
|
|
249
249
|
e = sys.get_potential_energy() if len(sys) > 0 else 0.0
|
@@ -253,7 +253,7 @@ def pack_molecules(
|
|
253
253
|
accept = False
|
254
254
|
for _itry in range(ntries):
|
255
255
|
mol = load_molecule(molecule)
|
256
|
-
tv = get_insertion_position(where, center,
|
256
|
+
tv = get_insertion_position(where, center, a, b, c, radius, height)
|
257
257
|
mol = rotate_molecule(mol)
|
258
258
|
mol.translate(tv)
|
259
259
|
|
@@ -274,18 +274,31 @@ def pack_molecules(
|
|
274
274
|
csys = tsys.copy()
|
275
275
|
e = en
|
276
276
|
print(f"Inserted particle {i + 1}")
|
277
|
-
write(f"{sysname}+{i + 1}{Path(molecule).stem}.cif", csys)
|
277
|
+
write(Path(out_path) / f"{sysname}+{i + 1}{Path(molecule).stem}.cif", csys)
|
278
278
|
else:
|
279
|
+
# Things are bad, maybe geomatry optimisation saves us
|
279
280
|
print(f"Failed to insert particle {i + 1} after {ntries} tries")
|
280
|
-
optimize_geometry(
|
281
|
-
f"{sysname}+{i + 1}{Path(molecule).stem}.cif",
|
281
|
+
_ = optimize_geometry(
|
282
|
+
f"{sysname}+{i + 1}{Path(molecule).stem}.cif",
|
283
|
+
device,
|
284
|
+
arch,
|
285
|
+
model,
|
286
|
+
fmax,
|
287
|
+
out_path,
|
282
288
|
)
|
289
|
+
energy_final = e
|
283
290
|
|
284
291
|
# Perform final geometry optimization if requested
|
285
292
|
if geometry:
|
286
|
-
optimize_geometry(
|
287
|
-
f"{sysname}+{nmols}{Path(molecule).stem}.cif",
|
293
|
+
energy_final = optimize_geometry(
|
294
|
+
f"{sysname}+{nmols}{Path(molecule).stem}.cif",
|
295
|
+
device,
|
296
|
+
arch,
|
297
|
+
model,
|
298
|
+
fmax,
|
299
|
+
out_path,
|
288
300
|
)
|
301
|
+
return energy_final
|
289
302
|
|
290
303
|
|
291
304
|
def load_molecule(molecule: str):
|
@@ -299,25 +312,24 @@ def load_molecule(molecule: str):
|
|
299
312
|
def get_insertion_position(
|
300
313
|
where: str,
|
301
314
|
center: tuple[float, float, float],
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
height: float | None,
|
315
|
+
a: float = None,
|
316
|
+
b: float = None,
|
317
|
+
c: float = None,
|
318
|
+
radius: float = None,
|
319
|
+
height: float = None,
|
308
320
|
) -> tuple[float, float, float]:
|
309
321
|
"""Get a random insertion position based on the region."""
|
310
322
|
if where == "sphere":
|
311
323
|
return random_point_in_sphere(center, radius)
|
312
324
|
if where == "box":
|
313
|
-
return random_point_in_box(center,
|
325
|
+
return random_point_in_box(center, a, b, c)
|
314
326
|
if where == "ellipsoid":
|
315
|
-
return random_point_in_ellipsoid(center,
|
327
|
+
return random_point_in_ellipsoid(center, a, b, c)
|
316
328
|
if where in ["cylinderZ", "cylinderY", "cylinderX"]:
|
317
329
|
axis = where[-1].lower()
|
318
|
-
return random_point_in_cylinder(center, radius,
|
330
|
+
return random_point_in_cylinder(center, radius, height, axis)
|
319
331
|
# now is anywhere
|
320
|
-
return random.random(3) * [a, b, c]
|
332
|
+
return random.random(3) * [a, b, c]
|
321
333
|
|
322
334
|
|
323
335
|
def rotate_molecule(mol):
|
@@ -330,16 +342,21 @@ def rotate_molecule(mol):
|
|
330
342
|
|
331
343
|
|
332
344
|
def optimize_geometry(
|
333
|
-
struct_path: str,
|
345
|
+
struct_path: str,
|
346
|
+
device: str,
|
347
|
+
arch: str,
|
348
|
+
model: str,
|
349
|
+
fmax: float,
|
350
|
+
out_path: str = ".",
|
334
351
|
) -> float:
|
335
352
|
"""Optimize the geometry of a structure."""
|
336
353
|
geo = GeomOpt(
|
337
354
|
struct_path=struct_path,
|
338
355
|
device=device,
|
339
356
|
fmax=fmax,
|
340
|
-
calc_kwargs={"model_paths": model
|
357
|
+
calc_kwargs={"model_paths": model},
|
341
358
|
filter_kwargs={"hydrostatic_strain": True},
|
342
359
|
)
|
343
360
|
geo.run()
|
344
|
-
write(f"{struct_path}-opt.cif", geo.struct)
|
361
|
+
write(Path(out_path) / f"{struct_path}-opt.cif", geo.struct)
|
345
362
|
return geo.struct.get_potential_energy()
|
@@ -0,0 +1,8 @@
|
|
1
|
+
pack_mm-0.0.19.dist-info/METADATA,sha256=XknqobiQ9HNQyOPmGsJ_auICisxR128TXg_0cU4kh8Y,1583
|
2
|
+
pack_mm-0.0.19.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
3
|
+
pack_mm-0.0.19.dist-info/entry_points.txt,sha256=ajKA2oehIa_LCVCP2XTRxV0VNgjGl9c2wYkwk0BasrQ,66
|
4
|
+
pack_mm-0.0.19.dist-info/licenses/LICENSE,sha256=ZOYkPdn_vQ8wYJqZnjesow79F_grMbVlHcJ9V91G1pE,1100
|
5
|
+
pack_mm/__init__.py,sha256=ct7qfCmTDwhLYip6JKYWRLasmmaGYt0ColbK0CpvYZk,150
|
6
|
+
pack_mm/cli/packmm.py,sha256=VqumDT_f1Nf1LCZ1WsF5D6MoLmKEQPEimYenibQHIU4,4944
|
7
|
+
pack_mm/core/core.py,sha256=RT9EgMkejhMkqVHN0t_Yn8MDXgQDFD_HOw6Sf3pBeSc,11373
|
8
|
+
pack_mm-0.0.19.dist-info/RECORD,,
|
pack_mm-0.0.14.dist-info/RECORD
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
pack_mm-0.0.14.dist-info/METADATA,sha256=KOiX1eZqUv1OmoKdtpFyUC_ueiJ3BYGBhZjv6D8ZPMg,1583
|
2
|
-
pack_mm-0.0.14.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
3
|
-
pack_mm-0.0.14.dist-info/entry_points.txt,sha256=ajKA2oehIa_LCVCP2XTRxV0VNgjGl9c2wYkwk0BasrQ,66
|
4
|
-
pack_mm-0.0.14.dist-info/licenses/LICENSE,sha256=ZOYkPdn_vQ8wYJqZnjesow79F_grMbVlHcJ9V91G1pE,1100
|
5
|
-
pack_mm/__init__.py,sha256=ct7qfCmTDwhLYip6JKYWRLasmmaGYt0ColbK0CpvYZk,150
|
6
|
-
pack_mm/cli/packmm.py,sha256=FWiBUQBG6Z5Vi5A1VH9DYUn8iBwIs4NbFR5a8mPtEpM,4797
|
7
|
-
pack_mm/core/core.py,sha256=gP8j1NJg3_8dj4it3V2Z80dbAKhy0emwUjd_-wuKvws,10839
|
8
|
-
pack_mm-0.0.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|