TB2J 0.9.9rc6__py3-none-any.whl → 0.9.9rc7__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 (26) hide show
  1. TB2J/Jdownfolder.py +11 -9
  2. TB2J/MAEGreen.py +96 -22
  3. TB2J/anisotropy.py +104 -30
  4. TB2J/exchange.py +0 -11
  5. TB2J/interfaces/siesta_interface.py +2 -1
  6. TB2J/io_exchange/io_exchange.py +44 -5
  7. TB2J/io_exchange/io_matjes.py +225 -0
  8. TB2J/mathutils/fibonacci_sphere.py +74 -0
  9. TB2J/orbital_magmom.py +36 -0
  10. TB2J/symmetrize_J.py +23 -12
  11. {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/METADATA +16 -7
  12. {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/RECORD +26 -23
  13. {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/WHEEL +1 -1
  14. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_downfold.py +0 -0
  15. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_eigen.py +0 -0
  16. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_magnon.py +0 -0
  17. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_magnon_dos.py +0 -0
  18. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_merge.py +0 -0
  19. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_rotate.py +0 -0
  20. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_rotateDM.py +0 -0
  21. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/abacus2J.py +0 -0
  22. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/siesta2J.py +0 -0
  23. {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/wann2J.py +0 -0
  24. {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/entry_points.txt +0 -0
  25. {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info/licenses}/LICENSE +0 -0
  26. {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/top_level.txt +0 -0
TB2J/Jdownfolder.py CHANGED
@@ -1,7 +1,9 @@
1
1
  import os
2
2
  from collections import defaultdict
3
+
3
4
  import numpy as np
4
5
  from ase.dft.kpoints import monkhorst_pack
6
+
5
7
  from TB2J.io_exchange import SpinIO
6
8
  from TB2J.Jtensor import decompose_J_tensor
7
9
 
@@ -73,8 +75,8 @@ class JDownfolder:
73
75
  class PWFDownfolder:
74
76
  def __init__(self, JR, Rlist, iM, iL, qmesh, atoms=None, iso_only=False, **kwargs):
75
77
  from lawaf.interfaces.magnon.magnon_downfolder import (
76
- MagnonWrapper,
77
78
  MagnonDownfolder,
79
+ MagnonWrapper,
78
80
  )
79
81
 
80
82
  model = MagnonWrapper(JR, Rlist, atoms)
@@ -92,13 +94,13 @@ class PWFDownfolder:
92
94
  # anchors={(0, 0, 0): (-1, -2, -3, -4)},
93
95
  # anchors={(0, 0, 0): ()},
94
96
  # use_proj=True,
95
- enhance_Amn=2.0,
97
+ enhance_Amn=0.0,
96
98
  )
97
99
  params.update(kwargs)
98
100
  wann.set_parameters(**params)
99
101
  print("begin downfold")
100
102
  ewf = wann.downfold()
101
- ewf.save_hr_pickle("downfolded_JR.pickle")
103
+ # ewf.save_hr_pickle("downfolded_JR.pickle")
102
104
 
103
105
  # Plot the band structure.
104
106
  wann.plot_band_fitting(
@@ -135,7 +137,7 @@ class JDownfolder_pickle:
135
137
  qmesh=[7, 7, 7],
136
138
  iso_only=False,
137
139
  method="pwf",
138
- **kwargs
140
+ **kwargs,
139
141
  ):
140
142
  self.exc = SpinIO.load_pickle(path=inpath, fname="TB2J.pickle")
141
143
 
@@ -193,7 +195,7 @@ class JDownfolder_pickle:
193
195
  qmesh=self.qmesh,
194
196
  atoms=self.atoms,
195
197
  iso_only=self.iso_only,
196
- **kwargs
198
+ **kwargs,
197
199
  )
198
200
  Jd, Rlist = d.get_JR()
199
201
  return Jd, Rlist
@@ -274,10 +276,10 @@ class JDownfolder_pickle:
274
276
  def test():
275
277
  # pass
276
278
  # inpath = "/home/hexu/projects/NiCl2/vasp_inputs/TB2J_results"
277
- # inpath = "/home/hexu/projects/TB2J_example/CrI3/TB2J_results"
278
- inpath = "/home/hexu/projects/TB2J_projects/NiCl2/TB2J_NiCl/TB2J_results"
279
- fname = os.path.join(inpath, "TB2J.pickle")
280
- p = JDownfolder_pickle(
279
+ inpath = "/home/hexu/projects/TB2J_example/CrI3/TB2J_results"
280
+ # inpath = "/home/hexu/projects/TB2J_projects/NiCl2/TB2J_NiCl/TB2J_results"
281
+ _fname = os.path.join(inpath, "TB2J.pickle")
282
+ _p = JDownfolder_pickle(
281
283
  inpath=inpath, metals=["Ni"], ligands=["Cl"], outpath="TB2J_results_downfolded"
282
284
  )
283
285
 
TB2J/MAEGreen.py CHANGED
@@ -1,13 +1,17 @@
1
+ import gc
1
2
  from pathlib import Path
2
3
 
4
+ import matplotlib.pyplot as plt
3
5
  import numpy as np
4
6
  import tqdm
5
7
  from HamiltonIO.abacus.abacus_wrapper import AbacusSplitSOCParser
6
8
  from HamiltonIO.model.occupations import GaussOccupations
7
9
  from typing_extensions import DefaultDict
8
10
 
11
+ from TB2J.anisotropy import Anisotropy
9
12
  from TB2J.exchange import ExchangeNCL
10
13
  from TB2J.external import p_map
14
+ from TB2J.mathutils.fibonacci_sphere import fibonacci_semisphere
11
15
 
12
16
  # from HamiltonIO.model.rotate_spin import rotate_Matrix_from_z_to_axis, rotate_Matrix_from_z_to_sperical
13
17
  # from TB2J.abacus.abacus_wrapper import AbacusWrapper, AbacusParser
@@ -23,12 +27,21 @@ def get_occupation(evals, kweights, nel, width=0.1):
23
27
 
24
28
  class MAEGreen(ExchangeNCL):
25
29
  def __init__(self, angles=None, **kwargs):
30
+ """
31
+ angles are defined as theta, phi pairs, where theta is the angle between the z-axis and the magnetization direction, and phi is the angle between the x-axis and the projection of the magnetization direction on the x-y plane.
32
+ """
26
33
  super().__init__(**kwargs)
27
34
  self.natoms = len(self.atoms)
28
35
  if angles is None or angles == "axis":
29
36
  self.set_angles_axis()
30
37
  elif angles == "scan":
31
38
  self.set_angles_scan()
39
+ elif angles == "fib":
40
+ self.set_angles_fib()
41
+ elif angles == "random":
42
+ self.set_angles_random()
43
+ elif angles == "miller":
44
+ self.set_angles_miller()
32
45
  else:
33
46
  self.thetas = angles[0]
34
47
  self.phis = angles[1]
@@ -39,9 +52,32 @@ class MAEGreen(ExchangeNCL):
39
52
  self.es_atom_orb = DefaultDict(lambda: 0)
40
53
 
41
54
  def set_angles_axis(self):
42
- """theta and phi are defined as the x, y, z, axis."""
43
- self.thetas = [0, np.pi / 2, np.pi / 2, np.pi / 2, np.pi]
44
- self.phis = [0, 0, np.pi / 2, np.pi / 4, 0]
55
+ """theta and phi are defined as the x, y, z, xy, yz, xz, xyz, x-yz, -xyz, -x-yz axis."""
56
+ self.thetas = [0, np.pi / 2, np.pi / 2, np.pi / 2, np.pi, 0, np.pi / 2, 0, 0, 0]
57
+ self.phis = [0, 0, np.pi / 2, np.pi / 4, 0, 0, 0, np.pi]
58
+
59
+ def set_angles_miller(self, nmax=2):
60
+ """theta and angles corresponding to the miller index. remove duplicates.
61
+ e.g. 002 and 001 are the same.
62
+ """
63
+ thetas = []
64
+ phis = []
65
+ for k in range(0, nmax + 1):
66
+ for j in range(-nmax, nmax + 1):
67
+ for i in range(-nmax, nmax + 1):
68
+ if i == 0 and j == 0 and k == 0:
69
+ continue
70
+ thetas.append(np.arccos(k / np.sqrt(i**2 + j**2 + k**2)))
71
+ if i == 0 and j == 0:
72
+ phis.append(0)
73
+ else:
74
+ phis.append(np.arctan2(j, i))
75
+ self.thetas = thetas
76
+ self.phis = phis
77
+ self.angle_pairs = list(zip(thetas, phis))
78
+ # remove duplicates of angles using sets.
79
+ self.angle_pairs = list(set(self.angle_pairs))
80
+ self.thetas, self.phis = zip(*self.angle_pairs)
45
81
 
46
82
  def set_angles_scan(self, step=15):
47
83
  self.thetas = []
@@ -51,6 +87,21 @@ class MAEGreen(ExchangeNCL):
51
87
  self.thetas.append(i * np.pi / 180)
52
88
  self.phis.append(j * np.pi / 180)
53
89
 
90
+ def set_angles_random(self, n=16):
91
+ # n random pairs of theta, phi
92
+ self.thetas = np.random.random(n) * np.pi
93
+ self.phis = np.random.random(n) * 2 * np.pi
94
+
95
+ def set_angles_fib(self, n=35):
96
+ self.thetas, self.phis = fibonacci_semisphere(n)
97
+ # thetas and phis are np.array
98
+ # add (theta, phi): (pi/2, pi/2) and (pi/2, pi/4)
99
+ # self.thetas += [np.pi / 2, np.pi / 2]
100
+ # self.phis += [np.pi / 2, np.pi / 4]
101
+ for i in range(8):
102
+ self.thetas = np.concatenate([self.thetas, [np.pi / 2]])
103
+ self.phis = np.concatenate([self.phis, [np.pi * i / 8]])
104
+
54
105
  def get_band_energy_vs_angles_from_eigen(
55
106
  self,
56
107
  thetas,
@@ -79,16 +130,12 @@ class MAEGreen(ExchangeNCL):
79
130
 
80
131
  def get_perturbed(self, e, thetas, phis):
81
132
  self.tbmodel.set_so_strength(0.0)
82
- maxsoc = self.tbmodel.get_max_Hsoc_abs()
83
- maxH0 = self.tbmodel.get_max_H0_spin_abs()
84
- if maxsoc > maxH0 * 0.01:
85
- print(f"""Warning: The SOC of the Hamiltonian has a maximum of {maxsoc} eV,
86
- comparing to the maximum of {maxH0} eV of the spin part of the Hamiltonian.
87
- The SOC is too strong, the perturbation theory may not be valid.""")
88
-
89
- print(f"""Warning: The SOC of the Hamiltonian has a maximum of {maxsoc} eV,
90
- comparing to the maximum of {maxH0} eV of the spin part of the Hamiltonian.
91
- The SOC is too strong, the perturbation theory may not be valid.""")
133
+ # maxsoc = self.tbmodel.get_max_Hsoc_abs()
134
+ # maxH0 = self.tbmodel.get_max_H0_spin_abs()
135
+ # if maxsoc > maxH0 * 0.01:
136
+ # print(f"""Warning: The SOC of the Hamiltonian has a maximum of {maxsoc} eV,
137
+ # comparing to the maximum of {maxH0} eV of the spin part of the Hamiltonian.
138
+ # The SOC is too strong, the perturbation theory may not be valid.""")
92
139
 
93
140
  G0K = self.G.get_Gk_all(e)
94
141
  Hsoc_k = self.tbmodel.get_Hk_soc(self.G.kpts)
@@ -104,12 +151,12 @@ class MAEGreen(ExchangeNCL):
104
151
  GdH = G0K[ik] @ dHi
105
152
  # dE += np.trace(GdH @ G0K[i].T.conj() @ dHi) * self.kweights[i]
106
153
  # diagonal of second order perturbation.
107
- dG2diag = np.diag(GdH @ GdH)
154
+ # dG2diag = np.diag(GdH @ GdH)
108
155
  # dG2 = np.einsum("ij,ji->ij", GdH, GdH)
109
156
  dG2 = GdH * GdH.T
110
157
  dG2sum = np.sum(dG2)
111
158
  # print(f"dG2sum-sum: {dG2sum}")
112
- dG2sum = np.sum(dG2diag)
159
+ # dG2sum = np.sum(dG2diag)
113
160
 
114
161
  # dG2sum = np.trace(GdH @ GdH)
115
162
  # print(f"dG2sum-Tr: {dG2sum}")
@@ -130,13 +177,11 @@ class MAEGreen(ExchangeNCL):
130
177
  + dE_atom_orb[1::2, ::2]
131
178
  + dE_atom_orb[::2, 1::2]
132
179
  )
180
+ dE_atom = np.sum(dE_atom_orb)
133
181
  mmat = self.mmats[iatom]
134
182
  dE_atom_orb = mmat.T @ dE_atom_orb @ mmat
135
-
136
183
  dE_angle_atom_orb[(iangle, iatom)] += dE_atom_orb
137
-
138
- dE_atom = np.sum(dG2diag[iorb]) * self.G.kweights[ik]
139
- # dE_atom = np.sum(dE_atom_orb)
184
+ # dE_atom = np.sum(dG2diag[iorb]) * self.G.kweights[ik]
140
185
  dE_angle_atom[iangle, iatom] += dE_atom
141
186
  return dE_angle, dE_angle_atom, dE_angle_atom_orb
142
187
 
@@ -189,10 +234,25 @@ class MAEGreen(ExchangeNCL):
189
234
  for key, value in self.es_atom_orb.items():
190
235
  self.es_atom_orb[key] = -np.imag(value) / (2 * np.pi)
191
236
 
192
- def output(self, output_path="TB2J_anisotropy", with_eigen=True):
237
+ def fit_anisotropy_tensor(self):
238
+ self.ani = Anisotropy.fit_from_data(self.thetas, self.phis, self.es)
239
+ return self.ani
240
+
241
+ def output(
242
+ self,
243
+ output_path="TB2J_anisotropy",
244
+ with_eigen=True,
245
+ figure3d="MAE_3d.png",
246
+ figure_contourf="MAE_contourf.png",
247
+ ):
193
248
  Path(output_path).mkdir(exist_ok=True)
194
249
  fname = f"{output_path}/MAE.dat"
195
250
  fname_orb = f"{output_path}/MAE_orb.dat"
251
+ fname_tensor = f"{output_path}/MAE_tensor.dat"
252
+ if figure3d is not None:
253
+ fname_fig3d = f"{output_path}/{figure3d}"
254
+ if figure_contourf is not None:
255
+ fname_figcontourf = f"{output_path}/{figure_contourf}"
196
256
 
197
257
  # ouput with eigenvalues.
198
258
  if with_eigen:
@@ -208,15 +268,29 @@ class MAEGreen(ExchangeNCL):
208
268
  f.write("\n")
209
269
 
210
270
  with open(fname, "w") as f:
211
- f.write("# theta, phi, MAE(total), MAE(atom-wise) Unit: meV\n")
271
+ f.write("# theta (rad), phi(rad), MAE(total), MAE(atom-wise) Unit: meV\n")
212
272
  for i, (theta, phi, e, es) in enumerate(
213
273
  zip(self.thetas, self.phis, self.es, self.es_atom)
214
274
  ):
215
- f.write(f"{theta:.5f} {phi:.5f} {e*1e3:.8f} ")
275
+ f.write(f"{theta%np.pi:.5f} {phi%(2*np.pi):.5f} {e*1e3:.8f} ")
216
276
  for ea in es:
217
277
  f.write(f"{ea*1e3:.8f} ")
218
278
  f.write("\n")
219
279
 
280
+ self.ani = self.fit_anisotropy_tensor()
281
+ with open(fname_tensor, "w") as f:
282
+ f.write("# Anisotropy tensor in meV\n")
283
+ f.write(f"{self.ani.tensor_strings(include_isotropic=False)}\n")
284
+
285
+ if figure3d is not None:
286
+ self.ani.plot_3d(figname=fname_fig3d, show=False)
287
+
288
+ if figure_contourf is not None:
289
+ self.ani.plot_contourf(figname=fname_figcontourf, show=False)
290
+
291
+ plt.close()
292
+ gc.collect()
293
+
220
294
  with open(fname_orb, "w") as f:
221
295
  f.write("=" * 80 + "\n")
222
296
  f.write("Orbitals for each atom: \n")
TB2J/anisotropy.py CHANGED
@@ -1,11 +1,12 @@
1
1
  import random
2
- import numpy as np
2
+ from dataclasses import dataclass
3
+ from pathlib import Path
4
+
3
5
  import matplotlib.pyplot as plt
6
+ import numpy as np
7
+ from numpy.linalg import matrix_rank
4
8
  from scipy.interpolate import LinearNDInterpolator
5
9
  from scipy.optimize import curve_fit
6
- from numpy.linalg import matrix_rank
7
- from dataclasses import dataclass
8
- from pathlib import Path
9
10
 
10
11
 
11
12
  @dataclass
@@ -18,8 +19,9 @@ class Anisotropy:
18
19
  @classmethod
19
20
  def from_T6(cls, T6):
20
21
  T = T6_to_T(T6)
21
- T -= np.trace(T) / 3 * np.eye(3)
22
- direction, amplitude = aniostropy_tensor_to_vector(T)
22
+ isotropic_part = np.trace(T) / 3
23
+ T -= isotropic_part * np.eye(3)
24
+ direction, amplitude = anisotropy_tensor_to_vector(T)
23
25
  return cls(
24
26
  direction=direction, amplitude=amplitude, isotropic_part=isotropic_part, T=T
25
27
  )
@@ -33,9 +35,9 @@ class Anisotropy:
33
35
 
34
36
  @classmethod
35
37
  def from_tensor(cls, T):
36
- isotropic_part = np.trace(T) / 3
38
+ isotropic_part = np.trace(T) / 3 * np.eye(3)
37
39
  T -= np.trace(T) / 3 * np.eye(3)
38
- direction, amplitude = aniostropy_tensor_to_vector(T)
40
+ direction, amplitude = anisotropy_tensor_to_vector(T)
39
41
  return cls(
40
42
  T=T, direction=direction, amplitude=amplitude, isotropic_part=isotropic_part
41
43
  )
@@ -44,13 +46,14 @@ class Anisotropy:
44
46
  if self.isotropic_part is None:
45
47
  self.isotropic_part = 0
46
48
  if self.T is None:
47
- self.T = anisotropy_vector_to_tensor(
48
- self.direction, self.amplitude
49
- ) + self.isotropic_part * np.eye(3)
49
+ self.T = (
50
+ anisotropy_vector_to_tensor(self.direction, self.amplitude)
51
+ + self.isotropic_part
52
+ )
50
53
  elif self.direction is None or self.amplitude is None:
51
- self.isotropic_part = np.trace(self.T) / 3
52
- # print(f'aniostropic tensor = {self.anisotropic_part}')
53
- self.direction, self.amplitude = aniostropy_tensor_to_vector(
54
+ self.isotropic_part = np.trace(self.T) / 3 * np.eye(3)
55
+ # print(f'anisotropic tensor = {self.anisotropic_part}')
56
+ self.direction, self.amplitude = anisotropy_tensor_to_vector(
54
57
  self.anisotropic_part
55
58
  )
56
59
  self.isotropic_part = np.trace(self.T) / 3
@@ -113,7 +116,6 @@ class Anisotropy:
113
116
  E = -self.amplitude * (S @ self.direction) ** 2
114
117
  if include_isotropic:
115
118
  E = E + self.isotropic_part
116
- print(f"E shape = {E.shape}")
117
119
  return E
118
120
 
119
121
  def energy_tensor_form(self, S=None, angle=None, include_isotropic=False):
@@ -126,7 +128,7 @@ class Anisotropy:
126
128
  return -S.T @ self.anisotropic_part @ S
127
129
 
128
130
  @classmethod
129
- def fit_from_data(cls, thetas, phis, values, test=False):
131
+ def fit_from_data(cls, thetas, phis, values, test=False, units="rad"):
130
132
  """
131
133
  Fit the anisotropic tensor to the data
132
134
  parameters:
@@ -137,14 +139,16 @@ class Anisotropy:
137
139
  the anisotropic object fitted from the data
138
140
  """
139
141
  angles = np.vstack([thetas, phis])
142
+ if units.lower().startswith("deg"):
143
+ angles = np.deg2rad(angles)
140
144
  params, cov = curve_fit(anisotropy_energy, angles, values)
141
145
  fitted_values = anisotropy_energy(angles, *params)
142
146
 
143
147
  delta = fitted_values - values
144
148
 
145
149
  # print(f'Max value = {np.max(values)}, Min value = {np.min(values)}')
146
- if np.abs(delta).max() > 1e-5:
147
- print(f"Warning: The fitting is not consistent with the data.")
150
+ if np.abs(delta).max() > 1e-4:
151
+ print("Warning: The fitting is not consistent with the data.")
148
152
  print(f"Max-min = {np.max(values) - np.min(values)}")
149
153
  print(f"delta = {np.max(np.abs(delta))}")
150
154
  T = T6_to_T(params)
@@ -157,7 +161,7 @@ class Anisotropy:
157
161
  angle=[thetas[i], phis[i]], include_isotropic=True
158
162
  )
159
163
  values2.append(E)
160
- delta2 = np.array(values2) - values
164
+ # delta2 = np.array(values2) - values
161
165
  # print(delta2)
162
166
 
163
167
  ax = plot_3D_scatter(angles, values - np.min(values), color="r")
@@ -183,7 +187,7 @@ class Anisotropy:
183
187
  delta = fitted_values - values
184
188
  print(f"Max value = {np.max(values)}, Min value = {np.min(values)}")
185
189
  print(f"Max-min = {np.max(values) - np.min(values)}")
186
- # print(f'delta = {delta}')
190
+ print(f"delta = {delta}")
187
191
  theta_a, phi_a, amplitude, isotropic_part = params
188
192
  direction = sphere_to_cartesian([theta_a, phi_a])
189
193
  return cls.from_direction_amplitude(
@@ -208,6 +212,25 @@ class Anisotropy:
208
212
  else:
209
213
  raise ValueError(f"Unknown method {method}")
210
214
 
215
+ @classmethod
216
+ def fit_from_xyz_data_file(cls, fname, method="tensor"):
217
+ """
218
+ Fit the anisotropic tensor to the data with x y z val form
219
+ parameters:
220
+ fname: the file name of the data
221
+ Return:
222
+ anisotropy: the anisotropic object
223
+ """
224
+ data = np.loadtxt(fname)
225
+ xyz, value = data[:, 0:3], data[:, 3]
226
+ theta, phi = np.array([cartesian_to_sphere(t) for t in xyz]).T
227
+ if method == "tensor":
228
+ return cls.fit_from_data(theta, phi, value)
229
+ elif method == "vector":
230
+ return cls.fit_from_data_vector_form(theta, phi, value)
231
+ else:
232
+ raise ValueError(f"Unknown method {method}")
233
+
211
234
  def plot_3d(self, ax=None, figname=None, show=True, surface=True):
212
235
  """
213
236
  plot the anisotropic energy in all directions in 3D
@@ -297,6 +320,55 @@ class Anisotropy:
297
320
  if show:
298
321
  plt.show()
299
322
 
323
+ def plot_contourf(self, ax=None, figname=None, show=False):
324
+ if ax is None:
325
+ fig, ax = plt.subplots()
326
+ X, Y = np.meshgrid(np.arange(0, 180, 1), np.arange(-180, 180, 1))
327
+ Z = np.zeros(X.shape)
328
+ ntheta, nphi = X.shape
329
+ for i in range(ntheta):
330
+ for j in range(nphi):
331
+ E = self.energy_tensor_form(angle=[X[i, j], Y[i, j]])
332
+ Z[i, j] = E
333
+ # find the X, Y for min and max of Z
334
+ X_max, Y_max = np.unravel_index(np.argmax(Z), Z.shape)
335
+ X_min, Y_min = np.unravel_index(np.argmin(Z), Z.shape)
336
+ X_max, Y_max = X[X_max, Y_max], Y[X_max, Y_max]
337
+ X_min, Y_min = X[X_min, Y_min], Y[X_min, Y_min]
338
+ c = ax.contourf(X, Y, Z, cmap="viridis", levels=200)
339
+ # print(X_max, Y_max, X_min, Y_min)
340
+ # ax.scatter(X_max, Y_max, color="r", marker="o")
341
+ # ax.scatter(X_min, Y_min, color="b", marker="o")
342
+ ax.set_xlabel("$\theta$ (degree)")
343
+ ax.set_ylabel("$\phi$ degree")
344
+ # ax.scatter(X_max, Y_max, color="r", marker="o")
345
+ # ax.scatter(X_min, Y_min, color="r", marker="o")
346
+
347
+ # colorbar
348
+ _cbar = plt.colorbar(c, ax=ax)
349
+
350
+ if figname is not None:
351
+ plt.savefig(figname)
352
+ plt.close()
353
+ if show:
354
+ print(f"Max = {X_max}, {Y_max}, Min = {X_min}, {Y_min}")
355
+ plt.show()
356
+ return ax
357
+
358
+ def tensor_strings(self, include_isotropic=False, multiplier=1):
359
+ """
360
+ convert the energy tensor to strings for easy printing
361
+ parameters:
362
+ include_isotropic: if include the isotropic part
363
+ multiplier: the multiplier for the tensor. Use for scaling the tensor by units.
364
+ """
365
+ if include_isotropic:
366
+ T = self.T
367
+ else:
368
+ T = self.T
369
+ strings = np.array2string(T * multiplier, precision=5, separator=" ")
370
+ return strings
371
+
300
372
 
301
373
  def plot_3D_scatter(angles, values, ax=None, **kwargs):
302
374
  if ax is None:
@@ -389,7 +461,7 @@ def sphere_to_cartesian(angles, r=1):
389
461
  return np.array([x, y, z])
390
462
 
391
463
 
392
- def cartesian_to_sphere(xyz):
464
+ def cartesian_to_sphere(xyz, unit="deg"):
393
465
  """
394
466
  Transform the cartesian coordinates to the spherical coordinates
395
467
  parameters:
@@ -401,8 +473,13 @@ def cartesian_to_sphere(xyz):
401
473
  r = np.linalg.norm(xyz)
402
474
  theta = np.arccos(z / r)
403
475
  phi = np.arctan2(y, x)
404
- theta = theta * 180 / np.pi
405
- phi = phi * 180 / np.pi
476
+ if unit.lower().startswith("deg"):
477
+ theta = theta * 180 / np.pi
478
+ phi = phi * 180 / np.pi
479
+ elif unit.lower.startswith("rad"):
480
+ pass
481
+ else:
482
+ raise ValueError("unit must be 'deg' or 'rad'")
406
483
  return np.array([theta, phi])
407
484
 
408
485
 
@@ -470,7 +547,7 @@ def fit_anisotropy(thetas, phis, values):
470
547
  params, cov = curve_fit(anisotropy_energy, angles, values)
471
548
  # check if the fitting is consistent with the data
472
549
  T = T6_to_T(params)
473
- direction, amp = anisostropy_tensor_to_vector(T)
550
+ direction, amp = anisotropy_tensor_to_vector(T)
474
551
  return direction, amp
475
552
 
476
553
 
@@ -518,7 +595,7 @@ def anisotropy_vector_to_tensor(direction, amplitude):
518
595
  return T
519
596
 
520
597
 
521
- def aniostropy_tensor_to_vector(T):
598
+ def anisotropy_tensor_to_vector(T):
522
599
  """
523
600
  Transform the anisotropic tensor to the anisotropic vector
524
601
  parameters:
@@ -553,7 +630,7 @@ def test_anisotorpy_vector_to_tensor():
553
630
  assert np.abs(diff) < 1e-10
554
631
 
555
632
  # test if the inverse transformation get the direction and amplitude back
556
- dir2, amp2 = aniostropy_tensor_to_vector(T)
633
+ dir2, amp2 = anisotropy_tensor_to_vector(T)
557
634
 
558
635
  # set the first element of the direction to be positive
559
636
  if direction[0] * dir2[0] < 0:
@@ -569,7 +646,7 @@ def test_anisotropy_tensor_to_vector():
569
646
  T = np.random.rand(3, 3)
570
647
  T = T + T.T
571
648
  T = T - np.trace(T) / 3
572
- direction, amplitude = aniostropy_tensor_to_vector(T)
649
+ direction, amplitude = anisotropy_tensor_to_vector(T)
573
650
  T2 = anisotropy_vector_to_tensor(direction, amplitude)
574
651
  print(f"T = {T}")
575
652
  print(f"T2 = {T2}")
@@ -597,9 +674,6 @@ def test_fit_anisotropy():
597
674
  f"theta = {theta[i]}, phi = {phi[i]}, value = {value[i]}, fitted value = {fitted_values[i]}, delta = {delta[i]}"
598
675
  )
599
676
 
600
- easy_axis = easy_axis_from_tensor(T6)
601
- print(f"easy_axis = {easy_axis}")
602
-
603
677
 
604
678
  def view_anisotropy_strain():
605
679
  strains1 = [(x, 0.0) for x in np.arange(0.000, 0.021, 0.002)]
TB2J/exchange.py CHANGED
@@ -566,23 +566,12 @@ class ExchangeNCL(Exchange):
566
566
  AijRs: a list of AijR,
567
567
  wherer AijR: array of ((nR, n, n, 4,4), dtype=complex)
568
568
  """
569
- # if method == "trapezoidal":
570
- # integrate = trapezoidal_nonuniform
571
- # elif method == "simpson":
572
- # integrate = simpson_nonuniform
573
- #
574
-
575
- # self.rho = integrate(self.contour.path, rhoRs)
576
569
  for iR, R in enumerate(self.R_ijatom_dict):
577
570
  for iatom, jatom in self.R_ijatom_dict[R]:
578
571
  f = AijRs[(R, iatom, jatom)]
579
- # self.A_ijR[(R, iatom, jatom)] = integrate(self.contour.path, f)
580
572
  self.A_ijR[(R, iatom, jatom)] = self.contour.integrate_values(f)
581
573
 
582
574
  if self.orb_decomposition:
583
- # self.A_ijR_orb[(R, iatom, jatom)] = integrate(
584
- # self.contour.path, AijRs_orb[(R, iatom, jatom)]
585
- # )
586
575
  self.contour.integrate_values(AijRs_orb[(R, iatom, jatom)])
587
576
 
588
577
  def get_quantities_per_e(self, e):
@@ -156,6 +156,7 @@ Warning: The DMI component parallel to the spin orientation, the Jani which has
156
156
  atoms=model.atoms,
157
157
  basis=basis,
158
158
  efermi=None,
159
+ angles="miller",
159
160
  # magnetic_elements=magnetic_elements,
160
161
  # include_orbs=include_orbs,
161
162
  **exargs,
@@ -163,7 +164,7 @@ Warning: The DMI component parallel to the spin orientation, the Jani which has
163
164
  # thetas = [0, np.pi / 2, np.pi, 3 * np.pi / 2]
164
165
  # phis = [0, 0, 0, 0]
165
166
  # MAE.set_angles(thetas=thetas, phis=phis)
166
- MAE.run(output_path=f"{output_path}_anisotropy", with_eigen=True)
167
+ MAE.run(output_path=f"{output_path}_anisotropy", with_eigen=False)
167
168
  print(
168
169
  f"MAE calculation finished. The results are in {output_path} directory."
169
170
  )
@@ -13,6 +13,11 @@ import pickle
13
13
  from collections.abc import Iterable
14
14
  from datetime import datetime
15
15
 
16
+ import matplotlib
17
+
18
+ matplotlib.use("Agg")
19
+ import gc
20
+
16
21
  import matplotlib.pyplot as plt
17
22
  import numpy as np
18
23
 
@@ -320,7 +325,11 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
320
325
  i = self.i_spin(i)
321
326
  j = self.i_spin(j)
322
327
  if iso_only:
323
- Jtensor = np.eye(3) * self.get_J(i, j, R)
328
+ J = self.get_Jiso(i, j, R)
329
+ if J is not None:
330
+ Jtensor = np.eye(3) * self.get_J(i, j, R)
331
+ else:
332
+ Jtensor = np.eye(3) * 0
324
333
  else:
325
334
  Jtensor = combine_J_tensor(
326
335
  Jiso=self.get_J(i, j, R),
@@ -329,7 +338,13 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
329
338
  )
330
339
  return Jtensor
331
340
 
332
- def get_full_Jtensor_for_one_R(self, R):
341
+ def get_J_tensor_dict(self):
342
+ Jdict = {}
343
+ for i, j, R in self.ijR_list():
344
+ Jdict[(i, j, R)] = self.get_J_tensor(i, j, R)
345
+ return Jdict
346
+
347
+ def get_full_Jtensor_for_one_R(self, R, iso_only=False):
333
348
  """
334
349
  Return the full exchange tensor of all i and j for cell R.
335
350
  param R (tuple of integers): cell index R
@@ -340,15 +355,17 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
340
355
  Jmat = np.zeros((n3, n3), dtype=float)
341
356
  for i in range(self.nspin):
342
357
  for j in range(self.nspin):
343
- Jmat[i * 3 : i * 3 + 3, j * 3 : j * 3 + 3] = self.get_J_tensor(i, j, R)
358
+ Jmat[i * 3 : i * 3 + 3, j * 3 : j * 3 + 3] = self.get_J_tensor(
359
+ i, j, R, iso_only=iso_only
360
+ )
344
361
  return Jmat
345
362
 
346
- def get_full_Jtensor_for_Rlist(self, asr=False, iso_only=False):
363
+ def get_full_Jtensor_for_Rlist(self, asr=False, iso_only=True):
347
364
  n3 = self.nspin * 3
348
365
  nR = len(self.Rlist)
349
366
  Jmat = np.zeros((nR, n3, n3), dtype=float)
350
367
  for iR, R in enumerate(self.Rlist):
351
- Jmat[iR] = self.get_full_Jtensor_for_one_R(R)
368
+ Jmat[iR] = self.get_full_Jtensor_for_one_R(R, iso_only=iso_only)
352
369
  if asr:
353
370
  iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
354
371
  assert np.linalg.norm(self.Rlist[iR0]) == 0
@@ -394,6 +411,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
394
411
  self.write_multibinit(path=os.path.join(path, "Multibinit"))
395
412
  self.write_tom_format(path=os.path.join(path, "TomASD"))
396
413
  self.write_vampire(path=os.path.join(path, "Vampire"))
414
+ self.write_matjes(path=os.path.join(path, "Matjes"))
397
415
 
398
416
  self.plot_all(savefile=os.path.join(path, "JvsR.pdf"))
399
417
  # self.write_Jq(kmesh=[9, 9, 9], path=path)
@@ -544,6 +562,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
544
562
  plt.show()
545
563
  plt.clf()
546
564
  plt.close()
565
+ gc.collect() # This is to fix the tk error if multiprocess is used.
547
566
  return fig, axes
548
567
 
549
568
  def write_tom_format(self, path):
@@ -561,6 +580,11 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
561
580
 
562
581
  write_uppasd(self, path=path)
563
582
 
583
+ def write_matjes(self, path):
584
+ from TB2J.io_exchange.io_matjes import write_matjes
585
+
586
+ write_matjes(self, path=path)
587
+
564
588
 
565
589
  def gen_distance_dict(ind_mag_atoms, atoms, Rlist):
566
590
  distance_dict = {}
@@ -576,6 +600,21 @@ def gen_distance_dict(ind_mag_atoms, atoms, Rlist):
576
600
  return distance_dict
577
601
 
578
602
 
603
+ def get_ind_shell(distance_dict, symprec=1e-5):
604
+ """
605
+ return a dictionary of shell index for each pair of atoms.
606
+ The index of shell is the ith shortest distances between all magnetic atom pairs.
607
+ """
608
+ shell_dict = {}
609
+ distances = np.array([x[1] for x in distance_dict.values()])
610
+ distances_int = np.round(distances / symprec).astype(int)
611
+ dint = sorted(np.unique(distances_int))
612
+ dintdict = dict(zip(dint, range(len(dint))))
613
+ for key, val in enumerate(distances_int):
614
+ shell_dict[key] = dintdict[val]
615
+ return shell_dict
616
+
617
+
579
618
  def test_spin_io():
580
619
  import numpy as np
581
620
  from ase import Atoms
@@ -0,0 +1,225 @@
1
+ import os
2
+
3
+ import numpy as np
4
+
5
+ from TB2J.utils import symbol_number
6
+ from ase.units import Bohr, nm
7
+
8
+
9
+ def get_ind_shell(distance_dict, symprec=1e-5):
10
+ """
11
+ return a dictionary of shell index for each pair of atoms.
12
+ The index of shell is the ith shortest distances between all magnetic atom pairs.
13
+ """
14
+ shell_dict = {}
15
+ distances = np.array([x[1] for x in distance_dict.values()])
16
+ distances_int = np.round(distances / symprec).astype(int)
17
+ dint = sorted(np.unique(distances_int))
18
+ dintdict = dict(zip(dint, range(len(dint))))
19
+ for key, val in distance_dict.items():
20
+ di = np.round(val[1] / symprec).astype(int)
21
+ shell_dict[key] = dintdict[di]
22
+ return shell_dict
23
+
24
+
25
+ def _write_symmetry(cls, path, fname="symmetry.in", symmetry=True):
26
+ fname = os.path.join(path, fname)
27
+ if not symmetry:
28
+ with open(fname, "w") as myfile:
29
+ myfile.write("1 \n")
30
+ myfile.write("\n")
31
+ myfile.write("! identity operation\n")
32
+ myfile.write("1.0000000000 0.0000000000 0.0000000000\n")
33
+ myfile.write("0.0000000000 1.0000000000 0.0000000000\n")
34
+ myfile.write("0.0000000000 0.0000000000 1.0000000000\n")
35
+ myfile.write("0.0000000000 0.0000000000 0.0000000000\n")
36
+ else:
37
+ raise NotImplementedError("Symmetry not implemented yet")
38
+
39
+
40
+ def write_matjes(cls, path="TB2J_results/Matjes", symmetry=False):
41
+ if not os.path.exists(path):
42
+ os.makedirs(path)
43
+ inputfname = os.path.join(path, "input")
44
+ with open(inputfname, "w") as myfile:
45
+ _write_lattice_supercell(cls, myfile)
46
+ _write_atoms(cls, myfile)
47
+ _write_magnetic_interactions(cls, myfile)
48
+ #_write_isotropic_exchange(cls, myfile, symmetry=symmetry)
49
+ _write_magnetic_anisotropy(cls, myfile)
50
+ # _write_dmi(cls, myfile)
51
+ _write_exchange_tensor(cls, myfile, symmetry=symmetry)
52
+ _write_parameters(cls, myfile, symmetry=symmetry)
53
+ print(f"writting symmetries ")
54
+ _write_symmetry(cls, path, fname="symmetries.in", symmetry=False)
55
+
56
+ def _write_parameters(cls, myfile, symmetry=False):
57
+ if symmetry:
58
+ myfile.write("cal_sym T\n")
59
+ myfile.write("sym_mode 2\n")
60
+ else:
61
+ myfile.write("cal_sym F \n")
62
+ myfile.write("sym_mode 0\n")
63
+
64
+ def _write_lattice_supercell(cls, myfile):
65
+ myfile.write("# Lattice and supercell\n")
66
+ myfile.write(
67
+ "Periodic_log .T. .T. .T. # periodic boundary conditions along vector 1, 2 and 3\n"
68
+ )
69
+ myfile.write("Nsize 8 8 8 # size of the supercell\n")
70
+ try:
71
+ unitcell = cls.atoms.get_cell().reshape((3, 3))
72
+ except Exception:
73
+ unitcell = cls.atoms.get_cell().array.reshape((3, 3))
74
+ uc_lengths = np.linalg.norm(unitcell, axis=1)
75
+ unitcell /= uc_lengths[:, None]
76
+ myfile.write(f"alat {uc_lengths[0]/nm} {uc_lengths[1]/nm} {uc_lengths[2]/nm} #lattice constant lengths\n")
77
+ myfile.write(
78
+ "lattice #lattice vector should be orthogonal or expressed in cartesian\n")
79
+ myfile.write(
80
+ f"{unitcell[0][0]} {unitcell[0][1]} {unitcell[0][2]} # a_11 a_12 a_1 first lattice vector in line (does not need to be normalize)\n"
81
+ )
82
+ myfile.write(
83
+ f"{unitcell[1][0]} {unitcell[1][1]} {unitcell[1][2]} # a_21 a_22 a_23 second lattice vector in line (does not need to be normalize)\n"
84
+ )
85
+ myfile.write(
86
+ f"{unitcell[2][0]} {unitcell[2][1]} {unitcell[2][2]} # a_31 a_32 a_33 third lattice vector in line\n"
87
+ )
88
+
89
+
90
+ def get_atoms_info(atoms, spinat, symmetry=False):
91
+ if symmetry:
92
+ raise NotImplementedError("Symmetry not implemented yet")
93
+ else:
94
+ symnum = symbol_number(atoms)
95
+ atom_types = list(symnum.keys())
96
+ magmoms = np.linalg.norm(spinat, axis=1)
97
+ masses = atoms.get_masses()
98
+ tags = [i for i in range(len(atom_types))]
99
+ return atom_types, magmoms, masses, tags
100
+
101
+
102
+ def _write_atoms(cls, myfile, symmetry=False):
103
+ myfile.write("\n")
104
+ myfile.write("# Atoms\n")
105
+ atom_types, magmoms, masses, tags = get_atoms_info(
106
+ cls.atoms, cls.spinat, symmetry=symmetry
107
+ )
108
+
109
+ myfile.write(f"atomtypes {len(atom_types)} #Number of types atom\n")
110
+ for i, atom_type in enumerate(atom_types):
111
+ m = magmoms[i]
112
+ mass = masses[i]
113
+ charge = 0
114
+ myfile.write(
115
+ f"{atom_type} {m} {mass} 0.0 F 0 # atom type: (name, mag. moment, mass, charge, displacement, number TB-orb.)\n"
116
+ )
117
+
118
+ myfile.write(
119
+ f"\natoms {len(cls.atoms)} positions of the atom in the unit cell\n"
120
+ )
121
+ for i, atom in enumerate(cls.atoms):
122
+ spos = cls.atoms.get_scaled_positions()[i]
123
+ myfile.write(f"{atom_types[tags[i]]} {spos[0]}, {spos[1]}, {spos[2]} \n")
124
+
125
+
126
+ def _write_magnetic_interactions(cls, myfile, symmetry=False):
127
+ myfile.write("\nThe Hamiltonian\n")
128
+ myfile.write("# Magnetic interactions\n")
129
+
130
+
131
+ def _write_isotropic_exchange(cls, myfile, symmetry=False):
132
+ myfile.write("\nmagnetic_J\n")
133
+ shell_dict = get_ind_shell(cls.distance_dict)
134
+ if symmetry:
135
+ Jdict = cls.reduced_exchange_Jdict
136
+ else:
137
+ Jdict = cls.exchange_Jdict
138
+ for key, val in Jdict.items():
139
+ R, i, j = key
140
+ ishell = shell_dict[(R, i, j)]
141
+ myfile.write(
142
+ f"{i+1} {j+1} {ishell} {val} # between atoms type {i+1} and {j+1}, shell {R}, amplitude in eV \n"
143
+ )
144
+ myfile.write(
145
+ "\nc_H_J -1 apply 1/2 in front of the sum of the exchange energy - default is -1\n"
146
+ )
147
+
148
+
149
+ def _write_magnetic_anisotropy(cls, myfile):
150
+ if cls.k1 is None:
151
+ return
152
+ else:
153
+ myfile.write("\nmagnetic_anisotropy \n")
154
+ for i, k1 in enumerate(cls.k1):
155
+ myfile.write(
156
+ f"{i+1} {cls.k1dir[i][0]} {cls.k1dir[i][1]} {cls.k1dir[i][2]} {k1} anisotropy of atoms type {i+1}, direction {cls.k1dir[i][0]} {cls.k1dir[i][1]} {cls.k1dir[i][2]} and amplitude in eV\n"
157
+ )
158
+ myfile.write("\nc_H_ani 1.0\n")
159
+
160
+
161
+ def _write_dmi(cls, myfile):
162
+ if cls.has_dmi:
163
+ myfile.write("\nmagnetic_D\n")
164
+ for key, val in cls.dmi_ddict.items():
165
+ R, i, j = key
166
+ myfile.write(
167
+ f"{i+1} {j+1} {R[0]} {R[1]} {R[2]} {val} #between atoms type {i+1} and {j+1}, mediated by atom type {R}, shell {R}, amplitude in eV\n"
168
+ )
169
+ myfile.write(
170
+ "\nc_H_D -1.0 # coefficients to put in from of the sum - default is -1\n"
171
+ )
172
+
173
+
174
+ def _write_exchange_tensor(cls, myfile, symmetry=False):
175
+ myfile.write(
176
+ "\nmagnetic_r2_tensor #Exchange tensor elements, middle 9 entries: xx, xy, xz, yx, etc. (in units of eV) and direction in which it should be applied\n"
177
+ )
178
+
179
+ Jtensor = cls.get_J_tensor_dict()
180
+ shelldict = get_ind_shell(cls.distance_dict)
181
+ unitcell = cls.atoms.get_cell().array.reshape((3, 3))
182
+ uc_lengths = np.linalg.norm(unitcell, axis=1)
183
+ if Jtensor is not None:
184
+ for key, val in Jtensor.items():
185
+ i, j, R = key
186
+ # distance vector
187
+ dvec = cls.distance_dict[(R, i, j)][0]/nm/uc_lengths
188
+ dscalar = np.linalg.norm(dvec)
189
+ val = np.real(val)
190
+ ishell = shelldict[(R, i, j)]
191
+ myfile.write(
192
+ f"{i+1} {j+1} {ishell} {' '.join([str(x) for x in val.flatten()])} {dvec[0]} {dvec[1]} {dvec[2]} \n"
193
+ #f"{i+1} {j+1} {dscalar} {' '.join([str(x) for x in val.flatten()])} {dvec[0]} {dvec[1]} {dvec[2]} \n"
194
+ )
195
+ myfile.write(
196
+ "\nc_H_Exchten -1 apply 1/2 in front of the sum of the exchange tensor energy - default is -1\n"
197
+ )
198
+
199
+ def rattle_atoms_and_cell(atoms, stdev=0.001, cell_stdev=0.001):
200
+ """
201
+ Rattle both atomic positions and cell parameters.
202
+
203
+ Parameters:
204
+ -----------
205
+ atoms: ASE atoms object
206
+ The atoms to be rattled
207
+ stdev: float
208
+ Standard deviation for atomic displacement in Angstrom
209
+ cell_stdev: float
210
+ Standard deviation for cell parameter variation (fractional)
211
+
212
+ Returns:
213
+ --------
214
+ None
215
+ The atoms object is modified in-place
216
+ """
217
+ # Rattle atomic positions
218
+ positions = atoms.get_positions()
219
+ displacement = np.random.normal(0, stdev, positions.shape)
220
+ atoms.set_positions(positions + displacement)
221
+
222
+ # Rattle cell parameters
223
+ cell = atoms.get_cell()
224
+ cell_noise = np.random.normal(0, cell_stdev, cell.shape)
225
+ atoms.set_cell(cell * (1 + cell_noise), scale_atoms=True)
@@ -0,0 +1,74 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ scan the spheres in the space of (theta, phi), so that the points are uniformly distributed on a sphere.
4
+ The algorithm is based on fibonacci spiral sampling method.
5
+ Reference:
6
+ https://extremelearning.com.au/how-to-evenly-distribute-points-on-a-sphere-more-effectively-than-the-canonical-fibonacci-lattice/
7
+
8
+ But note that the convention of theta and phi are different from the one in the reference.
9
+ Here, cos(theta) = z, and phi is the azimuthal angle in the x-y plane.
10
+ In the reference, theta is the azimuthal angle in the x-y plane, and phi is the polar angle.
11
+ """
12
+
13
+ import numpy as np
14
+ from numpy import pi
15
+
16
+
17
+ def fibonacci_sphere(samples=100):
18
+ """
19
+ Fibonacci Sphere Sampling Method
20
+ Parameters:
21
+ samples (int): number of points to sample on the sphere
22
+ Returns:
23
+ theta and phi: numpy arrays with shape (samples,) containing the angles of the sampled points.
24
+ """
25
+ # Initialize the golden ratio and angles
26
+ goldenRatio = (1 + np.sqrt(5)) / 2
27
+ phi = np.arange(samples) * (2 * pi / goldenRatio)
28
+ theta = np.arccos(1 - 2 * np.arange(samples) / samples)
29
+ return theta, phi
30
+
31
+
32
+ def fibonacci_semisphere(samples=100):
33
+ """
34
+ Fibonacci Sphere Sampling Method for the upper hemisphere of a sphere.
35
+
36
+ Parameters:
37
+ samples (int): number of points to sample on the sphere
38
+
39
+ Returns:
40
+ theta and phi: numpy arrays with shape (samples,) containing the angles of the sampled points.
41
+ """
42
+ # Initialize the golden ratio and angles
43
+ goldenRatio = (1 + np.sqrt(5)) / 2
44
+ phi = np.arange(samples) * (2 * pi / goldenRatio)
45
+ theta = np.arccos(np.linspace(0, 1, samples))
46
+ return theta, phi
47
+
48
+
49
+ def test_fibonacci_sphere():
50
+ import matplotlib.pyplot as plt
51
+
52
+ # Generate points on the sphere
53
+ samples = 20000
54
+ theta, phi = fibonacci_sphere(samples)
55
+ # theta, phi = fibonacci_semisphere(samples)
56
+
57
+ # Convert spherical coordinates to Cartesian coordinates
58
+ x = np.cos(phi) * np.sin(theta)
59
+ y = np.sin(phi) * np.sin(theta)
60
+ z = np.cos(theta)
61
+
62
+ fig = plt.figure()
63
+ ax = fig.add_subplot(111, projection="3d")
64
+ ax.scatter(x, y, z, s=13.5)
65
+ # ax.plot(x, y, z)
66
+
67
+ # Set aspect to 'equal' for equal scaling in all directions
68
+ ax.set_aspect("equal")
69
+
70
+ plt.show()
71
+
72
+
73
+ if __name__ == "__main__":
74
+ test_fibonacci_sphere()
TB2J/orbital_magmom.py ADDED
@@ -0,0 +1,36 @@
1
+ """
2
+ utilities to handel orbital magnetic moments
3
+ """
4
+
5
+ import numpy as np
6
+
7
+
8
+ def complex_spherical_harmonic_to_real_spherical_harmonic(l=1):
9
+ """
10
+ matrix to convert the complex spherical harmonics to real spherical harmonics
11
+ """
12
+ MCR = np.zeros((2 * l + 1, 2 * l + 1), dtype=np.complex128)
13
+ MCR[0 + l, 0 + l] = 1.0
14
+ for m in range(1, l + 1):
15
+ mi = m + l
16
+ mpi = -m + l
17
+ MCR[mi, mi] = (-1) ** m / 2
18
+ MCR[mi, mpi] = 1.0 / 2
19
+ MCR[mpi, mi] = -1j * (-1) ** m / 2
20
+ MCR[mpi, mpi] = 1j / 2
21
+
22
+ return MCR
23
+
24
+
25
+ def test_complex_spherical_harmonic_to_real_spherical_harmonic():
26
+ """
27
+ test the conversion matrix
28
+ """
29
+ l = 3
30
+ MCR = complex_spherical_harmonic_to_real_spherical_harmonic(l)
31
+ # print(MCR*np.sqrt(2))
32
+ print(MCR * 2)
33
+
34
+
35
+ if __name__ == "__main__":
36
+ test_complex_spherical_harmonic_to_real_spherical_harmonic()
TB2J/symmetrize_J.py CHANGED
@@ -9,7 +9,7 @@ from TB2J.versioninfo import print_license
9
9
 
10
10
 
11
11
  class TB2JSymmetrizer:
12
- def __init__(self, exc, symprec=1e-8, verbose=True, Jonly=False):
12
+ def __init__(self, exc, symprec=1e-8, verbose=True, Jonly=True):
13
13
  # list of pairs with the index of atoms
14
14
  ijRs = exc.ijR_list_index_atom()
15
15
  finder = SymmetryPairFinder(atoms=exc.atoms, pairs=ijRs, symprec=symprec)
@@ -25,6 +25,12 @@ class TB2JSymmetrizer:
25
25
  )
26
26
  print("-" * 30)
27
27
  if exc.has_dmi:
28
+ # raise NotImplementedError(
29
+ # "Symmetrization of DMI is not yet implemented."
30
+ # )
31
+ # raise Warning(
32
+ # "WARNING: Symmetrization of DMI is not yet implemented."
33
+ # )
28
34
  print(
29
35
  "WARNING: Currently only the isotropic exchange is symmetrized. Symmetrization of DMI and anisotropic exchange are not yet implemented."
30
36
  )
@@ -33,7 +39,7 @@ class TB2JSymmetrizer:
33
39
  print("Symmetry found:")
34
40
  print(finder.spacegroup)
35
41
  print("-" * 30)
36
- self.pgdict = finder.get_symmetry_pair_group_dict()
42
+ self.pldict = finder.get_symmetry_pair_list_dict()
37
43
  self.exc = exc
38
44
  self.new_exc = copy.deepcopy(exc)
39
45
  self.Jonly = Jonly
@@ -46,21 +52,25 @@ class TB2JSymmetrizer:
46
52
  Symmetrize the exchange parameters J.
47
53
  """
48
54
  symJdict = {}
55
+ reduced_symJdict = {}
49
56
  # Jdict = self.exc.exchange_Jdict
50
- # ngroup = self.pgdict
51
- for pairgroup in self.pgdict.groups:
52
- ijRs = pairgroup.get_all_ijR()
57
+ for ishell, pairlist in enumerate(self.pldict.pairlists):
58
+ ijRs = pairlist.get_all_ijR()
53
59
  ijRs_spin = [self.exc.ijR_index_atom_to_spin(*ijR) for ijR in ijRs]
54
60
  Js = [self.exc.get_J(*ijR_spin) for ijR_spin in ijRs_spin]
55
61
  Javg = np.average(Js)
56
62
  for i, j, R in ijRs_spin:
57
63
  symJdict[(R, i, j)] = Javg
64
+ ijRs_ref = ijRs_spin[0]
65
+ i, j, R = ijRs_ref
66
+ reduced_symJdict[(R, i, j)] = Javg
58
67
  self.new_exc.exchange_Jdict = symJdict
59
68
  if self.Jonly:
60
69
  self.new_exc.has_dmi = False
61
70
  self.new_exc.dmi_ddict = None
62
71
  self.new_exc.has_bilinear = False
63
72
  self.new_exc.Jani_dict = None
73
+ self.new_exc.reduced_exchange_Jdict = reduced_symJdict
64
74
  self.has_uniaxial_anisotropy = False
65
75
  self.k1 = None
66
76
  self.k1dir = None
@@ -124,12 +134,12 @@ def symmetrize_J_cli():
124
134
  help="precision for symmetry detection. default is 1e-5 Angstrom",
125
135
  )
126
136
 
127
- parser.add_argument(
128
- "--Jonly",
129
- action="store_true",
130
- help="symmetrize only the exchange parameters and discard the DMI and anisotropic exchange",
131
- default=False,
132
- )
137
+ # parser.add_argument(
138
+ # "--Jonly",
139
+ # action="store_true",
140
+ # help="symmetrize only the exchange parameters and discard the DMI and anisotropic exchange",
141
+ # default=True,
142
+ # )
133
143
 
134
144
  args = parser.parse_args()
135
145
  if args.inpath is None:
@@ -139,7 +149,8 @@ def symmetrize_J_cli():
139
149
  path=args.inpath,
140
150
  output_path=args.outpath,
141
151
  symprec=args.symprec,
142
- Jonly=args.Jonly,
152
+ # Jonly=args.Jonly,
153
+ Jonly=True,
143
154
  )
144
155
 
145
156
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: TB2J
3
- Version: 0.9.9rc6
3
+ Version: 0.9.9rc7
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
@@ -14,15 +14,24 @@ Classifier: Topic :: Scientific/Engineering :: Physics
14
14
  Classifier: License :: OSI Approved :: BSD License
15
15
  Requires-Python: >=3.6
16
16
  License-File: LICENSE
17
- Requires-Dist: numpy <2.0
17
+ Requires-Dist: numpy<2.0
18
18
  Requires-Dist: scipy
19
19
  Requires-Dist: matplotlib
20
- Requires-Dist: ase >=3.19
20
+ Requires-Dist: ase>=3.19
21
21
  Requires-Dist: tqdm
22
22
  Requires-Dist: pathos
23
- Requires-Dist: packaging >=20.0
24
- Requires-Dist: HamiltonIO >=0.1.9
23
+ Requires-Dist: packaging>=20.0
24
+ Requires-Dist: HamiltonIO>=0.1.9
25
25
  Requires-Dist: pre-commit
26
- Requires-Dist: sympair >0.1.0
26
+ Requires-Dist: sympair>0.1.0
27
+ Dynamic: author
28
+ Dynamic: author-email
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: license
32
+ Dynamic: license-file
33
+ Dynamic: requires-dist
34
+ Dynamic: requires-python
35
+ Dynamic: summary
27
36
 
28
37
  TB2J is a Python package aimed to compute automatically the magnetic interactions (superexchange and Dzyaloshinskii-Moriya) between atoms of magnetic crystals from DFT Hamiltonian based on Wannier functions or Linear combination of atomic orbitals. It uses the Green's function method and take the local rigid spin rotation as a perturbation. The package can take the output from Wannier90, which is interfaced with many density functional theory codes or from codes based on localised orbitals. A minimal user input is needed, which allows for an easily integration into a high-throughput workflows.
@@ -1,17 +1,17 @@
1
- TB2J/Jdownfolder.py,sha256=Rmg6KfQ-Lkhei5daTJ2POzr0XL-R1WM-rzUnDcfoDhc,9595
1
+ TB2J/Jdownfolder.py,sha256=xs0gmcKwOA3ejXqk3JPPgnYVu8CvkK8cteuvHuu1oSk,9603
2
2
  TB2J/Jtensor.py,sha256=t6OsqrSlYW6Im4H7ykVAW8Al_pFXN4C5yj2UEsV6r7g,3181
3
3
  TB2J/MAE.py,sha256=fM8U-Dgp9HcQOEeC_kyZV1oVrygBvcux9BraUXVouvY,10994
4
- TB2J/MAEGreen.py,sha256=MoLoE5fLpbp1knAP_oU-egHr0jnpTQpqD9FgoVtDkgk,12322
4
+ TB2J/MAEGreen.py,sha256=pCX12GDNaOz8XgMlui8NjAERM43Ux3HyOKfiD80czXI,15306
5
5
  TB2J/Oiju.py,sha256=cNGv8N5uH_swGq7cnAt2OyiDfqtjLlLrwseGu0E4iaM,3383
6
6
  TB2J/Oiju_epc.py,sha256=oytM3NYW7nWmklrGgNlqwIpI_JYv_hb7ZnR4o9nYNog,6809
7
7
  TB2J/__init__.py,sha256=hcEWkag_UvLm1ZSbjsgcTWkGVlR3Bwmzg1QYAwsvf-g,24
8
- TB2J/anisotropy.py,sha256=zz4ID6_Yjf5gxPJt1psc6liNKhTG8X5AtHdpq0dCEzM,22616
8
+ TB2J/anisotropy.py,sha256=0zmvXkmDmakbBOwGYLa3IIkv5cE99SHLAQJsGoZz7JQ,25463
9
9
  TB2J/basis.py,sha256=DFo6_QUwjBwisP6zGxvoO0lpGTMDPAOkiL9giNCjOjA,1558
10
10
  TB2J/citation.py,sha256=gcQeyJZaT1Qrtsl8Y3s4neOH3-vvgmIcCvXeV2o3vj0,2891
11
11
  TB2J/contour.py,sha256=zLHQZZ3hhgLkLFPATCraLOJyLJKLC0fba_L_5sRz23o,3246
12
12
  TB2J/density_matrix.py,sha256=D5k8Oe21OCiLVORNYbo4TZOFG0slrQSbj91kJ3TMFjs,1514
13
13
  TB2J/epc.py,sha256=zLbtqZJhDr8DnnGN6YENcXwrMb3Qxu6KB08mLy9Pw20,3474
14
- TB2J/exchange.py,sha256=1kkrrQvmoOaPgT_rKG7wgLy31rWpbFGNYXai1ysd58M,26221
14
+ TB2J/exchange.py,sha256=742vEE8DQJuWBTlq45RQoPy0-_n_TT6BZOb_DoTpDKI,25739
15
15
  TB2J/exchangeCL2.py,sha256=P7bklMXVYX_tn9DbjEPqeTir1SeZyfPBIP1fhWUzLmY,11419
16
16
  TB2J/exchange_params.py,sha256=AcGYYky27DXSF3yDZWVjksr_3rt6im6qeIzpOwvqssk,7141
17
17
  TB2J/exchange_pert.py,sha256=jmFMtQbYa_uczM4VAeS6TijkIHRFIqEzZJswzE9Wfuo,8523
@@ -23,6 +23,7 @@ TB2J/io_merge.py,sha256=E1_GfAB2HGpW-ipaO2lqU9SvaslwkiLxssn4DqJpMT8,6899
23
23
  TB2J/kpoints.py,sha256=9L7tBarFBHoIhpuc9zuwA6HdnlgH834SQrPek4yRoWk,3191
24
24
  TB2J/myTB.py,sha256=ok_B4my29bOIghMSZfx0Es6G8FaXaIiLP4gPxTdSj00,17659
25
25
  TB2J/mycfr.py,sha256=Wgj6PpA-oVuxm8Hh32FVw_vthozVBrDJhRV1hA1ku44,3752
26
+ TB2J/orbital_magmom.py,sha256=JTwO9ZDgRRQndqR9aFIua4eTvwLMoGsTiY_HaIPMZ2I,889
26
27
  TB2J/orbmap.py,sha256=XLQjKMxCy2eADaM5eb2F_zG08V7lzpXJxp5uEtTeVYI,7194
27
28
  TB2J/pauli.py,sha256=ESpAhk6LG5ugzuW1YFUTqiDxcg-pQ7wNnzR2FtUnvKM,5295
28
29
  TB2J/pert.py,sha256=RaCJfewl0doht4cjAnzzGKe-uj2le4aqe0iPKFrq9fo,1192
@@ -30,7 +31,7 @@ TB2J/plot.py,sha256=AnFIFWE2vlmj7Z6f_7-dX_O1stJN-qbuiurPj43dUCM,4104
30
31
  TB2J/rotate_atoms.py,sha256=Dwptn-wdDW4zYzjYb95yxTzuZOe9WPuLjh3d3-YcSs0,3277
31
32
  TB2J/rotate_siestaDM.py,sha256=eR97rspdrRaK9YTwQwUKfobI0S9UnEcbEZ2f5IgR7Tk,1070
32
33
  TB2J/sisl_wrapper.py,sha256=A5x1-tt8efUSPeGY5wM5m6-pJYQFXTCzQHVqD6RBa2g,14792
33
- TB2J/symmetrize_J.py,sha256=IypvLL0JxExq-cmqc4o0nLL8psE7OC9ijj9YMcsqJeA,4487
34
+ TB2J/symmetrize_J.py,sha256=gN6Y8zV4IrO5rPh0SG8KHkekrJjMQzNY4GGe2ZBtwbc,4993
34
35
  TB2J/tensor_rotate.py,sha256=4-DfT_Mg5e40fbd74M5W0D5DqmUq-kVOOLDkkkI834A,8083
35
36
  TB2J/utest.py,sha256=z_ahi7tpHQF9WlHNQihcQ7qzfezRJQXQt28eB1X_z64,3897
36
37
  TB2J/utils.py,sha256=DHkc7BK0KUGesfoAv1OxMgIw_iZzcFXh--3ybsFSd_c,12535
@@ -41,7 +42,7 @@ TB2J/interfaces/__init__.py,sha256=4tiLoXQ73Nlyi9L4j8jJXOYzXluVNPxQZkwfkQZEGHg,3
41
42
  TB2J/interfaces/gpaw_interface.py,sha256=GCDlJ-hRWfChvWwsgBDYSmVqO4sH9HAuGZTV9GqgN6c,1504
42
43
  TB2J/interfaces/lawaf_interface.py,sha256=PieLnmppdafOYsgeHznqOou1g9L1sam5jOm3KaObdqo,4408
43
44
  TB2J/interfaces/manager.py,sha256=PQMLEfMCT5GnDWSl2nI4JOgRPm_fysyR-6Y6l97xWcw,860
44
- TB2J/interfaces/siesta_interface.py,sha256=C9dMmFAc8HlCGFJeHLn0nUvOJA1rhVYIFIz8udMdJjE,7332
45
+ TB2J/interfaces/siesta_interface.py,sha256=olvo2xdBOSNk3zn67nuKrxbW--EKPoWwEzKRBwJbrVY,7366
45
46
  TB2J/interfaces/wannier90_interface.py,sha256=qzRgXUBb7t1Aiegrl_RV51BB8csdtVM0EP0Z4pjmTcs,4467
46
47
  TB2J/interfaces/abacus/__init__.py,sha256=leas71oCvM_HxrF4gnO5A_VKcJmDAgsI1BUctLU3OBw,177
47
48
  TB2J/interfaces/abacus/abacus_api.py,sha256=lNV4LNkLcKw7Zux4MQYM9wnh3eFTlcSqbf4Pb7pqhrk,7243
@@ -53,7 +54,8 @@ TB2J/interfaces/abacus/test_density_matrix.py,sha256=bMWWJYtDS57SpPZ-eZXZ9Hr_UK4
53
54
  TB2J/interfaces/abacus/test_read_HRSR.py,sha256=W1oO_yigT50Yb5_u-KB_IfTpM7kArGkBuMSMs0H4CTs,1235
54
55
  TB2J/interfaces/abacus/test_read_stru.py,sha256=hoKPHVco8vwzC7Gao4bOPCdAPhh29x-9DTJJqRr7AYM,788
55
56
  TB2J/io_exchange/__init__.py,sha256=KfGHum7B8E4G_KKfillqw0lErtoyKEuFUUttHLs-mg4,32
56
- TB2J/io_exchange/io_exchange.py,sha256=ZeA8lpYUAQO1CqZnQL70IgS9TbAFyfPuadLGfKVf-Ms,19769
57
+ TB2J/io_exchange/io_exchange.py,sha256=7-Xt7_K5FKVrFkFvBrHhJGCRrL2Sw7zY-93GRBBv2GM,21050
58
+ TB2J/io_exchange/io_matjes.py,sha256=klM5Z_t2kk0y_1IoqIILLnhUgjHQmXXmsXiono2LeMU,8594
57
59
  TB2J/io_exchange/io_multibinit.py,sha256=8PDmWxzGuv-GwJosj2ZTmiyNY_duFVWJ4ekCuSqGdd8,6739
58
60
  TB2J/io_exchange/io_tomsasd.py,sha256=NqkAC1Fl-CUnFA21eBzSy_S5F_oeQFJysw4UukQbN8o,4173
59
61
  TB2J/io_exchange/io_txt.py,sha256=BMr1eSILlKpgtjvDx7uw2VMAkEKSvGEPNxpaT_zev0I,10547
@@ -61,6 +63,7 @@ TB2J/io_exchange/io_uppasd.py,sha256=bI4iPEgnK4TvCZNvb6x2xYXgjW7pEehCqmcizy2pqFU
61
63
  TB2J/io_exchange/io_vampire.py,sha256=UllC4twf06_q2vBCnAYFzEDGvS8mSefwQXDquBuyc0M,5583
62
64
  TB2J/mathutils/__init__.py,sha256=tQLBfHkZqdVfVxPOahy42qMUkFYnFFFhM-uc4QsYFxI,27
63
65
  TB2J/mathutils/fermi.py,sha256=72tZ5CptGmYaBUD0xLWltuH7LBXcrMUwODyW6-WqlzI,638
66
+ TB2J/mathutils/fibonacci_sphere.py,sha256=l0USn25ZCAWF6l4UleyWaeLthsj9TThV9iWmfi6DbaM,2344
64
67
  TB2J/mathutils/kR_convert.py,sha256=p_9XWJVNanTzTK2rI6KRjTkbSq42la6N448-zJOsMwY,2671
65
68
  TB2J/mathutils/lowdin.py,sha256=RYbm9OcnFnjcZFdC5YcNUsI9cOJmoDLsWSSCaP0GqKQ,499
66
69
  TB2J/mathutils/rotate_spin.py,sha256=lbGzve_36FyNjanXqdxYDb102kA4_5OycRlBcm-tH-g,8360
@@ -77,19 +80,19 @@ TB2J/spinham/supercell.py,sha256=y17uUC6r3gQb278FhxIW4CABihfLTvKFj6flyXrCPR8,122
77
80
  TB2J/wannier/__init__.py,sha256=7ojCbM84PYv1X1Tbo4NHI-d3gWmQsZB_xiYqbfxVV1E,80
78
81
  TB2J/wannier/w90_parser.py,sha256=dbd63LuKyv2DVUzqRINGsbDzEsOxsQyE8_Ear_LQIRg,4620
79
82
  TB2J/wannier/w90_tb_parser.py,sha256=qt8pnuprmPp9iIAYwPkPbmEzk6ZPgMq2xognoQp7vwc,4610
80
- TB2J-0.9.9rc6.data/scripts/TB2J_downfold.py,sha256=i4BVqnpDdgrX_amookVWeLGefGBn-qeAutWiwuY9SfQ,2099
81
- TB2J-0.9.9rc6.data/scripts/TB2J_eigen.py,sha256=Qs9v2hnMm2Tpfoa4h53muUKty2dZjwx8948MBoQooNg,1128
82
- TB2J-0.9.9rc6.data/scripts/TB2J_magnon.py,sha256=q7UwAmorRcFNk4tfE7gl_ny05l6p7pbD9Wm_LkIpKEw,3101
83
- TB2J-0.9.9rc6.data/scripts/TB2J_magnon_dos.py,sha256=TMXQvD2dIbO5FZ4tUMmxJgCgH2O2hDAPUNfEKO4z-x4,110
84
- TB2J-0.9.9rc6.data/scripts/TB2J_merge.py,sha256=y834SF4rIRn1L1ptkhczvavQpC-8Px6DTmDOOSaq_DE,1854
85
- TB2J-0.9.9rc6.data/scripts/TB2J_rotate.py,sha256=zgiDFuYZNmzKK0rwDmTaYD2OpRlmKA_VGeBx83w2Xwc,873
86
- TB2J-0.9.9rc6.data/scripts/TB2J_rotateDM.py,sha256=kCvF7gotuqAX1VnJ06cwfVm7RrhrdtiV5v7d9P2Pn_E,567
87
- TB2J-0.9.9rc6.data/scripts/abacus2J.py,sha256=0HLXoJhAkiZ-ZM1cs26lncccxE8-TzC8JiDTba1h1uM,4163
88
- TB2J-0.9.9rc6.data/scripts/siesta2J.py,sha256=gp31LioqpPkDmMY0y_5gXIjOBPNnf080P37pRo0yjw8,4886
89
- TB2J-0.9.9rc6.data/scripts/wann2J.py,sha256=pTFDf6h72I_LN_NX5UivyCoJPgwvyAyHW175nSAJvLo,2987
90
- TB2J-0.9.9rc6.dist-info/LICENSE,sha256=CbZI-jyRTjiqIcWa244cRSHJdjjtUNqGR4HeJkgEwJw,1332
91
- TB2J-0.9.9rc6.dist-info/METADATA,sha256=liNOEG_iKPUW6hOrcu08SqCBqzeEd3RxGRt3_YxBh2M,1482
92
- TB2J-0.9.9rc6.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
93
- TB2J-0.9.9rc6.dist-info/entry_points.txt,sha256=Hdz1WC9waUzyFVmowKnbbZ6j-J4adHh_Ko6JpxGYAtE,131
94
- TB2J-0.9.9rc6.dist-info/top_level.txt,sha256=whYa5ByLYhl5XnTPBHSWr-IGD6VWmr5Ql2bye2qwV_s,5
95
- TB2J-0.9.9rc6.dist-info/RECORD,,
83
+ tb2j-0.9.9rc7.data/scripts/TB2J_downfold.py,sha256=i4BVqnpDdgrX_amookVWeLGefGBn-qeAutWiwuY9SfQ,2099
84
+ tb2j-0.9.9rc7.data/scripts/TB2J_eigen.py,sha256=Qs9v2hnMm2Tpfoa4h53muUKty2dZjwx8948MBoQooNg,1128
85
+ tb2j-0.9.9rc7.data/scripts/TB2J_magnon.py,sha256=q7UwAmorRcFNk4tfE7gl_ny05l6p7pbD9Wm_LkIpKEw,3101
86
+ tb2j-0.9.9rc7.data/scripts/TB2J_magnon_dos.py,sha256=TMXQvD2dIbO5FZ4tUMmxJgCgH2O2hDAPUNfEKO4z-x4,110
87
+ tb2j-0.9.9rc7.data/scripts/TB2J_merge.py,sha256=y834SF4rIRn1L1ptkhczvavQpC-8Px6DTmDOOSaq_DE,1854
88
+ tb2j-0.9.9rc7.data/scripts/TB2J_rotate.py,sha256=zgiDFuYZNmzKK0rwDmTaYD2OpRlmKA_VGeBx83w2Xwc,873
89
+ tb2j-0.9.9rc7.data/scripts/TB2J_rotateDM.py,sha256=kCvF7gotuqAX1VnJ06cwfVm7RrhrdtiV5v7d9P2Pn_E,567
90
+ tb2j-0.9.9rc7.data/scripts/abacus2J.py,sha256=0HLXoJhAkiZ-ZM1cs26lncccxE8-TzC8JiDTba1h1uM,4163
91
+ tb2j-0.9.9rc7.data/scripts/siesta2J.py,sha256=gp31LioqpPkDmMY0y_5gXIjOBPNnf080P37pRo0yjw8,4886
92
+ tb2j-0.9.9rc7.data/scripts/wann2J.py,sha256=pTFDf6h72I_LN_NX5UivyCoJPgwvyAyHW175nSAJvLo,2987
93
+ tb2j-0.9.9rc7.dist-info/licenses/LICENSE,sha256=CbZI-jyRTjiqIcWa244cRSHJdjjtUNqGR4HeJkgEwJw,1332
94
+ tb2j-0.9.9rc7.dist-info/METADATA,sha256=H5g8ApE237zxKx8_mx0Fr1fUCz2m97VkgDY5LBFYzlg,1660
95
+ tb2j-0.9.9rc7.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
96
+ tb2j-0.9.9rc7.dist-info/entry_points.txt,sha256=Hdz1WC9waUzyFVmowKnbbZ6j-J4adHh_Ko6JpxGYAtE,131
97
+ tb2j-0.9.9rc7.dist-info/top_level.txt,sha256=whYa5ByLYhl5XnTPBHSWr-IGD6VWmr5Ql2bye2qwV_s,5
98
+ tb2j-0.9.9rc7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (77.0.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5