flashmd 0.2.0__tar.gz → 0.2.2__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.
- {flashmd-0.2.0/src/flashmd.egg-info → flashmd-0.2.2}/PKG-INFO +2 -1
- {flashmd-0.2.0 → flashmd-0.2.2}/README.md +1 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/pyproject.toml +1 -1
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/ase/bussi.py +3 -2
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/ase/langevin.py +9 -4
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/ase/velocity_verlet.py +36 -0
- flashmd-0.2.2/src/flashmd/constraints.py +97 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/ipi.py +10 -9
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/stepper.py +7 -0
- {flashmd-0.2.0 → flashmd-0.2.2/src/flashmd.egg-info}/PKG-INFO +2 -1
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd.egg-info/SOURCES.txt +1 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/LICENSE +0 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/setup.cfg +0 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/__init__.py +0 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/ase/__init__.py +0 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/ase/npt.py +0 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd/models.py +0 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd.egg-info/dependency_links.txt +0 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd.egg-info/requires.txt +0 -0
- {flashmd-0.2.0 → flashmd-0.2.2}/src/flashmd.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flashmd
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Accelerated molecular dynamics with large-time-step predictions
|
|
5
5
|
Author: flashmd developers
|
|
6
6
|
License: Apache-2.0
|
|
@@ -65,6 +65,7 @@ time_step = 16 # 16 fs; also available: 1, 2, 4, 8, 32, 64, 128 fs
|
|
|
65
65
|
# Create a structure and initialize velocities
|
|
66
66
|
atoms = ase.build.bulk("Al", "fcc", cubic=True)
|
|
67
67
|
MaxwellBoltzmannDistribution(atoms, temperature_K=300)
|
|
68
|
+
atoms.set_velocities(atoms.get_velocities() - atoms.get_velocities().mean(axis=0))
|
|
68
69
|
|
|
69
70
|
# Load models
|
|
70
71
|
device="cuda" if torch.cuda.is_available() else "cpu"
|
|
@@ -36,6 +36,7 @@ time_step = 16 # 16 fs; also available: 1, 2, 4, 8, 32, 64, 128 fs
|
|
|
36
36
|
# Create a structure and initialize velocities
|
|
37
37
|
atoms = ase.build.bulk("Al", "fcc", cubic=True)
|
|
38
38
|
MaxwellBoltzmannDistribution(atoms, temperature_K=300)
|
|
39
|
+
atoms.set_velocities(atoms.get_velocities() - atoms.get_velocities().mean(axis=0))
|
|
39
40
|
|
|
40
41
|
# Load models
|
|
41
42
|
device="cuda" if torch.cuda.is_available() else "cpu"
|
|
@@ -16,10 +16,11 @@ class Bussi(VelocityVerlet):
|
|
|
16
16
|
model: AtomisticModel | List[AtomisticModel],
|
|
17
17
|
time_constant: float = 10.0 * ase.units.fs,
|
|
18
18
|
device: str | torch.device = "auto",
|
|
19
|
-
rescale_energy: bool =
|
|
19
|
+
rescale_energy: bool = False,
|
|
20
|
+
random_rotation: bool = False,
|
|
20
21
|
**kwargs,
|
|
21
22
|
):
|
|
22
|
-
super().__init__(atoms, timestep, model, device, rescale_energy, **kwargs)
|
|
23
|
+
super().__init__(atoms, timestep, model, device, rescale_energy, random_rotation, **kwargs)
|
|
23
24
|
|
|
24
25
|
self.temperature_K = temperature_K
|
|
25
26
|
self.time_constant = time_constant
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from .velocity_verlet import VelocityVerlet
|
|
2
2
|
import ase.units
|
|
3
3
|
from typing import List
|
|
4
|
-
|
|
5
|
-
# from ..utils.pretrained import load_pretrained_models
|
|
6
4
|
from metatomic.torch import AtomisticModel
|
|
7
5
|
import torch
|
|
8
6
|
import ase
|
|
@@ -17,14 +15,19 @@ class Langevin(VelocityVerlet):
|
|
|
17
15
|
temperature_K: float,
|
|
18
16
|
model: AtomisticModel | List[AtomisticModel],
|
|
19
17
|
time_constant: float = 100.0 * ase.units.fs,
|
|
18
|
+
fixcm: bool = True,
|
|
20
19
|
device: str | torch.device = "auto",
|
|
21
|
-
rescale_energy: bool =
|
|
20
|
+
rescale_energy: bool = False,
|
|
21
|
+
random_rotation: bool = False,
|
|
22
22
|
**kwargs,
|
|
23
23
|
):
|
|
24
|
-
super().__init__(atoms, timestep, model, device, rescale_energy, **kwargs)
|
|
24
|
+
super().__init__(atoms, timestep, model, device, rescale_energy, random_rotation, **kwargs)
|
|
25
25
|
|
|
26
26
|
self.temperature_K = temperature_K
|
|
27
27
|
self.friction = 1.0 / time_constant
|
|
28
|
+
self.fixcm = fixcm
|
|
29
|
+
if self.fixcm:
|
|
30
|
+
self.atoms.set_velocities(self.atoms.get_velocities() - self.atoms.get_velocities().mean(axis=0))
|
|
28
31
|
|
|
29
32
|
def step(self):
|
|
30
33
|
self.apply_langevin_half_step()
|
|
@@ -39,3 +42,5 @@ class Langevin(VelocityVerlet):
|
|
|
39
42
|
ase.units.kB * self.temperature_K * self.atoms.get_masses()[:, None]
|
|
40
43
|
) * np.random.randn(*old_momenta.shape)
|
|
41
44
|
self.atoms.set_momenta(new_momenta)
|
|
45
|
+
if self.fixcm:
|
|
46
|
+
self.atoms.set_velocities(self.atoms.get_velocities() - self.atoms.get_velocities().mean(axis=0))
|
|
@@ -9,6 +9,7 @@ from metatomic.torch import System
|
|
|
9
9
|
import ase
|
|
10
10
|
from ..stepper import FlashMDStepper
|
|
11
11
|
import numpy as np
|
|
12
|
+
from scipy.spatial.transform import Rotation
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class VelocityVerlet(MolecularDynamics):
|
|
@@ -19,6 +20,7 @@ class VelocityVerlet(MolecularDynamics):
|
|
|
19
20
|
model: AtomisticModel | List[AtomisticModel],
|
|
20
21
|
device: str | torch.device = "auto",
|
|
21
22
|
rescale_energy: bool = True,
|
|
23
|
+
random_rotation: bool = False,
|
|
22
24
|
**kwargs,
|
|
23
25
|
):
|
|
24
26
|
super().__init__(atoms, timestep, **kwargs)
|
|
@@ -41,6 +43,7 @@ class VelocityVerlet(MolecularDynamics):
|
|
|
41
43
|
|
|
42
44
|
self.stepper = FlashMDStepper(model, self.device)
|
|
43
45
|
self.rescale_energy = rescale_energy
|
|
46
|
+
self.random_rotation = random_rotation
|
|
44
47
|
|
|
45
48
|
def step(self):
|
|
46
49
|
if self.rescale_energy:
|
|
@@ -49,7 +52,33 @@ class VelocityVerlet(MolecularDynamics):
|
|
|
49
52
|
system = _convert_atoms_to_system(
|
|
50
53
|
self.atoms, device=self.device, dtype=self.dtype
|
|
51
54
|
)
|
|
55
|
+
|
|
56
|
+
if self.random_rotation:
|
|
57
|
+
# generate a random rotation matrix with SciPy
|
|
58
|
+
R = torch.tensor(
|
|
59
|
+
_get_random_rotation(),
|
|
60
|
+
device=system.positions.device,
|
|
61
|
+
dtype=system.positions.dtype,
|
|
62
|
+
)
|
|
63
|
+
# apply the random rotation
|
|
64
|
+
old_cell = system.cell
|
|
65
|
+
system.cell = system.cell @ R.T
|
|
66
|
+
system.positions = system.positions @ R.T
|
|
67
|
+
# change momentum TensorMap in place
|
|
68
|
+
system.get_data("momenta").block().values[:] = (
|
|
69
|
+
(system.get_data("momenta").block().values.squeeze(-1) @ R.T).unsqueeze(-1)
|
|
70
|
+
)
|
|
71
|
+
|
|
52
72
|
new_system = self.stepper.step(system)
|
|
73
|
+
|
|
74
|
+
if self.random_rotation:
|
|
75
|
+
# revert q, p to the original reference frame, load old cell
|
|
76
|
+
new_system.cell = old_cell
|
|
77
|
+
new_system.positions = new_system.positions @ R
|
|
78
|
+
new_system.get_data("momenta").block().values[:] = (
|
|
79
|
+
(new_system.get_data("momenta").block().values.squeeze(-1) @ R).unsqueeze(-1)
|
|
80
|
+
)
|
|
81
|
+
|
|
53
82
|
self.atoms.set_positions(new_system.positions.detach().cpu().numpy())
|
|
54
83
|
self.atoms.set_momenta(
|
|
55
84
|
new_system.get_data("momenta")
|
|
@@ -120,3 +149,10 @@ def _convert_atoms_to_system(
|
|
|
120
149
|
),
|
|
121
150
|
)
|
|
122
151
|
return system
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _get_random_rotation():
|
|
155
|
+
R = Rotation.random().as_matrix()
|
|
156
|
+
if np.random.rand() < 0.5:
|
|
157
|
+
R *= -1 # allow improper rotations
|
|
158
|
+
return R
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from typing import Dict, List
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from metatensor.torch import TensorBlock, TensorMap
|
|
5
|
+
from metatomic.torch import System
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@torch.jit.script
|
|
9
|
+
def enforce_physical_constraints(
|
|
10
|
+
systems: List[System],
|
|
11
|
+
predictions: Dict[str, TensorMap],
|
|
12
|
+
timestep: float,
|
|
13
|
+
) -> Dict[str, TensorMap]:
|
|
14
|
+
"""
|
|
15
|
+
Enforces physical constraints in the predictions of a FlashMD model, namely
|
|
16
|
+
conservation of momentum of the center of mass and uniform linear motion of the
|
|
17
|
+
center of mass.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
new_predictions: Dict[str, TensorMap] = {}
|
|
21
|
+
|
|
22
|
+
for key, prediction_tmap in predictions.items():
|
|
23
|
+
if key == "momenta":
|
|
24
|
+
# conservation of momentum of the center of mass
|
|
25
|
+
system_sizes = [len(s) for s in systems]
|
|
26
|
+
masses = [s.get_data("masses").block().values for s in systems]
|
|
27
|
+
total_masses = [m.sum() for m in masses]
|
|
28
|
+
momenta_before = [s.get_data("momenta").block().values for s in systems]
|
|
29
|
+
momenta_now = torch.split(prediction_tmap.block().values, system_sizes)
|
|
30
|
+
velocities_now = [p / m[:, None] for p, m in zip(momenta_now, masses)]
|
|
31
|
+
velocities_com_before = [
|
|
32
|
+
torch.sum(p, dim=0) / M for p, M in zip(momenta_before, total_masses)
|
|
33
|
+
]
|
|
34
|
+
velocities_com_now = [
|
|
35
|
+
torch.sum(p, dim=0) / M for p, M in zip(momenta_now, total_masses)
|
|
36
|
+
]
|
|
37
|
+
velocities_now = [
|
|
38
|
+
v - v_com_now_i + v_com_before_i
|
|
39
|
+
for v, v_com_before_i, v_com_now_i in zip(
|
|
40
|
+
velocities_now, velocities_com_before, velocities_com_now
|
|
41
|
+
)
|
|
42
|
+
]
|
|
43
|
+
momenta_now = [v * m[:, None] for v, m in zip(velocities_now, masses)]
|
|
44
|
+
new_predictions[key] = TensorMap(
|
|
45
|
+
prediction_tmap.keys,
|
|
46
|
+
[
|
|
47
|
+
TensorBlock(
|
|
48
|
+
values=torch.concatenate(momenta_now),
|
|
49
|
+
samples=prediction_tmap.block().samples,
|
|
50
|
+
components=prediction_tmap.block().components,
|
|
51
|
+
properties=prediction_tmap.block().properties,
|
|
52
|
+
)
|
|
53
|
+
],
|
|
54
|
+
)
|
|
55
|
+
elif key == "positions":
|
|
56
|
+
# uniform linear motion of the center of mass
|
|
57
|
+
system_sizes = [len(s) for s in systems]
|
|
58
|
+
masses = [s.get_data("masses").block().values for s in systems]
|
|
59
|
+
total_masses = [m.sum() for m in masses]
|
|
60
|
+
positions_before = [s.positions.unsqueeze(-1) for s in systems]
|
|
61
|
+
momenta = [s.get_data("momenta").block().values for s in systems]
|
|
62
|
+
positions_now = torch.split(prediction_tmap.block().values, system_sizes)
|
|
63
|
+
velocities_com = [
|
|
64
|
+
torch.sum(p, dim=0) / M for p, M in zip(momenta, total_masses)
|
|
65
|
+
]
|
|
66
|
+
positions_com_before = [
|
|
67
|
+
torch.sum(q * m[:, None], dim=0) / M
|
|
68
|
+
for q, m, M in zip(positions_before, masses, total_masses)
|
|
69
|
+
]
|
|
70
|
+
positions_com_now = [
|
|
71
|
+
torch.sum(q * m[:, None], dim=0) / M
|
|
72
|
+
for q, m, M in zip(positions_now, masses, total_masses)
|
|
73
|
+
]
|
|
74
|
+
positions_now = [
|
|
75
|
+
q - q_com_now_i + q_com_before_i + v_com_i * timestep
|
|
76
|
+
for q, q_com_now_i, q_com_before_i, v_com_i in zip(
|
|
77
|
+
positions_now,
|
|
78
|
+
positions_com_now,
|
|
79
|
+
positions_com_before,
|
|
80
|
+
velocities_com,
|
|
81
|
+
)
|
|
82
|
+
]
|
|
83
|
+
new_predictions[key] = TensorMap(
|
|
84
|
+
prediction_tmap.keys,
|
|
85
|
+
[
|
|
86
|
+
TensorBlock(
|
|
87
|
+
values=torch.concatenate(positions_now),
|
|
88
|
+
samples=prediction_tmap.block().samples,
|
|
89
|
+
components=prediction_tmap.block().components,
|
|
90
|
+
properties=prediction_tmap.block().properties,
|
|
91
|
+
)
|
|
92
|
+
],
|
|
93
|
+
)
|
|
94
|
+
else:
|
|
95
|
+
new_predictions[key] = prediction_tmap
|
|
96
|
+
|
|
97
|
+
return new_predictions
|
|
@@ -15,7 +15,7 @@ from metatensor.torch import Labels, TensorBlock, TensorMap
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def get_standard_vv_step(
|
|
18
|
-
sim, model=None, device=None, rescale_energy=
|
|
18
|
+
sim, model=None, device=None, rescale_energy=False, random_rotation=False
|
|
19
19
|
):
|
|
20
20
|
"""
|
|
21
21
|
Returns a velocity Verlet stepper function for i-PI simulations.
|
|
@@ -37,7 +37,7 @@ def get_standard_vv_step(
|
|
|
37
37
|
|
|
38
38
|
if rescale_energy:
|
|
39
39
|
info("@flashmd: Old energy", verbosity.debug)
|
|
40
|
-
old_energy = sim.properties("
|
|
40
|
+
old_energy = sim.properties("conserved")
|
|
41
41
|
|
|
42
42
|
motion.integrator.pstep(level=0)
|
|
43
43
|
motion.integrator.pconstraints()
|
|
@@ -48,7 +48,7 @@ def get_standard_vv_step(
|
|
|
48
48
|
|
|
49
49
|
if rescale_energy:
|
|
50
50
|
info("@flashmd: Energy rescale", verbosity.debug)
|
|
51
|
-
new_energy = sim.properties("
|
|
51
|
+
new_energy = sim.properties("conserved")
|
|
52
52
|
kinetic_energy = sim.properties("kinetic_md")
|
|
53
53
|
alpha = np.sqrt(1.0 - (new_energy - old_energy) / kinetic_energy)
|
|
54
54
|
motion.beads.p[:] = alpha * dstrip(motion.beads.p)
|
|
@@ -56,7 +56,7 @@ def get_standard_vv_step(
|
|
|
56
56
|
return vv_step
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def get_flashmd_vv_step(sim, model, device, rescale_energy=
|
|
59
|
+
def get_flashmd_vv_step(sim, model, device, rescale_energy=False, random_rotation=False):
|
|
60
60
|
capabilities = model.capabilities()
|
|
61
61
|
|
|
62
62
|
model_timestep = float(model.module.timestep)
|
|
@@ -76,7 +76,7 @@ def get_flashmd_vv_step(sim, model, device, rescale_energy=True, random_rotation
|
|
|
76
76
|
info("@flashmd: Starting VV", verbosity.debug)
|
|
77
77
|
if rescale_energy:
|
|
78
78
|
info("@flashmd: Old energy", verbosity.debug)
|
|
79
|
-
old_energy = sim.properties("
|
|
79
|
+
old_energy = sim.properties("conserved")
|
|
80
80
|
|
|
81
81
|
info("@flashmd: Stepper", verbosity.debug)
|
|
82
82
|
system = ipi_to_system(motion, device, dtype)
|
|
@@ -109,11 +109,12 @@ def get_flashmd_vv_step(sim, model, device, rescale_energy=True, random_rotation
|
|
|
109
109
|
|
|
110
110
|
if rescale_energy:
|
|
111
111
|
info("@flashmd: Energy rescale", verbosity.debug)
|
|
112
|
-
new_energy = sim.properties("
|
|
112
|
+
new_energy = sim.properties("conserved")
|
|
113
113
|
kinetic_energy = sim.properties("kinetic_md")
|
|
114
114
|
alpha = np.sqrt(1.0 - (new_energy - old_energy) / kinetic_energy)
|
|
115
115
|
motion.beads.p[:] = alpha * dstrip(motion.beads.p)
|
|
116
|
-
|
|
116
|
+
motion.integrator.pconstraints() # just to be sure
|
|
117
|
+
|
|
117
118
|
info("@flashmd: End of VV step", verbosity.debug)
|
|
118
119
|
|
|
119
120
|
return flashmd_vv
|
|
@@ -155,7 +156,7 @@ def get_nvt_stepper(
|
|
|
155
156
|
sim,
|
|
156
157
|
model,
|
|
157
158
|
device,
|
|
158
|
-
rescale_energy=
|
|
159
|
+
rescale_energy=False,
|
|
159
160
|
random_rotation=False,
|
|
160
161
|
use_standard_vv=False,
|
|
161
162
|
):
|
|
@@ -223,7 +224,7 @@ def get_npt_stepper(
|
|
|
223
224
|
sim,
|
|
224
225
|
model,
|
|
225
226
|
device,
|
|
226
|
-
rescale_energy=
|
|
227
|
+
rescale_energy=False,
|
|
227
228
|
random_rotation=False,
|
|
228
229
|
use_standard_vv=False,
|
|
229
230
|
):
|
|
@@ -5,6 +5,8 @@ import torch
|
|
|
5
5
|
from metatomic.torch import System
|
|
6
6
|
from metatrain.utils.neighbor_lists import get_system_with_neighbor_lists
|
|
7
7
|
from metatomic.torch import AtomisticModel
|
|
8
|
+
from .constraints import enforce_physical_constraints
|
|
9
|
+
import ase.units
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class FlashMDStepper:
|
|
@@ -14,6 +16,7 @@ class FlashMDStepper:
|
|
|
14
16
|
device: torch.device,
|
|
15
17
|
):
|
|
16
18
|
self.model = model.to(device)
|
|
19
|
+
self.time_step = float(model.module.timestep) * ase.units.fs
|
|
17
20
|
|
|
18
21
|
# one of these for each model:
|
|
19
22
|
self.evaluation_options = ModelEvaluationOptions(
|
|
@@ -41,6 +44,10 @@ class FlashMDStepper:
|
|
|
41
44
|
model_outputs = self.model(
|
|
42
45
|
[system], self.evaluation_options, check_consistency=False
|
|
43
46
|
)
|
|
47
|
+
model_outputs = enforce_physical_constraints(
|
|
48
|
+
[system], model_outputs, timestep=self.time_step
|
|
49
|
+
)
|
|
50
|
+
|
|
44
51
|
new_q = model_outputs["positions"].block().values.squeeze(-1)
|
|
45
52
|
new_p = model_outputs["momenta"].block().values.squeeze(-1)
|
|
46
53
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flashmd
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Accelerated molecular dynamics with large-time-step predictions
|
|
5
5
|
Author: flashmd developers
|
|
6
6
|
License: Apache-2.0
|
|
@@ -65,6 +65,7 @@ time_step = 16 # 16 fs; also available: 1, 2, 4, 8, 32, 64, 128 fs
|
|
|
65
65
|
# Create a structure and initialize velocities
|
|
66
66
|
atoms = ase.build.bulk("Al", "fcc", cubic=True)
|
|
67
67
|
MaxwellBoltzmannDistribution(atoms, temperature_K=300)
|
|
68
|
+
atoms.set_velocities(atoms.get_velocities() - atoms.get_velocities().mean(axis=0))
|
|
68
69
|
|
|
69
70
|
# Load models
|
|
70
71
|
device="cuda" if torch.cuda.is_available() else "cpu"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|