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,88 @@
1
+ """ Generate new structure by stochastically replace atoms in given structures """
2
+
3
+ # Copyright (c) 2024-2025.7.4, BUCToolkit.
4
+ # Authors: Pu Pengxin, Song Xin
5
+ # Version: 0.9a
6
+ # File: random_replace.py
7
+ # Environment: Python 3.12
8
+
9
+ from typing import Iterable, Dict, Any, List, Literal, Optional, Callable, Sequence, Tuple # noqa: F401
10
+ import time
11
+
12
+ import torch as th
13
+
14
+ import numpy as np
15
+
16
+ from BUCToolkit.utils._Element_info import ATOMIC_SYMBOL
17
+
18
+
19
+ class RandReplace:
20
+ """
21
+
22
+
23
+ """
24
+
25
+ def __init__(self,
26
+ size: int = 1,
27
+ seed: int = None,
28
+ device: str | th.device = 'cpu',
29
+ verbose: int = 2
30
+ ):
31
+ """
32
+
33
+ Args:
34
+ size: the number of structures to generate.
35
+ seed: random seed.
36
+ device: device that program run on.
37
+ verbose: verboseness of output information.
38
+ """
39
+ self.size = size
40
+ self.seed = seed
41
+ self.device = device
42
+ self.verbose = verbose
43
+ pass
44
+
45
+ def run(self,
46
+ X: th.Tensor,
47
+ Element_list: List[List[str]] | List[List[int]],
48
+ replace_element_range: Dict[str | int, Tuple[str | int]],
49
+ replace_numbers: Sequence[int],
50
+ batch_indices: Sequence[int] | th.Tensor | np.ndarray | None = None,
51
+
52
+ ):
53
+ """
54
+
55
+ Args:
56
+ X: Tensor[n_batch, n_atom, 3], the atom coordinates.
57
+ Element_list: List[List[str | int]], the atomic type (element) corresponding to each row of each batch in X.
58
+ replace_element_range: dict((symbol of element to be replaced | index of atom in X) to be replaced : tuple(symbol | atomic number of elements to replace))
59
+ replace_numbers: the number of times that each atom is replaced. It must have the same length of `replace_element_range`.
60
+ batch_indices: Sequence | th.Tensor | np.ndarray | None, the split points for given X, Element_list & V_init, must be 1D integer array_like.
61
+ the format of batch_indices is same as `split_size_or_sections` in torch.split:
62
+ batch_indices = (n1, n2, ..., nN) will split X, Element_list & V_init into N parts, and ith parts has ni atoms. sum(n1, ..., nN) = X.shape[1]
63
+
64
+ Returns:
65
+
66
+ """
67
+ t_main = time.perf_counter()
68
+ n_batch, n_atom, n_dim = X.shape
69
+ # Check batch indices
70
+ if batch_indices is not None:
71
+ if n_batch != 1:
72
+ raise RuntimeError(f'If batch_indices was specified, the 1st dimension of X must be 1 instead of {n_batch}.')
73
+ if isinstance(batch_indices, (th.Tensor, np.ndarray)):
74
+ batch_indices = batch_indices.tolist()
75
+ elif not isinstance(batch_indices, Sequence):
76
+ raise TypeError(f'Invalid type of batch_indices {type(batch_indices)}. '
77
+ f'It must be Sequence[int] | th.Tensor | np.ndarray | None')
78
+ for i in batch_indices: assert isinstance(i, int), f'All elements in batch_indices must be int, but occurred {type(i)}'
79
+
80
+ # Manage Atomic Type & Masses
81
+ repl_atoms = list()
82
+ for _Elem in Element_list:
83
+ repl_atoms.append([__elem if isinstance(__elem, int) else ATOMIC_SYMBOL[__elem] for __elem in _Elem])
84
+ repl_atoms = th.tensor(repl_atoms, dtype=th.int16, device=self.device)
85
+ repl_atoms = repl_atoms.unsqueeze(-1).expand_as(X) # (n_batch, n_atom, n_dim)
86
+
87
+
88
+ pass
@@ -0,0 +1,210 @@
1
+ # Copyright (c) 2025.7.4, BUCToolkit.
2
+ # Authors: Pu Pengxin, Song Xin
3
+ # Version: 0.9a
4
+ # File: MetropolisMC.py
5
+ # Environment: Python 3.12
6
+ import copy
7
+ import math
8
+
9
+ import torch as th
10
+ from ._BaseMC import _BaseMC
11
+ from typing import Callable, Literal
12
+
13
+
14
+ class MMC(_BaseMC):
15
+ """
16
+
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ iter_scheme: Literal['Gaussian', 'Cauchy', 'Uniform'] = 'Gaussian',
22
+ maxiter: int = 100,
23
+ temperature_init: float = 1000.,
24
+ temperature_scheme: Literal['constant', 'linear', 'exponential', 'log', 'fast'] = 'constant',
25
+ temperature_update_freq: int = 1,
26
+ temperature_scheme_param: float | None = None,
27
+ coordinate_update_param: float = 0.2,
28
+ output_file: str | None = None,
29
+ output_structures_per_step: int = 1,
30
+ device: str | th.device = 'cpu',
31
+ verbose: int = 2,
32
+ is_compile: bool = False,
33
+ compile_kwargs: dict | None = None
34
+ ):
35
+ """
36
+
37
+ Args:
38
+ iter_scheme: Algorithm for stochastic X update.
39
+ * `Gaussian`: perturbating X by Gaussian distribution.
40
+ * `Cauchy`: perturbating X by Cauchy distribution.
41
+ * `Uniform`: perturbating X by uniform distribution.
42
+ maxiter: max iteration steps.
43
+ temperature_init: initial temperature.
44
+ temperature_scheme: temperature update scheme.
45
+ * `constant` for a fixed temperature during the whole simulation.
46
+ * `linear` for a linearly changed temperature from `TEMPERATURE_INIT` to `TEMPERATURE_SCHEME_PARAM` during MAXITER steps.
47
+ * `exponential` for temperature (T) changing by T^(i + 1) = `TEMPERATURE_SCHEME_PARAM` * T^(i).
48
+ * `log` for temperature (T) changing by T^(i) = `TEMPERATURE_INIT`/(1. + log(1. + `TEMPERATURE_SCHEME_PARAM` * i)), `i` is the step number.
49
+ * `fast` for temperature (T) changing by T^(i) = `TEMPERATURE_INIT`/(1. + `TEMPERATURE_SCHEME_PARAM` * i), `i` is the step number.
50
+ temperature_scheme_param: to control the temperature scheme. see args `temperature_scheme`.
51
+ temperature_update_freq: update temperature per `temperature_update_freq` step.
52
+ coordinate_update_param: float, the scale parameter for coordinates update. variation for Gaussian/range for Uniform/scale for Cauchy.
53
+ output_file: the path to the binary file that stores trajectories.
54
+ output_structures_per_step: int, output structures per output_structures_per_step steps.
55
+ device: the device that the program runs on.
56
+ verbose: verbosity of output.
57
+ is_compile: whether to use jit to compile integrator or not.
58
+ compile_kwargs: keyword arguments passed to compile. Only work when is_compile is True.
59
+ default value: {'mode':'max-autotune-no-cudagraphs'}
60
+ """
61
+ super().__init__(
62
+ iter_scheme,
63
+ maxiter,
64
+ output_file,
65
+ output_structures_per_step,
66
+ device,
67
+ verbose,
68
+ is_compile,
69
+ compile_kwargs
70
+ )
71
+ self.T_begin = float(temperature_init)
72
+ self.T_now = copy.deepcopy(self.T_begin)
73
+ self.temperature_update_freq = temperature_update_freq
74
+ __T_alpha_default = {
75
+ 'constant': 0.,
76
+ 'linear': 1e-3,
77
+ 'exponential': 0.99,
78
+ 'log': 1.,
79
+ 'fast': 0.2
80
+ }
81
+ self.T_alpha = float(temperature_scheme_param) if temperature_scheme_param is not None else __T_alpha_default[temperature_scheme]
82
+ __T_scheme_dict = {
83
+ 'constant': self.__t_constant,
84
+ 'linear': self.__t_linear,
85
+ 'exponential': self.__t_exponential,
86
+ 'log': self.__t_log,
87
+ 'fast': self.__t_fast
88
+ }
89
+ self.T_update_func = __T_scheme_dict[temperature_scheme]
90
+
91
+ self.coordinate_update_param = coordinate_update_param
92
+ __X_scheme_dict = {
93
+ 'Gaussian': self.__x_Gaussian,
94
+ 'Cauchy': self.__x_Cauchy,
95
+ 'Uniform': self.__x_Uniform,
96
+ }
97
+ self.X_update_func = __X_scheme_dict[iter_scheme]
98
+
99
+
100
+ @staticmethod
101
+ def __t_constant(i):
102
+ pass
103
+
104
+ def __t_linear(self, i):
105
+ self.T_now += (self.T_alpha - self.T_begin)/self.maxiter
106
+
107
+ def __t_exponential(self, i):
108
+ self.T_now *= self.T_alpha
109
+
110
+ def __t_log(self, i):
111
+ self.T_now = self.T_begin/(1. + th.log(1. + self.T_alpha * i))
112
+
113
+ def __t_fast(self, i):
114
+ self.T_now = self.T_begin/(1. + self.T_alpha * i)
115
+
116
+ def __x_Gaussian(self, X: th.Tensor):
117
+ T_tmp = max(10., self.T_now)
118
+ _sigma_short = self.coordinate_update_param * math.sqrt(T_tmp/298.15) / th.sqrt(self.free_degree)
119
+ if self.is_concat_X:
120
+ _std = _sigma_short.reshape(1, -1, 1).index_select(1, self.batch_scatter)
121
+ else:
122
+ _std = _sigma_short.reshape(-1, 1, 1)
123
+ g = th.distributions.Normal(X, _std)
124
+ X_new = g.sample()
125
+ return X_new
126
+
127
+ def __x_Cauchy(self, X: th.Tensor):
128
+ T_tmp = max(10., self.T_now)
129
+ _sigma_short = self.coordinate_update_param * math.sqrt(T_tmp / 298.15) / th.sqrt(self.free_degree)
130
+ if self.is_concat_X:
131
+ _std = _sigma_short.reshape(1, -1, 1).index_select(1, self.batch_scatter)
132
+ else:
133
+ _std = _sigma_short.reshape(-1, 1, 1)
134
+ g = th.distributions.Cauchy(X, _std)
135
+ X_new = g.sample()
136
+ return X_new
137
+
138
+ def __x_Uniform(self, X: th.Tensor):
139
+ T_tmp = max(10., self.T_now)
140
+ _sigma_short = self.coordinate_update_param * math.sqrt(T_tmp / 298.15) / th.sqrt(self.free_degree)
141
+ if self.is_concat_X:
142
+ _std = _sigma_short.reshape(1, -1, 1).index_select(1, self.batch_scatter)
143
+ else:
144
+ _std = _sigma_short.reshape(-1, 1, 1)
145
+ g = th.distributions.Uniform(X - _std, X + _std)
146
+ X_new = g.sample()
147
+ return X_new
148
+
149
+ def _update_X(self, func, func_args, func_kwargs, energies_old, X: th.Tensor):
150
+ """
151
+ Override this method to implement X update algorithm.
152
+ Args:
153
+ X: (n_batch, n_atom, 3), the independent vars X.
154
+ func,
155
+ func_args,
156
+ func_kwargs,
157
+ energies_old,
158
+
159
+ Returns:
160
+ p: th.Tensor, the new update direction of X.
161
+ """
162
+ _X = self.X_update_func(X) # atom_mask will be handled within update_func
163
+ _energy = th.compiler.disable(func)(_X, *func_args, **func_kwargs)
164
+ delta_E = _energy - energies_old # (n_batch, )
165
+ metropolis_mask = th.exp(- delta_E / (8.617333262145e-5 * self.T_now)) # (n_batch, ); Boltzmann constant kB = 8.617333262145e-5 eV/K
166
+ # metropolis_mask.clamp_max_(1.) # no need to cut-off because rand_like always < 1.
167
+ _x_mask = th.rand_like(_energy) < metropolis_mask
168
+ energy_new = th.where(
169
+ _x_mask,
170
+ _energy,
171
+ energies_old
172
+ )
173
+ # delta_E = th.where(
174
+ # _x_mask,
175
+ # delta_E,
176
+ # 0.
177
+ # )
178
+ self.is_accept = _x_mask
179
+ if self.batch_scatter is not None:
180
+ X_new = th.where(
181
+ _x_mask.reshape(1, -1, 1).index_select(1, self.batch_scatter),
182
+ _X,
183
+ X
184
+ )
185
+ else:
186
+ X_new = th.where(
187
+ _x_mask.reshape(-1, 1, 1),
188
+ _X,
189
+ X
190
+ )
191
+
192
+ return energy_new, delta_E, X_new
193
+
194
+ def _update_algo_param(self, i:int, displace: th.Tensor) -> None:
195
+ """
196
+ Override this method to update the parameters of X update algorithm i.e., self.iterform.
197
+ Args:
198
+ i: iteration step now.
199
+ displace: (n_batch, n_atom*3, 1), the displacement of X at this step. displace = step-length * p
200
+
201
+ Returns: None
202
+ """
203
+ # update T
204
+ if i%self.temperature_update_freq == 0:
205
+ self.T_update_func(i/self.temperature_update_freq)
206
+
207
+ pass
208
+
209
+ def initialize_algo_param(self):
210
+ pass