TB2J 0.9.9rc16__py3-none-any.whl → 0.9.9rc17__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.
@@ -1 +1,3 @@
1
1
  from .io_exchange import SpinIO
2
+
3
+ __all__ = ["SpinIO"]
@@ -0,0 +1,3 @@
1
+ from .io_exchange2 import ExchangeIO, plot_tb2j_magnon_bands
2
+
3
+ __all__ = ["ExchangeIO", "plot_tb2j_magnon_bands"]
@@ -0,0 +1,688 @@
1
+ """
2
+ This module provides functionality for handling magnetic exchange interactions and computing magnon band structures.
3
+
4
+ It includes classes and functions for:
5
+ - Reading and writing exchange interaction data
6
+ - Computing exchange tensors and magnon energies
7
+ - Plotting magnon band structures
8
+ - Converting between different magnetic structure representations
9
+ """
10
+
11
+ import numpy as np
12
+ from scipy.spatial.transform import Rotation
13
+
14
+ from ..mathutils import generate_grid, get_rotation_arrays, round_to_precision, uz
15
+ from .plot import BandsPlot
16
+ from .structure import BaseMagneticStructure, get_attribute_array
17
+
18
+ __all__ = [
19
+ "ExchangeIO",
20
+ "plot_tb2j_magnon_bands",
21
+ ]
22
+
23
+
24
+ def branched_keys(tb2j_keys, npairs):
25
+ """
26
+ Organize TB2J keys into branches based on magnetic site pairs.
27
+
28
+ Parameters
29
+ ----------
30
+ tb2j_keys : list
31
+ List of TB2J dictionary keys containing interaction information
32
+ npairs : int
33
+ Number of magnetic site pairs
34
+
35
+ Returns
36
+ -------
37
+ list
38
+ List of branched keys organized by magnetic site pairs
39
+ """
40
+ msites = int((2 * npairs) ** 0.5)
41
+ branch_size = len(tb2j_keys) // msites**2
42
+ new_keys = sorted(tb2j_keys, key=lambda x: -x[1] + x[2])[
43
+ (npairs - msites) * branch_size :
44
+ ]
45
+ new_keys.sort(key=lambda x: x[1:])
46
+ bkeys = [
47
+ new_keys[i : i + branch_size] for i in range(0, len(new_keys), branch_size)
48
+ ]
49
+
50
+ return [sorted(branch, key=lambda x: np.linalg.norm(x[0])) for branch in bkeys]
51
+
52
+
53
+ def correct_content(content, quadratic=False):
54
+ """
55
+ Ensure content dictionary has all required entries with proper initialization.
56
+
57
+ Parameters
58
+ ----------
59
+ content : dict
60
+ Dictionary containing exchange interaction data
61
+ quadratic : bool, optional
62
+ Whether to include biquadratic interactions, by default False
63
+ """
64
+ n = max(content["index_spin"]) + 1
65
+ data_shape = {"exchange_Jdict": ()}
66
+
67
+ if not content["colinear"]:
68
+ data_shape |= {"Jani_dict": (3, 3), "dmi_ddict": (3,)}
69
+ if quadratic:
70
+ data_shape["biquadratic_Jdict"] = (2,)
71
+
72
+ for label, shape in data_shape.items():
73
+ content[label] |= {((0, 0, 0), i, i): np.zeros(shape) for i in range(n)}
74
+
75
+
76
+ def Hermitize(array):
77
+ """
78
+ Convert an array into its Hermitian form by constructing a Hermitian matrix.
79
+
80
+ A Hermitian matrix H has the property that H = H†, where H† is the conjugate transpose.
81
+ This means H[i,j] = conj(H[j,i]) for all indices i,j. The function takes an input array
82
+ representing the upper triangular part of the matrix and constructs the full Hermitian
83
+ matrix by:
84
+ 1. Placing the input values in the upper triangular part
85
+ 2. Computing the conjugate transpose of these values for the lower triangular part
86
+
87
+ This is commonly used in quantum mechanics and magnetic systems where Hamiltonians
88
+ must be Hermitian to ensure real eigenvalues.
89
+
90
+ Parameters
91
+ ----------
92
+ array : numpy.ndarray
93
+ Input array containing the upper triangular elements of the matrix.
94
+ Shape should be (n*(n+1)/2, ...) where n is the dimension of
95
+ the resulting square matrix.
96
+
97
+ Returns
98
+ -------
99
+ numpy.ndarray
100
+ Full Hermitian matrix with shape (n, n, ...), where n is computed
101
+ from the input array size. The result satisfies result[i,j] = conj(result[j,i])
102
+ for all indices i,j.
103
+
104
+ Example
105
+ -------
106
+ >>> arr = np.array([1+0j, 2+1j, 3+0j]) # Upper triangular elements for 2x2 matrix
107
+ >>> Hermitize(arr)
108
+ array([[1.+0.j, 2.+1.j],
109
+ [2.-1.j, 3.+0.j]])
110
+ """
111
+ n = int((2 * array.shape[0]) ** 0.5)
112
+ result = np.zeros((n, n) + array.shape[1:], dtype=complex)
113
+ u_indices = np.triu_indices(n)
114
+
115
+ result[*u_indices] = array
116
+ result.swapaxes(0, 1)[*u_indices] = array.swapaxes(-1, -2).conj()
117
+
118
+ return result
119
+
120
+
121
+ class ExchangeIO(BaseMagneticStructure):
122
+ """
123
+ Class for handling magnetic exchange interactions and computing magnon properties.
124
+
125
+ This class provides functionality for:
126
+ - Managing magnetic structure information
127
+ - Computing exchange tensors
128
+ - Calculating magnon band structures
129
+ - Reading TB2J format files
130
+ - Visualizing magnon bands
131
+
132
+ Parameters
133
+ ----------
134
+ atoms : ase.Atoms, optional
135
+ ASE atoms object containing the structure
136
+ cell : array_like, optional
137
+ 3x3 matrix defining the unit cell
138
+ elements : list, optional
139
+ List of chemical symbols for atoms
140
+ positions : array_like, optional
141
+ Atomic positions
142
+ magmoms : array_like, optional
143
+ Magnetic moments for each atom
144
+ pbc : tuple, optional
145
+ Periodic boundary conditions, default (True, True, True)
146
+ magnetic_elements : list, optional
147
+ List of magnetic elements in the structure
148
+ kmesh : list, optional
149
+ k-point mesh dimensions, default [1, 1, 1]
150
+ collinear : bool, optional
151
+ Whether the magnetic structure is collinear, default True
152
+ """
153
+
154
+ def __init__(
155
+ self,
156
+ atoms=None,
157
+ cell=None,
158
+ elements=None,
159
+ positions=None,
160
+ magmoms=None,
161
+ pbc=(True, True, True),
162
+ magnetic_elements=[],
163
+ kmesh=[1, 1, 1],
164
+ collinear=True,
165
+ ):
166
+ super().__init__(
167
+ atoms=atoms,
168
+ cell=cell,
169
+ elements=elements,
170
+ positions=positions,
171
+ pbc=pbc,
172
+ magmoms=magmoms,
173
+ collinear=collinear,
174
+ )
175
+
176
+ self.magnetic_elements = magnetic_elements
177
+ self.kmesh = kmesh
178
+
179
+ num_terms = 4 if collinear else 18
180
+ self._exchange_values = np.empty((0, 0, num_terms), dtype=float)
181
+
182
+ @property
183
+ def magnetic_elements(self):
184
+ """List of magnetic elements in the structure."""
185
+ return self._magnetic_elements
186
+
187
+ @magnetic_elements.setter
188
+ def magnetic_elements(self, value):
189
+ from .structure import validate_symbols
190
+
191
+ symbols = validate_symbols(value)
192
+ for symbol in symbols:
193
+ if symbol not in self.elements:
194
+ raise ValueError(f"Symbol '{symbol}' not in 'elements'.")
195
+
196
+ self._magnetic_elements = symbols
197
+ self._set_index_pairs()
198
+
199
+ @property
200
+ def interacting_pairs(self):
201
+ """List of pairs of interacting magnetic sites."""
202
+ return self._pairs
203
+
204
+ def _set_index_pairs(self):
205
+ from itertools import combinations_with_replacement
206
+
207
+ magnetic_elements = self.magnetic_elements
208
+ elements = self.elements
209
+ indices = [
210
+ i for i, symbol in enumerate(elements) if symbol in magnetic_elements
211
+ ]
212
+ index_pairs = list(combinations_with_replacement(indices, 2))
213
+ index_spin = np.sort(np.unique(index_pairs))
214
+
215
+ self._pairs = index_pairs
216
+ self._index_spin = index_spin
217
+
218
+ @property
219
+ def kmesh(self):
220
+ """K-point mesh dimensions for sampling the Brillouin zone."""
221
+ return self._kmesh
222
+
223
+ @kmesh.setter
224
+ def kmesh(self, values):
225
+ try:
226
+ the_kmesh = [int(k) for k in values]
227
+ except (ValueError, TypeError):
228
+ raise ValueError("Argument must be an iterable with 'int' elements.")
229
+ if len(the_kmesh) != 3:
230
+ raise ValueError("Argument must be of length 3.")
231
+ if any(k < 1 for k in the_kmesh):
232
+ raise ValueError("Argument must contain only positive numbers.")
233
+
234
+ self._kmesh = the_kmesh
235
+
236
+ @property
237
+ def vectors(self):
238
+ """Array of interaction vectors between magnetic sites."""
239
+ return self._exchange_values[:, :, :3]
240
+
241
+ def set_vectors(self, values=None, cartesian=False):
242
+ """
243
+ Set the interaction vectors between magnetic sites.
244
+
245
+ Parameters
246
+ ----------
247
+ values : array_like, optional
248
+ Array of interaction vectors
249
+ cartesian : bool, optional
250
+ Whether the vectors are in Cartesian coordinates, default False
251
+ """
252
+ try:
253
+ pairs = self._pairs
254
+ except AttributeError:
255
+ raise AttributeError("'magnetic_elements' attribute has not been set yet.")
256
+ else:
257
+ n_pairs = len(pairs)
258
+
259
+ if values is None:
260
+ i, j = zip(*pairs)
261
+ positions = self.positions
262
+ base_vectors = positions[i, :] - positions[j, :]
263
+ grid = generate_grid(self.kmesh)
264
+ vectors = base_vectors[:, None, :] + grid[None, :, :]
265
+ m_interactions = np.prod(self.kmesh)
266
+ else:
267
+ vectors = get_attribute_array(values, "vectors", dtype=float)
268
+ if vectors.ndim != 3 or vectors.shape[::2] != (n_pairs, 3):
269
+ raise ValueError(
270
+ f"'vectors' must have the shape (n, m, ), where n={n_pairs} is the number of\n"
271
+ "pairs of interacting species."
272
+ )
273
+ if cartesian:
274
+ vectors = np.linalg.solve(self.cell.T, vectors.swapaxes(1, -1))
275
+ vectors = vectors.swapaxes(-1, 1)
276
+ m_interactions = vectors.shape[1]
277
+
278
+ shape = (
279
+ (n_pairs, m_interactions, 4)
280
+ if self.collinear
281
+ else (n_pairs, m_interactions, 18)
282
+ )
283
+ exchange_values = np.zeros(shape, dtype=float)
284
+ exchange_values[:, :, :3] = vectors
285
+ self._exchange_values = exchange_values
286
+
287
+ def _get_neighbor_indices(self, neighbors, tol=1e-4):
288
+ """
289
+ Get indices of neighbor pairs based on distance.
290
+
291
+ Parameters
292
+ ----------
293
+ neighbors : list
294
+ List of neighbor shells to consider
295
+ tol : float, optional
296
+ Distance tolerance for neighbor shell assignment, default 1e-4
297
+
298
+ Returns
299
+ -------
300
+ tuple
301
+ Indices corresponding to the specified neighbor shells
302
+ """
303
+ distance = np.linalg.norm(self.vectors @ self.cell, axis=-1)
304
+ distance = round_to_precision(distance, tol)
305
+ neighbors_distance = np.unique(np.sort(distance))
306
+ indices = np.where(
307
+ distance[:, :, None] == neighbors_distance[neighbors][None, None, :]
308
+ )
309
+
310
+ return indices
311
+
312
+ def set_exchange_array(self, name, values, neighbors=None, tol=1e-4):
313
+ """
314
+ Set exchange interaction values for specified neighbors.
315
+
316
+ Parameters
317
+ ----------
318
+ name : str
319
+ Type of exchange interaction ('Jiso', 'Biquad', 'DMI', or 'Jani')
320
+ values : array_like
321
+ Exchange interaction values
322
+ neighbors : list, optional
323
+ List of neighbor shells to assign values to
324
+ tol : float, optional
325
+ Distance tolerance for neighbor shell assignment, default 1e-4
326
+ """
327
+ if self.vectors.size == 0:
328
+ raise AttributeError("The intraction vectors must be set first.")
329
+
330
+ array = get_attribute_array(values, name, dtype=float)
331
+
332
+ if neighbors is not None:
333
+ if len(array) != len(neighbors):
334
+ raise ValueError(
335
+ "The number of neighbors and exchange values does not coincide."
336
+ )
337
+ *array_indices, value_indices = self._get_neighbor_indices(
338
+ list(neighbors), tol=tol
339
+ )
340
+ else:
341
+ if self._exchange_values.shape[:2] != array.shape[:2]:
342
+ raise ValueError(
343
+ f"The shape of the array is incompatible with '{self.exchange_values.shape}'"
344
+ )
345
+ array_indices, value_indices = (
346
+ [slice(None), slice(None)],
347
+ (slice(None), slice(None)),
348
+ )
349
+
350
+ if name == "Jiso":
351
+ self._exchange_values[*array_indices, 3] = array[value_indices]
352
+ elif name == "Biquad":
353
+ self._exchange_values[*array_indices, 4:6] = array[value_indices]
354
+ elif name == "DMI":
355
+ self._exchange_values[*array_indices, 6:9] = array[value_indices]
356
+ elif name == "Jani":
357
+ self._exchange_values[*array_indices, 9:] = array[value_indices].reshape(
358
+ array.shape[:2] + (9,)
359
+ )
360
+ else:
361
+ raise ValueError(f"Unrecognized exchange array name: '{name}'.")
362
+
363
+ @property
364
+ def Jiso(self):
365
+ return self._exchange_values[:, :, 3]
366
+
367
+ @property
368
+ def Biquad(self):
369
+ return self._exchange_values[:, :, 4:6]
370
+
371
+ @property
372
+ def DMI(self):
373
+ return self._exchange_values[:, :, 6:9]
374
+
375
+ @property
376
+ def Jani(self):
377
+ matrix_shape = self._exchange_values.shape[:2] + (3, 3)
378
+ return self._exchange_values[:, :, 9:].reshape(matrix_shape)
379
+
380
+ def exchange_tensor(self, anisotropic=True):
381
+ """
382
+ Compute the exchange interaction tensor.
383
+
384
+ Parameters
385
+ ----------
386
+ anisotropic : bool, optional
387
+ Whether to include anisotropic interactions, default True
388
+
389
+ Returns
390
+ -------
391
+ numpy.ndarray
392
+ Exchange interaction tensor
393
+ """
394
+ shape = self._exchange_values.shape[:2] + (3, 3)
395
+ tensor = np.zeros(shape, dtype=float)
396
+
397
+ if anisotropic and not self.collinear:
398
+ tensor += self._exchange_values[:, :, 9:].reshape(shape)
399
+ pos_indices = ([1, 2, 0], [2, 0, 1])
400
+ neg_indices = ([2, 0, 1], [1, 2, 0])
401
+ tensor[:, :, *pos_indices] += self._exchange_values[:, :, 6:9]
402
+ tensor[:, :, *neg_indices] -= self._exchange_values[:, :, 6:9]
403
+ diag_indices = ([0, 1, 2], [0, 1, 2])
404
+ tensor[:, :, *diag_indices] += self._exchange_values[:, :, 3, None]
405
+
406
+ return tensor
407
+
408
+ def Jq(self, kpoints, anisotropic=True):
409
+ """
410
+ Compute the exchange interactions in reciprocal space.
411
+
412
+ Parameters
413
+ ----------
414
+ kpoints : array_like
415
+ k-points at which to evaluate the exchange interactions
416
+ anisotropic : bool, optional
417
+ Whether to include anisotropic interactions, default True
418
+
419
+ Returns
420
+ -------
421
+ numpy.ndarray
422
+ Exchange interactions in reciprocal space
423
+ """
424
+ vectors = self._exchange_values[:, :, :3].copy()
425
+ tensor = self.exchange_tensor(anisotropic=anisotropic)
426
+
427
+ if self._Q is not None:
428
+ phi = 2 * np.pi * vectors.round(3).astype(int) @ self._Q
429
+ rv = np.einsum("ij,k->ijk", phi, self._n)
430
+ R = (
431
+ Rotation.from_rotvec(rv.reshape(-1, 3))
432
+ .as_matrix()
433
+ .reshape(vectors.shape[:2] + (3, 3))
434
+ )
435
+ np.einsum("nmij,nmjk->nmik", tensor, R, out=tensor)
436
+
437
+ exp_summand = np.exp(2j * np.pi * vectors @ kpoints.T)
438
+ Jexp = exp_summand[:, :, :, None, None] * tensor[:, :, None]
439
+ Jq = np.sum(Jexp, axis=1)
440
+
441
+ pairs = np.array(self._pairs)
442
+ idx = np.where(pairs[:, 0] == pairs[:, 1])
443
+ Jq[idx] /= 2
444
+
445
+ return Jq
446
+
447
+ def Hq(self, kpoints, anisotropic=True, u=uz):
448
+ """
449
+ Compute the magnon Hamiltonian in reciprocal space.
450
+
451
+ Parameters
452
+ ----------
453
+ kpoints : array_like
454
+ k-points at which to evaluate the Hamiltonian
455
+ anisotropic : bool, optional
456
+ Whether to include anisotropic interactions, default True
457
+ u : array_like, optional
458
+ Reference direction for spin quantization axis
459
+
460
+ Returns
461
+ -------
462
+ numpy.ndarray
463
+ Magnon Hamiltonian matrix at each k-point
464
+ """
465
+ if self.collinear:
466
+ magmoms = np.zeros((self._index_spin.size, 3))
467
+ magmoms[:, 2] = self.magmoms[self._index_spin]
468
+ else:
469
+ magmoms = self.magmoms[self._index_spin]
470
+ magmoms /= np.linalg.norm(magmoms, axis=-1)[:, None]
471
+
472
+ U, V = get_rotation_arrays(magmoms, u=u)
473
+
474
+ J0 = self.Jq(np.zeros((1, 3)), anisotropic=anisotropic)
475
+ J0 = -Hermitize(J0)[:, :, 0]
476
+ Jq = -Hermitize(self.Jq(kpoints, anisotropic=anisotropic))
477
+
478
+ C = np.diag(np.einsum("ix,ijxy,jy->i", V, 2 * J0, V))
479
+ B = np.einsum("ix,ijkxy,jy->kij", U, Jq, U)
480
+ A1 = np.einsum("ix,ijkxy,jy->kij", U, Jq, U.conj())
481
+ A2 = np.einsum("ix,ijkxy,jy->kij", U.conj(), Jq, U)
482
+
483
+ return np.block([[A1 - C, B], [B.swapaxes(-1, -2).conj(), A2 - C]])
484
+
485
+ def _magnon_energies(self, kpoints, anisotropic=True, u=uz):
486
+ H = self.Hq(kpoints, anisotropic=anisotropic, u=u)
487
+ n = H.shape[-1] // 2
488
+ I = np.eye(n)
489
+
490
+ min_eig = 0.0
491
+ try:
492
+ K = np.linalg.cholesky(H)
493
+ except np.linalg.LinAlgError:
494
+ try:
495
+ K = np.linalg.cholesky(H + 1e-6 * np.eye(2 * n))
496
+ except np.linalg.LinAlgError:
497
+ from warnings import warn
498
+
499
+ min_eig = np.min(np.linalg.eigvalsh(H))
500
+ K = np.linalg.cholesky(H - (min_eig - 1e-6) * np.eye(2 * n))
501
+ warn(
502
+ f"WARNING: The system may be far from the magnetic ground-state. Minimum eigenvalue: {min_eig}. The magnon energies might be unphysical."
503
+ )
504
+
505
+ g = np.block([[1 * I, 0 * I], [0 * I, -1 * I]])
506
+ KH = K.swapaxes(-1, -2).conj()
507
+
508
+ return np.linalg.eigvalsh(KH @ g @ K)[:, n:] + min_eig
509
+
510
+ def get_magnon_bands(
511
+ self,
512
+ kpoints: np.array = np.array([]),
513
+ path: str = None,
514
+ npoints: int = 300,
515
+ special_points: dict = None,
516
+ tol: float = 2e-4,
517
+ pbc: tuple = None,
518
+ cartesian: bool = False,
519
+ labels: list = None,
520
+ anisotropic: bool = True,
521
+ u: np.array = uz,
522
+ ):
523
+ pbc = self._pbc if pbc is None else pbc
524
+
525
+ if kpoints.size == 0:
526
+ from ase.cell import Cell
527
+
528
+ bandpath = Cell(self._cell).bandpath(
529
+ path=path,
530
+ npoints=npoints,
531
+ special_points=special_points,
532
+ eps=tol,
533
+ pbc=pbc,
534
+ )
535
+ kpoints = bandpath.kpts
536
+ spk = bandpath.special_points
537
+ spk[r"$\Gamma$"] = spk.pop("G", np.zeros(3))
538
+ labels = [
539
+ (i, symbol)
540
+ for symbol in spk
541
+ for i in np.where((kpoints == spk[symbol]).all(axis=1))[0]
542
+ ]
543
+ elif cartesian:
544
+ kpoints = np.linalg.solve(self._cell.T, kpoints.T).T
545
+
546
+ bands = self._magnon_energies(kpoints, anisotropic=anisotropic, u=u)
547
+
548
+ return labels, bands
549
+
550
+ def plot_magnon_bands(self, **kwargs):
551
+ """
552
+ Plot magnon band structure.
553
+
554
+ Parameters
555
+ ----------
556
+ **kwargs
557
+ Additional keyword arguments passed to get_magnon_bands and plotting functions
558
+ """
559
+ filename = kwargs.pop("filename", None)
560
+ kpath, bands = self.get_magnon_bands(**kwargs)
561
+ bands_plot = BandsPlot(bands, kpath)
562
+ bands_plot.plot(filename=filename)
563
+
564
+ @classmethod
565
+ def load_tb2j(
566
+ cls,
567
+ pickle_file: str = "TB2J.pickle",
568
+ pbc: tuple = (True, True, True),
569
+ anisotropic: bool = False,
570
+ quadratic: bool = False,
571
+ ):
572
+ from pickle import load
573
+
574
+ try:
575
+ with open(pickle_file, "rb") as File:
576
+ content = load(File)
577
+ except FileNotFoundError:
578
+ raise FileNotFoundError(
579
+ f"No such file or directory: '{pickle_file}'. Please provide a valid .pickle file."
580
+ )
581
+ else:
582
+ correct_content(content)
583
+
584
+ magmoms = content["magmoms"] if content["colinear"] else content["spinat"]
585
+ magnetic_elements = {
586
+ content["atoms"].numbers[i]
587
+ for i, j in enumerate(content["index_spin"])
588
+ if j > -1
589
+ }
590
+
591
+ exchange = cls(
592
+ atoms=content["atoms"],
593
+ magmoms=magmoms,
594
+ pbc=pbc,
595
+ collinear=content["colinear"],
596
+ magnetic_elements=magnetic_elements,
597
+ )
598
+
599
+ num_pairs = len(exchange.interacting_pairs)
600
+ bkeys = branched_keys(content["distance_dict"].keys(), num_pairs)
601
+
602
+ vectors = [
603
+ [content["distance_dict"][key][0] for key in branch] for branch in bkeys
604
+ ]
605
+ exchange.set_vectors(vectors, cartesian=True)
606
+ Jiso = [[content["exchange_Jdict"][key] for key in branch] for branch in bkeys]
607
+ exchange.set_exchange_array("Jiso", Jiso)
608
+
609
+ if not content["colinear"] and anisotropic:
610
+ Jani = [[content["Jani_dict"][key] for key in branch] for branch in bkeys]
611
+ exchange.set_exchange_array("Jani", Jani)
612
+ DMI = [[content["dmi_ddict"][key] for key in branch] for branch in bkeys]
613
+ exchange.set_exchange_array("DMI", DMI)
614
+ if quadratic:
615
+ Biquad = [
616
+ [content["biquadratic_Jdict"][key] for key in branch]
617
+ for branch in bkeys
618
+ ]
619
+ exchange.set_exchange_array("Biquad", Biquad)
620
+
621
+ return exchange
622
+
623
+
624
+ def plot_tb2j_magnon_bands(
625
+ pickle_file: str = "TB2J.pickle",
626
+ path: str = None,
627
+ npoints: int = 300,
628
+ special_points: dict = None,
629
+ anisotropic: bool = False,
630
+ quadratic: bool = False,
631
+ pbc: tuple = (True, True, True),
632
+ filename: str = None,
633
+ ):
634
+ """
635
+ Load TB2J data and plot magnon band structure in one step.
636
+
637
+ This is a convenience function that combines loading TB2J data and plotting
638
+ magnon bands. It first loads the magnetic structure and exchange interactions
639
+ from a TB2J pickle file, then calculates and plots the magnon band structure.
640
+
641
+ Parameters
642
+ ----------
643
+ pickle_file : str, optional
644
+ Path to the TB2J pickle file, default "TB2J.pickle"
645
+ path : str, optional
646
+ High-symmetry k-point path for band structure plot
647
+ (e.g., "GXMG" for a square lattice)
648
+ npoints : int, optional
649
+ Number of k-points for band structure calculation, default 300
650
+ special_points : dict, optional
651
+ Dictionary of special k-points for custom paths
652
+ anisotropic : bool, optional
653
+ Whether to include anisotropic interactions, default False
654
+ quadratic : bool, optional
655
+ Whether to include biquadratic interactions, default False
656
+ pbc : tuple, optional
657
+ Periodic boundary conditions, default (True, True, True)
658
+ filename : str, optional
659
+ If provided, save the plot to this file
660
+
661
+ Returns
662
+ -------
663
+ exchange : ExchangeIO
664
+ The ExchangeIO instance containing the loaded data and plot
665
+
666
+ Example
667
+ -------
668
+ >>> # Basic usage with default parameters
669
+ >>> plot_tb2j_magnon_bands()
670
+
671
+ >>> # Custom path and saving to file
672
+ >>> plot_tb2j_magnon_bands(
673
+ ... path="GXMG",
674
+ ... anisotropic=True,
675
+ ... filename="magnon_bands.png"
676
+ ... )
677
+ """
678
+ # Load the TB2J data
679
+ exchange = ExchangeIO.load_tb2j(
680
+ pickle_file=pickle_file, pbc=pbc, anisotropic=anisotropic, quadratic=quadratic
681
+ )
682
+
683
+ # Plot the magnon bands
684
+ exchange.plot_magnon_bands(
685
+ path=path, npoints=npoints, special_points=special_points, filename=filename
686
+ )
687
+
688
+ return exchange
TB2J/magnon/plot.py ADDED
@@ -0,0 +1,58 @@
1
+ import matplotlib.pyplot as plt
2
+ import numpy as np
3
+
4
+
5
+ class BandsPlot:
6
+ _UNITS = "meV"
7
+ _NSYSTEMS = 1
8
+
9
+ def __init__(self, bands, kpath, **kwargs):
10
+ self.bands = bands
11
+ self.kpath = kpath
12
+ self.bands *= 1000
13
+
14
+ plot_options = kwargs
15
+ self.linewidth = plot_options.pop("linewidth", 1.5)
16
+ self.color = plot_options.pop("color", "blue")
17
+ self.fontsize = plot_options.pop("fontsize", 12)
18
+ self.ticksize = plot_options.pop("ticksize", 10)
19
+ self.plot_options = plot_options
20
+
21
+ def plot(self, filename=None):
22
+ fig, axs = plt.subplots(1, self._NSYSTEMS, constrained_layout=True)
23
+
24
+ kdata = np.arange(self.bands.shape[0])
25
+ for band in self.bands.T:
26
+ axs.plot(
27
+ kdata,
28
+ band,
29
+ linewidth=self.linewidth,
30
+ color=self.color,
31
+ **self.plot_options,
32
+ )
33
+
34
+ bmin, bmax = self.bands.min(), self.bands.max()
35
+ ymin, ymax = (
36
+ bmin - 0.05 * np.abs(bmin - bmax),
37
+ bmax + 0.05 * np.abs(bmax - bmin),
38
+ )
39
+
40
+ axs.set_ylim([ymin, ymax])
41
+ axs.set_xlim([0, kdata[-1]])
42
+
43
+ kpoint_labels = list(zip(*self.kpath))
44
+ axs.set_xticks(*kpoint_labels, fontsize=self.ticksize)
45
+ axs.vlines(
46
+ x=kpoint_labels[0],
47
+ ymin=ymin,
48
+ ymax=ymax,
49
+ color="black",
50
+ linewidth=self.linewidth / 5,
51
+ )
52
+
53
+ axs.set_ylabel(f"Energy ({self._UNITS})", fontsize=self.fontsize)
54
+
55
+ if filename is None:
56
+ plt.show()
57
+ else:
58
+ fig.save(filename, dpi=300, bbox_inches="tight")
@@ -0,0 +1,348 @@
1
+ from warnings import warn
2
+
3
+ import numpy as np
4
+
5
+ valid_symbols = [
6
+ # 0
7
+ "Ae",
8
+ # 1
9
+ "H",
10
+ "He",
11
+ # 2
12
+ "Li",
13
+ "Be",
14
+ "B",
15
+ "C",
16
+ "N",
17
+ "O",
18
+ "F",
19
+ "Ne",
20
+ # 3
21
+ "Na",
22
+ "Mg",
23
+ "Al",
24
+ "Si",
25
+ "P",
26
+ "S",
27
+ "Cl",
28
+ "Ar",
29
+ # 4
30
+ "K",
31
+ "Ca",
32
+ "Sc",
33
+ "Ti",
34
+ "V",
35
+ "Cr",
36
+ "Mn",
37
+ "Fe",
38
+ "Co",
39
+ "Ni",
40
+ "Cu",
41
+ "Zn",
42
+ "Ga",
43
+ "Ge",
44
+ "As",
45
+ "Se",
46
+ "Br",
47
+ "Kr",
48
+ # 5
49
+ "Rb",
50
+ "Sr",
51
+ "Y",
52
+ "Zr",
53
+ "Nb",
54
+ "Mo",
55
+ "Tc",
56
+ "Ru",
57
+ "Rh",
58
+ "Pd",
59
+ "Ag",
60
+ "Cd",
61
+ "In",
62
+ "Sn",
63
+ "Sb",
64
+ "Te",
65
+ "I",
66
+ "Xe",
67
+ # 6
68
+ "Cs",
69
+ "Ba",
70
+ "La",
71
+ "Ce",
72
+ "Pr",
73
+ "Nd",
74
+ "Pm",
75
+ "Sm",
76
+ "Eu",
77
+ "Gd",
78
+ "Tb",
79
+ "Dy",
80
+ "Ho",
81
+ "Er",
82
+ "Tm",
83
+ "Yb",
84
+ "Lu",
85
+ "Hf",
86
+ "Ta",
87
+ "W",
88
+ "Re",
89
+ "Os",
90
+ "Ir",
91
+ "Pt",
92
+ "Au",
93
+ "Hg",
94
+ "Tl",
95
+ "Pb",
96
+ "Bi",
97
+ "Po",
98
+ "At",
99
+ "Rn",
100
+ # 7
101
+ "Fr",
102
+ "Ra",
103
+ "Ac",
104
+ "Th",
105
+ "Pa",
106
+ "U",
107
+ "Np",
108
+ "Pu",
109
+ "Am",
110
+ "Cm",
111
+ "Bk",
112
+ "Cf",
113
+ "Es",
114
+ "Fm",
115
+ "Md",
116
+ "No",
117
+ "Lr",
118
+ "Rf",
119
+ "Db",
120
+ "Sg",
121
+ "Bh",
122
+ "Hs",
123
+ "Mt",
124
+ "Ds",
125
+ "Rg",
126
+ "Cn",
127
+ "Nh",
128
+ "Fl",
129
+ "Mc",
130
+ "Lv",
131
+ "Ts",
132
+ "Og",
133
+ ]
134
+
135
+
136
+ def get_attribute_array(array, attribute, dtype=float):
137
+ try:
138
+ the_array = np.array(array, dtype=dtype)
139
+ except (IndexError, ValueError, TypeError) as err:
140
+ typename = dtype.__name__
141
+ raise type(err)(
142
+ f"'{attribute}' must be an arraylike object of '{typename}' elements."
143
+ )
144
+
145
+ return the_array
146
+
147
+
148
+ def validate_symbols(value):
149
+ try:
150
+ values_list = value.split()
151
+ except AttributeError:
152
+ try:
153
+ values_list = list(value)
154
+ except (ValueError, TypeError):
155
+ raise TypeError("'elements' must be an iterable of 'str' or 'int' entries.")
156
+
157
+ if all(isinstance(s, str) for s in values_list):
158
+ symbols = values_list
159
+ for symbol in symbols:
160
+ if symbol not in valid_symbols:
161
+ raise ValueError(f"Unrecognized element '{symbol}'.")
162
+ elif all(isinstance(i, (int, np.integer)) for i in values_list):
163
+ if any(i < 0 for i in values_list):
164
+ raise ValueError("Atomic numbers must be positive.")
165
+ try:
166
+ symbols = [valid_symbols[i] for i in values_list]
167
+ except IndexError:
168
+ raise ValueError("Atomic number exceeds 118.")
169
+ else:
170
+ raise ValueError("'elements' must be an iterable of 'str' or 'int' entries.")
171
+
172
+ return symbols
173
+
174
+
175
+ class BaseMagneticStructure:
176
+ def __init__(
177
+ self,
178
+ atoms=None,
179
+ cell=None,
180
+ elements=None,
181
+ positions=None,
182
+ magmoms=None,
183
+ pbc=None,
184
+ collinear=True,
185
+ ):
186
+ if atoms is not None:
187
+ if any(arg is not None for arg in [cell, elements, positions]):
188
+ warn(
189
+ "WARNING: 'atoms' overrides the 'cell', 'elements', and 'positions' arguments."
190
+ )
191
+ cell = atoms.cell.array
192
+ elements = atoms.numbers
193
+ positions = atoms.get_scaled_positions()
194
+ pbc = atoms.pbc if pbc is None else pbc
195
+ else:
196
+ if cell is None:
197
+ cell = np.zeros((3, 3))
198
+ if elements is None:
199
+ elements = ()
200
+ if positions is None:
201
+ positions = np.zeros((len(elements), 3))
202
+ if pbc is None:
203
+ pbc = (True, True, True)
204
+ if magmoms is None:
205
+ magmoms_shape = positions.shape[0] if collinear else positions.shape
206
+ magmoms = np.zeros(magmoms_shape)
207
+
208
+ self.cell = cell
209
+ self.elements = elements
210
+ self.positions = positions
211
+ self.collinear = collinear
212
+ self.magmoms = magmoms
213
+ self.pbc = pbc
214
+
215
+ self._Q = None
216
+ self._n = np.array([0, 0, 1])
217
+
218
+ @property
219
+ def cell(self):
220
+ return self._cell
221
+
222
+ @property
223
+ def reciprocal_cell(self):
224
+ return 2 * np.pi * np.linalg.inv(self._cell)
225
+
226
+ @cell.setter
227
+ def cell(self, value):
228
+ cell_array = get_attribute_array(value, "cell")
229
+ if cell_array.shape != (3, 3):
230
+ raise ValueError("'cell' must have a (3, 3) shape.")
231
+
232
+ self._cell = cell_array
233
+
234
+ @property
235
+ def elements(self):
236
+ return self._symbols
237
+
238
+ @elements.setter
239
+ def elements(self, value):
240
+ symbols = validate_symbols(value)
241
+ self._symbols = symbols
242
+
243
+ @property
244
+ def numbers(self):
245
+ return [valid_symbols.index(symbol) for symbol in self._symbols]
246
+
247
+ @property
248
+ def positions(self):
249
+ return self._positions
250
+
251
+ @property
252
+ def cartesian_positions(self):
253
+ return self._positions @ self._cell
254
+
255
+ @positions.setter
256
+ def positions(self, value):
257
+ natoms = len(self.elements)
258
+ posarray = get_attribute_array(value, "positions")
259
+ if posarray.shape != (natoms, 3):
260
+ raise ValueError(
261
+ "'positions' must have a (natoms, 3) shape, where natoms is the length of 'elements'. Make sure to set 'elements' first."
262
+ )
263
+
264
+ self._positions = posarray
265
+
266
+ @property
267
+ def magmoms(self):
268
+ return self._magmoms
269
+
270
+ @magmoms.setter
271
+ def magmoms(self, value):
272
+ magarray = get_attribute_array(value, "magmoms")
273
+ if self.collinear and magarray.shape != (len(self._positions),):
274
+ raise ValueError(
275
+ "'magmoms' must be a 1dim array with the same length as 'positions'. Make sure to set 'positions' first."
276
+ )
277
+ elif not self.collinear and magarray.shape != self._positions.shape:
278
+ raise ValueError(
279
+ "'magmoms' must have the same shape as 'positions'. Make sure to set 'positions' first."
280
+ )
281
+
282
+ self._magmoms = magarray
283
+
284
+ @property
285
+ def pbc(self):
286
+ return self._pbc
287
+
288
+ @pbc.setter
289
+ def pbc(self, value):
290
+ try:
291
+ periodic_boundary_conditions = tuple(bool(b) for b in value)
292
+ except (ValueError, TypeError):
293
+ raise TypeError("'pbc' must be an iterable of 'bool' entries.")
294
+
295
+ if len(periodic_boundary_conditions) != 3:
296
+ raise ValueError("'pbc' must be of length 3.")
297
+
298
+ self._pbc = periodic_boundary_conditions
299
+
300
+ @property
301
+ def propagation_vector(self):
302
+ return self._Q
303
+
304
+ @propagation_vector.setter
305
+ def propagation_vector(self, value):
306
+ Q = get_attribute_array(value, "propagation_vector")
307
+ if Q.shape != (3,):
308
+ raise ValueError(
309
+ "The 'propagation_vector' must be a vector with 3 numbers."
310
+ )
311
+
312
+ self._Q = Q
313
+
314
+ @property
315
+ def normal_vector(self):
316
+ return self._n
317
+
318
+ @normal_vector.setter
319
+ def normal_vector(self, value):
320
+ n = get_attribute_array(value, "normal_vector")
321
+ if n.shape != (3,):
322
+ raise ValueError("The 'normal_vector' must be a vector with 3 numbers.")
323
+
324
+ self._n = n
325
+
326
+ def to_ase(self):
327
+ from ase.atoms import Atoms
328
+
329
+ atoms = Atoms(
330
+ cell=self.cell,
331
+ positions=self.cartesian_positions,
332
+ numbers=[valid_symbols.index(symbol) for symbol in self.elements],
333
+ pbc=self.pbc,
334
+ )
335
+
336
+ return atoms
337
+
338
+ @classmethod
339
+ def from_structure_file(
340
+ cls, filename, magmoms=None, collinear=True, pbc=(True, True, True)
341
+ ):
342
+ from ase.io import read
343
+
344
+ atoms = read(filename)
345
+ atoms.pbc = pbc
346
+ obj = cls(atoms=atoms, magmoms=magmoms, collinear=collinear)
347
+
348
+ return obj
@@ -1 +1,2 @@
1
1
  from .lowdin import Lowdin
2
+ from .magnons import *
@@ -0,0 +1,45 @@
1
+ import numpy as np
2
+
3
+ __all__ = ["generate_grid", "get_rotation_arrays", "round_to_precision", "uz", "I"]
4
+
5
+ I = np.eye(3)
6
+ uz = np.array([[0.0, 0.0, 1.0]])
7
+
8
+
9
+ def generate_grid(kmesh, sort=True):
10
+ half_grid = [int(n / 2) for n in kmesh]
11
+ grid = np.stack(
12
+ np.meshgrid(*[np.arange(-n, n + 1) for n in half_grid]), axis=-1
13
+ ).reshape(-1, 3)
14
+
15
+ if sort:
16
+ idx = np.linalg.norm(grid, axis=-1).argsort()
17
+ grid = grid[idx]
18
+
19
+ return grid
20
+
21
+
22
+ def get_rotation_arrays(magmoms, u=uz):
23
+ dim = magmoms.shape[0]
24
+ v = magmoms
25
+ n = np.cross(u, v)
26
+ n /= np.linalg.norm(n, axis=-1).reshape(dim, 1)
27
+ z = np.repeat(u, dim, axis=0)
28
+ A = np.stack([z, np.cross(n, z), n], axis=1)
29
+ B = np.stack([v, np.cross(n, v), n], axis=1)
30
+ R = np.einsum("nki,nkj->nij", A, B)
31
+
32
+ Rnan = np.isnan(R)
33
+ if Rnan.any():
34
+ nanidx = np.where(Rnan)[0]
35
+ R[nanidx] = I
36
+ R[nanidx, 2] = v[nanidx]
37
+
38
+ U = R[:, 0] + 1j * R[:, 1]
39
+ V = R[:, 2]
40
+
41
+ return U, V
42
+
43
+
44
+ def round_to_precision(array, precision):
45
+ return precision * np.round(array / precision)
@@ -0,0 +1,77 @@
1
+ from TB2J.magnon import plot_tb2j_magnon_bands
2
+
3
+
4
+ def plot_tb2j_magnon_bands_cli():
5
+ """Command line interface for plotting magnon band structures from TB2J data."""
6
+ import argparse
7
+
8
+ parser = argparse.ArgumentParser(
9
+ description="Plot magnon band structure from TB2J data",
10
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
11
+ )
12
+
13
+ parser.add_argument(
14
+ "-f", "--file", default="TB2J.pickle", help="Path to TB2J pickle file"
15
+ )
16
+
17
+ parser.add_argument(
18
+ "-p",
19
+ "--path",
20
+ help="High-symmetry k-point path (e.g., 'GXMG' for square lattice)",
21
+ )
22
+
23
+ parser.add_argument(
24
+ "-n",
25
+ "--npoints",
26
+ type=int,
27
+ default=300,
28
+ help="Number of k-points for band structure calculation",
29
+ )
30
+
31
+ parser.add_argument(
32
+ "-a",
33
+ "--anisotropic",
34
+ action="store_true",
35
+ help="Include anisotropic interactions",
36
+ )
37
+
38
+ parser.add_argument(
39
+ "-q",
40
+ "--quadratic",
41
+ action="store_true",
42
+ help="Include biquadratic interactions",
43
+ )
44
+
45
+ parser.add_argument("-o", "--output", help="Output filename for the plot")
46
+
47
+ parser.add_argument(
48
+ "--no-pbc",
49
+ nargs="+",
50
+ type=int,
51
+ choices=[0, 1, 2],
52
+ help="Disable periodic boundary conditions in specified directions (0=x, 1=y, 2=z)",
53
+ )
54
+
55
+ args = parser.parse_args()
56
+
57
+ # Set up periodic boundary conditions
58
+ pbc = [True, True, True]
59
+ if args.no_pbc:
60
+ for direction in args.no_pbc:
61
+ pbc[direction] = False
62
+ pbc = tuple(pbc)
63
+
64
+ # Plot the bands
65
+ plot_tb2j_magnon_bands(
66
+ pickle_file=args.file,
67
+ path=args.path,
68
+ npoints=args.npoints,
69
+ anisotropic=args.anisotropic,
70
+ quadratic=args.quadratic,
71
+ pbc=pbc,
72
+ filename=args.output,
73
+ )
74
+
75
+
76
+ if __name__ == "__main__":
77
+ plot_tb2j_magnon_bands_cli()
@@ -29,8 +29,8 @@ def run_abacus2J():
29
29
  args = parser.parse_args()
30
30
 
31
31
  index_magnetic_atoms = args.index_magnetic_atoms
32
- if not index_magnetic_atoms is None:
33
- index_magnetic_atoms = [i-1 for i in index_magnetic_atoms]
32
+ if index_magnetic_atoms is not None:
33
+ index_magnetic_atoms = [i - 1 for i in index_magnetic_atoms]
34
34
 
35
35
  if args.elements is None and index_magnetic_atoms is None:
36
36
  print("Please input the magnetic elements, e.g. --elements Fe Ni")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TB2J
3
- Version: 0.9.9rc16
3
+ Version: 0.9.9rc17
4
4
  Summary: TB2J: First principle to Heisenberg exchange J using tight-binding Green function method
5
5
  Author: Xu He
6
6
  Author-email: mailhexu@gmail.com
@@ -54,18 +54,23 @@ TB2J/interfaces/abacus/stru_api.py,sha256=Ac03ikHRsZRXqTul4IUge7D2iG_xLh4_oyYfeP
54
54
  TB2J/interfaces/abacus/test_density_matrix.py,sha256=bMWWJYtDS57SpPZ-eZXZ9Hr_UK4mv8ZHM7SzItG3IVA,774
55
55
  TB2J/interfaces/abacus/test_read_HRSR.py,sha256=W1oO_yigT50Yb5_u-KB_IfTpM7kArGkBuMSMs0H4CTs,1235
56
56
  TB2J/interfaces/abacus/test_read_stru.py,sha256=hoKPHVco8vwzC7Gao4bOPCdAPhh29x-9DTJJqRr7AYM,788
57
- TB2J/io_exchange/__init__.py,sha256=KfGHum7B8E4G_KKfillqw0lErtoyKEuFUUttHLs-mg4,32
57
+ TB2J/io_exchange/__init__.py,sha256=LqEnG67qDVKt4hCUywDEQvUIJ7jsQjmtueOW_J16NOE,54
58
58
  TB2J/io_exchange/io_exchange.py,sha256=6fefcfYAyRojkCJA5bFwAa2Hjwx6epysfyX8Snj9DWc,20102
59
59
  TB2J/io_exchange/io_multibinit.py,sha256=8PDmWxzGuv-GwJosj2ZTmiyNY_duFVWJ4ekCuSqGdd8,6739
60
60
  TB2J/io_exchange/io_tomsasd.py,sha256=NqkAC1Fl-CUnFA21eBzSy_S5F_oeQFJysw4UukQbN8o,4173
61
61
  TB2J/io_exchange/io_txt.py,sha256=BMr1eSILlKpgtjvDx7uw2VMAkEKSvGEPNxpaT_zev0I,10547
62
62
  TB2J/io_exchange/io_uppasd.py,sha256=bI4iPEgnK4TvCZNvb6x2xYXgjW7pEehCqmcizy2pqFU,3301
63
63
  TB2J/io_exchange/io_vampire.py,sha256=UllC4twf06_q2vBCnAYFzEDGvS8mSefwQXDquBuyc0M,5583
64
- TB2J/mathutils/__init__.py,sha256=tQLBfHkZqdVfVxPOahy42qMUkFYnFFFhM-uc4QsYFxI,27
64
+ TB2J/magnon/__init__.py,sha256=jdUFkOlhrpHRmKavAXVcFdZgtY7lKZVzMPFlLmmCErM,113
65
+ TB2J/magnon/io_exchange2.py,sha256=vKKQXhPSvoeKNIc0Kh0GaTFph36jSW0Z46nAAbSEcAg,22993
66
+ TB2J/magnon/plot.py,sha256=kwq9LL0FRVo-SLYNkE-ghlyjz8DZD-c4LAWg8si5GJo,1674
67
+ TB2J/magnon/structure.py,sha256=rKefzXgQlEjVvV-A7E2IogVDWwf5egZr3Wxxu6HuJU4,7685
68
+ TB2J/mathutils/__init__.py,sha256=u3QCXBUmKOtJc5PWMFS65e1WhP43UpSf48S7aJZCohc,50
65
69
  TB2J/mathutils/fermi.py,sha256=72tZ5CptGmYaBUD0xLWltuH7LBXcrMUwODyW6-WqlzI,638
66
70
  TB2J/mathutils/fibonacci_sphere.py,sha256=l0USn25ZCAWF6l4UleyWaeLthsj9TThV9iWmfi6DbaM,2344
67
71
  TB2J/mathutils/kR_convert.py,sha256=p_9XWJVNanTzTK2rI6KRjTkbSq42la6N448-zJOsMwY,2671
68
72
  TB2J/mathutils/lowdin.py,sha256=RYbm9OcnFnjcZFdC5YcNUsI9cOJmoDLsWSSCaP0GqKQ,499
73
+ TB2J/mathutils/magnons.py,sha256=o9O7OR39RK1cl46MtiUO9Kd1IZYDOR3ANe82FZ7laY8,1085
69
74
  TB2J/mathutils/rotate_spin.py,sha256=lbGzve_36FyNjanXqdxYDb102kA4_5OycRlBcm-tH-g,8360
70
75
  TB2J/spinham/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
76
  TB2J/spinham/base_parser.py,sha256=oQRHvFE_BlUtTaTZykKgvicu40oXcbICB-D1aAt-qlA,2196
@@ -80,19 +85,20 @@ TB2J/spinham/supercell.py,sha256=y17uUC6r3gQb278FhxIW4CABihfLTvKFj6flyXrCPR8,122
80
85
  TB2J/wannier/__init__.py,sha256=7ojCbM84PYv1X1Tbo4NHI-d3gWmQsZB_xiYqbfxVV1E,80
81
86
  TB2J/wannier/w90_parser.py,sha256=dbd63LuKyv2DVUzqRINGsbDzEsOxsQyE8_Ear_LQIRg,4620
82
87
  TB2J/wannier/w90_tb_parser.py,sha256=qt8pnuprmPp9iIAYwPkPbmEzk6ZPgMq2xognoQp7vwc,4610
83
- tb2j-0.9.9rc16.data/scripts/TB2J_downfold.py,sha256=i4BVqnpDdgrX_amookVWeLGefGBn-qeAutWiwuY9SfQ,2099
84
- tb2j-0.9.9rc16.data/scripts/TB2J_eigen.py,sha256=Qs9v2hnMm2Tpfoa4h53muUKty2dZjwx8948MBoQooNg,1128
85
- tb2j-0.9.9rc16.data/scripts/TB2J_magnon.py,sha256=q7UwAmorRcFNk4tfE7gl_ny05l6p7pbD9Wm_LkIpKEw,3101
86
- tb2j-0.9.9rc16.data/scripts/TB2J_magnon_dos.py,sha256=TMXQvD2dIbO5FZ4tUMmxJgCgH2O2hDAPUNfEKO4z-x4,110
87
- tb2j-0.9.9rc16.data/scripts/TB2J_merge.py,sha256=y834SF4rIRn1L1ptkhczvavQpC-8Px6DTmDOOSaq_DE,1854
88
- tb2j-0.9.9rc16.data/scripts/TB2J_rotate.py,sha256=zgiDFuYZNmzKK0rwDmTaYD2OpRlmKA_VGeBx83w2Xwc,873
89
- tb2j-0.9.9rc16.data/scripts/TB2J_rotateDM.py,sha256=kCvF7gotuqAX1VnJ06cwfVm7RrhrdtiV5v7d9P2Pn_E,567
90
- tb2j-0.9.9rc16.data/scripts/abacus2J.py,sha256=d84wBYkZJn6BfIFi2QorCLLdW32HB00cuZKM5RHrO-g,1824
91
- tb2j-0.9.9rc16.data/scripts/siesta2J.py,sha256=QJ6c0DbqxaqYEesxiL5R9nK9-flNLrr7hajKfCwirYc,2318
92
- tb2j-0.9.9rc16.data/scripts/wann2J.py,sha256=OA31VHEXbQMD-JozoLUHDF6vB9Sr62d804OApSKtSnU,3240
93
- tb2j-0.9.9rc16.dist-info/licenses/LICENSE,sha256=CbZI-jyRTjiqIcWa244cRSHJdjjtUNqGR4HeJkgEwJw,1332
94
- tb2j-0.9.9rc16.dist-info/METADATA,sha256=xNYF0Ciw882g-_mqBsHoZ7r3wrKNDy8dWN_0s1HFiII,1688
95
- tb2j-0.9.9rc16.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
96
- tb2j-0.9.9rc16.dist-info/entry_points.txt,sha256=Hdz1WC9waUzyFVmowKnbbZ6j-J4adHh_Ko6JpxGYAtE,131
97
- tb2j-0.9.9rc16.dist-info/top_level.txt,sha256=whYa5ByLYhl5XnTPBHSWr-IGD6VWmr5Ql2bye2qwV_s,5
98
- tb2j-0.9.9rc16.dist-info/RECORD,,
88
+ tb2j-0.9.9rc17.data/scripts/TB2J_downfold.py,sha256=i4BVqnpDdgrX_amookVWeLGefGBn-qeAutWiwuY9SfQ,2099
89
+ tb2j-0.9.9rc17.data/scripts/TB2J_eigen.py,sha256=Qs9v2hnMm2Tpfoa4h53muUKty2dZjwx8948MBoQooNg,1128
90
+ tb2j-0.9.9rc17.data/scripts/TB2J_magnon.py,sha256=q7UwAmorRcFNk4tfE7gl_ny05l6p7pbD9Wm_LkIpKEw,3101
91
+ tb2j-0.9.9rc17.data/scripts/TB2J_magnon2.py,sha256=9HKNXwPlwaEnAlJXVLrbQFhWj3fGLlBAp-nMXWDTbE0,1911
92
+ tb2j-0.9.9rc17.data/scripts/TB2J_magnon_dos.py,sha256=TMXQvD2dIbO5FZ4tUMmxJgCgH2O2hDAPUNfEKO4z-x4,110
93
+ tb2j-0.9.9rc17.data/scripts/TB2J_merge.py,sha256=y834SF4rIRn1L1ptkhczvavQpC-8Px6DTmDOOSaq_DE,1854
94
+ tb2j-0.9.9rc17.data/scripts/TB2J_rotate.py,sha256=zgiDFuYZNmzKK0rwDmTaYD2OpRlmKA_VGeBx83w2Xwc,873
95
+ tb2j-0.9.9rc17.data/scripts/TB2J_rotateDM.py,sha256=kCvF7gotuqAX1VnJ06cwfVm7RrhrdtiV5v7d9P2Pn_E,567
96
+ tb2j-0.9.9rc17.data/scripts/abacus2J.py,sha256=VD1ldQP-PIMyxo3Rzzwk_06fJEbbU7oiU3aRXNHK0a0,1826
97
+ tb2j-0.9.9rc17.data/scripts/siesta2J.py,sha256=QJ6c0DbqxaqYEesxiL5R9nK9-flNLrr7hajKfCwirYc,2318
98
+ tb2j-0.9.9rc17.data/scripts/wann2J.py,sha256=OA31VHEXbQMD-JozoLUHDF6vB9Sr62d804OApSKtSnU,3240
99
+ tb2j-0.9.9rc17.dist-info/licenses/LICENSE,sha256=CbZI-jyRTjiqIcWa244cRSHJdjjtUNqGR4HeJkgEwJw,1332
100
+ tb2j-0.9.9rc17.dist-info/METADATA,sha256=r-lDBhH1xqc2V1ekn53vtmh6yUSfXzjEW0vJ8B7BDGA,1688
101
+ tb2j-0.9.9rc17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
102
+ tb2j-0.9.9rc17.dist-info/entry_points.txt,sha256=Hdz1WC9waUzyFVmowKnbbZ6j-J4adHh_Ko6JpxGYAtE,131
103
+ tb2j-0.9.9rc17.dist-info/top_level.txt,sha256=whYa5ByLYhl5XnTPBHSWr-IGD6VWmr5Ql2bye2qwV_s,5
104
+ tb2j-0.9.9rc17.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5