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.
Files changed (88) hide show
  1. BUCToolkit/BatchGenerate/CheckStructures.py +198 -0
  2. BUCToolkit/BatchGenerate/__init__.py +6 -0
  3. BUCToolkit/BatchGenerate/coords_interp.py +127 -0
  4. BUCToolkit/BatchGenerate/initial_ads_structures.py +351 -0
  5. BUCToolkit/BatchGenerate/random_replace.py +88 -0
  6. BUCToolkit/BatchMC/MetropolisMC.py +210 -0
  7. BUCToolkit/BatchMC/_BaseMC.py +863 -0
  8. BUCToolkit/BatchMC/__init__.py +8 -0
  9. BUCToolkit/BatchMD/BiasedMD.py +276 -0
  10. BUCToolkit/BatchMD/NVE.py +92 -0
  11. BUCToolkit/BatchMD/NVT.py +510 -0
  12. BUCToolkit/BatchMD/_BaseMD.py +1251 -0
  13. BUCToolkit/BatchMD/__init__.py +18 -0
  14. BUCToolkit/BatchMD/constrained_md/BiasedMD.py +273 -0
  15. BUCToolkit/BatchMD/constrained_md/ConstrNVE.py +169 -0
  16. BUCToolkit/BatchMD/constrained_md/ConstrNVT.py +462 -0
  17. BUCToolkit/BatchMD/constrained_md/NVE.py +78 -0
  18. BUCToolkit/BatchMD/constrained_md/NVT.py +346 -0
  19. BUCToolkit/BatchMD/constrained_md/_BaseMD.py +440 -0
  20. BUCToolkit/BatchMD/constrained_md/_ConstrBaseMD.py +607 -0
  21. BUCToolkit/BatchMD/constrained_md/__init__.py +18 -0
  22. BUCToolkit/BatchOptim/TS/CI_NEB.py +286 -0
  23. BUCToolkit/BatchOptim/TS/Dimer.py +950 -0
  24. BUCToolkit/BatchOptim/TS/__init__.py +10 -0
  25. BUCToolkit/BatchOptim/_BaseOpt.py +702 -0
  26. BUCToolkit/BatchOptim/__init__.py +22 -0
  27. BUCToolkit/BatchOptim/_utils/__init__.py +6 -0
  28. BUCToolkit/BatchOptim/_utils/_line_search.py +972 -0
  29. BUCToolkit/BatchOptim/_utils/_warnings.py +11 -0
  30. BUCToolkit/BatchOptim/frequency.py +496 -0
  31. BUCToolkit/BatchOptim/minimize/CG.py +256 -0
  32. BUCToolkit/BatchOptim/minimize/FIRE.py +310 -0
  33. BUCToolkit/BatchOptim/minimize/QN.py +205 -0
  34. BUCToolkit/BatchOptim/minimize/__init__.py +17 -0
  35. BUCToolkit/BatchStructures/BatchStructuresBase.py +2261 -0
  36. BUCToolkit/BatchStructures/StructuresIO.py +1394 -0
  37. BUCToolkit/BatchStructures/__init__.py +15 -0
  38. BUCToolkit/Preprocessing/__init__.py +7 -0
  39. BUCToolkit/Preprocessing/load_files.py +1680 -0
  40. BUCToolkit/Preprocessing/preprocessing.py +923 -0
  41. BUCToolkit/Preprocessing/sampling.py +299 -0
  42. BUCToolkit/Preprocessing/supercells.py +140 -0
  43. BUCToolkit/Preprocessing/write_files.py +524 -0
  44. BUCToolkit/StandardTemplate.py +417 -0
  45. BUCToolkit/__init__.py +33 -0
  46. BUCToolkit/_version.py +7 -0
  47. BUCToolkit/api/ConstrainedMolecularDynamics.py +347 -0
  48. BUCToolkit/api/DataLoaders.py +321 -0
  49. BUCToolkit/api/Losses.py +170 -0
  50. BUCToolkit/api/Metrics.py +84 -0
  51. BUCToolkit/api/ModelOptims.py +296 -0
  52. BUCToolkit/api/MolecularDynamics.py +282 -0
  53. BUCToolkit/api/MonteCarlo.py +280 -0
  54. BUCToolkit/api/NEB.py +325 -0
  55. BUCToolkit/api/Predictor.py +292 -0
  56. BUCToolkit/api/StructureOptimization.py +484 -0
  57. BUCToolkit/api/Trainer.py +635 -0
  58. BUCToolkit/api/VibrationAnalysis.py +279 -0
  59. BUCToolkit/api/__init__.py +38 -0
  60. BUCToolkit/api/_io.py +1026 -0
  61. BUCToolkit/cli/__init__.py +20 -0
  62. BUCToolkit/cli/cli.py +934 -0
  63. BUCToolkit/cli/input_stub.py +140 -0
  64. BUCToolkit/cli/main.py +368 -0
  65. BUCToolkit/cli/print_logo.py +149 -0
  66. BUCToolkit/cli/template_model.py +6 -0
  67. BUCToolkit/io.py +30 -0
  68. BUCToolkit/utils/AtomSelector.py +64 -0
  69. BUCToolkit/utils/AtomicNumber2Properties.py +39 -0
  70. BUCToolkit/utils/ElemListReduce.py +52 -0
  71. BUCToolkit/utils/Ewald.py +210 -0
  72. BUCToolkit/utils/IrregularTensorReformat.py +59 -0
  73. BUCToolkit/utils/_CheckModules.py +30 -0
  74. BUCToolkit/utils/_Element_info.py +527 -0
  75. BUCToolkit/utils/__init__.py +22 -0
  76. BUCToolkit/utils/_para_flatt_list.py +131 -0
  77. BUCToolkit/utils/_print_formatter.py +59 -0
  78. BUCToolkit/utils/check_structures.py +204 -0
  79. BUCToolkit/utils/function_utils.py +147 -0
  80. BUCToolkit/utils/grad_functions.py +69 -0
  81. BUCToolkit/utils/index_ops.py +432 -0
  82. BUCToolkit/utils/setup_loggers.py +137 -0
  83. buctoolkit-1.0b0.dist-info/METADATA +28 -0
  84. buctoolkit-1.0b0.dist-info/RECORD +88 -0
  85. buctoolkit-1.0b0.dist-info/WHEEL +5 -0
  86. buctoolkit-1.0b0.dist-info/entry_points.txt +6 -0
  87. buctoolkit-1.0b0.dist-info/licenses/LICENSE +21 -0
  88. 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
+