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,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
|