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,432 @@
1
+ """ Indexed operations with given batch_indices """
2
+ # Copyright (c) 2026.1.22, BUCToolkit.
3
+ # Authors: Pu Pengxin, Song Xin
4
+ # Version: 0.9a
5
+ # File: index_ops.py
6
+ # Environment: Python 3.12
7
+
8
+ import warnings
9
+
10
+ import numpy as np
11
+ import torch as th
12
+ from typing import Optional, Literal, Callable, Tuple, Dict
13
+
14
+
15
+ def scatter_reduce(
16
+ src: th.Tensor,
17
+ batch_indices: th.Tensor,
18
+ dim: int = -1,
19
+ ops: Literal['sum', 'prod', 'mean', 'amin', 'amax'] = 'sum',
20
+ init_value: float|int|bool = 0.
21
+ ) -> th.Tensor:
22
+ """
23
+ scatter_reduce method that reduces element in `src` with the same indices in `batch_indices` by `ops`.
24
+ Args:
25
+ src: source tensor
26
+ batch_indices: indices tensor
27
+ dim: reduce dimension
28
+ ops: reduce operation
29
+ init_value: default value of the output tensor that reduced values filled in.
30
+
31
+ Returns:
32
+
33
+ """
34
+ # manage batch_indices
35
+ _batch_indices = batch_indices.clone().to(th.int64)
36
+ if dim < 0:
37
+ dim = src.dim() + dim
38
+ if _batch_indices.dim() == 1:
39
+ for _ in range(0, dim): _batch_indices.unsqueeze_(0)
40
+ for _ in range(_batch_indices.dim(), src.dim()): _batch_indices.unsqueeze_(-1)
41
+ _batch_indices = _batch_indices.expand(src.shape)
42
+ # main
43
+ src_shape = list(src.shape)
44
+ src_shape[dim] = th.max(_batch_indices).item() + 1
45
+ out = th.full(src_shape, init_value, device=src.device)
46
+ out.scatter_reduce_(
47
+ dim,
48
+ _batch_indices,
49
+ src,
50
+ ops
51
+ )
52
+
53
+ return out
54
+
55
+ def index_reduce(
56
+ src: th.Tensor,
57
+ batch_indices: th.Tensor,
58
+ dim: int = 0,
59
+ ops: Literal['prod', 'mean', 'amin', 'amax', 'sum'] = 'sum',
60
+ init_value: float = 0.,
61
+ out_size: int|None = None,
62
+ include_self: bool = True,
63
+ out: th.Tensor|None = None
64
+ ):
65
+ """
66
+ scatter_reduce method that reduces element in `src` with the same indices in `batch_indices` by `ops`.
67
+ Args:
68
+ src: source tensor
69
+ batch_indices: indices tensor of 1D
70
+ dim: reduce dimension
71
+ ops: reduce operation
72
+ init_value: default value of the output tensor that reduced values filled in.
73
+ out_size: size of the output tensor at the given dim. if None, it is equal to `batch_indices.max().item() + 1`
74
+ include_self: whether to let `init_value` really participate in the reduction.
75
+ out: the output tensor. If None, a new one will be returned.
76
+
77
+ Returns:
78
+
79
+ """
80
+ assert batch_indices.dim() == 1, f'Invalid dimension of batch indices: {batch_indices.shape}. It must be 1D.'
81
+ dim = dim if dim >= 0 else src.dim() + dim
82
+ output_size = list(src.shape)
83
+ output_size[dim] = (batch_indices.max().item() + 1) if out_size is None else out_size
84
+
85
+ out = th.full(output_size, init_value, device=src.device, dtype=src.dtype) if out is None else out.fill_(init_value)
86
+ if ops == 'sum' and include_self:
87
+ out.index_add_(dim, batch_indices, src)
88
+ else:
89
+ out.index_reduce_(dim, batch_indices, src, ops, include_self=include_self)
90
+ return out
91
+
92
+ def index_inner_product(
93
+ u: th.Tensor,
94
+ v: th.Tensor,
95
+ dim: int,
96
+ batch_indices: th.Tensor,
97
+ out_size = None,
98
+ ):
99
+ """
100
+ Calculate the inner product of two tensors with given batch_indices .
101
+ Args:
102
+ u: tensor 1, in the dual space.
103
+ v: tensor 2, in the linear space.
104
+ dim: reduce dimension
105
+ batch_indices: indices tensor of 1D
106
+ out_size: size of the output tensor. If None, it is equal to `batch_indices.max().item() + 1`
107
+
108
+ Returns: u @ v with shape (u.shape[: `dim`], batch_number, u.shape[`dim` + 1: ])
109
+
110
+ """
111
+ w = u * v
112
+ w = index_reduce(w, batch_indices, dim, ops='sum', init_value=0., out_size=out_size)
113
+ return w
114
+
115
+ def indices_pairwise_dist(
116
+ X: th.Tensor, # [N, M]
117
+ Y: th.Tensor,
118
+ Qx: th.Tensor, # [N],continuous sample indices in the batch X, 0,0,...,1,1,...,B-1
119
+ Qy: th.Tensor | None = None,
120
+ thres: float | None = None,
121
+ metric: Literal['euclidean', 'dot', 'cosine']|Callable[[th.Tensor, th.Tensor, ...], th.Tensor] = "euclidean", # 'euclidean' | 'dot' | 'cosine'
122
+ metric_kwargs: Dict|None = None,
123
+ relation: Literal['lt', 'gt'] = "gt",
124
+ exclude_diag: bool = True,
125
+ is_symmetric: bool = False,
126
+ return_values: bool = True,
127
+ is_coalesce: bool = True,
128
+ eps: float = 1e-12,
129
+ ):
130
+ """
131
+ Compute pairwise distance between two tensors on only the same indices in Qx and Qy.
132
+ Args:
133
+ X: input tensor 1.
134
+ Y: input tensor 2.
135
+ Qx: Tensor[int64], indices tensor of X.
136
+ Qy: Tensor[int64], indices tensor of Y.
137
+ thres: if not None, only distance greater/less (depends on `relation`) than `thres` will be output. Otherwise, all dist will be output.
138
+ metric: distance calculation method.
139
+ metric_kwargs: the keyword arguments passed to `metric` if `metric` is Callable.
140
+ relation: check `thres` greater (gt) or less (lt) than `thres`.
141
+ exclude_diag: whether to exclude diagonals.
142
+ is_symmetric: whether the distance between `X` and `Y` is symmetric. If True, only lower triangular of dist. mat. will be computed.
143
+ return_values: whether to return both indices and distance or only indices of distance < `thres`.
144
+ is_coalesce: whether to coalesce/resort indices and distance before output.
145
+ eps: epsilon for numerical stability.
146
+
147
+ Returns:
148
+ Tuple[indices: Tensor[int64], batch_tensor of dist: Tensor[int64]] |
149
+ Tuple[indices: Tensor[int64], distance: Tensor, batch_tensor of dist: Tensor[int64]]
150
+
151
+ """
152
+ # check
153
+ assert relation in ("lt", "gt")
154
+ assert X.device == Y.device, f'X and Y must be on the same device, but got {X.device} and {Y.device}.'
155
+ assert X.dtype == Y.dtype, f'X and Y must be on the same dtype, but got {X.dtype} and {Y.dtype}.'
156
+ # initialize
157
+ if metric_kwargs is None:
158
+ metric_kwargs = dict()
159
+ if is_symmetric:
160
+ assert X.shape == Y.shape, f'For symmetric metric, X and Y must have the same shape.'
161
+ if Qy is None:
162
+ Qy = Qx.clone()
163
+ device = X.device
164
+ if thres is None:
165
+ thres = th.inf if relation == "lt" else -th.inf
166
+ # sort & rearrange X, Y, Qx, Qy
167
+ Qx_sorted, Qx_sort_indx = th.sort(Qx)
168
+ Qy_sorted, Qy_sort_indx = th.sort(Qy)
169
+ Xs, Ys = X[Qx_sort_indx], Y[Qy_sort_indx]
170
+
171
+ # calc. indices
172
+ def _starts_sizes(Qs):
173
+ N = Qs.numel()
174
+ ch = th.ones(N, dtype=th.bool, device=Qs.device)
175
+ ch[1:] = (Qs[1:] != Qs[:-1])
176
+ st = th.nonzero(ch, as_tuple=True)[0]
177
+ en = th.cat([st[1:], th.tensor([N], device=Qs.device)])
178
+ sz = (en - st).to(th.long)
179
+ labs = Qs[st]
180
+ return st, sz, labs
181
+
182
+ ptr_x, nx, labs_x = _starts_sizes(Qx_sorted)
183
+ ptr_y, ny, labs_y = _starts_sizes(Qy_sorted)
184
+
185
+ # align two indices
186
+ ix = th.searchsorted(labs_x, labs_y)
187
+ # screen into the intersection (labs_y[pos] == labs_x)
188
+ valid = (ix < labs_x.numel()) & (labs_x[ix] == labs_y)
189
+ labs_y = labs_y[valid]
190
+ ptr_y, ny = ptr_y[valid], ny[valid]
191
+ ix = ix[valid]
192
+ ptr_x, nx = ptr_x[ix], nx[ix] # ptr_x that aligned with y
193
+ if labs_y.numel() == 0:
194
+ empty = th.empty(2, 0, dtype=th.long, device=device)
195
+ Qe = th.empty(0, dtype=Qx.dtype, device=device)
196
+ return (empty, th.empty(0, device=device), Qe) if return_values else (empty, Qe)
197
+
198
+ # vectorized enumerate pairwise nx_i * ny_i, and mapping to Xs and Ys
199
+ pairs_per_img = nx * ny # [B]
200
+ total_pairs = int(pairs_per_img.sum().item())
201
+
202
+ pair_ptr = th.cumsum(pairs_per_img, 0) - pairs_per_img # [B]
203
+ pair_off_ex = th.repeat_interleave(pair_ptr, pairs_per_img) # [∑ nx_i*ny_i]
204
+ ny_ex = th.repeat_interleave(ny, pairs_per_img) # [∑ nx_i*ny_i]
205
+ k = th.arange(total_pairs, device=device) - pair_off_ex
206
+
207
+ i_loc = th.div(k, ny_ex, rounding_mode="floor") # [∑ nx_i*ny_i]
208
+ j_loc = (k % ny_ex)
209
+
210
+ xs_ex = th.repeat_interleave(ptr_x, pairs_per_img) # [∑ nx_i*ny_i]
211
+ ys_ex = th.repeat_interleave(ptr_y, pairs_per_img)
212
+
213
+ row_s = xs_ex + i_loc # indices of Xs
214
+ col_s = ys_ex + j_loc # indices of Ys
215
+ Qe_s = th.repeat_interleave(labs_y, pairs_per_img) # indices of pairwise-dist, i.e., the edge.
216
+
217
+ # Symmetric reduce
218
+ if is_symmetric:
219
+ if not th.all(nx == ny):
220
+ raise ValueError(f"Symmetric requires Qx, Qy have the same shape in each sample, but got {nx} and {ny}")
221
+ mask = th.ones(total_pairs, dtype=th.bool, device=device)
222
+ mask &= (i_loc <= j_loc) # select only lower triangular parts.
223
+ if exclude_diag:
224
+ mask &= (i_loc != j_loc) # remove diag.
225
+ row_s = row_s[mask]
226
+ col_s = col_s[mask]
227
+ Qe_s = Qe_s[mask]
228
+
229
+ if row_s.numel() == 0:
230
+ empty = th.empty(2, 0, dtype=th.long, device=device)
231
+ Qe = th.empty(0, dtype=Qx.dtype, device=device)
232
+ return (empty, th.empty(0, device=device), Qe) if return_values else (empty, Qe)
233
+
234
+ # Main Calc. Dist.
235
+ xr, yc = Xs[row_s], Ys[col_s] # [E, M], x_row, y_column
236
+
237
+ if isinstance(metric, Callable):
238
+ val = metric(xr, yc, **metric_kwargs)
239
+ elif metric == "euclidean":
240
+ n2x = th.linalg.vecdot(Xs, Xs)
241
+ n2y = th.linalg.vecdot(Ys, Ys)
242
+ dot = th.linalg.vecdot(xr, yc)
243
+ val = n2x[row_s] + n2y[col_s] - 2.0 * dot # [E]
244
+ thres **= 2 if th.isfinite(th.tensor(thres)) else 1
245
+ elif metric == "dot":
246
+ val = th.linalg.vecdot(xr, yc)
247
+ elif metric == "cosine":
248
+ val = th.linalg.vecdot(xr, yc) / (th.linalg.vector_norm(xr, dim=-1) * th.linalg.vector_norm(yc, dim=-1) + eps)
249
+ else:
250
+ raise ValueError(f"Unknown metric {metric}.")
251
+
252
+ is_selected = (th.abs(val) < thres) if relation == "lt" else (th.abs(val) > thres)
253
+ row_s, col_s, Qe = row_s[is_selected], col_s[is_selected], Qe_s[is_selected]
254
+
255
+ # remapping to unsorted original X, Y
256
+ row = Qx_sort_indx[row_s]
257
+ col = Qy_sort_indx[col_s]
258
+ indices = th.stack([row, col], dim=0)
259
+ if return_values:
260
+ out_vals: th.Tensor = val[is_selected].clone()
261
+ if metric == "euclidean": out_vals.add_(eps**2).sqrt_()
262
+ else:
263
+ out_vals = val # only a placeholder
264
+ if is_symmetric: # appending upper diagonal part
265
+ nd_mask = (row != col) # non-diagonal
266
+ if th.any(nd_mask):
267
+ upper_diag = th.stack([col[nd_mask], row[nd_mask]], dim=0)
268
+ indices = th.cat([indices, upper_diag], dim=1)
269
+ if return_values:
270
+ out_vals = th.cat([out_vals, out_vals[nd_mask]], dim=0)
271
+ Qe = th.cat([Qe, Qe[nd_mask]], dim=0)
272
+
273
+ # coalesce & rearrange indices
274
+ if is_coalesce:
275
+ coalesce_indx = th.argsort(indices[0], dim=0)
276
+ indices = indices[:, coalesce_indx]
277
+ Qe = Qe[coalesce_indx]
278
+ out_vals = out_vals[coalesce_indx] if return_values else out_vals
279
+
280
+ if return_values:
281
+ return indices, out_vals, Qe
282
+ return indices, Qe
283
+
284
+
285
+ def inner_product_for_halfspace(u: th.Tensor, v: th.Tensor, volumes: th.Tensor|float = 1.) -> th.Tensor:
286
+ """
287
+
288
+ Args:
289
+ u:
290
+ v:
291
+ volumes:
292
+
293
+ Returns:
294
+
295
+ """
296
+ u0 = u[:, 0] # (B,)
297
+ v0 = v[:, 0]
298
+ u_tail = u[:, 1:] # (B, nG-1)
299
+ v_tail = v[:, 1:] # (B, nG-1)
300
+
301
+ p = th.baddbmm(
302
+ th.empty_like(u[:,0]).unsqueeze(-1).unsqueeze(-1),
303
+ u_tail.conj().unsqueeze(1).contiguous(), # (B, 1, n)
304
+ v_tail.unsqueeze(-1).contiguous(), # (B, n, 1)
305
+ beta=0.0,
306
+ alpha=2.0,
307
+ ).squeeze(-2, -1) # (B,)
308
+
309
+ # adding gamma point
310
+ p.add_(u0.conj() * v0) # (B,)
311
+ p = p.real
312
+ if isinstance(volumes, th.Tensor) or volumes != 1.0:
313
+ p.mul_(volumes)
314
+
315
+ return p
316
+
317
+
318
+ def standardize_cell(cell: th.Tensor) -> Tuple[th.Tensor, th.Tensor]:
319
+ """
320
+ Rotation a cell vector matrix into a standardized form where ay = az = 0, bz = 0, cz > 0.
321
+ Args:
322
+ cell: Tensor[B, 3, 3], batch of cell vectors.
323
+
324
+ Returns:
325
+ L: Tensor[B, 3, 3], standardized cell vector matrix with lower triangular forms.
326
+ Q: Tensor[B, 3, 3], the corresponding rotation transform
327
+
328
+ """
329
+ # determined the cell is a right-hand system
330
+ _cell_std = cell.clone()
331
+ vol = th.linalg.det(_cell_std).unsqueeze(-1) # (B, 1)
332
+ _cell_std[:, 2, :] = th.where(vol < 0., - cell[:, 2, :], cell[:, 2, :])
333
+ ill_mask = th.abs(vol) < 1e-5
334
+ if ill_mask.any():
335
+ warnings.warn(f'A cell very close to low-dimensional system was detected in [{th.argwhere(ill_mask)}]-th samples.')
336
+ # QR factorization
337
+ Q, R = th.linalg.qr(_cell_std.mT.contiguous())
338
+ _cell_std = R.mT.contiguous()
339
+ _rot_op = Q.mT.contiguous()
340
+ # put diag of R.mT positive
341
+ cell_diag = th.diagonal(_cell_std, dim1=-2, dim2=-1) # (B, 3)
342
+ neg_diag_mask = (cell_diag < 0.) # (B, 3)
343
+ _rot_op = th.where(neg_diag_mask.unsqueeze(-1), -_rot_op, _rot_op) # D Q^T
344
+ _cell_std = th.where(neg_diag_mask.unsqueeze(-2), -_cell_std, _cell_std) # R^T D
345
+
346
+ return _cell_std, _rot_op
347
+
348
+ def generate_half_space_grids(n_grids: Tuple[int, int, int], device: str = 'cpu') -> th.Tensor:
349
+ """
350
+ Generate a half space grid of given `n_grids`. where: h > 0 or (h = 0 and k > 0) or ((h=0 and k=0) or l>=0)
351
+ * Conventionally, position 0 is set to the gamma point [0, 0, 0] *
352
+ Args:
353
+ n_grids: the number of grids at directions x, y, z.
354
+ device: the device on which the grid is to be generated.
355
+
356
+ Returns: (N_grids, 3), grids
357
+
358
+ """
359
+ N = [abs(int(_)) + (_ + 1) % 2 for _ in n_grids] # to ensure an odd positive integer grid number.
360
+
361
+ # check the longest axis
362
+ cut_axis = max(range(3), key=lambda i: N[i])
363
+ perm = [cut_axis] + [i for i in range(3) if i != cut_axis]
364
+ N_h, N_k, N_l = (N[perm[0]], N[perm[1]], N[perm[2]])
365
+
366
+ # centered int indices [-Nh//2, ..., 0, ..., -Nh//2+Nh-1]
367
+ h_vals = th.arange(0, -(N_h // 2) + N_h, device=device, dtype=th.int64)
368
+ k_vals = th.arange(-(N_k // 2), -(N_k // 2) + N_k, device=device, dtype=th.int64)
369
+ l_vals = th.arange(-(N_l // 2), -(N_l // 2) + N_l, device=device, dtype=th.int64)
370
+
371
+ idx_parts = []
372
+
373
+ # ---------- 1) h > 0, k,l \in Z ----------
374
+ h_pos = h_vals[1:]
375
+ if h_pos.numel() > 0:
376
+ N_h_pos = h_pos.numel()
377
+ N_k_all, N_l_all = N_k, N_l
378
+
379
+ hh1 = h_pos.view(-1, 1, 1).expand(-1, N_k_all, N_l_all)
380
+ kk1 = k_vals.view(1, -1, 1).expand(N_h_pos, -1, N_l_all)
381
+ ll1 = l_vals.view(1, 1, -1).expand(N_h_pos, N_k_all, -1)
382
+
383
+ idx1 = th.empty((N_h_pos * N_k_all * N_l_all, 3), device=device, dtype=th.int64)
384
+ idx1[:, 0] = hh1.reshape(-1)
385
+ idx1[:, 1] = kk1.reshape(-1)
386
+ idx1[:, 2] = ll1.reshape(-1)
387
+ idx_parts.append(idx1)
388
+
389
+ # ---------- 2) h = 0, k > 0, l \in Z ----------
390
+ k_pos = k_vals[k_vals > 0]
391
+ if k_pos.numel() > 0:
392
+ N_k_pos = k_pos.numel()
393
+ N_l_all = N_l
394
+
395
+ hh2 = th.zeros((N_k_pos, N_l_all), device=device, dtype=th.int64)
396
+ kk2 = k_pos.view(-1, 1).expand(-1, N_l_all)
397
+ ll2 = l_vals.view(1, -1).expand(N_k_pos, -1)
398
+
399
+ idx2 = th.empty((N_k_pos * N_l_all, 3), device=device, dtype=th.int64)
400
+ idx2[:, 0] = hh2.reshape(-1)
401
+ idx2[:, 1] = kk2.reshape(-1)
402
+ idx2[:, 2] = ll2.reshape(-1)
403
+ idx_parts.append(idx2)
404
+
405
+ # ---------- 3) h = 0, k = 0, l > 0 ----------
406
+ l_pos = l_vals[l_vals > 0]
407
+ if l_pos.numel() > 0:
408
+ idx3 = th.empty((l_pos.numel(), 3), device=device, dtype=th.int64)
409
+ idx3[:, 0] = 0
410
+ idx3[:, 1] = 0
411
+ idx3[:, 2] = l_pos
412
+ idx_parts.append(idx3)
413
+
414
+ # all non-zero (h,k,l)
415
+ if len(idx_parts) > 0:
416
+ indices_pos = th.cat(idx_parts, dim=0) # (nG_half, 3)
417
+ else:
418
+ # only G=0
419
+ indices_pos = th.empty((0, 3), device=device, dtype=th.int64)
420
+
421
+ # re-permute to original order
422
+ n_pos = indices_pos.shape[0]
423
+ indices_orig = th.empty_like(indices_pos)
424
+ indices_orig[:, perm[0]] = indices_pos[:, 0]
425
+ indices_orig[:, perm[1]] = indices_pos[:, 1]
426
+ indices_orig[:, perm[2]] = indices_pos[:, 2]
427
+ # manually set the 1st element to [0,0,0]
428
+ G_indices = th.empty((n_pos + 1, 3), device=device, dtype=th.int64)
429
+ G_indices[0, :] = 0.0
430
+ G_indices[1:, :] = indices_orig
431
+
432
+ return G_indices
@@ -0,0 +1,137 @@
1
+ """ Setup logger for all modules """
2
+ # Copyright (c) 2026.3.26, BUCToolkit.
3
+ # Authors: Pu Pengxin, Song Xin
4
+ # Version: 0.9a
5
+ # File: setup_loggers.py
6
+ # Environment: Python 3.12
7
+
8
+ import logging
9
+ import sys
10
+ import warnings
11
+ import os
12
+ from typing import Any
13
+
14
+ from BUCToolkit.BatchStructures.StructuresIO import structures_io_dumper
15
+
16
+
17
+ def clear_all_handlers(logger: logging.Logger):
18
+ """
19
+ Clear all handlers of this current logger and its all ancestors until one does not propagate.
20
+
21
+ Args:
22
+ logger: the input logger instance. If it is not a logging.Logger, nothing is done.
23
+ Returns:
24
+ the logger instance
25
+ """
26
+ current = logger
27
+ while current:
28
+ for _hdl in list(current.handlers):
29
+ current.removeHandler(_hdl)
30
+ try:
31
+ _hdl.close()
32
+ except Exception as ehdl:
33
+ warnings.warn(f'Failed to close handler {_hdl}: {ehdl}', RuntimeWarning)
34
+ # if propagate=False,stop checking
35
+ if not current.propagate:
36
+ break
37
+ current = current.parent
38
+
39
+ def has_any_handler(logger: logging.Logger) -> bool:
40
+ """
41
+ Check whether the current logger and its all ancestor loggers have any handlers.
42
+ Args:
43
+ logger: the input logger instance. If it is not a logging.Logger, False is returned.
44
+
45
+ Returns: bool, the logger has any handlers.
46
+
47
+ """
48
+ if not isinstance(logger, logging.Logger):
49
+ return False
50
+ has_handler = False
51
+ current = logger
52
+ while current:
53
+ if current.handlers:
54
+ has_handler = True
55
+ break
56
+ # if propagate=False,stop checking
57
+ if not current.propagate:
58
+ break
59
+ current = current.parent
60
+
61
+ return has_handler
62
+
63
+
64
+ class BaseIO:
65
+ """
66
+ Base class for logs of all classes.
67
+ """
68
+
69
+ def __init__(self, output_file: str|None = None) -> None:
70
+ self.logger = None
71
+ self.log_handler = None
72
+ self.output_file = str(output_file) if output_file is not None else None
73
+ self.dumper = structures_io_dumper(
74
+ path=self.output_file,
75
+ mode='x',
76
+ )
77
+
78
+ def init_logger(self, logger_name: str):
79
+ # logging
80
+ if self.logger is None:
81
+ # cut off propagation
82
+ supreme_name = logger_name.split('.')[0]
83
+ top_logger = logging.getLogger(supreme_name)
84
+ top_logger.propagate = False
85
+ # set true logger
86
+ self.logger = logging.getLogger(logger_name)
87
+ self.logger.setLevel(logging.INFO)
88
+ formatter = logging.Formatter('%(message)s')
89
+ if not has_any_handler(self.logger):
90
+ self.log_handler = logging.StreamHandler(sys.stdout)
91
+ self.log_handler.setLevel(logging.INFO)
92
+ self.log_handler.setFormatter(formatter)
93
+ top_logger.addHandler(self.log_handler)
94
+ else:
95
+ warnings.warn('Logger has already initialized. Nothing will be done.', RuntimeWarning)
96
+
97
+ def reset_logger_handler(self, handler: str|logging.StreamHandler|logging.FileHandler):
98
+ """
99
+ Clear all logging handlers including current logger and its ancestors, and reset one.
100
+ Args:
101
+ handler: the new handler.
102
+
103
+ Returns:
104
+
105
+ """
106
+ clear_all_handlers(self.logger)
107
+ # redirect to supreme logger
108
+ top_logger = self.logger
109
+ while top_logger.parent and top_logger.propagate:
110
+ top_logger = top_logger.parent
111
+
112
+ formatter = logging.Formatter('%(message)s')
113
+ if isinstance(handler, logging.Handler):
114
+ self.log_handler = handler
115
+ elif isinstance(handler, str):
116
+ output_path = os.path.dirname(handler)
117
+ # check whether path exists
118
+ if not os.path.isdir(output_path): os.makedirs(output_path)
119
+ # set log handler
120
+ self.log_handler = logging.FileHandler(handler, 'w', delay=True)
121
+ else:
122
+ raise TypeError("handler must be a string path or a logging.Handler instance")
123
+
124
+ self.log_handler.setLevel(logging.INFO)
125
+ self.log_handler.setFormatter(formatter)
126
+ top_logger.addHandler(self.log_handler)
127
+
128
+ def reset_dumper(self, dumper: Any) -> None:
129
+ if self.output_file is not None:
130
+ self.dumper.close()
131
+ del self.dumper
132
+ self.dumper = dumper
133
+ else:
134
+ self.logger.error(
135
+ "ERROR: No output file specified. Hence, resetting dumper is meaningless.\n"
136
+ "'reset_dumper': Operation REFUSED."
137
+ )
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: BUCToolkit
3
+ Version: 1.0b0
4
+ Summary: Batch-upscaled Catalysis Toolkit (BUCToolkit), which can apply PyTorch-based deep-learning model to perform training, predictions, batched structure optimization, batched molecular dynamics with/without constraints, and batched Monte Carlo simulations. Various tools for handling catalyst structure files are also included.
5
+ Home-page: https://github.com/TrinitroCat/BUCToolkit
6
+ Author: Pu Pengxin, Song xin
7
+ Author-email:
8
+ License: MIT
9
+ Requires-Python: >= 3.11
10
+ License-File: LICENSE
11
+ Requires-Dist: joblib>=1.4.2
12
+ Requires-Dist: numpy>=1.26.4
13
+ Requires-Dist: PyYAML>=6.0.1
14
+ Requires-Dist: torch>=2.4.1
15
+ Provides-Extra: dgl
16
+ Requires-Dist: dgl; extra == "dgl"
17
+ Provides-Extra: pyg
18
+ Requires-Dist: torch_geometric>=2.6.1; extra == "pyg"
19
+ Provides-Extra: prompt-toolkit
20
+ Requires-Dist: prompt-toolkit>=3.0.43; extra == "prompt-toolkit"
21
+ Dynamic: author
22
+ Dynamic: home-page
23
+ Dynamic: license
24
+ Dynamic: license-file
25
+ Dynamic: provides-extra
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary
@@ -0,0 +1,88 @@
1
+ BUCToolkit/StandardTemplate.py,sha256=kk0-8lELWLeXbBeSPoqgUzhSTG3GetDTjjjWae8s18A,14028
2
+ BUCToolkit/__init__.py,sha256=TJoGfkDSSLhT82miEG7lZmjeDDIdluu4XzJVquJmz-0,969
3
+ BUCToolkit/_version.py,sha256=4CEfQ0nGY9ug2lulGd9iwNCj3y4F3T8spLR6bilyNAY,159
4
+ BUCToolkit/io.py,sha256=rlQpRBX5NmyYyETioOzqa2fEnil_EA_EZqbjReddzH4,810
5
+ BUCToolkit/BatchGenerate/CheckStructures.py,sha256=kvjWipwJ33owshUUKmW0lKXTolsiyh9vARs4Epm_trE,8427
6
+ BUCToolkit/BatchGenerate/__init__.py,sha256=jSXI-67YFF7iu545d6IbC1agnYIJeCQ6sEarL7-sxfo,144
7
+ BUCToolkit/BatchGenerate/coords_interp.py,sha256=fQHsQfJEd4J73sscp3LiddgcmbhvgrXWtu4Nz66r4Mw,5183
8
+ BUCToolkit/BatchGenerate/initial_ads_structures.py,sha256=QYdt3O0oU6bfZE1yombqq2NALt-4wEV_aGPtM_QbN_0,14415
9
+ BUCToolkit/BatchGenerate/random_replace.py,sha256=5_oeVLrdbkm_vXWs1HTPNrNxkcLx5357eLy-XUY6v1c,3497
10
+ BUCToolkit/BatchMC/MetropolisMC.py,sha256=B48hKYNYCucSGwJhBexFdl9Wu8RT2Gsrr7Pj-6bw-30,8170
11
+ BUCToolkit/BatchMC/_BaseMC.py,sha256=-9izjbTi30hWEQ4aLGcAIa5AuBmwlnG2lxsBTr-6WqM,38495
12
+ BUCToolkit/BatchMC/__init__.py,sha256=MN9NTbJz-K7lT6xpRJ1W583kxiwFRz7iLUzbC0ti4RA,187
13
+ BUCToolkit/BatchMD/BiasedMD.py,sha256=-9i3XN9CEFyUZa6JNl9mxVj2bVb-_PJ6eO6GBTPwkUU,10050
14
+ BUCToolkit/BatchMD/NVE.py,sha256=tjZrLFOmKUhkDFYmUaCqP0qYyy8kTbdAscHCK8QvR_8,3445
15
+ BUCToolkit/BatchMD/NVT.py,sha256=49uETm96AA1b4T_JymBMJmK4qoSTgXbikX69AY8dAts,21166
16
+ BUCToolkit/BatchMD/_BaseMD.py,sha256=ZOzePJmbC484XsBxTPCSuqzxIE-sGy9dYKqucng3YRU,56911
17
+ BUCToolkit/BatchMD/__init__.py,sha256=iVpmxNuIbd-tBYZe13DtjYTIevOy_Q0nDI817eXVjO4,377
18
+ BUCToolkit/BatchMD/constrained_md/BiasedMD.py,sha256=qgktB1Z_a7AEjQtPJn1GGeB9hDzsZe08UtHqr4FbatM,9955
19
+ BUCToolkit/BatchMD/constrained_md/ConstrNVE.py,sha256=szKte1SPFUZBZRmvlFlHFclEQRlhAhTbowOliag_eoY,6964
20
+ BUCToolkit/BatchMD/constrained_md/ConstrNVT.py,sha256=MV6B9gy3ai3Hbrs0qvP9ZgGxXUwOFEAdElwXx6aBAWA,19494
21
+ BUCToolkit/BatchMD/constrained_md/NVE.py,sha256=o6QBhAAVx-3wOUI5dOmQz2kKnhAQhwxM9UuqAfnmAQ8,3087
22
+ BUCToolkit/BatchMD/constrained_md/NVT.py,sha256=9n6VEy4mjHNMLrKLvaJiqIfx5xAC-8MSRRHC-_4qN34,15160
23
+ BUCToolkit/BatchMD/constrained_md/_BaseMD.py,sha256=Ibu5YbPRgEcNKBKYfKN9icblNrI_m8eie1wk0xiEX1A,20665
24
+ BUCToolkit/BatchMD/constrained_md/_ConstrBaseMD.py,sha256=mlLsrezk4u8ImgzTCF1EeX-Pq-b5586MyaGhKGcMf-c,27592
25
+ BUCToolkit/BatchMD/constrained_md/__init__.py,sha256=--wBxaTL2jeFodU2WVFfYD7P2XQOYJtTxmV93CTi1O4,402
26
+ BUCToolkit/BatchOptim/_BaseOpt.py,sha256=saO1uicOX5cHrV1m5-6m5xb8vjnmEhifLjaeT59tFbY,34694
27
+ BUCToolkit/BatchOptim/__init__.py,sha256=CCSaaM3yYx2TP0QnOlQT4fubtV9Zb0Vbcskgxk1eupQ,529
28
+ BUCToolkit/BatchOptim/frequency.py,sha256=dzgD2-bt4vUKY8fTn1yiSXyZNsifm2Hp9JtHLFlWqAY,19376
29
+ BUCToolkit/BatchOptim/TS/CI_NEB.py,sha256=xIpVZBX0VmrvTCyzHVAFOX2DuLCpjU5Yo68duvRemUo,11994
30
+ BUCToolkit/BatchOptim/TS/Dimer.py,sha256=c_eSXVJaD6enWpfZyEPLe0w85XZ3mFnUidujr8OEAGU,42754
31
+ BUCToolkit/BatchOptim/TS/__init__.py,sha256=JgrkjZOnd30e8cunMAQYWKIygY0PWUeUMwfn8HF-8tQ,195
32
+ BUCToolkit/BatchOptim/_utils/__init__.py,sha256=jSXI-67YFF7iu545d6IbC1agnYIJeCQ6sEarL7-sxfo,144
33
+ BUCToolkit/BatchOptim/_utils/_line_search.py,sha256=dMvsU7UmxYY5HfbGqbxJOhFx2zWOqiT4u-lgXhc46Ro,39295
34
+ BUCToolkit/BatchOptim/_utils/_warnings.py,sha256=1Z-y3LbElwFF1mEQ75R8RWDQz5lTBlug_eDf7jzvzuc,261
35
+ BUCToolkit/BatchOptim/minimize/CG.py,sha256=-70KyQroR6rHujNrigWujNbKW5Jq_i13M-lmxjF8Mc0,8876
36
+ BUCToolkit/BatchOptim/minimize/FIRE.py,sha256=l8jyhP58G0Q5G1XHtRuphKtsVx9X9zIQuBI_r4h8Yvs,13070
37
+ BUCToolkit/BatchOptim/minimize/QN.py,sha256=GkmIU7Wvs_zjS4n24ISamjSXq3uIx_WP48TosazB6NQ,8309
38
+ BUCToolkit/BatchOptim/minimize/__init__.py,sha256=39lqioP-wQpBkpOZV0ZckyvwGzZ3C-jIkbYwAYZgcBM,325
39
+ BUCToolkit/BatchStructures/BatchStructuresBase.py,sha256=iN6bJzgYY64ShL4H4ou8V6capvUJ19phvqQUJF8kwuA,105716
40
+ BUCToolkit/BatchStructures/StructuresIO.py,sha256=JLCntgMC6te_g76caJBAGZzueoB0wXJxayck_r9E7hY,58804
41
+ BUCToolkit/BatchStructures/__init__.py,sha256=cBRJ7r7n2oqp6T91bGt9PkuMgX6L1QNUmsUaHN-Jqjs,397
42
+ BUCToolkit/Preprocessing/__init__.py,sha256=_8paUhW8u-B1PwNXyM5eniBu2KamjYTDHWK_MNmopcc,145
43
+ BUCToolkit/Preprocessing/load_files.py,sha256=kqPxa9Q4nLugNkymQ2HNmN0-iirOShZydC-jus2IzmI,77101
44
+ BUCToolkit/Preprocessing/preprocessing.py,sha256=wsB9ehxIGgKVn0NFgfQOSc3_ldS3b0tiia4vBXP3kJw,39534
45
+ BUCToolkit/Preprocessing/sampling.py,sha256=2eEAYJP2j_Yf5YVLWJ8H0LlDsyxsPbodfsvt-x5SnPs,11836
46
+ BUCToolkit/Preprocessing/supercells.py,sha256=86cfN9VLBM6AgAq_r8nlfuujs2rR_fN2JcbQdlt32_o,6006
47
+ BUCToolkit/Preprocessing/write_files.py,sha256=7XduHCBFGe6KuDo9WPNE2fWaREumemqWd9QWkeKlKFo,23012
48
+ BUCToolkit/api/ConstrainedMolecularDynamics.py,sha256=IW7N6tI30U_Xj7MkpilqIJwCN74xCDnGjxcoHWAaN4o,16618
49
+ BUCToolkit/api/DataLoaders.py,sha256=dLkdgih8RLVMJ0kpYbk-KZWJm6718Ux3_i-FcglYh_M,13057
50
+ BUCToolkit/api/Losses.py,sha256=qoofPFwe_lpc1hLaDbpAlX-v8x2r94g6azJGsq6lv3Y,5813
51
+ BUCToolkit/api/Metrics.py,sha256=NStR94teQfWQNPEbEwD43Ov91Uj55V2OL_MKIdkxGw0,2565
52
+ BUCToolkit/api/ModelOptims.py,sha256=4U1fYIm8LkjY6ti6kdRTnqhQ-M7_4EO6HU6jUG1NPQw,12442
53
+ BUCToolkit/api/MolecularDynamics.py,sha256=NN-h9UTJyShW3nvDjfCldQqOTRl185G-MT-MLe2PmE4,12787
54
+ BUCToolkit/api/MonteCarlo.py,sha256=CgnpNwLCwWQ_vp3h1cjj8CABTKnW_zm81B1cZOseRYM,12805
55
+ BUCToolkit/api/NEB.py,sha256=PHl0qbJl_iKKO5q2MqAaH1e_H3yd7WwdnisSKjqKGiE,15325
56
+ BUCToolkit/api/Predictor.py,sha256=skEoPLnGPOeDREBYjRzNy9ZLZQUVtGUuydJT6p5z9Fw,11836
57
+ BUCToolkit/api/StructureOptimization.py,sha256=kcxcJrHbuIF068oKxvRKfoCtxP0ZoslUK8_f497ZTYk,25131
58
+ BUCToolkit/api/Trainer.py,sha256=dxWUnGEZ-imdJTw0hV6ATASbU5q3T_F7pDaHVZEigBE,35442
59
+ BUCToolkit/api/VibrationAnalysis.py,sha256=o2t_TEyQB4CmPNFvSWMzniKE1NIUQDx-z_jrLQITtPs,13438
60
+ BUCToolkit/api/__init__.py,sha256=IE9cVu0Qf4sN1wckSoRo2JbxBPTXY8xQv3eHFwTIbiY,1006
61
+ BUCToolkit/api/_io.py,sha256=FMwaAh5ObYE9PlpTSP1f1UtiW9GLifg0g74PCCs4uJA,41158
62
+ BUCToolkit/cli/__init__.py,sha256=sy9uD5-x74DG21f7EEzWjJbWxN8vcqCIZ6WYF_a7nFU,425
63
+ BUCToolkit/cli/cli.py,sha256=9azg2EJKeKCmTTaU61iDXSfJgZjFz7I874SEm28s-OI,40523
64
+ BUCToolkit/cli/input_stub.py,sha256=p8PJFKfyvyB0r9jXkWLim3x0Y1noUyA5TFJQeEM9qic,9333
65
+ BUCToolkit/cli/main.py,sha256=xP3OKaxwfw76n2V_AWXwb4Y8TTPDrJqOTsHJOGjvTig,13948
66
+ BUCToolkit/cli/print_logo.py,sha256=TmMfeRtvIRDqrUQcxZe1FMpUlExKfUfsR6VhDqM982Q,4046
67
+ BUCToolkit/cli/template_model.py,sha256=x8PwATqv1VkeTwzOa5HYowJMLIx142t0GVZeBN07Axw,207
68
+ BUCToolkit/utils/AtomSelector.py,sha256=O8aLqCHy3tKNXsjSOYM9noIw0fAakug8LnrYL_KGxmc,2422
69
+ BUCToolkit/utils/AtomicNumber2Properties.py,sha256=T3LqXfw8TMdnqFu-9rgL1_4x5HsSeutgTq_pLkxzwI0,1031
70
+ BUCToolkit/utils/ElemListReduce.py,sha256=m3ZyulRtLRjMyb5RHrUUFT7_gVfqpun63Smq1EJXU4c,1715
71
+ BUCToolkit/utils/Ewald.py,sha256=DAWBJprknpCdOSZ-g72DwxXrGCER5Y3huilbusrzT88,9371
72
+ BUCToolkit/utils/IrregularTensorReformat.py,sha256=sgQAPI98NeGNVajojD1BWDQnYDQGtd9YrVDzEQWtDiI,1989
73
+ BUCToolkit/utils/_CheckModules.py,sha256=5i-GiY9qFmVvPl1QzDTMBVP-NvzZM7Ivlq3ll1ea_cI,968
74
+ BUCToolkit/utils/_Element_info.py,sha256=7bLGi7EA0Hn1OTz1iV_ZFTasOuebbgbFEuVhQsMtU4E,8086
75
+ BUCToolkit/utils/__init__.py,sha256=39qYNYXTbkva4oGb5vsCI6Squ-WX9o0NitJzrhdFXD0,741
76
+ BUCToolkit/utils/_para_flatt_list.py,sha256=vyQaqd1ghbo1oAW2Cq1Nuv5ceUYYiwaPbukeBBDLqSk,3503
77
+ BUCToolkit/utils/_print_formatter.py,sha256=dxG-yhyQPnpl6xa2OmniBXKfbYn5lci589YZYhGy6Bo,1507
78
+ BUCToolkit/utils/check_structures.py,sha256=0h4MpQsbANdRR2whLyk1GNi1KDoef_tisHWBxbU0UDk,9242
79
+ BUCToolkit/utils/function_utils.py,sha256=u8f5S8_odS0UPKCQK_4pniaY0lyenQFpYC6UrQTShFA,4530
80
+ BUCToolkit/utils/grad_functions.py,sha256=TAuaT6tIkjiS412_jknLiR9wb9uWSKl_FCKKs_hhmuw,2302
81
+ BUCToolkit/utils/index_ops.py,sha256=BlYo9UdN9GjLsEflI5KYqwmEjrkpl3MNWpLFLZuVn9Y,16292
82
+ BUCToolkit/utils/setup_loggers.py,sha256=F0Y-vPBFNw3ShHbeX0wb2Cmy-TcSfghBNutfOVwy_54,4579
83
+ buctoolkit-1.0b0.dist-info/licenses/LICENSE,sha256=Fy5D7kAV4b5O6_mK1bBg6dIvljsl1k5gHhRldMxKIRw,1082
84
+ buctoolkit-1.0b0.dist-info/METADATA,sha256=QzsUkmLqzfpVbbmDZZWdn4LbrcilTADSv4oJWScESK4,1047
85
+ buctoolkit-1.0b0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
86
+ buctoolkit-1.0b0.dist-info/entry_points.txt,sha256=PAZckBYiDWzv10IyRazgc957czw2Ww0SgDO9z__mN0Q,154
87
+ buctoolkit-1.0b0.dist-info/top_level.txt,sha256=dL0flE_C5sul1HwYQO_iR7mH9TlH5UgH9mGL9ns_yas,11
88
+ buctoolkit-1.0b0.dist-info/RECORD,,