flashmd 0.2.1__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.1/src/flashmd.egg-info → flashmd-0.2.2}/PKG-INFO +2 -1
- {flashmd-0.2.1 → flashmd-0.2.2}/README.md +1 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/pyproject.toml +1 -1
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd/ase/bussi.py +3 -2
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd/ase/langevin.py +9 -4
- {flashmd-0.2.1 → 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.1 → flashmd-0.2.2}/src/flashmd/ipi.py +5 -5
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd/stepper.py +7 -0
- {flashmd-0.2.1 → flashmd-0.2.2/src/flashmd.egg-info}/PKG-INFO +2 -1
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd.egg-info/SOURCES.txt +1 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/LICENSE +0 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/setup.cfg +0 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd/__init__.py +0 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd/ase/__init__.py +0 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd/ase/npt.py +0 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd/models.py +0 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd.egg-info/dependency_links.txt +0 -0
- {flashmd-0.2.1 → flashmd-0.2.2}/src/flashmd.egg-info/requires.txt +0 -0
- {flashmd-0.2.1 → 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.
|
|
@@ -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)
|
|
@@ -113,7 +113,7 @@ def get_flashmd_vv_step(sim, model, device, rescale_energy=True, random_rotation
|
|
|
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
|
-
motion.integrator.pconstraints() # just to
|
|
116
|
+
motion.integrator.pconstraints() # just to be sure
|
|
117
117
|
|
|
118
118
|
info("@flashmd: End of VV step", verbosity.debug)
|
|
119
119
|
|
|
@@ -156,7 +156,7 @@ def get_nvt_stepper(
|
|
|
156
156
|
sim,
|
|
157
157
|
model,
|
|
158
158
|
device,
|
|
159
|
-
rescale_energy=
|
|
159
|
+
rescale_energy=False,
|
|
160
160
|
random_rotation=False,
|
|
161
161
|
use_standard_vv=False,
|
|
162
162
|
):
|
|
@@ -224,7 +224,7 @@ def get_npt_stepper(
|
|
|
224
224
|
sim,
|
|
225
225
|
model,
|
|
226
226
|
device,
|
|
227
|
-
rescale_energy=
|
|
227
|
+
rescale_energy=False,
|
|
228
228
|
random_rotation=False,
|
|
229
229
|
use_standard_vv=False,
|
|
230
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
|