flashmd 0.2.4__tar.gz → 0.2.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flashmd
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Accelerated molecular dynamics with large-time-step predictions
5
5
  Author: flashmd developers
6
6
  License: Apache-2.0
@@ -22,9 +22,13 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Requires-Python: >=3.9
23
23
  Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
- Requires-Dist: metatrain==2025.11
25
+ Requires-Dist: metatrain==2026.1
26
26
  Requires-Dist: ase
27
27
  Requires-Dist: huggingface_hub
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest; extra == "dev"
30
+ Requires-Dist: ruff; extra == "dev"
31
+ Requires-Dist: mypy; extra == "dev"
28
32
  Dynamic: license-file
29
33
 
30
34
  FlashMD: universal long-stride molecular dynamics
@@ -32,13 +36,13 @@ FlashMD: universal long-stride molecular dynamics
32
36
 
33
37
  This repository contains custom integrators to run MD trajectories with FlashMD models. These models are
34
38
  designed to learn and predict molecular dynamics trajectories using long strides, therefore allowing
35
- very large time steps. Before using this method, make sure you are aware of its limitations, which are
39
+ very large time steps. When using this method, make sure you are aware of its limitations, which are
36
40
  discussed in [this preprint](http://arxiv.org/abs/2505.19350).
37
41
 
38
42
  The pre-trained models we make available are trained to reproduce ab-initio MD at the r2SCAN level of theory.
39
43
 
40
- Quickstart
41
- ----------
44
+ ASE Quickstart (see below for LAMMPS)
45
+ -------------------------------------
42
46
 
43
47
  You can install the package with
44
48
 
@@ -46,7 +50,7 @@ You can install the package with
46
50
  pip install flashmd
47
51
  ```
48
52
 
49
- After installation, you can run accelerated molecular dynamics with ASE as follows:
53
+ After installation, you can run accelerated molecular dynamics as follows:
50
54
 
51
55
  ```py
52
56
  import ase.build
@@ -71,7 +75,7 @@ atoms.set_velocities( # it is generally a good idea to remove any net velocity
71
75
 
72
76
  # Load models
73
77
  device="cuda" if torch.cuda.is_available() else "cpu"
74
- energy_model, flashmd_model = get_pretrained("pet-omatpes", time_step)
78
+ energy_model, flashmd_model = get_pretrained("pet-omatpes-v2", time_step)
75
79
 
76
80
  # Set the energy model (see below for more precise usage)
77
81
  calculator = MetatomicCalculator(energy_model, device=device)
@@ -99,6 +103,11 @@ Other available integrators:
99
103
  from flashmd.ase.bussi import Bussi
100
104
  ```
101
105
 
106
+ Along with all FlashMD models, we also provide the potential energy model whose
107
+ dynamics they are trained to reproduce. See this short [guide](docs/energy.md) on how to
108
+ best use the energy models if you want to enforce exact energy conservation during
109
+ FlashMD runs, run traditional MD with the energy model, and more.
110
+
102
111
  Common pitfalls
103
112
  ---------------
104
113
 
@@ -107,77 +116,59 @@ above is good for metals. However,
107
116
  - for most materials: try 32 fs (aggressive) or 16 fs (conservative)
108
117
  - for aqueous and/or organic systems: try 16 fs (aggressive) or 8 fs (conservative)
109
118
 
119
+ Using FlashMD in LAMMPS
120
+ -----------------------
110
121
 
111
- Companion energy models and exact energy conservation
112
- -----------------------------------------------------
113
-
114
- You might have noticed that ``get_pretrained()`` does not only return a FlashMD model,
115
- but also an energy model, which is itself just a machine-learned interatomic potential.
116
- This is the energy model that the FlashMD model was trained on. You might want to use it
117
- if...
118
-
119
- Case 1: you want to run FlashMD with exact energy conservation, available through the
120
- integrator's (``dyn`` above) parameter ``rescale_energy=True`` (this is enabled by
121
- default only when targeting the NVE ensemble with ``VelocityVerlet``). In that case,
122
- besides setting this flag, you should attach the energy calculator to the atoms before
123
- running FlashMD, exactly as shown above (and below with the more precise
124
- ``do_gradients_with_energy=False`` which will save you memory and computation):
122
+ LAMMPS can allow you to run FlashMD with better computational and memory efficiency.
123
+ Furthermore, it will give you access to more sophisticated types of simulations, such as
124
+ simulations in the NPT ensemble and metadynamics. See [here](docs/lammps.md) for a guide
125
+ on using FlashMD in LAMMPS.
125
126
 
126
- ```
127
- from metatomic.torch.ase_calculator import MetatomicCalculator
128
-
129
- ... # setting up atoms
130
- calculator = MetatomicCalculator(energy_model, device=device, do_gradients_with_energy=False)
131
- atoms.calc = calculator
132
- ... # running FlashMD
133
- ```
127
+ Using FlashMD in i-PI
128
+ ---------------------
134
129
 
135
- Case 2: you want to compute energies after running FlashMD for your own analysis. In
136
- this case, you can create the calculator just like in case 1, but possibly after running
137
- FlashMD and/or in a different script.
130
+ You can see
131
+ [this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
132
+ for usage examples. i-PI is our most mature interface, and the one that was used to
133
+ generate all our published results.
138
134
 
139
- Case 3: you found something interesting during a FlashMD run and you want to confirm it
140
- with traditional MD. Then, you can just use ASE's MD modules as usual after attaching
141
- the energy calculator:
135
+ Models
136
+ ------
142
137
 
143
- ```
144
- from metatomic.torch.ase_calculator import MetatomicCalculator
138
+ See [here](docs/models.md) for the complete list of the models we provide. If you are
139
+ new to FlashMD, we recommend starting with the ``pet-omatpes-v2`` models.
145
140
 
146
- ... # setting up atoms
147
- calculator = MetatomicCalculator(energy_model, device=device)
148
- atoms.calc = calculator
149
- ... # running MD
150
- ```
141
+ Training/fine-tuning your own FlashMD models
142
+ --------------------------------------------
151
143
 
152
- In general, the energy models are slower and have a larger memory footprint compared to
153
- the FlashMD models. As summarized above, you should use `do_gradients_with_energy=False`
154
- to save computation and memory when you don't need forces.
144
+ FlashMD models can be trained from the metatrain library. This
145
+ [tutorial](https://docs.metatensor.org/metatrain/latest/generated_examples/1-advanced/04-flashmd.html)
146
+ shows how to train your own FlashMD model, either from scratch or by fine-tuning of one of our universal models.
155
147
 
156
148
  Disclaimer
157
149
  ----------
158
150
 
159
- This is experimental software and should only be used if you know what you're doing.
160
- We recommend using the i-PI integrators for any serious work and/or if you need to perform
161
- constant-pressure (NPT) molecular dynamics. You can see
162
- [this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
163
- for a usage example.
164
- Given that the main issue we observe in direct MD trajectories is loss of equipartition
165
- of energy between different degrees of freedom, we recommend using a local Langevin
166
- thermostat, and to monitor the temperature of different atomic types or different
167
- parts of the simulated system.
151
+ This is experimental software and should only be used if you know what you are doing.
152
+ Given that the main issue we observe in FlashMD is loss of equipartition
153
+ of energy between different degrees of freedom, we recommend using a Langevin
154
+ thermostat, possibly monitoring the temperature of different atomic types or different
155
+ parts of the simulated system. For the time being, we also recommend checking all
156
+ FlashMD-powered findings with traditional MD. The energy models that were used to train
157
+ FlashMD, and that we make available in this repository, can be used for this purpose.
168
158
 
169
159
 
170
160
  Publication
171
161
  -----------
172
162
 
173
- If you found FlashMD useful, you can cite the corresponding article:
163
+ If you found FlashMD useful, you can cite the corresponding [article](https://arxiv.org/abs/2505.19350):
174
164
 
175
165
  ```
176
- @article{FlashMD,
177
- title={FlashMD: long-stride, universal prediction of molecular dynamics},
178
- author={Bigi, Filippo and Chong, Sanggyu and Kristiadi, Agustinus and Ceriotti, Michele},
179
- journal={arXiv preprint arXiv:2505.19350},
180
- year={2025}
166
+ @inproceedings{FlashMD,
167
+ title = {FlashMD: long-stride, universal prediction of molecular dynamics},
168
+ author = {Bigi, Filippo and Chong, Sanggyu and Kristiadi, Agustinus and Ceriotti, Michele},
169
+ booktitle = {Advances in Neural Information Processing Systems},
170
+ year = {2025},
171
+ url = {https://arxiv.org/abs/2505.19350}
181
172
  }
182
173
  ```
183
174
 
@@ -3,13 +3,13 @@ FlashMD: universal long-stride molecular dynamics
3
3
 
4
4
  This repository contains custom integrators to run MD trajectories with FlashMD models. These models are
5
5
  designed to learn and predict molecular dynamics trajectories using long strides, therefore allowing
6
- very large time steps. Before using this method, make sure you are aware of its limitations, which are
6
+ very large time steps. When using this method, make sure you are aware of its limitations, which are
7
7
  discussed in [this preprint](http://arxiv.org/abs/2505.19350).
8
8
 
9
9
  The pre-trained models we make available are trained to reproduce ab-initio MD at the r2SCAN level of theory.
10
10
 
11
- Quickstart
12
- ----------
11
+ ASE Quickstart (see below for LAMMPS)
12
+ -------------------------------------
13
13
 
14
14
  You can install the package with
15
15
 
@@ -17,7 +17,7 @@ You can install the package with
17
17
  pip install flashmd
18
18
  ```
19
19
 
20
- After installation, you can run accelerated molecular dynamics with ASE as follows:
20
+ After installation, you can run accelerated molecular dynamics as follows:
21
21
 
22
22
  ```py
23
23
  import ase.build
@@ -42,7 +42,7 @@ atoms.set_velocities( # it is generally a good idea to remove any net velocity
42
42
 
43
43
  # Load models
44
44
  device="cuda" if torch.cuda.is_available() else "cpu"
45
- energy_model, flashmd_model = get_pretrained("pet-omatpes", time_step)
45
+ energy_model, flashmd_model = get_pretrained("pet-omatpes-v2", time_step)
46
46
 
47
47
  # Set the energy model (see below for more precise usage)
48
48
  calculator = MetatomicCalculator(energy_model, device=device)
@@ -70,6 +70,11 @@ Other available integrators:
70
70
  from flashmd.ase.bussi import Bussi
71
71
  ```
72
72
 
73
+ Along with all FlashMD models, we also provide the potential energy model whose
74
+ dynamics they are trained to reproduce. See this short [guide](docs/energy.md) on how to
75
+ best use the energy models if you want to enforce exact energy conservation during
76
+ FlashMD runs, run traditional MD with the energy model, and more.
77
+
73
78
  Common pitfalls
74
79
  ---------------
75
80
 
@@ -78,77 +83,59 @@ above is good for metals. However,
78
83
  - for most materials: try 32 fs (aggressive) or 16 fs (conservative)
79
84
  - for aqueous and/or organic systems: try 16 fs (aggressive) or 8 fs (conservative)
80
85
 
86
+ Using FlashMD in LAMMPS
87
+ -----------------------
81
88
 
82
- Companion energy models and exact energy conservation
83
- -----------------------------------------------------
84
-
85
- You might have noticed that ``get_pretrained()`` does not only return a FlashMD model,
86
- but also an energy model, which is itself just a machine-learned interatomic potential.
87
- This is the energy model that the FlashMD model was trained on. You might want to use it
88
- if...
89
-
90
- Case 1: you want to run FlashMD with exact energy conservation, available through the
91
- integrator's (``dyn`` above) parameter ``rescale_energy=True`` (this is enabled by
92
- default only when targeting the NVE ensemble with ``VelocityVerlet``). In that case,
93
- besides setting this flag, you should attach the energy calculator to the atoms before
94
- running FlashMD, exactly as shown above (and below with the more precise
95
- ``do_gradients_with_energy=False`` which will save you memory and computation):
89
+ LAMMPS can allow you to run FlashMD with better computational and memory efficiency.
90
+ Furthermore, it will give you access to more sophisticated types of simulations, such as
91
+ simulations in the NPT ensemble and metadynamics. See [here](docs/lammps.md) for a guide
92
+ on using FlashMD in LAMMPS.
96
93
 
97
- ```
98
- from metatomic.torch.ase_calculator import MetatomicCalculator
99
-
100
- ... # setting up atoms
101
- calculator = MetatomicCalculator(energy_model, device=device, do_gradients_with_energy=False)
102
- atoms.calc = calculator
103
- ... # running FlashMD
104
- ```
94
+ Using FlashMD in i-PI
95
+ ---------------------
105
96
 
106
- Case 2: you want to compute energies after running FlashMD for your own analysis. In
107
- this case, you can create the calculator just like in case 1, but possibly after running
108
- FlashMD and/or in a different script.
97
+ You can see
98
+ [this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
99
+ for usage examples. i-PI is our most mature interface, and the one that was used to
100
+ generate all our published results.
109
101
 
110
- Case 3: you found something interesting during a FlashMD run and you want to confirm it
111
- with traditional MD. Then, you can just use ASE's MD modules as usual after attaching
112
- the energy calculator:
102
+ Models
103
+ ------
113
104
 
114
- ```
115
- from metatomic.torch.ase_calculator import MetatomicCalculator
105
+ See [here](docs/models.md) for the complete list of the models we provide. If you are
106
+ new to FlashMD, we recommend starting with the ``pet-omatpes-v2`` models.
116
107
 
117
- ... # setting up atoms
118
- calculator = MetatomicCalculator(energy_model, device=device)
119
- atoms.calc = calculator
120
- ... # running MD
121
- ```
108
+ Training/fine-tuning your own FlashMD models
109
+ --------------------------------------------
122
110
 
123
- In general, the energy models are slower and have a larger memory footprint compared to
124
- the FlashMD models. As summarized above, you should use `do_gradients_with_energy=False`
125
- to save computation and memory when you don't need forces.
111
+ FlashMD models can be trained from the metatrain library. This
112
+ [tutorial](https://docs.metatensor.org/metatrain/latest/generated_examples/1-advanced/04-flashmd.html)
113
+ shows how to train your own FlashMD model, either from scratch or by fine-tuning of one of our universal models.
126
114
 
127
115
  Disclaimer
128
116
  ----------
129
117
 
130
- This is experimental software and should only be used if you know what you're doing.
131
- We recommend using the i-PI integrators for any serious work and/or if you need to perform
132
- constant-pressure (NPT) molecular dynamics. You can see
133
- [this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
134
- for a usage example.
135
- Given that the main issue we observe in direct MD trajectories is loss of equipartition
136
- of energy between different degrees of freedom, we recommend using a local Langevin
137
- thermostat, and to monitor the temperature of different atomic types or different
138
- parts of the simulated system.
118
+ This is experimental software and should only be used if you know what you are doing.
119
+ Given that the main issue we observe in FlashMD is loss of equipartition
120
+ of energy between different degrees of freedom, we recommend using a Langevin
121
+ thermostat, possibly monitoring the temperature of different atomic types or different
122
+ parts of the simulated system. For the time being, we also recommend checking all
123
+ FlashMD-powered findings with traditional MD. The energy models that were used to train
124
+ FlashMD, and that we make available in this repository, can be used for this purpose.
139
125
 
140
126
 
141
127
  Publication
142
128
  -----------
143
129
 
144
- If you found FlashMD useful, you can cite the corresponding article:
130
+ If you found FlashMD useful, you can cite the corresponding [article](https://arxiv.org/abs/2505.19350):
145
131
 
146
132
  ```
147
- @article{FlashMD,
148
- title={FlashMD: long-stride, universal prediction of molecular dynamics},
149
- author={Bigi, Filippo and Chong, Sanggyu and Kristiadi, Agustinus and Ceriotti, Michele},
150
- journal={arXiv preprint arXiv:2505.19350},
151
- year={2025}
133
+ @inproceedings{FlashMD,
134
+ title = {FlashMD: long-stride, universal prediction of molecular dynamics},
135
+ author = {Bigi, Filippo and Chong, Sanggyu and Kristiadi, Agustinus and Ceriotti, Michele},
136
+ booktitle = {Advances in Neural Information Processing Systems},
137
+ year = {2025},
138
+ url = {https://arxiv.org/abs/2505.19350}
152
139
  }
153
140
  ```
154
141
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "flashmd"
3
- version = "0.2.4"
3
+ version = "0.2.6"
4
4
  requires-python = ">=3.9"
5
5
 
6
6
  readme = "README.md"
@@ -9,7 +9,7 @@ description = "Accelerated molecular dynamics with large-time-step predictions"
9
9
  authors = [{name = "flashmd developers"}]
10
10
 
11
11
  dependencies = [
12
- "metatrain==2025.11",
12
+ "metatrain==2026.1",
13
13
  "ase",
14
14
  "huggingface_hub",
15
15
  ]
@@ -44,5 +44,40 @@ build-backend = "setuptools.build_meta"
44
44
  [tool.setuptools.packages.find]
45
45
  where = ["src"]
46
46
 
47
+ [project.optional-dependencies]
48
+ dev = [
49
+ "pytest",
50
+ "ruff",
51
+ "mypy",
52
+ ]
53
+
47
54
  [tool.ruff]
48
55
  line-length = 88
56
+
57
+ [tool.ruff.lint]
58
+ select = ["E", "F", "B", "I"]
59
+ ignore = ["B018", "B904", "E501"]
60
+
61
+ [tool.ruff.lint.per-file-ignores]
62
+ "**/{tests}/*" = ["D"]
63
+
64
+ [tool.ruff.lint.isort]
65
+ lines-after-imports = 2
66
+ known-first-party = ["flashmd"]
67
+
68
+ [tool.ruff.format]
69
+ docstring-code-format = true
70
+
71
+ [tool.mypy]
72
+ follow_imports = 'skip'
73
+ ignore_missing_imports = true
74
+ disable_error_code = ["union-attr"]
75
+
76
+ [tool.pytest.ini_options]
77
+ markers = [
78
+ "slow: marks tests as slow (deselect with '-m \"not slow\"')",
79
+ ]
80
+ filterwarnings = [
81
+ "error",
82
+ "ignore:custom data:UserWarning",
83
+ ]
@@ -1,5 +1,7 @@
1
+ import warnings
2
+
1
3
  from .models import get_pretrained as get_pretrained
4
+ from .models import save_checkpoint as save_checkpoint
2
5
 
3
- import warnings
4
6
 
5
7
  warnings.filterwarnings("ignore", category=UserWarning, message="custom data")
@@ -1,10 +1,10 @@
1
- from .velocity_verlet import VelocityVerlet
2
- import ase.units
3
- from typing import List
4
- from metatomic.torch import AtomisticModel
5
- import torch
6
1
  import ase
2
+ import ase.units
7
3
  import numpy as np
4
+ import torch
5
+ from metatomic.torch import AtomisticModel
6
+
7
+ from .velocity_verlet import VelocityVerlet
8
8
 
9
9
 
10
10
  class Bussi(VelocityVerlet):
@@ -13,14 +13,16 @@ class Bussi(VelocityVerlet):
13
13
  atoms: ase.Atoms,
14
14
  timestep: float,
15
15
  temperature_K: float,
16
- model: AtomisticModel | List[AtomisticModel],
16
+ model: AtomisticModel,
17
17
  time_constant: float = 10.0 * ase.units.fs,
18
18
  device: str | torch.device = "auto",
19
19
  rescale_energy: bool = False,
20
20
  random_rotation: bool = False,
21
21
  **kwargs,
22
22
  ):
23
- super().__init__(atoms, timestep, model, device, rescale_energy, random_rotation, **kwargs)
23
+ super().__init__(
24
+ atoms, timestep, model, device, rescale_energy, random_rotation, **kwargs
25
+ )
24
26
 
25
27
  self.temperature_K = temperature_K
26
28
  self.time_constant = time_constant
@@ -1,10 +1,10 @@
1
- from .velocity_verlet import VelocityVerlet
2
- import ase.units
3
- from typing import List
4
- from metatomic.torch import AtomisticModel
5
- import torch
6
1
  import ase
2
+ import ase.units
7
3
  import numpy as np
4
+ import torch
5
+ from metatomic.torch import AtomisticModel
6
+
7
+ from .velocity_verlet import VelocityVerlet
8
8
 
9
9
 
10
10
  class Langevin(VelocityVerlet):
@@ -13,7 +13,7 @@ class Langevin(VelocityVerlet):
13
13
  atoms: ase.Atoms,
14
14
  timestep: float,
15
15
  temperature_K: float,
16
- model: AtomisticModel | List[AtomisticModel],
16
+ model: AtomisticModel,
17
17
  time_constant: float = 100.0 * ase.units.fs,
18
18
  fixcm: bool = True,
19
19
  device: str | torch.device = "auto",
@@ -21,13 +21,18 @@ class Langevin(VelocityVerlet):
21
21
  random_rotation: bool = False,
22
22
  **kwargs,
23
23
  ):
24
- super().__init__(atoms, timestep, model, device, rescale_energy, random_rotation, **kwargs)
24
+ super().__init__(
25
+ atoms, timestep, model, device, rescale_energy, random_rotation, **kwargs
26
+ )
25
27
 
26
28
  self.temperature_K = temperature_K
27
29
  self.friction = 1.0 / time_constant
28
30
  self.fixcm = fixcm
29
31
  if self.fixcm:
30
- self.atoms.set_velocities(self.atoms.get_velocities() - self.atoms.get_momenta().sum(axis=0) / self.atoms.get_masses().sum())
32
+ self.atoms.set_velocities(
33
+ self.atoms.get_velocities()
34
+ - self.atoms.get_momenta().sum(axis=0) / self.atoms.get_masses().sum()
35
+ )
31
36
 
32
37
  def step(self):
33
38
  self.apply_langevin_half_step()
@@ -43,4 +48,7 @@ class Langevin(VelocityVerlet):
43
48
  ) * np.random.randn(*old_momenta.shape)
44
49
  self.atoms.set_momenta(new_momenta)
45
50
  if self.fixcm:
46
- self.atoms.set_velocities(self.atoms.get_velocities() - self.atoms.get_momenta().sum(axis=0) / self.atoms.get_masses().sum())
51
+ self.atoms.set_velocities(
52
+ self.atoms.get_velocities()
53
+ - self.atoms.get_momenta().sum(axis=0) / self.atoms.get_masses().sum()
54
+ )
@@ -1,23 +1,22 @@
1
- from ase.md.md import MolecularDynamics
2
- from typing import List
3
- from metatomic.torch import AtomisticModel
4
- from metatensor.torch import Labels, TensorBlock, TensorMap
1
+ import ase
5
2
  import ase.units
3
+ import numpy as np
6
4
  import torch
5
+ from ase.md.md import MolecularDynamics
6
+ from metatensor.torch import Labels, TensorBlock, TensorMap
7
+ from metatomic.torch import AtomisticModel, System
7
8
  from metatomic.torch.ase_calculator import _ase_to_torch_data
8
- from metatomic.torch import System
9
- import ase
10
- from ..stepper import FlashMDStepper
11
- import numpy as np
12
9
  from scipy.spatial.transform import Rotation
13
10
 
11
+ from ..stepper import FlashMDStepper
12
+
14
13
 
15
14
  class VelocityVerlet(MolecularDynamics):
16
15
  def __init__(
17
16
  self,
18
17
  atoms: ase.Atoms,
19
18
  timestep: float,
20
- model: AtomisticModel | List[AtomisticModel],
19
+ model: AtomisticModel,
21
20
  device: str | torch.device = "auto",
22
21
  rescale_energy: bool = True,
23
22
  random_rotation: bool = False,
@@ -66,8 +65,8 @@ class VelocityVerlet(MolecularDynamics):
66
65
  system.positions = system.positions @ R.T
67
66
  # change momentum TensorMap in place
68
67
  system.get_data("momenta").block().values[:] = (
69
- (system.get_data("momenta").block().values.squeeze(-1) @ R.T).unsqueeze(-1)
70
- )
68
+ system.get_data("momenta").block().values.squeeze(-1) @ R.T
69
+ ).unsqueeze(-1)
71
70
 
72
71
  new_system = self.stepper.step(system)
73
72
 
@@ -76,8 +75,8 @@ class VelocityVerlet(MolecularDynamics):
76
75
  new_system.cell = old_cell
77
76
  new_system.positions = new_system.positions @ R
78
77
  new_system.get_data("momenta").block().values[:] = (
79
- (new_system.get_data("momenta").block().values.squeeze(-1) @ R).unsqueeze(-1)
80
- )
78
+ new_system.get_data("momenta").block().values.squeeze(-1) @ R
79
+ ).unsqueeze(-1)
81
80
 
82
81
  self.atoms.set_positions(new_system.positions.detach().cpu().numpy())
83
82
  self.atoms.set_momenta(
@@ -127,6 +126,7 @@ class VelocityVerlet(MolecularDynamics):
127
126
  for _ in self.irun(steps=steps):
128
127
  pass
129
128
 
129
+
130
130
  def _convert_atoms_to_system(
131
131
  atoms: ase.Atoms, dtype: str, device: str | torch.device
132
132
  ) -> System:
@@ -1,17 +1,16 @@
1
+ import ase.data
2
+ import ase.units
3
+ import numpy as np
4
+ import torch
5
+ from ipi.engine.motion.dynamics import NPTIntegrator, NVEIntegrator, NVTIntegrator
1
6
  from ipi.utils.depend import dstrip
2
- from ipi.utils.units import Constants
3
- from ipi.utils.messages import verbosity, info
4
7
  from ipi.utils.mathtools import random_rotation as random_rotation_matrix
5
- from ipi.engine.motion.dynamics import NVEIntegrator, NVTIntegrator, NPTIntegrator
8
+ from ipi.utils.messages import info, verbosity
9
+ from ipi.utils.units import Constants
10
+ from metatensor.torch import Labels, TensorBlock, TensorMap
11
+ from metatomic.torch import System
6
12
 
7
13
  from flashmd.stepper import FlashMDStepper
8
- import ase.units
9
- import torch
10
- import numpy as np
11
- import ase.data
12
-
13
- from metatomic.torch import System
14
- from metatensor.torch import Labels, TensorBlock, TensorMap
15
14
 
16
15
 
17
16
  def get_standard_vv_step(
@@ -56,7 +55,9 @@ def get_standard_vv_step(
56
55
  return vv_step
57
56
 
58
57
 
59
- def get_flashmd_vv_step(sim, model, device, rescale_energy=False, random_rotation=False):
58
+ def get_flashmd_vv_step(
59
+ sim, model, device, rescale_energy=False, random_rotation=False
60
+ ):
60
61
  capabilities = model.capabilities()
61
62
 
62
63
  model_timestep = float(model.module.timestep)
@@ -0,0 +1,102 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ import time
5
+
6
+ from huggingface_hub import hf_hub_download
7
+ from metatomic.torch import AtomisticModel, load_atomistic_model
8
+
9
+
10
+ AVAILABLE_MLIPS = ["pet-omatpes", "pet-omatpes-v2"]
11
+ AVAILABLE_TIME_STEPS = {
12
+ "pet-omatpes": [1, 2, 4, 8, 16, 32, 64, 128],
13
+ "pet-omatpes-v2": [1, 2, 4, 8, 16, 32, 64, 128],
14
+ }
15
+
16
+
17
+ def get_pretrained(mlip: str = "pet-omatpes-v2", time_step: int = 16) -> AtomisticModel:
18
+ if mlip not in AVAILABLE_MLIPS:
19
+ raise ValueError(
20
+ f"MLIP '{mlip}' is not available. "
21
+ f"Available MLIPs are: {', '.join(AVAILABLE_MLIPS)}."
22
+ )
23
+
24
+ if time_step not in AVAILABLE_TIME_STEPS[mlip]:
25
+ raise ValueError(
26
+ f"Pre-trained FlashMD models based on the {mlip} MLIP are only available "
27
+ f"for time steps of {', '.join(map(str, AVAILABLE_TIME_STEPS[mlip]))} fs."
28
+ )
29
+
30
+ # Get checkpoints corresponding to the selected MLIP and FlashMD models
31
+ mlip_path = hf_hub_download(
32
+ repo_id="lab-cosmo/flashmd",
33
+ filename=f"mlip_{mlip}.ckpt",
34
+ cache_dir=None,
35
+ revision="main",
36
+ )
37
+ flashmd_path = hf_hub_download(
38
+ repo_id="lab-cosmo/flashmd",
39
+ filename=f"flashmd_{mlip}_{time_step}fs.ckpt",
40
+ cache_dir=None,
41
+ revision="main",
42
+ )
43
+
44
+ # Now we need to export both using metatrain. However, we don't want to do it if
45
+ # HuggingFace hasn't downloaded a new version of the files, so we only re-export
46
+ # if the files above have changed in the last 10 seconds.
47
+ reexport = False
48
+ exported_mlip_path = mlip_path.replace(".ckpt", ".pt")
49
+ exported_flashmd_path = flashmd_path.replace(".ckpt", ".pt")
50
+ if not os.path.exists(exported_mlip_path) or not os.path.exists(
51
+ exported_flashmd_path
52
+ ):
53
+ reexport = True
54
+ mlip_mtime = os.path.getmtime(mlip_path)
55
+ flashmd_mtime = os.path.getmtime(flashmd_path)
56
+ if (time.time() - mlip_mtime < 10) or (time.time() - flashmd_mtime < 10):
57
+ reexport = True
58
+ if reexport:
59
+ subprocess.run(
60
+ ["mtt", "export", mlip_path, "-o", exported_mlip_path], capture_output=True
61
+ )
62
+ subprocess.run(
63
+ ["mtt", "export", flashmd_path, "-o", exported_flashmd_path],
64
+ capture_output=True,
65
+ )
66
+
67
+ # Load as AtomisticModel instances.
68
+ # If it doesn't work, try to re-export once and load again (this will, among
69
+ # others, catch upgrades in metatomic that break compatibility)
70
+ try:
71
+ mlip_model = load_atomistic_model(exported_mlip_path)
72
+ flashmd_model = load_atomistic_model(exported_flashmd_path)
73
+ except Exception:
74
+ subprocess.run(
75
+ ["mtt", "export", mlip_path, "-o", exported_mlip_path], capture_output=True
76
+ )
77
+ subprocess.run(
78
+ ["mtt", "export", flashmd_path, "-o", exported_flashmd_path],
79
+ capture_output=True,
80
+ )
81
+ mlip_model = load_atomistic_model(exported_mlip_path)
82
+ flashmd_model = load_atomistic_model(exported_flashmd_path)
83
+
84
+ return mlip_model, flashmd_model
85
+
86
+
87
+ def save_checkpoint(mlip: str = "pet-omatpes-v2", time_step: int = 16):
88
+ if time_step not in AVAILABLE_TIME_STEPS[mlip]:
89
+ raise ValueError(
90
+ f"Pre-trained FlashMD models based on the {mlip} MLIP are only available "
91
+ f"for time steps of {', '.join(map(str, AVAILABLE_TIME_STEPS[mlip]))} fs."
92
+ )
93
+
94
+ checkpoint_path = hf_hub_download(
95
+ repo_id="lab-cosmo/flashmd",
96
+ filename=f"flashmd_{mlip}_{time_step}fs.ckpt",
97
+ cache_dir=None,
98
+ revision="main",
99
+ )
100
+
101
+ # Copy it to the current directory
102
+ shutil.copyfile(checkpoint_path, f"flashmd_{mlip}_{time_step}fs.ckpt")
@@ -1,12 +1,11 @@
1
1
  # from ..utils.pretrained import load_pretrained_models
2
- from metatomic.torch import ModelEvaluationOptions, ModelOutput
3
- from metatensor.torch import Labels, TensorBlock, TensorMap
2
+ import ase.units
4
3
  import torch
5
- from metatomic.torch import System
4
+ from metatensor.torch import Labels, TensorBlock, TensorMap
5
+ from metatomic.torch import AtomisticModel, ModelEvaluationOptions, ModelOutput, System
6
6
  from metatrain.utils.neighbor_lists import get_system_with_neighbor_lists
7
- from metatomic.torch import AtomisticModel
7
+
8
8
  from .constraints import enforce_physical_constraints
9
- import ase.units
10
9
 
11
10
 
12
11
  class FlashMDStepper:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flashmd
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Accelerated molecular dynamics with large-time-step predictions
5
5
  Author: flashmd developers
6
6
  License: Apache-2.0
@@ -22,9 +22,13 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Requires-Python: >=3.9
23
23
  Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
- Requires-Dist: metatrain==2025.11
25
+ Requires-Dist: metatrain==2026.1
26
26
  Requires-Dist: ase
27
27
  Requires-Dist: huggingface_hub
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest; extra == "dev"
30
+ Requires-Dist: ruff; extra == "dev"
31
+ Requires-Dist: mypy; extra == "dev"
28
32
  Dynamic: license-file
29
33
 
30
34
  FlashMD: universal long-stride molecular dynamics
@@ -32,13 +36,13 @@ FlashMD: universal long-stride molecular dynamics
32
36
 
33
37
  This repository contains custom integrators to run MD trajectories with FlashMD models. These models are
34
38
  designed to learn and predict molecular dynamics trajectories using long strides, therefore allowing
35
- very large time steps. Before using this method, make sure you are aware of its limitations, which are
39
+ very large time steps. When using this method, make sure you are aware of its limitations, which are
36
40
  discussed in [this preprint](http://arxiv.org/abs/2505.19350).
37
41
 
38
42
  The pre-trained models we make available are trained to reproduce ab-initio MD at the r2SCAN level of theory.
39
43
 
40
- Quickstart
41
- ----------
44
+ ASE Quickstart (see below for LAMMPS)
45
+ -------------------------------------
42
46
 
43
47
  You can install the package with
44
48
 
@@ -46,7 +50,7 @@ You can install the package with
46
50
  pip install flashmd
47
51
  ```
48
52
 
49
- After installation, you can run accelerated molecular dynamics with ASE as follows:
53
+ After installation, you can run accelerated molecular dynamics as follows:
50
54
 
51
55
  ```py
52
56
  import ase.build
@@ -71,7 +75,7 @@ atoms.set_velocities( # it is generally a good idea to remove any net velocity
71
75
 
72
76
  # Load models
73
77
  device="cuda" if torch.cuda.is_available() else "cpu"
74
- energy_model, flashmd_model = get_pretrained("pet-omatpes", time_step)
78
+ energy_model, flashmd_model = get_pretrained("pet-omatpes-v2", time_step)
75
79
 
76
80
  # Set the energy model (see below for more precise usage)
77
81
  calculator = MetatomicCalculator(energy_model, device=device)
@@ -99,6 +103,11 @@ Other available integrators:
99
103
  from flashmd.ase.bussi import Bussi
100
104
  ```
101
105
 
106
+ Along with all FlashMD models, we also provide the potential energy model whose
107
+ dynamics they are trained to reproduce. See this short [guide](docs/energy.md) on how to
108
+ best use the energy models if you want to enforce exact energy conservation during
109
+ FlashMD runs, run traditional MD with the energy model, and more.
110
+
102
111
  Common pitfalls
103
112
  ---------------
104
113
 
@@ -107,77 +116,59 @@ above is good for metals. However,
107
116
  - for most materials: try 32 fs (aggressive) or 16 fs (conservative)
108
117
  - for aqueous and/or organic systems: try 16 fs (aggressive) or 8 fs (conservative)
109
118
 
119
+ Using FlashMD in LAMMPS
120
+ -----------------------
110
121
 
111
- Companion energy models and exact energy conservation
112
- -----------------------------------------------------
113
-
114
- You might have noticed that ``get_pretrained()`` does not only return a FlashMD model,
115
- but also an energy model, which is itself just a machine-learned interatomic potential.
116
- This is the energy model that the FlashMD model was trained on. You might want to use it
117
- if...
118
-
119
- Case 1: you want to run FlashMD with exact energy conservation, available through the
120
- integrator's (``dyn`` above) parameter ``rescale_energy=True`` (this is enabled by
121
- default only when targeting the NVE ensemble with ``VelocityVerlet``). In that case,
122
- besides setting this flag, you should attach the energy calculator to the atoms before
123
- running FlashMD, exactly as shown above (and below with the more precise
124
- ``do_gradients_with_energy=False`` which will save you memory and computation):
122
+ LAMMPS can allow you to run FlashMD with better computational and memory efficiency.
123
+ Furthermore, it will give you access to more sophisticated types of simulations, such as
124
+ simulations in the NPT ensemble and metadynamics. See [here](docs/lammps.md) for a guide
125
+ on using FlashMD in LAMMPS.
125
126
 
126
- ```
127
- from metatomic.torch.ase_calculator import MetatomicCalculator
128
-
129
- ... # setting up atoms
130
- calculator = MetatomicCalculator(energy_model, device=device, do_gradients_with_energy=False)
131
- atoms.calc = calculator
132
- ... # running FlashMD
133
- ```
127
+ Using FlashMD in i-PI
128
+ ---------------------
134
129
 
135
- Case 2: you want to compute energies after running FlashMD for your own analysis. In
136
- this case, you can create the calculator just like in case 1, but possibly after running
137
- FlashMD and/or in a different script.
130
+ You can see
131
+ [this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
132
+ for usage examples. i-PI is our most mature interface, and the one that was used to
133
+ generate all our published results.
138
134
 
139
- Case 3: you found something interesting during a FlashMD run and you want to confirm it
140
- with traditional MD. Then, you can just use ASE's MD modules as usual after attaching
141
- the energy calculator:
135
+ Models
136
+ ------
142
137
 
143
- ```
144
- from metatomic.torch.ase_calculator import MetatomicCalculator
138
+ See [here](docs/models.md) for the complete list of the models we provide. If you are
139
+ new to FlashMD, we recommend starting with the ``pet-omatpes-v2`` models.
145
140
 
146
- ... # setting up atoms
147
- calculator = MetatomicCalculator(energy_model, device=device)
148
- atoms.calc = calculator
149
- ... # running MD
150
- ```
141
+ Training/fine-tuning your own FlashMD models
142
+ --------------------------------------------
151
143
 
152
- In general, the energy models are slower and have a larger memory footprint compared to
153
- the FlashMD models. As summarized above, you should use `do_gradients_with_energy=False`
154
- to save computation and memory when you don't need forces.
144
+ FlashMD models can be trained from the metatrain library. This
145
+ [tutorial](https://docs.metatensor.org/metatrain/latest/generated_examples/1-advanced/04-flashmd.html)
146
+ shows how to train your own FlashMD model, either from scratch or by fine-tuning of one of our universal models.
155
147
 
156
148
  Disclaimer
157
149
  ----------
158
150
 
159
- This is experimental software and should only be used if you know what you're doing.
160
- We recommend using the i-PI integrators for any serious work and/or if you need to perform
161
- constant-pressure (NPT) molecular dynamics. You can see
162
- [this cookbook recipe](https://atomistic-cookbook.org/examples/flashmd/flashmd-demo.html)
163
- for a usage example.
164
- Given that the main issue we observe in direct MD trajectories is loss of equipartition
165
- of energy between different degrees of freedom, we recommend using a local Langevin
166
- thermostat, and to monitor the temperature of different atomic types or different
167
- parts of the simulated system.
151
+ This is experimental software and should only be used if you know what you are doing.
152
+ Given that the main issue we observe in FlashMD is loss of equipartition
153
+ of energy between different degrees of freedom, we recommend using a Langevin
154
+ thermostat, possibly monitoring the temperature of different atomic types or different
155
+ parts of the simulated system. For the time being, we also recommend checking all
156
+ FlashMD-powered findings with traditional MD. The energy models that were used to train
157
+ FlashMD, and that we make available in this repository, can be used for this purpose.
168
158
 
169
159
 
170
160
  Publication
171
161
  -----------
172
162
 
173
- If you found FlashMD useful, you can cite the corresponding article:
163
+ If you found FlashMD useful, you can cite the corresponding [article](https://arxiv.org/abs/2505.19350):
174
164
 
175
165
  ```
176
- @article{FlashMD,
177
- title={FlashMD: long-stride, universal prediction of molecular dynamics},
178
- author={Bigi, Filippo and Chong, Sanggyu and Kristiadi, Agustinus and Ceriotti, Michele},
179
- journal={arXiv preprint arXiv:2505.19350},
180
- year={2025}
166
+ @inproceedings{FlashMD,
167
+ title = {FlashMD: long-stride, universal prediction of molecular dynamics},
168
+ author = {Bigi, Filippo and Chong, Sanggyu and Kristiadi, Agustinus and Ceriotti, Michele},
169
+ booktitle = {Advances in Neural Information Processing Systems},
170
+ year = {2025},
171
+ url = {https://arxiv.org/abs/2505.19350}
181
172
  }
182
173
  ```
183
174
 
@@ -14,5 +14,6 @@ src/flashmd.egg-info/top_level.txt
14
14
  src/flashmd/ase/__init__.py
15
15
  src/flashmd/ase/bussi.py
16
16
  src/flashmd/ase/langevin.py
17
- src/flashmd/ase/npt.py
18
- src/flashmd/ase/velocity_verlet.py
17
+ src/flashmd/ase/velocity_verlet.py
18
+ tests/test_models.py
19
+ tests/test_readme.py
@@ -0,0 +1,8 @@
1
+ metatrain==2026.1
2
+ ase
3
+ huggingface_hub
4
+
5
+ [dev]
6
+ pytest
7
+ ruff
8
+ mypy
@@ -0,0 +1,65 @@
1
+ import pytest
2
+
3
+ from flashmd.models import AVAILABLE_MLIPS, AVAILABLE_TIME_STEPS
4
+
5
+
6
+ def test_available_mlips():
7
+ """Test that AVAILABLE_MLIPS is defined and contains expected values."""
8
+ assert isinstance(AVAILABLE_MLIPS, list)
9
+ assert len(AVAILABLE_MLIPS) > 0
10
+ assert "pet-omatpes" in AVAILABLE_MLIPS
11
+ assert "pet-omatpes-v2" in AVAILABLE_MLIPS
12
+
13
+
14
+ def test_available_time_steps():
15
+ """Test that AVAILABLE_TIME_STEPS is defined and contains expected values."""
16
+ assert isinstance(AVAILABLE_TIME_STEPS, dict)
17
+ assert len(AVAILABLE_TIME_STEPS) > 0
18
+
19
+ # Check that each MLIP has time steps defined
20
+ for mlip in AVAILABLE_MLIPS:
21
+ assert mlip in AVAILABLE_TIME_STEPS
22
+ assert isinstance(AVAILABLE_TIME_STEPS[mlip], list)
23
+ assert len(AVAILABLE_TIME_STEPS[mlip]) > 0
24
+
25
+
26
+ def test_get_pretrained_invalid_mlip():
27
+ """Test that get_pretrained raises ValueError for invalid MLIP."""
28
+ from flashmd.models import get_pretrained
29
+
30
+ with pytest.raises(ValueError, match="MLIP 'invalid_mlip' is not available"):
31
+ get_pretrained(mlip="invalid_mlip", time_step=16)
32
+
33
+
34
+ def test_get_pretrained_invalid_time_step():
35
+ """Test that get_pretrained raises ValueError for invalid time step."""
36
+ from flashmd.models import get_pretrained
37
+
38
+ with pytest.raises(ValueError, match="Pre-trained FlashMD models"):
39
+ get_pretrained(mlip="pet-omatpes", time_step=999)
40
+
41
+
42
+ def test_save_checkpoint_invalid_time_step():
43
+ """Test that save_checkpoint raises ValueError for invalid time step."""
44
+ from flashmd.models import save_checkpoint
45
+
46
+ with pytest.raises(ValueError, match="Pre-trained FlashMD models"):
47
+ save_checkpoint(mlip="pet-omatpes-v2", time_step=999)
48
+
49
+
50
+ def test_save_checkpoint(monkeypatch):
51
+ """Test that save_checkpoint saves the checkpoint file."""
52
+ from flashmd.models import save_checkpoint
53
+
54
+ # Mock hf_hub_download and shutil.copyfile
55
+ def mock_hf_hub_download(repo_id, filename, cache_dir, revision):
56
+ return f"/path/to/{filename}"
57
+
58
+ def mock_copyfile(src, dst):
59
+ assert src == "/path/to/flashmd_pet-omatpes-v2_16fs.ckpt"
60
+ assert dst == "flashmd_pet-omatpes-v2_16fs.ckpt"
61
+
62
+ monkeypatch.setattr("flashmd.models.hf_hub_download", mock_hf_hub_download)
63
+ monkeypatch.setattr("flashmd.models.shutil.copyfile", mock_copyfile)
64
+
65
+ save_checkpoint(mlip="pet-omatpes-v2", time_step=16)
@@ -0,0 +1,37 @@
1
+ """Test that the README example works correctly."""
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+
7
+ def extract_python_code_from_readme():
8
+ """Extract the first Python code block from README.md."""
9
+ readme_path = Path(__file__).parent.parent / "README.md"
10
+ content = readme_path.read_text()
11
+
12
+ # Find the first ```py or ```python code block
13
+ pattern = r"```(?:py|python)\n(.*?)\n```"
14
+ matches = re.findall(pattern, content, re.DOTALL)
15
+
16
+ if not matches:
17
+ raise ValueError("No Python code block found in README.md")
18
+
19
+ # Return the first code block (the ASE usage example)
20
+ return matches[0]
21
+
22
+
23
+ def test_readme_example():
24
+ """Test that the README example runs successfully with 10 steps instead of 1000."""
25
+ # Extract the code from README
26
+ code = extract_python_code_from_readme()
27
+
28
+ # Modify the code to run for only 10 steps instead of 1000
29
+ modified_code = code.replace("dyn.run(1000)", "dyn.run(10)")
30
+
31
+ # Execute the code
32
+ exec_globals = {}
33
+ exec(modified_code, exec_globals)
34
+
35
+ # Basic validation: check that the dynamics object was created
36
+ assert "dyn" in exec_globals
37
+ assert exec_globals["dyn"] is not None
File without changes
@@ -1,61 +0,0 @@
1
- from huggingface_hub import hf_hub_download
2
- from metatomic.torch import AtomisticModel, load_atomistic_model
3
- import subprocess
4
- import os
5
- import time
6
-
7
-
8
- AVAILABLE_MLIPS = ["pet-omatpes"]
9
- AVAILABLE_TIME_STEPS = {
10
- "pet-omatpes": [1, 2, 4, 8, 16, 32, 64, 128]
11
- }
12
-
13
-
14
- def get_pretrained(mlip: str = "pet-omatpes", time_step: int = 16) -> AtomisticModel:
15
- if mlip not in AVAILABLE_MLIPS:
16
- raise ValueError(
17
- f"MLIP '{mlip}' is not available. "
18
- f"Available MLIPs are: {', '.join(AVAILABLE_MLIPS)}."
19
- )
20
-
21
- if time_step not in AVAILABLE_TIME_STEPS[mlip]:
22
- raise ValueError(
23
- f"Pretrained FlashMD models based on the {mlip} MLIP are only available "
24
- f"for time steps of {', '.join(map(str, AVAILABLE_TIME_STEPS[mlip]))} fs."
25
- )
26
-
27
- # Get checkpoints corresponding to the selected MLIP and FlashMD models
28
- mlip_path = hf_hub_download(
29
- repo_id="lab-cosmo/flashmd",
30
- filename=f"mlip_{mlip}.ckpt",
31
- cache_dir=None,
32
- revision="main",
33
- )
34
- flashmd_path = hf_hub_download(
35
- repo_id="lab-cosmo/flashmd",
36
- filename=f"flashmd_{mlip}_{time_step}fs.ckpt",
37
- cache_dir=None,
38
- revision="main",
39
- )
40
-
41
- # Now we need to export both using metatrain. However, we don't want to do it if
42
- # HuggingFace hasn't downloaded a new version of the files, so we only re-export
43
- # if the files above have changed in the last 10 seconds.
44
- reexport = False
45
- exported_mlip_path = mlip_path.replace(".ckpt", ".pt")
46
- exported_flashmd_path = flashmd_path.replace(".ckpt", ".pt")
47
- if not os.path.exists(exported_mlip_path) or not os.path.exists(exported_flashmd_path):
48
- reexport = True
49
- mlip_mtime = os.path.getmtime(mlip_path)
50
- flashmd_mtime = os.path.getmtime(flashmd_path)
51
- if (time.time() - mlip_mtime < 10) or (time.time() - flashmd_mtime < 10):
52
- reexport = True
53
- if reexport:
54
- subprocess.run(["mtt", "export", mlip_path, "-o", exported_mlip_path], capture_output=True)
55
- subprocess.run(["mtt", "export", flashmd_path, "-o", exported_flashmd_path], capture_output=True)
56
-
57
- # Load as AtomisticModel instances
58
- mlip_model = load_atomistic_model(exported_mlip_path)
59
- flashmd_model = load_atomistic_model(exported_flashmd_path)
60
-
61
- return mlip_model, flashmd_model
@@ -1,3 +0,0 @@
1
- metatrain==2025.11
2
- ase
3
- huggingface_hub
File without changes
File without changes