BUCToolkit 1.0b0__py3-none-any.whl
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.
- BUCToolkit/BatchGenerate/CheckStructures.py +198 -0
- BUCToolkit/BatchGenerate/__init__.py +6 -0
- BUCToolkit/BatchGenerate/coords_interp.py +127 -0
- BUCToolkit/BatchGenerate/initial_ads_structures.py +351 -0
- BUCToolkit/BatchGenerate/random_replace.py +88 -0
- BUCToolkit/BatchMC/MetropolisMC.py +210 -0
- BUCToolkit/BatchMC/_BaseMC.py +863 -0
- BUCToolkit/BatchMC/__init__.py +8 -0
- BUCToolkit/BatchMD/BiasedMD.py +276 -0
- BUCToolkit/BatchMD/NVE.py +92 -0
- BUCToolkit/BatchMD/NVT.py +510 -0
- BUCToolkit/BatchMD/_BaseMD.py +1251 -0
- BUCToolkit/BatchMD/__init__.py +18 -0
- BUCToolkit/BatchMD/constrained_md/BiasedMD.py +273 -0
- BUCToolkit/BatchMD/constrained_md/ConstrNVE.py +169 -0
- BUCToolkit/BatchMD/constrained_md/ConstrNVT.py +462 -0
- BUCToolkit/BatchMD/constrained_md/NVE.py +78 -0
- BUCToolkit/BatchMD/constrained_md/NVT.py +346 -0
- BUCToolkit/BatchMD/constrained_md/_BaseMD.py +440 -0
- BUCToolkit/BatchMD/constrained_md/_ConstrBaseMD.py +607 -0
- BUCToolkit/BatchMD/constrained_md/__init__.py +18 -0
- BUCToolkit/BatchOptim/TS/CI_NEB.py +286 -0
- BUCToolkit/BatchOptim/TS/Dimer.py +950 -0
- BUCToolkit/BatchOptim/TS/__init__.py +10 -0
- BUCToolkit/BatchOptim/_BaseOpt.py +702 -0
- BUCToolkit/BatchOptim/__init__.py +22 -0
- BUCToolkit/BatchOptim/_utils/__init__.py +6 -0
- BUCToolkit/BatchOptim/_utils/_line_search.py +972 -0
- BUCToolkit/BatchOptim/_utils/_warnings.py +11 -0
- BUCToolkit/BatchOptim/frequency.py +496 -0
- BUCToolkit/BatchOptim/minimize/CG.py +256 -0
- BUCToolkit/BatchOptim/minimize/FIRE.py +310 -0
- BUCToolkit/BatchOptim/minimize/QN.py +205 -0
- BUCToolkit/BatchOptim/minimize/__init__.py +17 -0
- BUCToolkit/BatchStructures/BatchStructuresBase.py +2261 -0
- BUCToolkit/BatchStructures/StructuresIO.py +1394 -0
- BUCToolkit/BatchStructures/__init__.py +15 -0
- BUCToolkit/Preprocessing/__init__.py +7 -0
- BUCToolkit/Preprocessing/load_files.py +1680 -0
- BUCToolkit/Preprocessing/preprocessing.py +923 -0
- BUCToolkit/Preprocessing/sampling.py +299 -0
- BUCToolkit/Preprocessing/supercells.py +140 -0
- BUCToolkit/Preprocessing/write_files.py +524 -0
- BUCToolkit/StandardTemplate.py +417 -0
- BUCToolkit/__init__.py +33 -0
- BUCToolkit/_version.py +7 -0
- BUCToolkit/api/ConstrainedMolecularDynamics.py +347 -0
- BUCToolkit/api/DataLoaders.py +321 -0
- BUCToolkit/api/Losses.py +170 -0
- BUCToolkit/api/Metrics.py +84 -0
- BUCToolkit/api/ModelOptims.py +296 -0
- BUCToolkit/api/MolecularDynamics.py +282 -0
- BUCToolkit/api/MonteCarlo.py +280 -0
- BUCToolkit/api/NEB.py +325 -0
- BUCToolkit/api/Predictor.py +292 -0
- BUCToolkit/api/StructureOptimization.py +484 -0
- BUCToolkit/api/Trainer.py +635 -0
- BUCToolkit/api/VibrationAnalysis.py +279 -0
- BUCToolkit/api/__init__.py +38 -0
- BUCToolkit/api/_io.py +1026 -0
- BUCToolkit/cli/__init__.py +20 -0
- BUCToolkit/cli/cli.py +934 -0
- BUCToolkit/cli/input_stub.py +140 -0
- BUCToolkit/cli/main.py +368 -0
- BUCToolkit/cli/print_logo.py +149 -0
- BUCToolkit/cli/template_model.py +6 -0
- BUCToolkit/io.py +30 -0
- BUCToolkit/utils/AtomSelector.py +64 -0
- BUCToolkit/utils/AtomicNumber2Properties.py +39 -0
- BUCToolkit/utils/ElemListReduce.py +52 -0
- BUCToolkit/utils/Ewald.py +210 -0
- BUCToolkit/utils/IrregularTensorReformat.py +59 -0
- BUCToolkit/utils/_CheckModules.py +30 -0
- BUCToolkit/utils/_Element_info.py +527 -0
- BUCToolkit/utils/__init__.py +22 -0
- BUCToolkit/utils/_para_flatt_list.py +131 -0
- BUCToolkit/utils/_print_formatter.py +59 -0
- BUCToolkit/utils/check_structures.py +204 -0
- BUCToolkit/utils/function_utils.py +147 -0
- BUCToolkit/utils/grad_functions.py +69 -0
- BUCToolkit/utils/index_ops.py +432 -0
- BUCToolkit/utils/setup_loggers.py +137 -0
- buctoolkit-1.0b0.dist-info/METADATA +28 -0
- buctoolkit-1.0b0.dist-info/RECORD +88 -0
- buctoolkit-1.0b0.dist-info/WHEEL +5 -0
- buctoolkit-1.0b0.dist-info/entry_points.txt +6 -0
- buctoolkit-1.0b0.dist-info/licenses/LICENSE +21 -0
- buctoolkit-1.0b0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Batched Verlet Molecular Dynamics by pytorch models or functions.
|
|
3
|
+
|
|
4
|
+
2024/7/30 PPX
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Copyright (c) 2024-2025.7.4, BUCToolkit.
|
|
8
|
+
# Authors: Pu Pengxin, Song Xin
|
|
9
|
+
# Version: 0.9a
|
|
10
|
+
# File: __init__.py
|
|
11
|
+
# Environment: Python 3.12
|
|
12
|
+
|
|
13
|
+
# ruff: noqa: E701, E702, E703
|
|
14
|
+
from .NVE import NVE
|
|
15
|
+
from .NVT import NVT
|
|
16
|
+
from .BiasedMD import BiasedMD
|
|
17
|
+
|
|
18
|
+
__all__ = ['NVE', 'NVT', 'BiasedMD']
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Copyright (c) 2025.7.4, BUCToolkit.
|
|
2
|
+
# Authors: Pu Pengxin, Song Xin
|
|
3
|
+
# Version: 0.9a
|
|
4
|
+
# File: BiasedMD.py
|
|
5
|
+
# Environment: Python 3.12
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from typing import Iterable, Dict, Any, List, Literal, Optional, Callable, Sequence, Tuple # noqa: F401
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import torch as th
|
|
13
|
+
from torch import nn
|
|
14
|
+
|
|
15
|
+
from ._BaseMD import _rBaseMD
|
|
16
|
+
from .NVT import NVT
|
|
17
|
+
from .NVE import NVE
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BiasedMD(NVT):
|
|
21
|
+
"""
|
|
22
|
+
Performing MD with given external-biased potentials.
|
|
23
|
+
"""
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
time_step: float,
|
|
27
|
+
max_step: int,
|
|
28
|
+
thermostat: Literal['Langevin', 'VR', 'Nose-Hoover', 'CSVR'],
|
|
29
|
+
thermostat_config: Dict | None = None,
|
|
30
|
+
T_init: float = 298.15,
|
|
31
|
+
output_file: str | None = None,
|
|
32
|
+
output_structures_per_step: int = 1,
|
|
33
|
+
device: str | th.device = 'cpu',
|
|
34
|
+
verbose: int = 2
|
|
35
|
+
):
|
|
36
|
+
super().__init__(
|
|
37
|
+
time_step,
|
|
38
|
+
max_step,
|
|
39
|
+
thermostat,
|
|
40
|
+
thermostat_config,
|
|
41
|
+
T_init,
|
|
42
|
+
output_file,
|
|
43
|
+
output_structures_per_step,
|
|
44
|
+
device,
|
|
45
|
+
verbose
|
|
46
|
+
)
|
|
47
|
+
self.ext_pot_grad_kwargs = None
|
|
48
|
+
self.ext_pot_grad_args = None
|
|
49
|
+
self.ext_pot_grad = None
|
|
50
|
+
self.ext_pot_kwargs = None
|
|
51
|
+
self.ext_pot_args = None
|
|
52
|
+
self.ext_pot = None
|
|
53
|
+
self.__is_set_ext_func = False
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def _auto_grad_func(X: th.Tensor, y: th.Tensor | None = None):
|
|
57
|
+
grad_shape = th.ones_like(y)
|
|
58
|
+
g = th.autograd.grad(y, X, grad_shape)
|
|
59
|
+
return g[0]
|
|
60
|
+
|
|
61
|
+
def set_external_potential(
|
|
62
|
+
self,
|
|
63
|
+
potential_func: Callable,
|
|
64
|
+
potential_grad_func: Callable | None = None,
|
|
65
|
+
func_args:Tuple = tuple(),
|
|
66
|
+
func_kwargs: None | Dict = None,
|
|
67
|
+
grad_func_args: Tuple = tuple(),
|
|
68
|
+
grad_func_kwargs: None | Dict = None,
|
|
69
|
+
is_grad_contain_y: bool = True,
|
|
70
|
+
#is_dynamic: bool = False
|
|
71
|
+
):
|
|
72
|
+
"""
|
|
73
|
+
Set the external potential function.
|
|
74
|
+
Args:
|
|
75
|
+
potential_func: the function of external potential
|
|
76
|
+
potential_grad_func: the gradient of external potential. if None, torch.autograd is applied.
|
|
77
|
+
func_args: arguments of `potential_func`
|
|
78
|
+
func_kwargs: key words arguments of `potential_func`
|
|
79
|
+
grad_func_args: arguments of `potential_grad_func`
|
|
80
|
+
grad_func_kwargs: key words arguments of `potential_grad_func`
|
|
81
|
+
is_grad_contain_y: whether `potential_grad_func` needs dependent variable y as input. if True, potential_grad_func = g(X, y), else g(X)
|
|
82
|
+
#is_dynamic: whether potential function containing time as independent variables. if True, potential_func = f(X, t), else f(X).
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
if func_kwargs is None: func_kwargs = {}
|
|
88
|
+
if grad_func_kwargs is None: grad_func_kwargs = {}
|
|
89
|
+
self.ext_pot = potential_func
|
|
90
|
+
self.ext_pot_args = func_args
|
|
91
|
+
self.ext_pot_kwargs = func_kwargs
|
|
92
|
+
# ext_grad_func
|
|
93
|
+
if potential_grad_func is None:
|
|
94
|
+
self.ext_pot_grad = self._auto_grad_func
|
|
95
|
+
else:
|
|
96
|
+
self.ext_pot_grad = potential_grad_func
|
|
97
|
+
|
|
98
|
+
self.ext_pot_grad_args = grad_func_args
|
|
99
|
+
self.ext_pot_grad_kwargs = grad_func_kwargs
|
|
100
|
+
self.is_grad_contain_y = is_grad_contain_y
|
|
101
|
+
#self.is_dynamic = is_dynamic
|
|
102
|
+
self.__is_set_ext_func = True
|
|
103
|
+
|
|
104
|
+
def run(
|
|
105
|
+
self,
|
|
106
|
+
func: Any | nn.Module,
|
|
107
|
+
X: th.Tensor,
|
|
108
|
+
Element_list: List[List[str]] | List[List[int]],
|
|
109
|
+
V_init: th.Tensor | None = None,
|
|
110
|
+
grad_func: Any | nn.Module = None,
|
|
111
|
+
func_args: Tuple|List = tuple(),
|
|
112
|
+
func_kwargs: Dict | None = None,
|
|
113
|
+
grad_func_args: Tuple|List = tuple(),
|
|
114
|
+
grad_func_kwargs: Dict | None = None,
|
|
115
|
+
is_grad_func_contain_y: bool = True,
|
|
116
|
+
batch_indices: List[int] | Tuple[int, ...] | th.Tensor | np.ndarray | None = None,
|
|
117
|
+
fixed_atom_tensor: Optional[th.Tensor] = None,
|
|
118
|
+
is_fix_mass_center: bool = False
|
|
119
|
+
) -> None:
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
func:
|
|
124
|
+
X:
|
|
125
|
+
Element_list:
|
|
126
|
+
V_init:
|
|
127
|
+
grad_func:
|
|
128
|
+
func_args:
|
|
129
|
+
func_kwargs:
|
|
130
|
+
grad_func_args:
|
|
131
|
+
grad_func_kwargs:
|
|
132
|
+
is_grad_func_contain_y:
|
|
133
|
+
batch_indices:
|
|
134
|
+
fixed_atom_tensor:
|
|
135
|
+
is_fix_mass_center:
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
if func_kwargs is None: func_kwargs = dict()
|
|
141
|
+
if grad_func_kwargs is None: grad_func_kwargs = dict()
|
|
142
|
+
if grad_func is not None:
|
|
143
|
+
_grad_func = grad_func
|
|
144
|
+
else:
|
|
145
|
+
_grad_func = self._auto_grad_func
|
|
146
|
+
is_grad_func_contain_y = True
|
|
147
|
+
|
|
148
|
+
if self.__is_set_ext_func:
|
|
149
|
+
wrapper = _FuncWrapperWithExtPot(
|
|
150
|
+
func,
|
|
151
|
+
self.ext_pot,
|
|
152
|
+
_grad_func,
|
|
153
|
+
self.ext_pot_grad,
|
|
154
|
+
len(func_args),
|
|
155
|
+
func_kwargs.keys(),
|
|
156
|
+
len(grad_func_args),
|
|
157
|
+
grad_func_kwargs.keys()
|
|
158
|
+
)
|
|
159
|
+
wrapped_args = func_args + self.ext_pot_args
|
|
160
|
+
wrapped_kwargs = func_kwargs.update(self.ext_pot_kwargs)
|
|
161
|
+
wrapped_grad_args = grad_func_args + self.ext_pot_grad_args
|
|
162
|
+
wrapped_grad_kwargs = grad_func_kwargs.update(self.ext_pot_grad_kwargs)
|
|
163
|
+
|
|
164
|
+
wrapped_func = wrapper.Energy
|
|
165
|
+
if self.is_grad_contain_y:
|
|
166
|
+
wrapped_grad_func = wrapper.Grad_y
|
|
167
|
+
else:
|
|
168
|
+
wrapped_grad_func = wrapper.Grad
|
|
169
|
+
|
|
170
|
+
res = super().run(
|
|
171
|
+
wrapped_func,
|
|
172
|
+
X,
|
|
173
|
+
Element_list,
|
|
174
|
+
V_init,
|
|
175
|
+
wrapped_grad_func,
|
|
176
|
+
wrapped_args,
|
|
177
|
+
wrapped_kwargs,
|
|
178
|
+
wrapped_grad_args,
|
|
179
|
+
wrapped_grad_kwargs,
|
|
180
|
+
is_grad_func_contain_y,
|
|
181
|
+
batch_indices,
|
|
182
|
+
fixed_atom_tensor,
|
|
183
|
+
is_fix_mass_center
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
return res
|
|
187
|
+
else:
|
|
188
|
+
raise RuntimeError(f'The external potential function is not set.')
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class _FuncWrapperWithExtPot:
|
|
192
|
+
def __init__(
|
|
193
|
+
self,
|
|
194
|
+
func,
|
|
195
|
+
ext_func,
|
|
196
|
+
grad_func,
|
|
197
|
+
ext_grad_func,
|
|
198
|
+
n_func_args = 0,
|
|
199
|
+
k_func_kwargs = None,
|
|
200
|
+
n_grad_func_args = 0,
|
|
201
|
+
k_grad_func_kwargs = None,
|
|
202
|
+
|
|
203
|
+
) -> None:
|
|
204
|
+
"""
|
|
205
|
+
A wrapper to add external function
|
|
206
|
+
Wrap the f(X, ...) into f(X, ...) + ext_func(X, ...)
|
|
207
|
+
|
|
208
|
+
And the wrapped func_args would be the concatenation of `func_args` and `ext_func_args`.
|
|
209
|
+
Hence, the number of func_args, i.e., the `n_func_args` is required to determine that
|
|
210
|
+
the first `n_func_args`-th args belong to `func` and the rest belong to `ext_func`
|
|
211
|
+
|
|
212
|
+
Keyword Args is similar, that require the keys of `func_kwargs`, and the rest belongs to `ext_func`.
|
|
213
|
+
|
|
214
|
+
`grad_func` is so, too.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
func: the original function
|
|
218
|
+
ext_func: the external adding function
|
|
219
|
+
n_func_args: number of args that belongs to `func`
|
|
220
|
+
k_func_kwargs: keywords list that belongs to `func`
|
|
221
|
+
n_grad_func_args,
|
|
222
|
+
k_grad_func_kwargs
|
|
223
|
+
|
|
224
|
+
Methods:
|
|
225
|
+
Energy: input Tensor `X` and PygData `graph`, it will update graph.pos into X and return model(graph)['energy'].
|
|
226
|
+
Grad: input Tensor `X` and PygData `graph`, it will update graph.pos into X and return model(graph)['forces'].
|
|
227
|
+
|
|
228
|
+
"""
|
|
229
|
+
self._model = func
|
|
230
|
+
self._ext_func = ext_func
|
|
231
|
+
self._grad_func = grad_func
|
|
232
|
+
self._ext_grad_func = ext_grad_func
|
|
233
|
+
self.n_func_args = n_func_args
|
|
234
|
+
self.k_func_kwargs = set(k_func_kwargs) if k_func_kwargs is not None else set()
|
|
235
|
+
self.n_grad_func_args = n_grad_func_args
|
|
236
|
+
self.k_grad_func_kwargs = set(k_grad_func_kwargs) if k_grad_func_kwargs is not None else set()
|
|
237
|
+
|
|
238
|
+
self.forces = None
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
def _auto_grad_func(X: th.Tensor, y: th.Tensor | None = None):
|
|
242
|
+
grad_shape = th.ones_like(y)
|
|
243
|
+
g = th.autograd.grad(y, X, grad_shape)
|
|
244
|
+
return g[0]
|
|
245
|
+
|
|
246
|
+
def Energy(self, X, *func_args, **func_kwargs, ):
|
|
247
|
+
self.X = X
|
|
248
|
+
true_func_args = func_args[:self.n_func_args]
|
|
249
|
+
ext_func_args = func_args[self.n_func_args:]
|
|
250
|
+
true_func_kwargs = {k:func_kwargs[k] for k in (func_kwargs.keys() & self.k_func_kwargs)}
|
|
251
|
+
ext_func_kwargs = {k:func_kwargs[k] for k in (func_kwargs.keys() - self.k_func_kwargs)}
|
|
252
|
+
|
|
253
|
+
self.y = self._model(self.X, *true_func_args, **true_func_kwargs) + self._ext_func(X, *ext_func_args, **ext_func_kwargs)
|
|
254
|
+
|
|
255
|
+
return self.y
|
|
256
|
+
|
|
257
|
+
def Grad(self, X, *grad_func_args, **grad_func_kwargs,):
|
|
258
|
+
true_func_args = grad_func_args[:self.n_func_args]
|
|
259
|
+
ext_func_args = grad_func_args[self.n_func_args:]
|
|
260
|
+
true_func_kwargs = {k: grad_func_kwargs[k] for k in (grad_func_kwargs.keys() & self.k_func_kwargs)}
|
|
261
|
+
ext_func_kwargs = {k: grad_func_kwargs[k] for k in (grad_func_kwargs.keys() - self.k_func_kwargs)}
|
|
262
|
+
g = self._grad_func(X, *true_func_args, **true_func_kwargs) + self._ext_grad_func(X, *ext_func_args, **ext_func_kwargs)
|
|
263
|
+
|
|
264
|
+
return g
|
|
265
|
+
|
|
266
|
+
def Grad_y(self, X, y, *grad_func_args, **grad_func_kwargs, ):
|
|
267
|
+
true_func_args = grad_func_args[:self.n_func_args]
|
|
268
|
+
ext_func_args = grad_func_args[self.n_func_args:]
|
|
269
|
+
true_func_kwargs = {k: grad_func_kwargs[k] for k in (grad_func_kwargs.keys() & self.k_func_kwargs)}
|
|
270
|
+
ext_func_kwargs = {k: grad_func_kwargs[k] for k in (grad_func_kwargs.keys() - self.k_func_kwargs)}
|
|
271
|
+
g = self._grad_func(X, y, *true_func_args, **true_func_kwargs) + self._ext_grad_func(X, y, *ext_func_args, **ext_func_kwargs)
|
|
272
|
+
|
|
273
|
+
return g
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
""" Micro canonical ensemble (NVE) Molecular Dynamics via Verlet algo. """
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2024-2025.7.4, BUCToolkit.
|
|
4
|
+
# Authors: Pu Pengxin, Song Xin
|
|
5
|
+
# Version: 0.9a
|
|
6
|
+
# File: NVE.py
|
|
7
|
+
# Environment: Python 3.12
|
|
8
|
+
|
|
9
|
+
# ruff: noqa: E701, E702, E703
|
|
10
|
+
from typing import Iterable, Dict, Any, List, Literal, Optional, Callable, Sequence, Tuple # noqa: F401
|
|
11
|
+
|
|
12
|
+
import torch as th
|
|
13
|
+
from torch import nn
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from ._ConstrBaseMD import _rConstrBase
|
|
17
|
+
from BUCToolkit.utils._print_formatter import FLOAT_ARRAY_FORMAT, SCIENTIFIC_ARRAY_FORMAT
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ConstrNVE(_rConstrBase):
|
|
21
|
+
"""
|
|
22
|
+
Constrained micro canonical ensemble (NVE) molecular dynamics implemented via velocity Verlet algo.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
time_step: float, time per step (ps).
|
|
26
|
+
max_step: int, maximum steps.
|
|
27
|
+
T_init: float, initial temperature, only to generate initial velocities of atoms by Maxwell-Boltzmann distribution. If V_init is given, T_init will be ignored.
|
|
28
|
+
output_structures_per_step: int, output structures per output_structures_per_step steps.
|
|
29
|
+
device: str|torch.device, device that program rum on.
|
|
30
|
+
verbose: int, control the detailed degree of output information. 0 for silence, 1 for output Energy and Forces per step, 2 for output all structures.
|
|
31
|
+
|
|
32
|
+
Methods:
|
|
33
|
+
run: run BatchMD.
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
time_step: float,
|
|
40
|
+
max_step: int,
|
|
41
|
+
constr_func: Callable[[th.Tensor], th.Tensor] = None,
|
|
42
|
+
constr_val: Callable[[th.Tensor], th.Tensor|Tuple[th.Tensor]] | th.Tensor = None,
|
|
43
|
+
constr_threshold: float = 1e-5,
|
|
44
|
+
T_init: float = 298.15,
|
|
45
|
+
output_file: str | None = None,
|
|
46
|
+
dump_path: str | None = None,
|
|
47
|
+
output_structures_per_step: int = 1,
|
|
48
|
+
device: str | th.device = 'cpu',
|
|
49
|
+
verbose: int = 2
|
|
50
|
+
):
|
|
51
|
+
super().__init__(
|
|
52
|
+
time_step,
|
|
53
|
+
max_step,
|
|
54
|
+
T_init,
|
|
55
|
+
constr_func,
|
|
56
|
+
constr_val,
|
|
57
|
+
constr_threshold,
|
|
58
|
+
output_file,
|
|
59
|
+
dump_path,
|
|
60
|
+
output_structures_per_step,
|
|
61
|
+
device,
|
|
62
|
+
verbose
|
|
63
|
+
)
|
|
64
|
+
self._X, self._V = None, None
|
|
65
|
+
|
|
66
|
+
def initialize(
|
|
67
|
+
self,
|
|
68
|
+
func: Any | nn.Module,
|
|
69
|
+
X: th.Tensor,
|
|
70
|
+
Element_list: List[List[str]] | List[List[int]],
|
|
71
|
+
masses: th.Tensor,
|
|
72
|
+
V_init: th.Tensor | None = None,
|
|
73
|
+
grad_func: Any | nn.Module = None,
|
|
74
|
+
func_args: Sequence = tuple(),
|
|
75
|
+
func_kwargs: Dict | None = None,
|
|
76
|
+
grad_func_args: Sequence = tuple(),
|
|
77
|
+
grad_func_kwargs: Dict | None = None,
|
|
78
|
+
is_grad_func_contain_y: bool = True,
|
|
79
|
+
require_grad: bool = False,
|
|
80
|
+
batch_indices: List[int] | Tuple[int, ...] | th.Tensor | np.ndarray | None = None,
|
|
81
|
+
fixed_atom_tensor: Optional[th.Tensor] = None,
|
|
82
|
+
is_fix_mass_center: bool = False
|
|
83
|
+
):
|
|
84
|
+
super().initialize(
|
|
85
|
+
func,
|
|
86
|
+
X,
|
|
87
|
+
Element_list,
|
|
88
|
+
masses,
|
|
89
|
+
V_init,
|
|
90
|
+
grad_func,
|
|
91
|
+
func_args,
|
|
92
|
+
func_kwargs,
|
|
93
|
+
grad_func_args,
|
|
94
|
+
grad_func_kwargs,
|
|
95
|
+
is_grad_func_contain_y,
|
|
96
|
+
require_grad,
|
|
97
|
+
batch_indices,
|
|
98
|
+
fixed_atom_tensor,
|
|
99
|
+
is_fix_mass_center
|
|
100
|
+
)
|
|
101
|
+
self._X, self._V = X, V_init
|
|
102
|
+
|
|
103
|
+
def _updateXV(
|
|
104
|
+
self, X, V, Force,
|
|
105
|
+
func, grad_func_, func_args, func_kwargs, grad_func_args, grad_func_kwargs,
|
|
106
|
+
masses, atom_masks, is_grad_func_contain_y, batch_indices,
|
|
107
|
+
) -> Tuple[th.Tensor, th.Tensor, th.Tensor, th.Tensor]:
|
|
108
|
+
""" Update X, V, Force, and return X, V, Energy, Force. """
|
|
109
|
+
# X: th.Tensor = X.contiguous()
|
|
110
|
+
# V: th.Tensor = V.contiguous()
|
|
111
|
+
# masses: th.Tensor = masses.contiguous()
|
|
112
|
+
with th.no_grad():
|
|
113
|
+
# X = X + V * self.time_step + (Force / (2. * masses)) * self.time_step ** 2 * 9.64853329045427e-3
|
|
114
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
115
|
+
X.add_(V, alpha=self.time_step)
|
|
116
|
+
Fc = self._project2(X) # in-place update
|
|
117
|
+
if self.verbose > 0:
|
|
118
|
+
self.logger.info(f'Constraint forces \\lambda: {np.array2string(Fc.squeeze().numpy(force=True), **SCIENTIFIC_ARRAY_FORMAT)}')
|
|
119
|
+
# V = V + (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 # half-step veloc. update, to avoid saving 2 Forces Tensors.
|
|
120
|
+
# Update V
|
|
121
|
+
with th.set_grad_enabled(self.require_grad):
|
|
122
|
+
X.requires_grad_(self.require_grad)
|
|
123
|
+
Energy = func(X, *func_args, **func_kwargs)
|
|
124
|
+
if is_grad_func_contain_y:
|
|
125
|
+
Force = - grad_func_(X, Energy, *grad_func_args, **grad_func_kwargs) * atom_masks
|
|
126
|
+
else:
|
|
127
|
+
Force = - grad_func_(X, *grad_func_args, **grad_func_kwargs) * atom_masks
|
|
128
|
+
|
|
129
|
+
# V = V + (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3
|
|
130
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
131
|
+
V.copy_(self._project1(V))
|
|
132
|
+
|
|
133
|
+
return X, V, Energy, Force
|
|
134
|
+
|
|
135
|
+
def __updateXV(
|
|
136
|
+
self, X, V, Force,
|
|
137
|
+
func, grad_func_, func_args, func_kwargs, grad_func_args, grad_func_kwargs,
|
|
138
|
+
masses, atom_masks, is_grad_func_contain_y,
|
|
139
|
+
) -> Tuple[th.Tensor, th.Tensor, th.Tensor]:
|
|
140
|
+
"""
|
|
141
|
+
TESTING...
|
|
142
|
+
Here the returned X, V, Force and Energy are all the quantities at the midpoint.
|
|
143
|
+
And the quantities in the end point are additionally stored in external attr. self._X, self._V,
|
|
144
|
+
and these attributes would be updated in-place within `self.implicit_midpoint_step`
|
|
145
|
+
"""
|
|
146
|
+
# predict the new endpoint by last midpoint and endpoint
|
|
147
|
+
X0 = self._X
|
|
148
|
+
V0 = self._V
|
|
149
|
+
f_constr = th.einsum('bijk, bi -> bjk', self.jac, self.lamb)
|
|
150
|
+
V_mid_pred = V.add((Force + f_constr) / (2. * masses), alpha=self.time_step * 9.64853329045427e-3)
|
|
151
|
+
X_mid_pred = X.add(V_mid_pred, alpha=self.time_step)
|
|
152
|
+
self._V = 2 * V_mid_pred - self._V
|
|
153
|
+
self._X = 2 * X_mid_pred - self._X
|
|
154
|
+
|
|
155
|
+
X, V, E, F, lamb = self.implicit_midpoint_step(
|
|
156
|
+
X0, self._X, V0, self._V, Force, self.lamb,
|
|
157
|
+
func, grad_func_, func_args, func_kwargs, grad_func_args, grad_func_kwargs,
|
|
158
|
+
masses, atom_masks, is_grad_func_contain_y,
|
|
159
|
+
)
|
|
160
|
+
self.logger.info(
|
|
161
|
+
f'Constraint forces \\lambda: {np.array2string(lamb.squeeze().numpy(force=True), **SCIENTIFIC_ARRAY_FORMAT)}'
|
|
162
|
+
)
|
|
163
|
+
jac, y = self._jacobian(X)
|
|
164
|
+
self._debug_X_check.append(abs(y[0].item()))
|
|
165
|
+
self._debug_V_check.append(abs(th.sum(jac * V.unsqueeze(1), dim=(-2, -1))[0].item()))
|
|
166
|
+
print(abs(th.sum(jac * V.unsqueeze(1), dim=(-2, -1))[0].item()))
|
|
167
|
+
|
|
168
|
+
return X, V, E, F
|
|
169
|
+
|