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,346 @@
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
+
12
+ import torch as th
13
+
14
+ from BUCToolkit.utils.index_ops import index_reduce
15
+ from ._BaseMD import _rBaseMD
16
+
17
+
18
+ class NVT(_rBaseMD):
19
+ """
20
+ Canonical ensemble (NVT) molecular dynamics.
21
+
22
+ Parameters:
23
+ time_step: float, time per step (ps).
24
+ max_step: maxmimum steps.
25
+ thermostat: str, the thermostat of NVT ensemble.
26
+ thermostat_config: Dict|None, configs of thermostat. {'damping_coeff': float} for Langevin, {'time_const': float} for CSVR, {'virt_mass': float} for Nose-Hoover.
27
+ 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.
28
+ output_structures_per_step: int, output structures per output_structures_per_step steps.
29
+ device: device that the program rum on.
30
+ verbose: 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 the NVT ensemble BatchMD.
34
+ """
35
+
36
+ def __init__(self,
37
+ time_step: float,
38
+ max_step: int,
39
+ thermostat: Literal['Langevin', 'VR', 'Nose-Hoover', 'CSVR'],
40
+ thermostat_config: Dict | None = None,
41
+ T_init: float = 298.15,
42
+ output_file: str | None = None,
43
+ output_structures_per_step: int = 1,
44
+ device: str | th.device = 'cpu',
45
+ verbose: int = 2) -> None:
46
+ """
47
+ Parameters:
48
+ time_step: float, time per step (ps).
49
+ max_step: maxmimum steps.
50
+ thermostat: str, the thermostat of NVT ensemble.
51
+ thermostat_config: Dict|None, configs of thermostat. {'damping_coeff': float} for Langevin, {'time_const': float} for CSVR, {'virt_mass': float} for Nose-Hoover.
52
+ 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.
53
+ output_structures_per_step: int, output structures per output_structures_per_step steps.
54
+ device: device that program run on.
55
+ verbose: control the detailed degree of output information. 0 for silence, 1 for output Energy and Forces per step, 2 for output all structures.
56
+ """
57
+ super().__init__(
58
+ time_step,
59
+ max_step,
60
+ T_init,
61
+ output_file,
62
+ output_structures_per_step,
63
+ device,
64
+ verbose
65
+ )
66
+ __ENSEMBLES_DICT = {'Langevin': None, 'VR': None, 'Nose-Hoover': None, 'CSVR': None}
67
+ if thermostat not in {'Langevin', 'Langevin_old', 'test', 'VR', 'CSVR', 'Nose-Hoover'}: raise ValueError(f'Unknown Thermostat {thermostat}')
68
+ self.thermostat = thermostat
69
+ if thermostat_config is None:
70
+ thermostat_config = dict()
71
+ self.update_scheme = self.__resolve_update_scheme()
72
+ self.thermostat_config = thermostat_config
73
+
74
+ def __resolve_update_scheme(self):
75
+ """
76
+ resolve different iteration scheme
77
+ Returns:
78
+
79
+ """
80
+ if self.thermostat == "Langevin":
81
+ return self.__Langevin
82
+ elif self.thermostat == "VR":
83
+ return self.__VR
84
+ elif self.thermostat == "Nose-Hoover":
85
+ return self.__NoseHoover
86
+ elif self.thermostat == "CSVR":
87
+ return self.__CSVR
88
+ else:
89
+ raise NotImplementedError("Unknown Thermostat Type.")
90
+
91
+ def __Langevin(
92
+ self,
93
+ X,
94
+ V,
95
+ Force,
96
+ func,
97
+ grad_func_,
98
+ func_args,
99
+ func_kwargs,
100
+ grad_func_args,
101
+ grad_func_kwargs,
102
+ masses,
103
+ atom_masks,
104
+ is_grad_func_contain_y,
105
+ batch_indices
106
+ )-> (th.Tensor, th.Tensor, th.Tensor, th.Tensor):
107
+ # read thermostat configs
108
+ X = X.detach()
109
+ with th.no_grad():
110
+ damp_coeff = self.thermostat_config.get('damping_coeff', 0.01) # Unit: fs^-1
111
+ alpha = th.e ** (- damp_coeff * self.time_step)
112
+ # half-step
113
+ V.add_(Force / (2. * masses), alpha = self.time_step * 9.64853329045427e-3)
114
+ X.add_(V, alpha = 0.5 * self.time_step)
115
+ # stochastic update velocity
116
+ V.mul_(alpha)
117
+ V.add_(th.sqrt((8314.462618 * self.T_init * (1 - alpha ** 2)) / masses) * 1e-5 * th.randn_like(V))
118
+ #V = alpha * V + th.sqrt((8314.462618 * self.T_init * (1 - alpha ** 2)) / masses) * 1e-5 * th.randn_like(V)
119
+ # the rest half-step
120
+ X.add_(V, alpha = 0.5 * self.time_step)
121
+ V.add_(Force / (2. * masses), alpha = self.time_step * 9.64853329045427e-3)
122
+ # update energy & forces
123
+ with th.set_grad_enabled(self.require_grad):
124
+ X.requires_grad_(self.require_grad)
125
+ Energy = func(X, *func_args, **func_kwargs)
126
+ if is_grad_func_contain_y:
127
+ Force = - grad_func_(X, Energy, *grad_func_args, **grad_func_kwargs) * atom_masks
128
+ else:
129
+ Force = - grad_func_(X, *grad_func_args, **grad_func_kwargs) * atom_masks
130
+
131
+ return X, V, Energy, Force
132
+
133
+ def __VR(
134
+ self,
135
+ X,
136
+ V,
137
+ Force,
138
+ func,
139
+ grad_func_,
140
+ func_args,
141
+ func_kwargs,
142
+ grad_func_args,
143
+ grad_func_kwargs,
144
+ masses,
145
+ atom_masks,
146
+ is_grad_func_contain_y,
147
+ batch_indices
148
+ )-> (th.Tensor, th.Tensor, th.Tensor, th.Tensor):
149
+ # NVE Step
150
+ X = X.detach()
151
+ with th.no_grad():
152
+ X = X + V * self.time_step + (Force / (2. * masses)) * self.time_step ** 2 * 9.64853329045427e-3
153
+ V = V + (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 # half-step veloc. update, to avoid saving 2 Forces Tensors.
154
+ with th.set_grad_enabled(self.require_grad):
155
+ X.requires_grad_(self.require_grad)
156
+ Energy = func(X, *func_args, **func_kwargs)
157
+ if is_grad_func_contain_y:
158
+ Force = - grad_func_(X, Energy, *grad_func_args, **grad_func_kwargs) * atom_masks
159
+ else:
160
+ Force = - grad_func_(X, *grad_func_args, **grad_func_kwargs) * atom_masks
161
+
162
+ V = V + (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3
163
+ if batch_indices is not None:
164
+ # Rescaling factor
165
+ alpha = th.sqrt(self.EK_TARGET / self.Ek).unsqueeze(-1).unsqueeze(-1) # (n_batch, 1, 1) | (irregular n_batch, 1, 1)
166
+ V *= alpha.transpose(0, 1)[:, self.batch_scatter, :]
167
+ else:
168
+ # Rescaling factor
169
+ alpha = th.sqrt(self.EK_TARGET / self.Ek).unsqueeze(-1).unsqueeze(-1) # (n_batch, 1, 1) | (irregular n_batch, 1, 1)
170
+ V *= alpha # (n_batch, n_atom, n_dim) * (n_batch, 1, 1)
171
+
172
+ return X, V, Energy, Force
173
+
174
+ def __CSVR(
175
+ self,
176
+ X,
177
+ V,
178
+ Force,
179
+ func,
180
+ grad_func_,
181
+ func_args,
182
+ func_kwargs,
183
+ grad_func_args,
184
+ grad_func_kwargs,
185
+ masses,
186
+ atom_masks,
187
+ is_grad_func_contain_y,
188
+ batch_indices
189
+ )-> (th.Tensor, th.Tensor, th.Tensor, th.Tensor):
190
+ n_batch, n_atom, n_dim = X.shape
191
+ # read thermostat configs
192
+ time_const = self.thermostat_config.get('time_const', 10 * self.time_step) # Unit: fs^-1
193
+ # NVE Step
194
+ X: th.Tensor = X.detach()
195
+ with th.no_grad():
196
+ #X += V * self.time_step + (Force / (2. * masses)) * self.time_step ** 2 * 9.64853329045427e-3
197
+ X.add_(V, alpha=self.time_step)
198
+ X.add_(Force / (2. * masses), alpha=self.time_step ** 2 * 9.64853329045427e-3)
199
+ #V += (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 # half-step veloc. update, to avoid saving 2 Forces Tensors.
200
+ V.add_(Force / (2. * masses), alpha=self.time_step * 9.64853329045427e-3)
201
+ with th.set_grad_enabled(self.require_grad):
202
+ X.requires_grad_(self.require_grad)
203
+ Energy = func(X, *func_args, **func_kwargs)
204
+ if is_grad_func_contain_y:
205
+ Force = - grad_func_(X, Energy, *grad_func_args, **grad_func_kwargs) * atom_masks
206
+ else:
207
+ Force = - grad_func_(X, *grad_func_args, **grad_func_kwargs) * atom_masks
208
+
209
+ #V += (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3
210
+ V.add_(Force / (2. * masses), alpha=self.time_step * 9.64853329045427e-3)
211
+
212
+ # Kinetic Energy Redistribution, Rescale Velocities
213
+ # sigma = th.sqrt((2. * masses * self.T_init / (self.time_step * time_const)) * (0.138064853 / (6.022140857 * 1.602176634 ** 2)))
214
+ sigma = self.time_step ** 0.5
215
+ if batch_indices is not None:
216
+ self.Ekt_vir += (
217
+ (self.EK_TARGET - self.Ek) * self.time_step / time_const
218
+ + 2. * th.sqrt(self.EK_TARGET * self.Ek / ((3 * self.batch_tensor - 3) * time_const))
219
+ * th.normal(0., sigma, size=(len(batch_indices),), device=self.device)
220
+ ) # Unit: eV/Ang
221
+ self.Ekt_vir = th.where(self.Ekt_vir <= 0., 1e-6, self.Ekt_vir)
222
+ # Rescaling factor
223
+ alpha = th.sqrt(self.Ekt_vir / self.Ek).unsqueeze(-1).unsqueeze(-1) # (n_batch, 1, 1) | (irregular n_batch, 1, 1)
224
+ V *= alpha.transpose(0, 1)[:, self.batch_scatter, :]
225
+ else:
226
+ self.Ekt_vir += (
227
+ (self.EK_TARGET - self.Ek) * self.time_step / time_const
228
+ + 2. * th.sqrt(self.EK_TARGET * self.Ek / ((3 * n_atom - 3) * time_const))
229
+ * th.normal(0., sigma, size=(n_batch,), device=self.device)
230
+ ) # Unit: eV/Ang
231
+ # Rescaling factor
232
+ alpha = th.sqrt(self.Ekt_vir / self.Ek).unsqueeze(-1).unsqueeze(-1) # (n_batch, 1, 1) | (irregular n_batch, 1, 1)
233
+ V *= alpha # (n_batch, n_atom, n_dim) * (n_batch, 1, 1)
234
+
235
+ return X, V, Energy, Force
236
+
237
+ def __NoseHoover(
238
+ self,
239
+ X,
240
+ V,
241
+ Force,
242
+ func,
243
+ grad_func_,
244
+ func_args,
245
+ func_kwargs,
246
+ grad_func_args,
247
+ grad_func_kwargs,
248
+ masses,
249
+ atom_masks,
250
+ is_grad_func_contain_y,
251
+ batch_indices
252
+ ) -> (th.Tensor, th.Tensor, th.Tensor, th.Tensor):
253
+ n_batch, n_atom, n_dim = X.shape
254
+ # freedom degree
255
+ n_free = n_dim * (self.batch_tensor - 1) if batch_indices is not None else n_dim * (n_atom - 1)
256
+ # read thermostat config
257
+ smass = self.thermostat_config.get('virt_mass', n_free * 8.617333262145e-5 * self.T_init * 40 ** 2)
258
+ if isinstance(smass, float):
259
+ smass = th.tensor(smass, device=self.device).view(1, 1, 1)
260
+ if batch_indices is not None:
261
+ smass = smass.expand((1, len(batch_indices), 1))
262
+ smass.squeeze_(-1)
263
+ else:
264
+ smass = smass.expand(n_batch, 1, n_dim)
265
+ elif batch_indices is not None:
266
+ smass = smass.unsqueeze(0).expand((1, len(batch_indices)))
267
+ else:
268
+ smass = smass.unsqueeze(-1).expand(n_batch, 1)
269
+ # Main update
270
+ with th.no_grad():
271
+ if batch_indices is not None:
272
+ _iota = self.p_iota[:, self.batch_scatter, None]
273
+ else:
274
+ _iota = self.p_iota
275
+ V += (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 - 0.5 * _iota * V * self.time_step
276
+ X += V * self.time_step
277
+
278
+ with th.set_grad_enabled(self.require_grad):
279
+ X.requires_grad_(self.require_grad)
280
+ _Energy = func(X, *func_args, **func_kwargs)
281
+ if batch_indices is not None:
282
+ Energy = th.sum(_Energy, ).unsqueeze(0)
283
+ else:
284
+ Energy = _Energy
285
+ if is_grad_func_contain_y:
286
+ Force = - grad_func_(X, Energy, *grad_func_args, **grad_func_kwargs) * atom_masks
287
+ else:
288
+ Force = - grad_func_(X, *grad_func_args, **grad_func_kwargs) * atom_masks
289
+
290
+ if batch_indices is not None:
291
+ self.p_iota += 0.5 / smass * (
292
+ th.sum(index_reduce(masses * V ** 2 * 103.642696562621738, self.batch_scatter, 1), -1)
293
+ - n_free * 8.617333262145e-5 * self.T_init
294
+ ) * self.time_step # (1, n_batch)
295
+ _iota = self.p_iota[:, self.batch_scatter, None] # (1, n_batch*n_atom, 1)
296
+ V += (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 - 0.5 * _iota * V * self.time_step
297
+ self.p_iota += 0.5 / smass * (
298
+ th.sum(index_reduce(masses * V ** 2 * 103.642696562621738, self.batch_scatter, 1), -1)
299
+ - n_free * 8.617333262145e-5 * self.T_init
300
+ ) * self.time_step # (1, n_batch)
301
+ else:
302
+ self.p_iota += 0.5 / smass * (
303
+ th.sum(masses * V ** 2 * 103.642696562621738, dim=(-1, -2), keepdim=True) - n_free * 8.617333262145e-5 * self.T_init
304
+ ) * self.time_step # (n_batch, 1, n_dim)
305
+ V += (Force / (2. * masses)) * self.time_step * 9.64853329045427e-3 - 0.5 * self.p_iota * V * self.time_step
306
+ self.p_iota += 0.5 / smass * (
307
+ th.sum(masses * V ** 2 * 103.642696562621738, dim=(-1, -2), keepdim=True) - n_free * 8.617333262145e-5 * self.T_init
308
+ ) * self.time_step # (n_batch, 1, n_dim)
309
+
310
+ return X, V, _Energy, Force
311
+
312
+
313
+ def _updateXV(
314
+ self,
315
+ X,
316
+ V,
317
+ Force,
318
+ func,
319
+ grad_func_,
320
+ func_args,
321
+ func_kwargs,
322
+ grad_func_args,
323
+ grad_func_kwargs,
324
+ masses,
325
+ atom_masks,
326
+ is_grad_func_contain_y,
327
+ batch_indices
328
+ ) -> (th.Tensor, th.Tensor, th.Tensor, th.Tensor):
329
+
330
+ x, v, energy, forces = self.update_scheme(
331
+ X,
332
+ V,
333
+ Force,
334
+ func,
335
+ grad_func_,
336
+ func_args,
337
+ func_kwargs,
338
+ grad_func_args,
339
+ grad_func_kwargs,
340
+ masses,
341
+ atom_masks,
342
+ is_grad_func_contain_y,
343
+ batch_indices
344
+ )
345
+
346
+ return x, v, energy, forces