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,510 @@
|
|
|
1
|
+
""" Canonical ensemble (NVT) 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: NVT.py
|
|
7
|
+
# Environment: Python 3.12
|
|
8
|
+
|
|
9
|
+
from typing import Iterable, Dict, Any, List, Literal, Optional, Callable, Sequence, Tuple # noqa: F401
|
|
10
|
+
import warnings
|
|
11
|
+
import math
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import torch as th
|
|
15
|
+
from torch import nn
|
|
16
|
+
|
|
17
|
+
from BUCToolkit.utils.index_ops import index_reduce
|
|
18
|
+
from ._BaseMD import _BaseMD
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class NVT(_BaseMD):
|
|
22
|
+
"""
|
|
23
|
+
Canonical ensemble (NVT) molecular dynamics.
|
|
24
|
+
|
|
25
|
+
Parameters:
|
|
26
|
+
time_step: float, time per step (ps).
|
|
27
|
+
max_step: maxmimum steps.
|
|
28
|
+
thermostat: str, the thermostat of NVT ensemble.
|
|
29
|
+
thermostat_config: Dict|None, configs of thermostat. {'damping_coeff': float} for Langevin, {'time_const': float} for CSVR, {'virt_mass': float} for Nose-Hoover.
|
|
30
|
+
T_init: initial temperature, only to generate initial velocities of atoms by Maxwell-Boltzmann distribution. If V_init is given, T_init will be ignored.
|
|
31
|
+
output_structures_per_step: int, output structures per output_structures_per_step steps.
|
|
32
|
+
device: device that the program rum on.
|
|
33
|
+
verbose: control the detailed degree of output information. 0 for silence, 1 for output Energy and Forces per step, 2 for output all structures.
|
|
34
|
+
is_compile: whether to use jit to compile integrator or not.
|
|
35
|
+
compile_kwargs: keyword arguments passed to compile. Only work when is_compile is True.
|
|
36
|
+
Methods:
|
|
37
|
+
run: run the NVT ensemble BatchMD.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
time_step: float,
|
|
43
|
+
max_step: int,
|
|
44
|
+
thermostat: Literal['Langevin', 'VR', 'Nose-Hoover', 'CSVR'],
|
|
45
|
+
thermostat_config: Dict[Literal['damping_coeff', 'time_const', 'virt_mass'], float] | None = None,
|
|
46
|
+
T_init: float = 298.15,
|
|
47
|
+
output_file: str | None = None,
|
|
48
|
+
output_structures_per_step: int = 1,
|
|
49
|
+
device: str | th.device = 'cpu',
|
|
50
|
+
verbose: int = 2,
|
|
51
|
+
is_compile: bool = False,
|
|
52
|
+
compile_kwargs: dict | None = None,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Parameters:
|
|
56
|
+
time_step: float, time per step (ps).
|
|
57
|
+
max_step: maxmimum steps.
|
|
58
|
+
thermostat: str, the thermostat of NVT ensemble.
|
|
59
|
+
thermostat_config: Dict|None, configs of thermostat. {'damping_coeff': float} for Langevin, {'time_const': float} for CSVR, {'virt_mass': float} for Nose-Hoover.
|
|
60
|
+
T_init: initial temperature, only to generate initial velocities of atoms by Maxwell-Boltzmann distribution. If V_init is given, T_init will be ignored.
|
|
61
|
+
output_structures_per_step: int, output structures per output_structures_per_step steps.
|
|
62
|
+
device: device that program run on.
|
|
63
|
+
verbose: control the detailed degree of output information. 0 for silence, 1 for output Energy and Forces per step, 2 for output all structures.
|
|
64
|
+
"""
|
|
65
|
+
super().__init__(
|
|
66
|
+
time_step,
|
|
67
|
+
max_step,
|
|
68
|
+
T_init,
|
|
69
|
+
output_file,
|
|
70
|
+
output_structures_per_step,
|
|
71
|
+
device,
|
|
72
|
+
verbose,
|
|
73
|
+
is_compile,
|
|
74
|
+
compile_kwargs
|
|
75
|
+
)
|
|
76
|
+
__ENSEMBLES_DICT = {'Langevin': None, 'VR': None, 'Nose-Hoover': None, 'CSVR': None}
|
|
77
|
+
if thermostat not in {'Langevin', 'Langevin_old', 'test', 'VR', 'CSVR', 'Nose-Hoover'}:
|
|
78
|
+
raise ValueError(f'Unknown Thermostat {thermostat}')
|
|
79
|
+
self.thermostat = thermostat
|
|
80
|
+
if thermostat_config is None:
|
|
81
|
+
thermostat_config = dict()
|
|
82
|
+
self.thermostat_config = thermostat_config
|
|
83
|
+
self.update_scheme = None # lazy loaded in self.initialize
|
|
84
|
+
self.half_time_step_const = 0.5 * self.time_step * 9.64853329045427e-3
|
|
85
|
+
|
|
86
|
+
def __resolve_update_scheme(self, batch_indices):
|
|
87
|
+
"""
|
|
88
|
+
resolve different iteration scheme & initialize corresponding parameters.
|
|
89
|
+
Returns:
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
if self.thermostat == "Langevin":
|
|
93
|
+
damp_coeff = self.thermostat_config.get('damping_coeff', 0.01) # Unit: fs^-1
|
|
94
|
+
self.alpha = math.exp(- damp_coeff * self.time_step)
|
|
95
|
+
return self.__Langevin
|
|
96
|
+
elif self.thermostat == "VR":
|
|
97
|
+
return self.__VR
|
|
98
|
+
elif self.thermostat == "Nose-Hoover":
|
|
99
|
+
# read thermostat config
|
|
100
|
+
smass = self.thermostat_config.get('virt_mass', self.free_degree * 8.617333262145e-5 * self.T_init * (40. * self.time_step) ** 2)
|
|
101
|
+
if isinstance(smass, float):
|
|
102
|
+
smass = th.as_tensor(smass, device=self.device).view(1, 1, 1)
|
|
103
|
+
elif isinstance(smass, th.Tensor):
|
|
104
|
+
smass = smass.to(self.device)
|
|
105
|
+
smass = smass.view(1, -1, 1) if self.batch_tensor is not None else smass.view(-1, 1, 1)
|
|
106
|
+
self.smass = smass
|
|
107
|
+
if batch_indices is not None:
|
|
108
|
+
self.long_free_degree = self.free_degree.reshape(1, -1, 1)
|
|
109
|
+
else:
|
|
110
|
+
self.long_free_degree = self.free_degree.reshape(-1, 1, 1)
|
|
111
|
+
return self.__NoseHoover
|
|
112
|
+
elif self.thermostat == "CSVR":
|
|
113
|
+
# read thermostat configs
|
|
114
|
+
self.time_const = self.thermostat_config.get('time_const', 10 * self.time_step) # Unit: fs^-1
|
|
115
|
+
# NVE Step
|
|
116
|
+
dtT = self.time_step
|
|
117
|
+
tauT = self.time_const
|
|
118
|
+
# c = exp(-dt/tau) (scalar)
|
|
119
|
+
c = th.exp(th.scalar_tensor(-dtT / tauT, device=self.device, dtype=th.float32))
|
|
120
|
+
self.sqrt_c = th.sqrt(c)
|
|
121
|
+
self.one_sub_c = 1 - c
|
|
122
|
+
# avoid divide-by-zero in K
|
|
123
|
+
self.epsK = th.scalar_tensor(1e-12, device=self.device, dtype=th.float32)
|
|
124
|
+
return self.__CSVR
|
|
125
|
+
else:
|
|
126
|
+
raise NotImplementedError("Unknown Thermostat Type.")
|
|
127
|
+
|
|
128
|
+
def initialize(
|
|
129
|
+
self,
|
|
130
|
+
func: Any | nn.Module,
|
|
131
|
+
X: th.Tensor,
|
|
132
|
+
Element_list: List[List[str]] | List[List[int]],
|
|
133
|
+
masses: th.Tensor,
|
|
134
|
+
V_init: th.Tensor | None = None,
|
|
135
|
+
grad_func: Any | nn.Module = None,
|
|
136
|
+
func_args: Sequence = tuple(),
|
|
137
|
+
func_kwargs: Dict | None = None,
|
|
138
|
+
grad_func_args: Sequence = tuple(),
|
|
139
|
+
grad_func_kwargs: Dict | None = None,
|
|
140
|
+
is_grad_func_contain_y: bool = True,
|
|
141
|
+
require_grad: bool = False,
|
|
142
|
+
batch_indices: List[int] | Tuple[int, ...] | th.Tensor | np.ndarray | None = None,
|
|
143
|
+
fixed_atom_tensor: Optional[th.Tensor] = None,
|
|
144
|
+
is_fix_mass_center: bool = False
|
|
145
|
+
) -> None:
|
|
146
|
+
self.update_scheme = self.__resolve_update_scheme(batch_indices)
|
|
147
|
+
|
|
148
|
+
def __Langevin(
|
|
149
|
+
self,
|
|
150
|
+
X,
|
|
151
|
+
V,
|
|
152
|
+
Force,
|
|
153
|
+
func,
|
|
154
|
+
grad_func_,
|
|
155
|
+
func_args,
|
|
156
|
+
func_kwargs,
|
|
157
|
+
grad_func_args,
|
|
158
|
+
grad_func_kwargs,
|
|
159
|
+
masses,
|
|
160
|
+
atom_masks,
|
|
161
|
+
is_grad_func_contain_y,
|
|
162
|
+
batch_indices
|
|
163
|
+
)-> Tuple[th.Tensor, th.Tensor, th.Tensor, th.Tensor]:
|
|
164
|
+
# read thermostat configs
|
|
165
|
+
X = X.detach()
|
|
166
|
+
with th.no_grad():
|
|
167
|
+
alpha = self.alpha
|
|
168
|
+
# half-step
|
|
169
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
170
|
+
X.add_(V, alpha = 0.5 * self.time_step)
|
|
171
|
+
# stochastic update velocity
|
|
172
|
+
V.mul_(alpha)
|
|
173
|
+
V.add_(th.sqrt((8314.462618 * self.T_init * (1 - alpha ** 2)) / masses) * 1e-5 * th.randn_like(V))
|
|
174
|
+
#V = alpha * V + th.sqrt((8314.462618 * self.T_init * (1 - alpha ** 2)) / masses) * 1e-5 * th.randn_like(V)
|
|
175
|
+
# the rest half-step
|
|
176
|
+
X.add_(V, alpha = 0.5 * self.time_step)
|
|
177
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
178
|
+
# update energy & forces
|
|
179
|
+
Energy, Force = self._calc_EF(
|
|
180
|
+
X,
|
|
181
|
+
func,
|
|
182
|
+
func_args,
|
|
183
|
+
func_kwargs,
|
|
184
|
+
grad_func_,
|
|
185
|
+
grad_func_args,
|
|
186
|
+
grad_func_kwargs,
|
|
187
|
+
is_grad_func_contain_y
|
|
188
|
+
)
|
|
189
|
+
Force.mul_(atom_masks)
|
|
190
|
+
|
|
191
|
+
return X, V, Energy, Force
|
|
192
|
+
|
|
193
|
+
def __VR(
|
|
194
|
+
self,
|
|
195
|
+
X,
|
|
196
|
+
V,
|
|
197
|
+
Force,
|
|
198
|
+
func,
|
|
199
|
+
grad_func_,
|
|
200
|
+
func_args,
|
|
201
|
+
func_kwargs,
|
|
202
|
+
grad_func_args,
|
|
203
|
+
grad_func_kwargs,
|
|
204
|
+
masses,
|
|
205
|
+
atom_masks,
|
|
206
|
+
is_grad_func_contain_y,
|
|
207
|
+
batch_indices
|
|
208
|
+
)-> Tuple[th.Tensor, th.Tensor, th.Tensor, th.Tensor]:
|
|
209
|
+
# NVE Step
|
|
210
|
+
X = X.detach()
|
|
211
|
+
with th.no_grad():
|
|
212
|
+
# X = X + V * self.time_step + (Force / (2. * masses)) * self.time_step ** 2 * 9.64853329045427e-3
|
|
213
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
214
|
+
X.add_(V, alpha=self.time_step)
|
|
215
|
+
# V = V + (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 # half-step veloc. update, to avoid saving 2 Forces Tensors.
|
|
216
|
+
# Update V
|
|
217
|
+
Energy, Force = self._calc_EF(
|
|
218
|
+
X,
|
|
219
|
+
func,
|
|
220
|
+
func_args,
|
|
221
|
+
func_kwargs,
|
|
222
|
+
grad_func_,
|
|
223
|
+
grad_func_args,
|
|
224
|
+
grad_func_kwargs,
|
|
225
|
+
is_grad_func_contain_y
|
|
226
|
+
)
|
|
227
|
+
Force.mul_(atom_masks)
|
|
228
|
+
|
|
229
|
+
# V = V + (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3
|
|
230
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
231
|
+
if batch_indices is not None:
|
|
232
|
+
# Rescaling factor
|
|
233
|
+
alpha = th.sqrt(self.EK_TARGET / self.Ek).unsqueeze(-1).unsqueeze(-1) # (n_batch, 1, 1) | (irregular n_batch, 1, 1)
|
|
234
|
+
V *= alpha.transpose(0, 1)[:, self.batch_scatter, :]
|
|
235
|
+
else:
|
|
236
|
+
# Rescaling factor
|
|
237
|
+
alpha = th.sqrt(self.EK_TARGET / self.Ek).unsqueeze(-1).unsqueeze(-1) # (n_batch, 1, 1) | (irregular n_batch, 1, 1)
|
|
238
|
+
V *= alpha # (n_batch, n_atom, n_dim) * (n_batch, 1, 1)
|
|
239
|
+
|
|
240
|
+
return X, V, Energy, Force
|
|
241
|
+
|
|
242
|
+
def __CSVR_old(
|
|
243
|
+
self,
|
|
244
|
+
X,
|
|
245
|
+
V,
|
|
246
|
+
Force,
|
|
247
|
+
func,
|
|
248
|
+
grad_func_,
|
|
249
|
+
func_args,
|
|
250
|
+
func_kwargs,
|
|
251
|
+
grad_func_args,
|
|
252
|
+
grad_func_kwargs,
|
|
253
|
+
masses,
|
|
254
|
+
atom_masks,
|
|
255
|
+
is_grad_func_contain_y,
|
|
256
|
+
batch_indices
|
|
257
|
+
)-> Tuple[th.Tensor, th.Tensor, th.Tensor, th.Tensor]:
|
|
258
|
+
n_batch, n_atom, n_dim = X.shape
|
|
259
|
+
# read thermostat configs
|
|
260
|
+
time_const = self.thermostat_config.get('time_const', 10 * self.time_step) # Unit: fs^-1
|
|
261
|
+
# NVE Step
|
|
262
|
+
X: th.Tensor = X.detach()
|
|
263
|
+
with th.no_grad():
|
|
264
|
+
#X += V * self.time_step + (Force / (2. * masses)) * self.time_step ** 2 * 9.64853329045427e-3
|
|
265
|
+
X.add_(V, alpha=self.time_step)
|
|
266
|
+
X.add_(Force / (2. * masses), alpha=self.time_step ** 2 * 9.64853329045427e-3)
|
|
267
|
+
#V += (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 # half-step veloc. update, to avoid saving 2 Forces Tensors.
|
|
268
|
+
V.add_(Force / (2. * masses), alpha=self.time_step * 9.64853329045427e-3)
|
|
269
|
+
with th.set_grad_enabled(self.require_grad):
|
|
270
|
+
X.requires_grad_(self.require_grad)
|
|
271
|
+
Energy = func(X, *func_args, **func_kwargs)
|
|
272
|
+
if is_grad_func_contain_y:
|
|
273
|
+
Force = - grad_func_(X, Energy, *grad_func_args, **grad_func_kwargs) * atom_masks
|
|
274
|
+
else:
|
|
275
|
+
Force = - grad_func_(X, *grad_func_args, **grad_func_kwargs) * atom_masks
|
|
276
|
+
#V += (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3
|
|
277
|
+
V.add_(Force / (2. * masses), alpha=self.time_step * 9.64853329045427e-3)
|
|
278
|
+
|
|
279
|
+
# Kinetic Energy Redistribution, Rescale Velocities
|
|
280
|
+
# sigma = th.sqrt((2. * masses * self.T_init / (self.time_step * time_const)) * (0.138064853 / (6.022140857 * 1.602176634 ** 2)))
|
|
281
|
+
sigma = self.time_step ** 0.5
|
|
282
|
+
if batch_indices is not None:
|
|
283
|
+
self.Ekt_vir += (
|
|
284
|
+
(self.EK_TARGET - self.Ek) * self.time_step / time_const
|
|
285
|
+
+ 2. * th.sqrt(self.EK_TARGET * self.Ek / ((3 * self.batch_tensor - 3) * time_const))
|
|
286
|
+
* th.normal(0., sigma, size=(len(batch_indices),), device=self.device)
|
|
287
|
+
) # Unit: eV/Ang
|
|
288
|
+
self.Ekt_vir = th.where(self.Ekt_vir <= 0., 1e-6, self.Ekt_vir)
|
|
289
|
+
# Rescaling factor
|
|
290
|
+
alpha = th.sqrt(self.Ekt_vir / self.Ek).unsqueeze(-1).unsqueeze(-1) # (n_batch, 1, 1) | (irregular n_batch, 1, 1)
|
|
291
|
+
V *= alpha.transpose(0, 1)[:, self.batch_scatter, :]
|
|
292
|
+
else:
|
|
293
|
+
self.Ekt_vir += (
|
|
294
|
+
(self.EK_TARGET - self.Ek) * self.time_step / time_const
|
|
295
|
+
+ 2. * th.sqrt(self.EK_TARGET * self.Ek / ((3 * n_atom - 3) * time_const))
|
|
296
|
+
* th.normal(0., sigma, size=(n_batch,), device=self.device)
|
|
297
|
+
) # Unit: eV/Ang
|
|
298
|
+
# Rescaling factor
|
|
299
|
+
alpha = th.sqrt(self.Ekt_vir / self.Ek).unsqueeze(-1).unsqueeze(-1) # (n_batch, 1, 1) | (irregular n_batch, 1, 1)
|
|
300
|
+
V *= alpha # (n_batch, n_atom, n_dim) * (n_batch, 1, 1)
|
|
301
|
+
|
|
302
|
+
return X, V, Energy, Force
|
|
303
|
+
|
|
304
|
+
def __CSVR(
|
|
305
|
+
self,
|
|
306
|
+
X,
|
|
307
|
+
V,
|
|
308
|
+
Force,
|
|
309
|
+
func,
|
|
310
|
+
grad_func_,
|
|
311
|
+
func_args,
|
|
312
|
+
func_kwargs,
|
|
313
|
+
grad_func_args,
|
|
314
|
+
grad_func_kwargs,
|
|
315
|
+
masses,
|
|
316
|
+
atom_masks,
|
|
317
|
+
is_grad_func_contain_y,
|
|
318
|
+
batch_indices
|
|
319
|
+
) -> Tuple[th.Tensor, th.Tensor, th.Tensor, th.Tensor]:
|
|
320
|
+
"""
|
|
321
|
+
The Analytic solution of CSVR that uses Chi^2 distribution and exp.
|
|
322
|
+
Returns:
|
|
323
|
+
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
with th.no_grad():
|
|
327
|
+
# X = X + V * self.time_step + (Force / (2. * masses)) * self.time_step ** 2 * 9.64853329045427e-3
|
|
328
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
329
|
+
X.add_(V, alpha=self.time_step)
|
|
330
|
+
# V = V + (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 # half-step veloc. update, to avoid saving 2 Forces Tensors.
|
|
331
|
+
# Update V
|
|
332
|
+
Energy, Force = self._calc_EF(
|
|
333
|
+
X,
|
|
334
|
+
func,
|
|
335
|
+
func_args,
|
|
336
|
+
func_kwargs,
|
|
337
|
+
grad_func_,
|
|
338
|
+
grad_func_args,
|
|
339
|
+
grad_func_kwargs,
|
|
340
|
+
is_grad_func_contain_y
|
|
341
|
+
)
|
|
342
|
+
Force.mul_(atom_masks)
|
|
343
|
+
|
|
344
|
+
# V = V + (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3
|
|
345
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
346
|
+
Nf = self.free_degree # shape (n_batch,)
|
|
347
|
+
if batch_indices is not None:
|
|
348
|
+
K = th.clamp(self.Ek, min=self.epsK) # shape (n_batch,)
|
|
349
|
+
K0 = self.EK_TARGET # (n_batch, )
|
|
350
|
+
|
|
351
|
+
# f = (1-c) * K0 / (Nf*K)
|
|
352
|
+
f = self.one_sub_c * K0 / (Nf * K)
|
|
353
|
+
|
|
354
|
+
# R ~ N(0,1)
|
|
355
|
+
R = th.randn_like(K)
|
|
356
|
+
|
|
357
|
+
# S ~ Chi2(df=Nf-1)
|
|
358
|
+
df = th.clamp(Nf - 1.0, min=1.0)
|
|
359
|
+
S = th.distributions.chi2.Chi2(df=df).sample() # shape (n_batch,)
|
|
360
|
+
|
|
361
|
+
# alpha^2 = (sqrt(c)+sqrt(f)R)^2 + f S
|
|
362
|
+
sqrt_f = th.sqrt(th.clamp(f, min=0.0))
|
|
363
|
+
alpha2: th.Tensor = th.addcmul(self.sqrt_c, sqrt_f, R)**2
|
|
364
|
+
alpha2.addcmul_(f, S).clamp_min_(self.epsK)
|
|
365
|
+
|
|
366
|
+
# (optional bookkeeping: post-thermostat kinetic energy)
|
|
367
|
+
#self.Ekt_vir = alpha2 * K
|
|
368
|
+
|
|
369
|
+
alpha = th.sqrt(alpha2).reshape(1, -1, 1) # (n_batch, 1, 1)
|
|
370
|
+
V *= alpha.index_select(1, self.batch_scatter)
|
|
371
|
+
|
|
372
|
+
else:
|
|
373
|
+
K = th.clamp(self.Ek, min=self.epsK) # (n_batch,)
|
|
374
|
+
K0 = self.EK_TARGET # scalar or (n_batch,)
|
|
375
|
+
|
|
376
|
+
f = self.one_sub_c * K0 / (Nf * K)
|
|
377
|
+
|
|
378
|
+
R = th.randn_like(K)
|
|
379
|
+
df = th.clamp(Nf - 1.0, min=1.0)
|
|
380
|
+
# sample per-batch
|
|
381
|
+
S = th.distributions.chi2.Chi2(df=df).sample()
|
|
382
|
+
|
|
383
|
+
sqrt_f = th.sqrt(th.clamp(f, min=0.0))
|
|
384
|
+
alpha2: th.Tensor = th.addcmul(self.sqrt_c, sqrt_f, R) ** 2
|
|
385
|
+
alpha2.addcmul_(f, S).clamp_min_(self.epsK)
|
|
386
|
+
|
|
387
|
+
#self.Ekt_vir = alpha2 * K
|
|
388
|
+
|
|
389
|
+
alpha = th.sqrt(alpha2).reshape(-1, 1, 1) # (n_batch, 1, 1)
|
|
390
|
+
V *= alpha
|
|
391
|
+
|
|
392
|
+
return X, V, Energy, Force
|
|
393
|
+
|
|
394
|
+
def __NoseHoover(
|
|
395
|
+
self,
|
|
396
|
+
X,
|
|
397
|
+
V,
|
|
398
|
+
Force,
|
|
399
|
+
func,
|
|
400
|
+
grad_func_,
|
|
401
|
+
func_args,
|
|
402
|
+
func_kwargs,
|
|
403
|
+
grad_func_args,
|
|
404
|
+
grad_func_kwargs,
|
|
405
|
+
masses,
|
|
406
|
+
atom_masks,
|
|
407
|
+
is_grad_func_contain_y,
|
|
408
|
+
batch_indices
|
|
409
|
+
) -> Tuple[th.Tensor, th.Tensor, th.Tensor, th.Tensor]:
|
|
410
|
+
n_batch, n_atom, n_dim = X.shape
|
|
411
|
+
smass = self.smass
|
|
412
|
+
# Main update
|
|
413
|
+
with th.no_grad():
|
|
414
|
+
if batch_indices is not None:
|
|
415
|
+
_iota = self.p_iota[:, self.batch_scatter, :]
|
|
416
|
+
else:
|
|
417
|
+
_iota = self.p_iota
|
|
418
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
419
|
+
V.mul_(th.exp(- _iota * 0.5 * self.time_step))
|
|
420
|
+
X.add_(V, alpha=self.time_step)
|
|
421
|
+
|
|
422
|
+
Energy, Force = self._calc_EF(
|
|
423
|
+
X,
|
|
424
|
+
func,
|
|
425
|
+
func_args,
|
|
426
|
+
func_kwargs,
|
|
427
|
+
grad_func_,
|
|
428
|
+
grad_func_args,
|
|
429
|
+
grad_func_kwargs,
|
|
430
|
+
is_grad_func_contain_y
|
|
431
|
+
)
|
|
432
|
+
Force.mul_(atom_masks)
|
|
433
|
+
|
|
434
|
+
if batch_indices is not None: # for cuda, it would be further optimized by Graph.replay
|
|
435
|
+
reduced_Ek = th.sum(
|
|
436
|
+
index_reduce(masses * V ** 2 * 103.642696562621738, self.batch_scatter, 1, out_size=self.scatter_dim_out_size),
|
|
437
|
+
dim=-1,
|
|
438
|
+
keepdim=True
|
|
439
|
+
)
|
|
440
|
+
# self.p_iota = p_iota + 0.5 * dt * (reducedEk - Nf * T)/smass
|
|
441
|
+
self.p_iota.addcdiv_(
|
|
442
|
+
th.sub(reduced_Ek, self.long_free_degree, alpha=self.T_init * 8.617333262145e-5),
|
|
443
|
+
smass, value=self.time_step * 0.5
|
|
444
|
+
)
|
|
445
|
+
_iota = self.p_iota[:, self.batch_scatter, :] # (1, n_batch*n_atom, 1)
|
|
446
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
447
|
+
V.mul_(th.exp(- _iota * 0.5 * self.time_step))
|
|
448
|
+
reduced_Ek = th.sum(
|
|
449
|
+
index_reduce(masses * V ** 2 * 103.642696562621738, self.batch_scatter, 1, out_size=self.scatter_dim_out_size),
|
|
450
|
+
dim=-1,
|
|
451
|
+
keepdim=True
|
|
452
|
+
)
|
|
453
|
+
# self.p_iota = p_iota + 0.5 * dt * (reducedEk - Nf * T)/smass
|
|
454
|
+
self.p_iota.addcdiv_(
|
|
455
|
+
th.sub(reduced_Ek, self.long_free_degree, alpha=self.T_init * 8.617333262145e-5),
|
|
456
|
+
smass, value=self.time_step * 0.5
|
|
457
|
+
)
|
|
458
|
+
else:
|
|
459
|
+
reduced_Ek = th.sum(masses * V ** 2 * 103.642696562621738, dim=(-2, -1), keepdim=True)
|
|
460
|
+
# self.p_iota = p_iota + 0.5 * dt * (reducedEk - Nf * T)/smass
|
|
461
|
+
self.p_iota.addcdiv_(
|
|
462
|
+
th.sub(reduced_Ek, self.long_free_degree, alpha=self.T_init * 8.617333262145e-5),
|
|
463
|
+
smass, value=self.time_step * 0.5
|
|
464
|
+
)
|
|
465
|
+
V.addcdiv_(Force, masses, value=0.5 * self.time_step * 9.64853329045427e-3)
|
|
466
|
+
V.mul_(th.exp(- _iota * 0.5 * self.time_step))
|
|
467
|
+
reduced_Ek = th.sum(masses * V ** 2 * 103.642696562621738, dim=(-2, -1), keepdim=True)
|
|
468
|
+
# self.p_iota = p_iota + 0.5 * dt * (reducedEk - Nf * T)/smass
|
|
469
|
+
self.p_iota.addcdiv_(
|
|
470
|
+
th.sub(reduced_Ek, self.long_free_degree, alpha=self.T_init * 8.617333262145e-5),
|
|
471
|
+
smass, value=self.time_step * 0.5
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
return X, V, Energy, Force
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _updateXV(
|
|
478
|
+
self,
|
|
479
|
+
X,
|
|
480
|
+
V,
|
|
481
|
+
Force,
|
|
482
|
+
func,
|
|
483
|
+
grad_func_,
|
|
484
|
+
func_args,
|
|
485
|
+
func_kwargs,
|
|
486
|
+
grad_func_args,
|
|
487
|
+
grad_func_kwargs,
|
|
488
|
+
masses,
|
|
489
|
+
atom_masks,
|
|
490
|
+
is_grad_func_contain_y,
|
|
491
|
+
batch_indices
|
|
492
|
+
) -> Tuple[th.Tensor, th.Tensor, th.Tensor, th.Tensor]:
|
|
493
|
+
|
|
494
|
+
x, v, energy, forces = self.update_scheme(
|
|
495
|
+
X,
|
|
496
|
+
V,
|
|
497
|
+
Force,
|
|
498
|
+
func,
|
|
499
|
+
grad_func_,
|
|
500
|
+
func_args,
|
|
501
|
+
func_kwargs,
|
|
502
|
+
grad_func_args,
|
|
503
|
+
grad_func_kwargs,
|
|
504
|
+
masses,
|
|
505
|
+
atom_masks,
|
|
506
|
+
is_grad_func_contain_y,
|
|
507
|
+
batch_indices
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
return x, v, energy, forces
|