pack-mm 0.0.22__tar.gz → 0.0.24__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.
- {pack_mm-0.0.22 → pack_mm-0.0.24}/PKG-INFO +6 -1
- {pack_mm-0.0.22 → pack_mm-0.0.24}/README.md +5 -0
- {pack_mm-0.0.22 → pack_mm-0.0.24}/pack_mm/core/core.py +55 -36
- {pack_mm-0.0.22 → pack_mm-0.0.24}/pyproject.toml +3 -1
- pack_mm-0.0.24/tests/test_advanced.py +85 -0
- {pack_mm-0.0.22 → pack_mm-0.0.24}/tests/test_core.py +52 -8
- {pack_mm-0.0.22 → pack_mm-0.0.24}/LICENSE +0 -0
- {pack_mm-0.0.22 → pack_mm-0.0.24}/pack_mm/__init__.py +0 -0
- {pack_mm-0.0.22 → pack_mm-0.0.24}/pack_mm/cli/packmm.py +0 -0
- {pack_mm-0.0.22 → pack_mm-0.0.24}/tests/__init__.py +0 -0
- /pack_mm-0.0.22/tests/test_packmm.py → /pack_mm-0.0.24/tests/test_cli.py +0 -0
- {pack_mm-0.0.22 → pack_mm-0.0.24}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pack-mm
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.24
|
4
4
|
Summary: packing materials and molecules in boxes using for machine learnt interatomic potentials
|
5
5
|
Author: Alin M. Elena
|
6
6
|
Classifier: Programming Language :: Python
|
@@ -50,6 +50,11 @@ or install the latest
|
|
50
50
|
|
51
51
|
```
|
52
52
|
|
53
|
+
## Jupyter notebook examples
|
54
|
+
|
55
|
+
|
56
|
+
- [Basics](docs/source/tutorials/basics.ipynb) [](https://colab.research.google.com/github/ddmms/pack-mm/blob/main/docs/source/tutorials/basic.ipynb)
|
57
|
+
|
53
58
|
## CLI examples
|
54
59
|
|
55
60
|
|
@@ -29,6 +29,11 @@ or install the latest
|
|
29
29
|
|
30
30
|
```
|
31
31
|
|
32
|
+
## Jupyter notebook examples
|
33
|
+
|
34
|
+
|
35
|
+
- [Basics](docs/source/tutorials/basics.ipynb) [](https://colab.research.google.com/github/ddmms/pack-mm/blob/main/docs/source/tutorials/basic.ipynb)
|
36
|
+
|
32
37
|
## CLI examples
|
33
38
|
|
34
39
|
|
@@ -186,7 +186,7 @@ def set_defaults(
|
|
186
186
|
|
187
187
|
|
188
188
|
def pack_molecules(
|
189
|
-
system: str = None,
|
189
|
+
system: str | Atoms = None,
|
190
190
|
molecule: str = "H2O",
|
191
191
|
nmols: int = -1,
|
192
192
|
arch: str = "cpu",
|
@@ -214,13 +214,13 @@ def pack_molecules(
|
|
214
214
|
md_steps: int = 10,
|
215
215
|
md_timestep: float = 1.0,
|
216
216
|
md_temperature: float = 100.0,
|
217
|
-
) -> float:
|
217
|
+
) -> tuple(float, Atoms):
|
218
218
|
"""
|
219
219
|
Pack molecules into a system based on the specified parameters.
|
220
220
|
|
221
221
|
Parameters
|
222
222
|
----------
|
223
|
-
system (str): Path to the system file or name of the system.
|
223
|
+
system (str|Atoms): Path to the system file or name of the system.
|
224
224
|
molecule (str): Path to the molecule file or name of the molecule.
|
225
225
|
nmols (int): Number of molecules to insert.
|
226
226
|
arch (str): Architecture for the calculator.
|
@@ -246,6 +246,11 @@ def pack_molecules(
|
|
246
246
|
insert_strategy (str): Insert strategy, "random" or "md"
|
247
247
|
relax_strategy (str): Relax strategy, "geometry_optimisation" or "md"
|
248
248
|
|
249
|
+
Returns
|
250
|
+
-------
|
251
|
+
tuple: A tuple energy and Atoms object containing
|
252
|
+
original system and added molecules..
|
253
|
+
|
249
254
|
"""
|
250
255
|
kbt = temperature * kB
|
251
256
|
validate_value("temperature", temperature)
|
@@ -270,6 +275,9 @@ def pack_molecules(
|
|
270
275
|
if system is None:
|
271
276
|
sys = Atoms(cell=[cell_a, cell_b, cell_c], pbc=[True, True, True])
|
272
277
|
sysname = ""
|
278
|
+
elif isinstance(system, Atoms):
|
279
|
+
sys = system.copy()
|
280
|
+
sysname = sys.get_chemical_formula()
|
273
281
|
else:
|
274
282
|
sys = read(system)
|
275
283
|
sysname = Path(system).stem + "+"
|
@@ -306,6 +314,20 @@ def pack_molecules(
|
|
306
314
|
tsys, md_temperature, md_steps, md_timestep, arch, model, device
|
307
315
|
)
|
308
316
|
|
317
|
+
if every > 0 and _itry / every == 0:
|
318
|
+
tsys = save_the_day(
|
319
|
+
struct_path=tsys,
|
320
|
+
device=device,
|
321
|
+
arch=arch,
|
322
|
+
model=model,
|
323
|
+
fmax=fmax,
|
324
|
+
out_path=out_path,
|
325
|
+
md_temperature=md_temperature,
|
326
|
+
md_steps=md_steps,
|
327
|
+
md_timestep=md_timestep,
|
328
|
+
relax_strategy=relax_strategy,
|
329
|
+
)
|
330
|
+
|
309
331
|
tsys.calc = calc
|
310
332
|
en = tsys.get_potential_energy()
|
311
333
|
de = en - e
|
@@ -317,20 +339,6 @@ def pack_molecules(
|
|
317
339
|
if u <= acc:
|
318
340
|
accept = True
|
319
341
|
break
|
320
|
-
if every > 0 and _itry / every == 0:
|
321
|
-
csys = save_the_day(
|
322
|
-
Path(out_path) / f"{sysname}{i}{Path(molecule).stem}.cif",
|
323
|
-
device,
|
324
|
-
arch,
|
325
|
-
model,
|
326
|
-
fmax,
|
327
|
-
out_path,
|
328
|
-
md_temperature,
|
329
|
-
md_steps,
|
330
|
-
md_timestep,
|
331
|
-
relax_strategy,
|
332
|
-
)
|
333
|
-
|
334
342
|
if accept:
|
335
343
|
csys = tsys.copy()
|
336
344
|
e = en
|
@@ -342,7 +350,7 @@ def pack_molecules(
|
|
342
350
|
# once you hit here is bad, this can keep looping
|
343
351
|
print(f"Failed to insert particle {i + 1} after {ntries} tries")
|
344
352
|
csys = save_the_day(
|
345
|
-
|
353
|
+
csys,
|
346
354
|
device,
|
347
355
|
arch,
|
348
356
|
model,
|
@@ -358,7 +366,7 @@ def pack_molecules(
|
|
358
366
|
|
359
367
|
# Perform final geometry optimization if requested
|
360
368
|
if geometry:
|
361
|
-
energy_final = optimize_geometry(
|
369
|
+
energy_final, csys = optimize_geometry(
|
362
370
|
Path(out_path) / f"{sysname}{nmols}{Path(molecule).stem}.cif",
|
363
371
|
device,
|
364
372
|
arch,
|
@@ -367,7 +375,7 @@ def pack_molecules(
|
|
367
375
|
out_path,
|
368
376
|
True,
|
369
377
|
)
|
370
|
-
return energy_final
|
378
|
+
return (energy_final, csys)
|
371
379
|
|
372
380
|
|
373
381
|
def load_molecule(molecule: str):
|
@@ -410,7 +418,7 @@ def rotate_molecule(mol):
|
|
410
418
|
|
411
419
|
|
412
420
|
def save_the_day(
|
413
|
-
struct_path: str
|
421
|
+
struct_path: str | Atoms,
|
414
422
|
device: str = "",
|
415
423
|
arch: str = "",
|
416
424
|
model: str = "",
|
@@ -423,7 +431,7 @@ def save_the_day(
|
|
423
431
|
) -> Atoms:
|
424
432
|
"""Geometry optimisation or MD to get a better structure."""
|
425
433
|
if relax_strategy == "geometry_optimisation":
|
426
|
-
_ = optimize_geometry(
|
434
|
+
_, a = optimize_geometry(
|
427
435
|
struct_path,
|
428
436
|
device,
|
429
437
|
arch,
|
@@ -431,7 +439,7 @@ def save_the_day(
|
|
431
439
|
fmax,
|
432
440
|
out_path,
|
433
441
|
)
|
434
|
-
return
|
442
|
+
return a
|
435
443
|
if relax_strategy == "md":
|
436
444
|
return run_md_nve(
|
437
445
|
struct_path, md_temperature, md_steps, md_timestep, arch, model, device
|
@@ -476,23 +484,34 @@ def run_md_nve(
|
|
476
484
|
|
477
485
|
|
478
486
|
def optimize_geometry(
|
479
|
-
struct_path: str,
|
487
|
+
struct_path: str | Atoms,
|
480
488
|
device: str,
|
481
489
|
arch: str,
|
482
490
|
model: str,
|
483
491
|
fmax: float,
|
484
492
|
out_path: str = ".",
|
485
493
|
opt_cell: bool = False,
|
486
|
-
) -> float:
|
494
|
+
) -> tuple(float, Atoms):
|
487
495
|
"""Optimize the geometry of a structure."""
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
496
|
+
if isinstance(struct_path, Atoms):
|
497
|
+
geo = GeomOpt(
|
498
|
+
struct=struct_path,
|
499
|
+
device=device,
|
500
|
+
arch=arch,
|
501
|
+
fmax=fmax,
|
502
|
+
calc_kwargs={"model_paths": model},
|
503
|
+
filter_kwargs={"hydrostatic_strain": opt_cell},
|
504
|
+
)
|
505
|
+
geo.run()
|
506
|
+
else:
|
507
|
+
geo = GeomOpt(
|
508
|
+
struct_path=struct_path,
|
509
|
+
device=device,
|
510
|
+
arch=arch,
|
511
|
+
fmax=fmax,
|
512
|
+
calc_kwargs={"model_paths": model},
|
513
|
+
filter_kwargs={"hydrostatic_strain": opt_cell},
|
514
|
+
)
|
515
|
+
geo.run()
|
516
|
+
write(Path(out_path) / f"{Path(struct_path).stem}-opt.cif", geo.struct)
|
517
|
+
return (geo.struct.get_potential_energy(), geo.struct)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "pack-mm"
|
3
|
-
version = "0.0.
|
3
|
+
version = "0.0.24"
|
4
4
|
description = "packing materials and molecules in boxes using for machine learnt interatomic potentials"
|
5
5
|
authors = [
|
6
6
|
{ name = "Alin M. Elena" },
|
@@ -53,6 +53,8 @@ docs = [
|
|
53
53
|
"sphinx-autodoc-typehints<3.0.0,>=2.5.0",
|
54
54
|
"sphinx-collapse>=0.1.3",
|
55
55
|
"sphinx-copybutton<1.0.0,>=0.5.2",
|
56
|
+
"data-tutorials",
|
57
|
+
"weas-widget",
|
56
58
|
]
|
57
59
|
pre-commit = [
|
58
60
|
"pre-commit<4.0.0,>=3.6.0",
|
@@ -0,0 +1,85 @@
|
|
1
|
+
"""Advanced tests for packmm."""
|
2
|
+
|
3
|
+
# Author; alin m elena, alin@elena.re
|
4
|
+
# Contribs;
|
5
|
+
# Date: 23-02-2025
|
6
|
+
# ©alin m elena,
|
7
|
+
from __future__ import annotations
|
8
|
+
|
9
|
+
from ase.build import bulk
|
10
|
+
from ase.io import read, write
|
11
|
+
import pytest
|
12
|
+
from typer.testing import CliRunner
|
13
|
+
|
14
|
+
from pack_mm.cli.packmm import app
|
15
|
+
|
16
|
+
runner = CliRunner()
|
17
|
+
|
18
|
+
err = 1.0e-8
|
19
|
+
|
20
|
+
|
21
|
+
def test_packmm_hmc(tmp_path):
|
22
|
+
"""Check values."""
|
23
|
+
result = runner.invoke(
|
24
|
+
app,
|
25
|
+
[
|
26
|
+
"--nmols",
|
27
|
+
"2",
|
28
|
+
"--model",
|
29
|
+
"small-0b2",
|
30
|
+
"--insert-strategy",
|
31
|
+
"hmc",
|
32
|
+
"--seed",
|
33
|
+
"2042",
|
34
|
+
"--cell-a",
|
35
|
+
"10",
|
36
|
+
"--cell-b",
|
37
|
+
"10",
|
38
|
+
"--cell-c",
|
39
|
+
"10",
|
40
|
+
"--out-path",
|
41
|
+
tmp_path,
|
42
|
+
],
|
43
|
+
)
|
44
|
+
assert result.exit_code == 0
|
45
|
+
assert (tmp_path / "1H2O.cif").exists()
|
46
|
+
assert (tmp_path / "2H2O.cif").exists()
|
47
|
+
f = read(tmp_path / "2H2O.cif")
|
48
|
+
assert f[0].position == pytest.approx([7.83420049, 6.21661184, 5.21814887], abs=err)
|
49
|
+
assert (tmp_path / "2H2O-opt.cif").exists()
|
50
|
+
f = read(tmp_path / "2H2O-opt.cif")
|
51
|
+
assert f[0].position == pytest.approx([7.83120709, 6.21742874, 5.22597213], abs=err)
|
52
|
+
|
53
|
+
|
54
|
+
def test_packmm_every(tmp_path):
|
55
|
+
"""Check values."""
|
56
|
+
na = bulk("NaCl", "rocksalt", a=5.61, cubic=True)
|
57
|
+
write(tmp_path / "system.cif", na)
|
58
|
+
write("system.cif", na)
|
59
|
+
|
60
|
+
result = runner.invoke(
|
61
|
+
app,
|
62
|
+
[
|
63
|
+
"--system",
|
64
|
+
tmp_path / "system.cif",
|
65
|
+
"--molecule",
|
66
|
+
"H2",
|
67
|
+
"--every",
|
68
|
+
"1",
|
69
|
+
"--nmols",
|
70
|
+
"2",
|
71
|
+
"--model",
|
72
|
+
"small-0b2",
|
73
|
+
"--seed",
|
74
|
+
"2042",
|
75
|
+
"--out-path",
|
76
|
+
tmp_path,
|
77
|
+
"--no-geometry",
|
78
|
+
],
|
79
|
+
)
|
80
|
+
assert result.exit_code == 0
|
81
|
+
assert (tmp_path / "system.cif").exists()
|
82
|
+
assert (tmp_path / "system+1H2.cif").exists()
|
83
|
+
assert (tmp_path / "system+2H2.cif").exists()
|
84
|
+
f = read(tmp_path / "system+2H2.cif")
|
85
|
+
assert f[0].position == pytest.approx([1.24701529, 0.024216, 0.11632905], abs=err)
|
@@ -236,7 +236,7 @@ def test_optimize_geometry(tmp_path):
|
|
236
236
|
molecule.set_pbc([True, True, True])
|
237
237
|
structure_file = tmp_path / "water.cif"
|
238
238
|
write(structure_file, molecule)
|
239
|
-
optimized_energy = optimize_geometry(
|
239
|
+
optimized_energy, _ = optimize_geometry(
|
240
240
|
str(structure_file),
|
241
241
|
device="cpu",
|
242
242
|
arch="mace_mp",
|
@@ -256,7 +256,7 @@ def test_pack_molecules(tmp_path):
|
|
256
256
|
system_file = tmp_path / "system.cif"
|
257
257
|
write(system_file, system)
|
258
258
|
|
259
|
-
e = pack_molecules(
|
259
|
+
e, _ = pack_molecules(
|
260
260
|
system=str(system_file),
|
261
261
|
molecule="H2O",
|
262
262
|
nmols=2,
|
@@ -279,6 +279,33 @@ def test_pack_molecules(tmp_path):
|
|
279
279
|
assert e == pytest.approx(-29.21589570470306, abs=err)
|
280
280
|
|
281
281
|
|
282
|
+
def test_pack_molecules_atoms(tmp_path):
|
283
|
+
"""Test pack molecule."""
|
284
|
+
system = Atoms(
|
285
|
+
"Ca", positions=[(5.0, 5.0, 5.0)], cell=[10, 10, 10], pbc=[True, True, True]
|
286
|
+
)
|
287
|
+
|
288
|
+
e, _ = pack_molecules(
|
289
|
+
system=system,
|
290
|
+
molecule="H2O",
|
291
|
+
nmols=2,
|
292
|
+
arch="mace_mp",
|
293
|
+
model="small-0b2",
|
294
|
+
device="cpu",
|
295
|
+
where="sphere",
|
296
|
+
center=(5.0, 5.0, 5.0),
|
297
|
+
radius=5.0,
|
298
|
+
seed=2042,
|
299
|
+
temperature=300,
|
300
|
+
ntries=10,
|
301
|
+
geometry=False,
|
302
|
+
fmax=0.1,
|
303
|
+
out_path=tmp_path,
|
304
|
+
)
|
305
|
+
|
306
|
+
assert e == pytest.approx(-29.21589570470306, abs=err)
|
307
|
+
|
308
|
+
|
282
309
|
def test_pack_molecules_2(tmp_path, capsys):
|
283
310
|
"""Test pack molecule."""
|
284
311
|
system = Atoms(
|
@@ -287,7 +314,7 @@ def test_pack_molecules_2(tmp_path, capsys):
|
|
287
314
|
system_file = tmp_path / "system.cif"
|
288
315
|
write(system_file, system)
|
289
316
|
|
290
|
-
e = pack_molecules(
|
317
|
+
e, _ = pack_molecules(
|
291
318
|
system=str(system_file),
|
292
319
|
molecule="H2O",
|
293
320
|
nmols=3,
|
@@ -307,7 +334,7 @@ def test_pack_molecules_2(tmp_path, capsys):
|
|
307
334
|
captured = capsys.readouterr()
|
308
335
|
|
309
336
|
assert "Failed to insert particle 3 after 2 tries" in captured.out
|
310
|
-
assert e == pytest.approx(-47.
|
337
|
+
assert e == pytest.approx(-47.194755808249454, abs=err)
|
311
338
|
|
312
339
|
|
313
340
|
def test_save_the_day(tmp_path):
|
@@ -318,7 +345,6 @@ def test_save_the_day(tmp_path):
|
|
318
345
|
molecule.center()
|
319
346
|
structure_file = tmp_path / "water.cif"
|
320
347
|
write(structure_file, molecule)
|
321
|
-
print(molecule.positions)
|
322
348
|
s = save_the_day(
|
323
349
|
str(structure_file),
|
324
350
|
device="cpu",
|
@@ -327,7 +353,6 @@ def test_save_the_day(tmp_path):
|
|
327
353
|
fmax=0.01,
|
328
354
|
out_path=tmp_path,
|
329
355
|
)
|
330
|
-
print(s.positions)
|
331
356
|
assert s[0].position == pytest.approx([5.0, 4.99619815, 5.30704738], abs=err)
|
332
357
|
|
333
358
|
|
@@ -339,7 +364,6 @@ def test_save_the_day_md(tmp_path):
|
|
339
364
|
molecule.center()
|
340
365
|
structure_file = tmp_path / "water.cif"
|
341
366
|
write(structure_file, molecule)
|
342
|
-
print(molecule.positions)
|
343
367
|
s = save_the_day(
|
344
368
|
str(structure_file),
|
345
369
|
device="cpu",
|
@@ -350,10 +374,30 @@ def test_save_the_day_md(tmp_path):
|
|
350
374
|
md_steps=10.0,
|
351
375
|
md_temperature=100.0,
|
352
376
|
)
|
353
|
-
print(s.positions)
|
354
377
|
assert s[0].position == pytest.approx([4.99684244, 5.00440785, 5.2987255], abs=err)
|
355
378
|
|
356
379
|
|
380
|
+
def test_save_the_day_invalid(tmp_path):
|
381
|
+
"""Test save the day."""
|
382
|
+
molecule = build_molecule("H2O")
|
383
|
+
molecule.set_cell([10, 10, 10])
|
384
|
+
molecule.set_pbc([True, True, True])
|
385
|
+
molecule.center()
|
386
|
+
structure_file = tmp_path / "water.cif"
|
387
|
+
write(structure_file, molecule)
|
388
|
+
s = save_the_day(
|
389
|
+
str(structure_file),
|
390
|
+
device="cpu",
|
391
|
+
arch="mace_mp",
|
392
|
+
model="small-0b2",
|
393
|
+
relax_strategy="invalid_stratregy",
|
394
|
+
md_timestep=1.0,
|
395
|
+
md_steps=10.0,
|
396
|
+
md_temperature=100.0,
|
397
|
+
)
|
398
|
+
assert s is None
|
399
|
+
|
400
|
+
|
357
401
|
def test_run_md(tmp_path):
|
358
402
|
"""Test md."""
|
359
403
|
molecule = build_molecule("H2O")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|