Skip to content

Commit f362936

Browse files
authored
Add an option for dispersion corrections (#11)
* Add an option for dispersion corrections * Minor formatting corrections * Minor formatting correction * Remove whitespace
1 parent 16a6f81 commit f362936

File tree

5 files changed

+132
-5
lines changed

5 files changed

+132
-5
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ after optimisation
200200
│ [default: cpu] │
201201
│ --model TEXT ML model to use. │
202202
│ [default: medium-omat-0] │
203+
│ --dispersion --no-dispersion Include a dispersion │
204+
│ correction. │
205+
│ [default: no-dispersion] │
203206
│ --arch TEXT MLIP architecture to use. │
204207
│ [default: mace_mp] │
205208
│ --temperature FLOAT Temperature for the Monte │

pack_mm/cli/packmm.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def packmm(
113113
"cpu", help="Device to run calculations on (e.g., 'cpu' or 'cuda')."
114114
),
115115
model: str = Option("medium-omat-0", help="ML model to use."),
116+
dispersion: bool = Option(False, help="Include a dispersion correction."),
116117
arch: str = Option("mace_mp", help="MLIP architecture to use."),
117118
temperature: float = Option(
118119
300.0, help="Temperature for the Monte Carlo acceptance rule."
@@ -152,6 +153,7 @@ def packmm(
152153
print(f"{cell_c=}")
153154
print(f"{arch=}")
154155
print(f"{model=}")
156+
print(f"{dispersion=}")
155157
print(f"{device=}")
156158
print(f"{temperature=}")
157159
print(f"{fmax=}")

pack_mm/core/core.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ def pack_molecules(
191191
nmols: int = -1,
192192
arch: str = "mace_mp",
193193
model: str = "medium-omat-0",
194+
dispersion: bool = False,
194195
device: str = "cpu",
195196
where: str = "anywhere",
196197
center: tuple[float, float, float] = None,
@@ -226,6 +227,7 @@ def pack_molecules(
226227
nmols (int): Number of molecules to insert.
227228
arch (str): Architecture for the calculator.
228229
model (str): Path to the model file.
230+
dispersion (bool): If a dispersion correction is added to MLIP calculator.
229231
device (str): Device to run calculations on (e.g., "cpu" or "cuda").
230232
where (str): Region to insert molecules ("anywhere",
231233
"sphere", "cylinderZ", etc.).
@@ -297,7 +299,12 @@ def pack_molecules(
297299
cell, center, where, a, b, c, radius, height
298300
)
299301

300-
calc = choose_calculator(arch=arch, model_path=model, device=device)
302+
calc = choose_calculator(
303+
arch=arch,
304+
model_path=model,
305+
device=device,
306+
dispersion=dispersion,
307+
)
301308
sys.calc = calc
302309

303310
e = sys.get_potential_energy() if len(sys) > 0 else 0.0
@@ -319,7 +326,14 @@ def pack_molecules(
319326
tsys = csys.copy() + mol.copy()
320327
if insert_strategy == "hmc":
321328
tsys = run_md_nve(
322-
tsys, md_temperature, md_steps, md_timestep, arch, model, device
329+
tsys,
330+
md_temperature,
331+
md_steps,
332+
md_timestep,
333+
arch,
334+
model,
335+
dispersion,
336+
device,
323337
)
324338

325339
if every > 0 and _itry / every == 0:
@@ -328,6 +342,7 @@ def pack_molecules(
328342
device=device,
329343
arch=arch,
330344
model=model,
345+
dispersion=dispersion,
331346
fmax=fmax,
332347
out_path=out_path,
333348
md_temperature=md_temperature,
@@ -362,6 +377,7 @@ def pack_molecules(
362377
device,
363378
arch,
364379
model,
380+
dispersion,
365381
fmax,
366382
out_path,
367383
md_temperature,
@@ -382,6 +398,7 @@ def pack_molecules(
382398
fmax=fmax,
383399
out_path=out_path,
384400
opt_cell=True,
401+
dispersion=dispersion,
385402
)
386403
return (energy_final, csys)
387404

@@ -430,6 +447,7 @@ def save_the_day(
430447
device: str = "",
431448
arch: str = "",
432449
model: str = "",
450+
dispersion: bool = False,
433451
fmax: float = 0.01,
434452
out_path: str = ".",
435453
md_temperature: float = 100.0,
@@ -446,11 +464,19 @@ def save_the_day(
446464
model,
447465
fmax,
448466
out_path,
467+
dispersion=dispersion,
449468
)
450469
return a
451470
if relax_strategy == "md":
452471
return run_md_nve(
453-
struct, md_temperature, md_steps, md_timestep, arch, model, device
472+
struct,
473+
md_temperature,
474+
md_steps,
475+
md_timestep,
476+
arch,
477+
model,
478+
dispersion,
479+
device,
454480
)
455481
return None
456482

@@ -462,6 +488,7 @@ def run_md_nve(
462488
timestep: float = 1.0,
463489
arch: str = "",
464490
model: str = "",
491+
dispersion: bool = False,
465492
device: str = "",
466493
) -> Atoms:
467494
"""Run nve simulation."""
@@ -470,7 +497,7 @@ def run_md_nve(
470497
temp=temp,
471498
device=device,
472499
arch=arch,
473-
calc_kwargs={"model_paths": model},
500+
calc_kwargs={"model_paths": model, "dispersion": dispersion},
474501
stats_every=1,
475502
steps=steps,
476503
timestep=timestep,
@@ -487,14 +514,15 @@ def optimize_geometry(
487514
fmax: float,
488515
out_path: str = ".",
489516
opt_cell: bool = False,
517+
dispersion: bool = False,
490518
) -> tuple(float, Atoms):
491519
"""Optimize the geometry of a structure."""
492520
geo = GeomOpt(
493521
struct=struct,
494522
device=device,
495523
arch=arch,
496524
fmax=fmax,
497-
calc_kwargs={"model_paths": model},
525+
calc_kwargs={"model_paths": model, "dispersion": dispersion},
498526
filter_kwargs={"hydrostatic_strain": opt_cell},
499527
)
500528
geo.run()

tests/test_cli.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ def test_packmm_custom_threshold():
194194
assert "threshold=0.9" in strip_ansi_codes(result.output)
195195

196196

197+
def test_packmm_with_dispersion():
198+
"""Check dispersion."""
199+
result = runner.invoke(app, ["--dispersion"])
200+
assert result.exit_code == 0
201+
assert "dispersion=True" in strip_ansi_codes(result.output)
202+
203+
197204
def test_packmm_no_geometry_optimization():
198205
"""Check optimisation."""
199206
result = runner.invoke(app, ["--no-geometry"])

tests/test_core.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,26 @@ def test_optimize_geometry(tmp_path):
248248
assert optimized_energy == pytest.approx(-14.17098106193308, abs=err)
249249

250250

251+
def test_optimize_geometry_dispersion(tmp_path):
252+
"""Test go with dispersion."""
253+
molecule = build_molecule("H2O")
254+
molecule.set_cell([10, 10, 10])
255+
molecule.set_pbc([True, True, True])
256+
structure_file = tmp_path / "water.cif"
257+
write(structure_file, molecule)
258+
optimized_energy, _ = optimize_geometry(
259+
str(structure_file),
260+
device="cpu",
261+
arch="mace_mp",
262+
model="small-0b2",
263+
fmax=0.01,
264+
out_path=tmp_path,
265+
opt_cell=True,
266+
dispersion=True,
267+
)
268+
assert optimized_energy == pytest.approx(-14.180876622530265, abs=err)
269+
270+
251271
def test_pack_molecules(tmp_path):
252272
"""Test pack molecule."""
253273
system = Atoms(
@@ -337,6 +357,34 @@ def test_pack_molecules_2(tmp_path, capsys):
337357
assert e == pytest.approx(-47.194755808249454, abs=err)
338358

339359

360+
def test_pack_molecules_atoms_dispersion(tmp_path):
361+
"""Test pack molecule with dispersion."""
362+
system = Atoms(
363+
"Ca", positions=[(2.5, 2.5, 2.5)], cell=[5, 5, 5], pbc=[True, True, True]
364+
)
365+
366+
e, _ = pack_molecules(
367+
system=system,
368+
molecule="H2O",
369+
nmols=3,
370+
arch="mace_mp",
371+
model="small-0b2",
372+
dispersion=True,
373+
device="cpu",
374+
where="sphere",
375+
center=(2.5, 2.5, 2.5),
376+
radius=2.5,
377+
seed=2042,
378+
temperature=300,
379+
ntries=10,
380+
geometry=False,
381+
fmax=0.1,
382+
out_path=tmp_path,
383+
)
384+
# Without dispersion: -42.386622937612316
385+
assert e == pytest.approx(-42.93461715053182, abs=err)
386+
387+
340388
def test_save_the_day(tmp_path):
341389
"""Test save the day."""
342390
molecule = build_molecule("H2O")
@@ -377,6 +425,26 @@ def test_save_the_day_md(tmp_path):
377425
assert s[0].position == pytest.approx([4.99684244, 5.00440785, 5.2987255], abs=err)
378426

379427

428+
def test_save_the_day_dispersion(tmp_path):
429+
"""Test save the day with dispersion."""
430+
molecule = build_molecule("H2O")
431+
molecule.set_cell([10, 10, 10])
432+
molecule.set_pbc([True, True, True])
433+
molecule.center()
434+
structure_file = tmp_path / "water.cif"
435+
write(structure_file, molecule)
436+
s = save_the_day(
437+
str(structure_file),
438+
device="cpu",
439+
arch="mace_mp",
440+
model="small-0b2",
441+
dispersion=True,
442+
fmax=0.01,
443+
out_path=tmp_path,
444+
)
445+
assert s[0].position == pytest.approx([4.99998938, 4.99618389, 5.30704305], abs=err)
446+
447+
380448
def test_save_the_day_invalid(tmp_path):
381449
"""Test save the day."""
382450
molecule = build_molecule("H2O")
@@ -414,3 +482,22 @@ def test_run_md(tmp_path):
414482
temp=100.0,
415483
)
416484
assert s[0].position == pytest.approx([4.99684244, 5.00440785, 5.2987255], abs=err)
485+
486+
487+
def test_run_md_dispersion(tmp_path):
488+
"""Test md with dispersion."""
489+
molecule = build_molecule("H2O")
490+
molecule.set_cell([10, 10, 10])
491+
molecule.set_pbc([True, True, True])
492+
molecule.center()
493+
s = run_md_nve(
494+
molecule,
495+
device="cpu",
496+
arch="mace_mp",
497+
model="small-0b2",
498+
dispersion=True,
499+
timestep=1.0,
500+
steps=10.0,
501+
temp=100.0,
502+
)
503+
assert s[0].position == pytest.approx([4.99684244, 5.00440795, 5.29872694], abs=err)

0 commit comments

Comments
 (0)