flashmd 0.2.8__tar.gz → 0.3.0__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.8 → flashmd-0.3.0}/PKG-INFO +18 -9
- {flashmd-0.2.8 → flashmd-0.3.0}/README.md +10 -3
- flashmd-0.3.0/pyproject.toml +105 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/ase/__init__.py +1 -1
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/ase/bussi.py +17 -3
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/ase/langevin.py +10 -2
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/ase/velocity_verlet.py +45 -20
- flashmd-0.3.0/src/flashmd/fpi.py +86 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/ipi.py +122 -23
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/models.py +55 -3
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/stepper.py +24 -15
- flashmd-0.3.0/src/flashmd/symplectic_stepper.py +189 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd.egg-info/PKG-INFO +18 -9
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd.egg-info/SOURCES.txt +6 -1
- flashmd-0.3.0/src/flashmd.egg-info/requires.txt +11 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/tests/test_edge_cases.py +5 -5
- flashmd-0.3.0/tests/test_ipi.py +111 -0
- flashmd-0.3.0/tests/test_random_rotation.py +26 -0
- flashmd-0.3.0/tests/test_symplectic.py +66 -0
- flashmd-0.2.8/pyproject.toml +0 -82
- flashmd-0.2.8/src/flashmd.egg-info/requires.txt +0 -8
- {flashmd-0.2.8 → flashmd-0.3.0}/LICENSE +0 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/setup.cfg +0 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/__init__.py +0 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd/constraints.py +0 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd.egg-info/dependency_links.txt +0 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/src/flashmd.egg-info/top_level.txt +0 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/tests/test_energy.py +0 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/tests/test_models.py +0 -0
- {flashmd-0.2.8 → flashmd-0.3.0}/tests/test_readme.py +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flashmd
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Accelerated molecular dynamics with large-time-step predictions
|
|
5
5
|
Author: flashmd developers
|
|
6
|
-
License: Apache-2.0
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
7
|
Keywords: machine learning,molecular modeling,molecular dynamics
|
|
8
8
|
Classifier: Development Status :: 4 - Beta
|
|
9
9
|
Classifier: Intended Audience :: Science/Research
|
|
10
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
10
|
Classifier: Operating System :: POSIX
|
|
12
11
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
13
12
|
Classifier: Operating System :: Microsoft :: Windows
|
|
@@ -19,12 +18,15 @@ Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
|
19
18
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
20
19
|
Classifier: Topic :: Software Development :: Libraries
|
|
21
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.11
|
|
23
22
|
Description-Content-Type: text/markdown
|
|
24
23
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: metatrain==2026.
|
|
26
|
-
Requires-Dist: ase
|
|
24
|
+
Requires-Dist: metatrain==2026.3.1
|
|
25
|
+
Requires-Dist: metatomic-ase
|
|
26
|
+
Requires-Dist: vesin>=0.5.8
|
|
27
|
+
Requires-Dist: ase>=3.29.0
|
|
27
28
|
Requires-Dist: huggingface_hub
|
|
29
|
+
Requires-Dist: scipy
|
|
28
30
|
Provides-Extra: dev
|
|
29
31
|
Requires-Dist: pytest; extra == "dev"
|
|
30
32
|
Requires-Dist: ruff; extra == "dev"
|
|
@@ -56,7 +58,7 @@ After installation, you can run accelerated molecular dynamics as follows:
|
|
|
56
58
|
import ase.build
|
|
57
59
|
import ase.units
|
|
58
60
|
import torch
|
|
59
|
-
from ase.md.velocitydistribution import
|
|
61
|
+
from ase.md.velocitydistribution import thermalize_momenta
|
|
60
62
|
|
|
61
63
|
from flashmd import get_pretrained
|
|
62
64
|
from flashmd.ase import EnergyCalculator
|
|
@@ -68,7 +70,7 @@ time_step = 64 # 64 fs; also available: 1, 2, 4, 8, 16, 32, 128 fs
|
|
|
68
70
|
|
|
69
71
|
# Create a structure and initialize velocities
|
|
70
72
|
atoms = ase.build.bulk("Al", "fcc", cubic=True)
|
|
71
|
-
|
|
73
|
+
thermalize_momenta(atoms, temperature_K=300)
|
|
72
74
|
atoms.set_velocities( # it is generally a good idea to remove any net velocity
|
|
73
75
|
atoms.get_velocities() - atoms.get_momenta().sum(axis=0) / atoms.get_masses().sum()
|
|
74
76
|
)
|
|
@@ -128,7 +130,7 @@ Using FlashMD in i-PI
|
|
|
128
130
|
---------------------
|
|
129
131
|
|
|
130
132
|
You can see
|
|
131
|
-
[this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
|
|
133
|
+
[this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
|
|
132
134
|
for usage examples. i-PI is our most mature interface, and the one that was used to
|
|
133
135
|
generate all our published results.
|
|
134
136
|
|
|
@@ -191,3 +193,10 @@ Note that the results were obtained through the i-PI interface.
|
|
|
191
193
|
|
|
192
194
|
Instructions and material to reproduce the results in the paper are available on
|
|
193
195
|
Materials Cloud at https://doi.org/10.24435/materialscloud:b7-xq.
|
|
196
|
+
|
|
197
|
+
Maintainers
|
|
198
|
+
-----------
|
|
199
|
+
|
|
200
|
+
This project is [maintained](https://github.com/lab-cosmo/.github/blob/main/Maintainers.md) by [@johannes-spies](https://github.com/johannes-spies) and [@zyxwwxyz](https://github.com/zyxwwxyz), who will reply to issues and pull requests opened on this repository as soon as possible. You can mention them directly if you haven't received an answer after a couple of days.
|
|
201
|
+
|
|
202
|
+
It was previously maintained by [@frostedoyster](https://github.com/frostedoyster) (2025 to 2026), many thanks to them for creating FlashMD!
|
|
@@ -23,7 +23,7 @@ After installation, you can run accelerated molecular dynamics as follows:
|
|
|
23
23
|
import ase.build
|
|
24
24
|
import ase.units
|
|
25
25
|
import torch
|
|
26
|
-
from ase.md.velocitydistribution import
|
|
26
|
+
from ase.md.velocitydistribution import thermalize_momenta
|
|
27
27
|
|
|
28
28
|
from flashmd import get_pretrained
|
|
29
29
|
from flashmd.ase import EnergyCalculator
|
|
@@ -35,7 +35,7 @@ time_step = 64 # 64 fs; also available: 1, 2, 4, 8, 16, 32, 128 fs
|
|
|
35
35
|
|
|
36
36
|
# Create a structure and initialize velocities
|
|
37
37
|
atoms = ase.build.bulk("Al", "fcc", cubic=True)
|
|
38
|
-
|
|
38
|
+
thermalize_momenta(atoms, temperature_K=300)
|
|
39
39
|
atoms.set_velocities( # it is generally a good idea to remove any net velocity
|
|
40
40
|
atoms.get_velocities() - atoms.get_momenta().sum(axis=0) / atoms.get_masses().sum()
|
|
41
41
|
)
|
|
@@ -95,7 +95,7 @@ Using FlashMD in i-PI
|
|
|
95
95
|
---------------------
|
|
96
96
|
|
|
97
97
|
You can see
|
|
98
|
-
[this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
|
|
98
|
+
[this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
|
|
99
99
|
for usage examples. i-PI is our most mature interface, and the one that was used to
|
|
100
100
|
generate all our published results.
|
|
101
101
|
|
|
@@ -158,3 +158,10 @@ Note that the results were obtained through the i-PI interface.
|
|
|
158
158
|
|
|
159
159
|
Instructions and material to reproduce the results in the paper are available on
|
|
160
160
|
Materials Cloud at https://doi.org/10.24435/materialscloud:b7-xq.
|
|
161
|
+
|
|
162
|
+
Maintainers
|
|
163
|
+
-----------
|
|
164
|
+
|
|
165
|
+
This project is [maintained](https://github.com/lab-cosmo/.github/blob/main/Maintainers.md) by [@johannes-spies](https://github.com/johannes-spies) and [@zyxwwxyz](https://github.com/zyxwwxyz), who will reply to issues and pull requests opened on this repository as soon as possible. You can mention them directly if you haven't received an answer after a couple of days.
|
|
166
|
+
|
|
167
|
+
It was previously maintained by [@frostedoyster](https://github.com/frostedoyster) (2025 to 2026), many thanks to them for creating FlashMD!
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "flashmd"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
requires-python = ">=3.11"
|
|
5
|
+
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "Apache-2.0"
|
|
8
|
+
description = "Accelerated molecular dynamics with large-time-step predictions"
|
|
9
|
+
authors = [{name = "flashmd developers"}]
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
"metatrain==2026.3.1",
|
|
13
|
+
"metatomic-ase",
|
|
14
|
+
"vesin>=0.5.8",
|
|
15
|
+
"ase>=3.29.0", # thermalize_momenta replaces MaxwellBoltzmannDistribution
|
|
16
|
+
"huggingface_hub",
|
|
17
|
+
"scipy",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
keywords = ["machine learning", "molecular modeling", "molecular dynamics"]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 4 - Beta",
|
|
23
|
+
"Intended Audience :: Science/Research",
|
|
24
|
+
"Operating System :: POSIX",
|
|
25
|
+
"Operating System :: MacOS :: MacOS X",
|
|
26
|
+
"Operating System :: Microsoft :: Windows",
|
|
27
|
+
"Programming Language :: Python",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Topic :: Scientific/Engineering",
|
|
30
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
31
|
+
"Topic :: Scientific/Engineering :: Chemistry",
|
|
32
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
33
|
+
"Topic :: Software Development :: Libraries",
|
|
34
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
# TODO: add project URLs
|
|
38
|
+
|
|
39
|
+
[build-system]
|
|
40
|
+
requires = [
|
|
41
|
+
"setuptools",
|
|
42
|
+
"wheel",
|
|
43
|
+
]
|
|
44
|
+
build-backend = "setuptools.build_meta"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
where = ["src"]
|
|
48
|
+
|
|
49
|
+
[project.optional-dependencies]
|
|
50
|
+
dev = [
|
|
51
|
+
"pytest",
|
|
52
|
+
"ruff",
|
|
53
|
+
"mypy",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
line-length = 88
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
select = ["E", "F", "B", "I"]
|
|
61
|
+
ignore = ["B018", "B904", "E501"]
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint.per-file-ignores]
|
|
64
|
+
"**/{tests}/*" = ["D"]
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint.isort]
|
|
67
|
+
lines-after-imports = 2
|
|
68
|
+
known-first-party = ["flashmd"]
|
|
69
|
+
|
|
70
|
+
[tool.ruff.format]
|
|
71
|
+
docstring-code-format = true
|
|
72
|
+
|
|
73
|
+
[tool.mypy]
|
|
74
|
+
follow_imports = 'skip'
|
|
75
|
+
ignore_missing_imports = true
|
|
76
|
+
disable_error_code = ["union-attr"]
|
|
77
|
+
|
|
78
|
+
[tool.pytest.ini_options]
|
|
79
|
+
filterwarnings = [
|
|
80
|
+
"error",
|
|
81
|
+
"ignore:custom data:UserWarning",
|
|
82
|
+
"ignore:`torch.jit.script` is deprecated:DeprecationWarning",
|
|
83
|
+
"ignore:`torch.jit.load` is deprecated:DeprecationWarning",
|
|
84
|
+
"ignore:`torch.jit.save` is deprecated:DeprecationWarning",
|
|
85
|
+
"ignore:`torch.jit.save` is not supported:DeprecationWarning",
|
|
86
|
+
"ignore:`compute_requested_neighbors_from_options` is deprecated and will be removed in a future version:UserWarning",
|
|
87
|
+
"ignore:hf_xet\\.download_files\\(\\) is deprecated:DeprecationWarning",
|
|
88
|
+
"ignore:Due to '_pack_', the '.*' Structure will use memory layout compatible with MSVC:DeprecationWarning",
|
|
89
|
+
"ignore:warp.config.quiet is deprecated:DeprecationWarning",
|
|
90
|
+
"ignore:`torch.jit.script` is not supported:DeprecationWarning",
|
|
91
|
+
"ignore:`torch.jit.script_method` is not supported:DeprecationWarning",
|
|
92
|
+
"ignore:`torch.jit.script_method` is deprecated:DeprecationWarning",
|
|
93
|
+
"ignore:`torch.jit.load` is not supported:DeprecationWarning",
|
|
94
|
+
"ignore:The 'nvalchemiops.neighborlist' module has been renamed to 'nvalchemiops.neighbors':DeprecationWarning",
|
|
95
|
+
"ignore:From version 0.3.0 onwards, PyTorch is now an optional dependency:DeprecationWarning",
|
|
96
|
+
"ignore:`torch.jit.interface` is deprecated:DeprecationWarning",
|
|
97
|
+
"ignore:`torch.jit.set_fusion_strategy` is deprecated:DeprecationWarning",
|
|
98
|
+
"ignore:the '.*' output name is deprecated:UserWarning",
|
|
99
|
+
"ignore:the '.*' quantity is deprecated:UserWarning",
|
|
100
|
+
"ignore:the '.*' quantity is deprecated:DeprecationWarning",
|
|
101
|
+
"ignore:ModelOutput.quantity is deprecated:DeprecationWarning",
|
|
102
|
+
"ignore:Setting the shape on a NumPy array has been deprecated in NumPy 2.5:DeprecationWarning",
|
|
103
|
+
"ignore:currentThread\\(\\) is deprecated:DeprecationWarning",
|
|
104
|
+
"ignore:unclosed file:ResourceWarning", # i-PI's initializer leaks its handle
|
|
105
|
+
]
|
|
@@ -18,15 +18,29 @@ class Bussi(VelocityVerlet):
|
|
|
18
18
|
device: str | torch.device = "auto",
|
|
19
19
|
rescale_energy: bool = False,
|
|
20
20
|
random_rotation: bool = False,
|
|
21
|
+
rng: np.random.Generator | None = None,
|
|
21
22
|
**kwargs,
|
|
22
23
|
):
|
|
23
24
|
super().__init__(
|
|
24
|
-
atoms,
|
|
25
|
+
atoms,
|
|
26
|
+
timestep,
|
|
27
|
+
model,
|
|
28
|
+
device,
|
|
29
|
+
rescale_energy,
|
|
30
|
+
random_rotation,
|
|
31
|
+
rng,
|
|
32
|
+
**kwargs,
|
|
25
33
|
)
|
|
26
34
|
|
|
27
35
|
self.temperature_K = temperature_K
|
|
28
36
|
self.time_constant = time_constant
|
|
29
37
|
|
|
38
|
+
if np.isclose(self.atoms.get_kinetic_energy(), 0.0, rtol=0, atol=1e-12):
|
|
39
|
+
raise ValueError(
|
|
40
|
+
"Initial kinetic energy is zero. "
|
|
41
|
+
"Please set the initial velocities before running Bussi NVT."
|
|
42
|
+
)
|
|
43
|
+
|
|
30
44
|
def step(self):
|
|
31
45
|
self.apply_bussi_half_step()
|
|
32
46
|
super().step()
|
|
@@ -34,7 +48,7 @@ class Bussi(VelocityVerlet):
|
|
|
34
48
|
|
|
35
49
|
def apply_bussi_half_step(self):
|
|
36
50
|
old_kinetic_energy = self.atoms.get_kinetic_energy()
|
|
37
|
-
n_degrees_of_freedom =
|
|
51
|
+
n_degrees_of_freedom = self.atoms.get_number_of_degrees_of_freedom()
|
|
38
52
|
target_kinetic_energy = (
|
|
39
53
|
0.5 * ase.units.kB * self.temperature_K * n_degrees_of_freedom
|
|
40
54
|
)
|
|
@@ -46,7 +60,7 @@ class Bussi(VelocityVerlet):
|
|
|
46
60
|
/ old_kinetic_energy
|
|
47
61
|
/ n_degrees_of_freedom
|
|
48
62
|
)
|
|
49
|
-
r =
|
|
63
|
+
r = self.rng.standard_normal(n_degrees_of_freedom)
|
|
50
64
|
alpha_sq = (
|
|
51
65
|
exp_term
|
|
52
66
|
+ energy_scaling_term * np.sum(r**2)
|
|
@@ -19,10 +19,18 @@ class Langevin(VelocityVerlet):
|
|
|
19
19
|
device: str | torch.device = "auto",
|
|
20
20
|
rescale_energy: bool = False,
|
|
21
21
|
random_rotation: bool = False,
|
|
22
|
+
rng: np.random.Generator | None = None,
|
|
22
23
|
**kwargs,
|
|
23
24
|
):
|
|
24
25
|
super().__init__(
|
|
25
|
-
atoms,
|
|
26
|
+
atoms,
|
|
27
|
+
timestep,
|
|
28
|
+
model,
|
|
29
|
+
device,
|
|
30
|
+
rescale_energy,
|
|
31
|
+
random_rotation,
|
|
32
|
+
rng,
|
|
33
|
+
**kwargs,
|
|
26
34
|
)
|
|
27
35
|
|
|
28
36
|
self.temperature_K = temperature_K
|
|
@@ -45,7 +53,7 @@ class Langevin(VelocityVerlet):
|
|
|
45
53
|
1.0 - np.exp(-self.friction * self.dt)
|
|
46
54
|
) * np.sqrt(
|
|
47
55
|
ase.units.kB * self.temperature_K * self.atoms.get_masses()[:, None]
|
|
48
|
-
) *
|
|
56
|
+
) * self.rng.standard_normal(old_momenta.shape)
|
|
49
57
|
self.atoms.set_momenta(new_momenta)
|
|
50
58
|
if self.fixcm:
|
|
51
59
|
self.atoms.set_velocities(
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from types import ModuleType
|
|
2
|
+
|
|
1
3
|
import ase
|
|
2
4
|
import ase.units
|
|
3
5
|
import numpy as np
|
|
@@ -5,10 +7,11 @@ import torch
|
|
|
5
7
|
from ase.md.md import MolecularDynamics
|
|
6
8
|
from metatensor.torch import Labels, TensorBlock, TensorMap
|
|
7
9
|
from metatomic.torch import AtomisticModel, System
|
|
8
|
-
from
|
|
10
|
+
from metatomic_ase._calculator import _ase_to_torch_data
|
|
9
11
|
from scipy.spatial.transform import Rotation
|
|
10
12
|
|
|
11
13
|
from ..stepper import FlashMDStepper
|
|
14
|
+
from ..symplectic_stepper import SymplecticStepper
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
class VelocityVerlet(MolecularDynamics):
|
|
@@ -20,13 +23,25 @@ class VelocityVerlet(MolecularDynamics):
|
|
|
20
23
|
device: str | torch.device = "auto",
|
|
21
24
|
rescale_energy: bool = True,
|
|
22
25
|
random_rotation: bool = False,
|
|
26
|
+
rng: np.random.Generator | None = None,
|
|
23
27
|
**kwargs,
|
|
24
28
|
):
|
|
25
29
|
super().__init__(atoms, timestep, **kwargs)
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
self.rng = np.random if rng is None else rng
|
|
32
|
+
|
|
33
|
+
if isinstance(model, tuple):
|
|
34
|
+
flashmd_model, symplectic_part = model
|
|
35
|
+
if isinstance(symplectic_part, tuple):
|
|
36
|
+
symplectic_model, symplectic_config = symplectic_part
|
|
37
|
+
else:
|
|
38
|
+
symplectic_model, symplectic_config = symplectic_part, None
|
|
39
|
+
else:
|
|
40
|
+
flashmd_model, symplectic_model, symplectic_config = model, None, None
|
|
41
|
+
|
|
42
|
+
capabilities = flashmd_model.capabilities()
|
|
28
43
|
|
|
29
|
-
model_timestep = float(
|
|
44
|
+
model_timestep = float(flashmd_model.module.timestep)
|
|
30
45
|
if not np.allclose(model_timestep, self.dt / ase.units.fs):
|
|
31
46
|
raise ValueError(
|
|
32
47
|
f"Mismatch between timestep ({self.dt / ase.units.fs} fs) "
|
|
@@ -40,7 +55,14 @@ class VelocityVerlet(MolecularDynamics):
|
|
|
40
55
|
self.device = torch.device(device)
|
|
41
56
|
self.dtype = getattr(torch, capabilities.dtype)
|
|
42
57
|
|
|
43
|
-
|
|
58
|
+
flashmd_stepper = FlashMDStepper(flashmd_model, self.device)
|
|
59
|
+
self.stepper: FlashMDStepper | SymplecticStepper
|
|
60
|
+
if symplectic_model is not None:
|
|
61
|
+
self.stepper = SymplecticStepper(
|
|
62
|
+
flashmd_stepper, symplectic_model, symplectic_config
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
self.stepper = flashmd_stepper
|
|
44
66
|
self.rescale_energy = rescale_energy
|
|
45
67
|
self.random_rotation = random_rotation
|
|
46
68
|
|
|
@@ -55,7 +77,7 @@ class VelocityVerlet(MolecularDynamics):
|
|
|
55
77
|
if self.random_rotation:
|
|
56
78
|
# generate a random rotation matrix with SciPy
|
|
57
79
|
R = torch.tensor(
|
|
58
|
-
_get_random_rotation(),
|
|
80
|
+
_get_random_rotation(self.rng),
|
|
59
81
|
device=system.positions.device,
|
|
60
82
|
dtype=system.positions.dtype,
|
|
61
83
|
)
|
|
@@ -132,6 +154,14 @@ def _convert_atoms_to_system(
|
|
|
132
154
|
) -> System:
|
|
133
155
|
system_data = _ase_to_torch_data(atoms, dtype=dtype, device=device)
|
|
134
156
|
system = System(*system_data)
|
|
157
|
+
n = len(atoms)
|
|
158
|
+
atom_samples = Labels(
|
|
159
|
+
names=["system", "atom"],
|
|
160
|
+
values=torch.vstack(
|
|
161
|
+
[torch.full((n,), 0, device=device), torch.arange(n, device=device)]
|
|
162
|
+
).T,
|
|
163
|
+
assume_unique=True,
|
|
164
|
+
)
|
|
135
165
|
system.add_data(
|
|
136
166
|
"momenta",
|
|
137
167
|
TensorMap(
|
|
@@ -141,12 +171,7 @@ def _convert_atoms_to_system(
|
|
|
141
171
|
values=torch.tensor(
|
|
142
172
|
atoms.get_momenta(), dtype=dtype, device=device
|
|
143
173
|
).unsqueeze(-1),
|
|
144
|
-
samples=
|
|
145
|
-
names=["system", "atom"],
|
|
146
|
-
values=torch.tensor(
|
|
147
|
-
[[0, j] for j in range(len(atoms))], device=device
|
|
148
|
-
),
|
|
149
|
-
),
|
|
174
|
+
samples=atom_samples,
|
|
150
175
|
components=[
|
|
151
176
|
Labels(
|
|
152
177
|
names="xyz",
|
|
@@ -167,12 +192,7 @@ def _convert_atoms_to_system(
|
|
|
167
192
|
values=torch.tensor(
|
|
168
193
|
atoms.get_masses(), dtype=dtype, device=device
|
|
169
194
|
).unsqueeze(-1),
|
|
170
|
-
samples=
|
|
171
|
-
names=["system", "atom"],
|
|
172
|
-
values=torch.tensor(
|
|
173
|
-
[[0, j] for j in range(len(atoms))], device=device
|
|
174
|
-
),
|
|
175
|
-
),
|
|
195
|
+
samples=atom_samples,
|
|
176
196
|
components=[],
|
|
177
197
|
properties=Labels.single().to(device),
|
|
178
198
|
)
|
|
@@ -182,8 +202,13 @@ def _convert_atoms_to_system(
|
|
|
182
202
|
return system
|
|
183
203
|
|
|
184
204
|
|
|
185
|
-
def _get_random_rotation():
|
|
186
|
-
|
|
187
|
-
|
|
205
|
+
def _get_random_rotation(rng: np.random.Generator | ModuleType = np.random):
|
|
206
|
+
# Haar-uniform rotation from a normalised Gaussian quaternion. This only
|
|
207
|
+
# needs standard_normal/random, so it works with any RNG following the
|
|
208
|
+
# ASE convention (the np.random module, a Generator, ...), unlike
|
|
209
|
+
# Rotation.random(rng=...), which requires an actual Generator.
|
|
210
|
+
quaternion = rng.standard_normal(4)
|
|
211
|
+
R = Rotation.from_quat(quaternion / np.linalg.norm(quaternion)).as_matrix()
|
|
212
|
+
if rng.random() < 0.5:
|
|
188
213
|
R *= -1 # allow improper rotations
|
|
189
214
|
return R
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def anderson_solver(
|
|
7
|
+
f: Callable[[torch.Tensor], torch.Tensor],
|
|
8
|
+
x0: torch.Tensor,
|
|
9
|
+
m: int = 5,
|
|
10
|
+
max_iter: int = 50,
|
|
11
|
+
tol: float = 1e-5,
|
|
12
|
+
beta: float = 1.0,
|
|
13
|
+
lambda_reg: float = 1e-4,
|
|
14
|
+
return_residual_norms: bool = False,
|
|
15
|
+
) -> torch.Tensor | tuple[torch.Tensor, list[float]]:
|
|
16
|
+
"""
|
|
17
|
+
Solve fixed-point problem x = f(x) using Anderson acceleration.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
f: Fixed-point mapping.
|
|
21
|
+
x0: Initial guess.
|
|
22
|
+
m: Number of previous iterates to use for acceleration.
|
|
23
|
+
max_iter: Maximum number of iterations.
|
|
24
|
+
tol: Convergence tolerance based on residual norm.
|
|
25
|
+
beta: Mixing parameter for the fixed-point step.
|
|
26
|
+
lambda_reg: Regularization parameter for least-squares solve.
|
|
27
|
+
return_residual_norms: If True, also return list of residual norms.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Approximate solution x, and optionally list of residual norms.
|
|
31
|
+
"""
|
|
32
|
+
# history buffers
|
|
33
|
+
delta_xs: list[torch.Tensor] = []
|
|
34
|
+
delta_gs: list[torch.Tensor] = []
|
|
35
|
+
residual_norms = []
|
|
36
|
+
|
|
37
|
+
# run fixed-pointer iteration
|
|
38
|
+
x = x0
|
|
39
|
+
fx = f(x)
|
|
40
|
+
g = fx - x
|
|
41
|
+
x_prev, g_prev = None, None
|
|
42
|
+
for k in range(max_iter):
|
|
43
|
+
# evaluate residual and compute convergence
|
|
44
|
+
res_norm = torch.norm(g).item()
|
|
45
|
+
residual_norms.append(res_norm)
|
|
46
|
+
if res_norm < tol:
|
|
47
|
+
break
|
|
48
|
+
|
|
49
|
+
# update history
|
|
50
|
+
if k > 0:
|
|
51
|
+
assert x_prev is not None and g_prev is not None
|
|
52
|
+
delta_xs.append(x - x_prev)
|
|
53
|
+
delta_gs.append(g - g_prev)
|
|
54
|
+
|
|
55
|
+
# truncate history to hold at most m elements
|
|
56
|
+
if len(delta_xs) > m:
|
|
57
|
+
delta_xs.pop(0)
|
|
58
|
+
delta_gs.pop(0)
|
|
59
|
+
x_prev, g_prev = x, g
|
|
60
|
+
|
|
61
|
+
# compute Anderson acceleration step
|
|
62
|
+
if len(delta_xs) > 0:
|
|
63
|
+
# create matrices from history of shape (features, history_length)
|
|
64
|
+
X = torch.stack(delta_xs, dim=1) # (n, k)
|
|
65
|
+
G = torch.stack(delta_gs, dim=1) # (n, k)
|
|
66
|
+
|
|
67
|
+
# solve regularized least-squares problem
|
|
68
|
+
A = G.T @ G + lambda_reg * torch.eye(G.shape[1], device=G.device)
|
|
69
|
+
b = G.T @ g
|
|
70
|
+
try:
|
|
71
|
+
coeffs = torch.linalg.solve(A, b)
|
|
72
|
+
# update iterate with momentum + Anderson step
|
|
73
|
+
x = x + beta * g - (X + beta * G) @ coeffs
|
|
74
|
+
except RuntimeError:
|
|
75
|
+
x = x + beta * g # fallback to fixed-point step if matrix is singular
|
|
76
|
+
else:
|
|
77
|
+
x = x + beta * g # fixed-point step if there is no history
|
|
78
|
+
|
|
79
|
+
# update iterate and residual
|
|
80
|
+
fx = f(x)
|
|
81
|
+
g = fx - x
|
|
82
|
+
|
|
83
|
+
if return_residual_norms:
|
|
84
|
+
return x, residual_norms
|
|
85
|
+
else:
|
|
86
|
+
return x
|