TB2J 0.8.1__tar.gz → 0.8.2.1__tar.gz

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 (82) hide show
  1. {TB2J-0.8.1 → TB2J-0.8.2.1}/PKG-INFO +1 -1
  2. TB2J-0.8.2.1/TB2J/__init__.py +1 -0
  3. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/abacus/abacus_wrapper.py +9 -1
  4. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/abacus/stru_api.py +3 -2
  5. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/io_exchange/io_exchange.py +29 -0
  6. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/io_merge.py +2 -2
  7. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/manager.py +15 -0
  8. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/myTB.py +26 -13
  9. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/sisl_wrapper.py +104 -0
  10. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/wannier/__init__.py +1 -1
  11. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/wannier/w90_parser.py +11 -1
  12. TB2J-0.8.2.1/TB2J/wannier/w90_tb_parser.py +129 -0
  13. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J.egg-info/PKG-INFO +1 -1
  14. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J.egg-info/SOURCES.txt +1 -0
  15. {TB2J-0.8.1 → TB2J-0.8.2.1}/setup.py +1 -1
  16. TB2J-0.8.1/TB2J/__init__.py +0 -1
  17. {TB2J-0.8.1 → TB2J-0.8.2.1}/LICENSE +0 -0
  18. {TB2J-0.8.1 → TB2J-0.8.2.1}/README.md +0 -0
  19. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/Jdownfolder.py +0 -0
  20. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/Jtensor.py +0 -0
  21. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/Oiju.py +0 -0
  22. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/Oiju_epc.py +0 -0
  23. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/abacus/__init__.py +0 -0
  24. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/abacus/abacus_api.py +0 -0
  25. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/abacus/gen_exchange_abacus.py +0 -0
  26. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/abacus/orbital_api.py +0 -0
  27. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/abacus/test_read_HRSR.py +0 -0
  28. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/abacus/test_read_stru.py +0 -0
  29. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/basis.py +0 -0
  30. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/citation.py +0 -0
  31. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/contour.py +0 -0
  32. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/density_matrix.py +0 -0
  33. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/epc.py +0 -0
  34. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/exchange.py +0 -0
  35. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/exchangeCL2.py +0 -0
  36. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/exchange_pert.py +0 -0
  37. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/exchange_qspace.py +0 -0
  38. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/external/__init__.py +0 -0
  39. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/external/p_tqdm.py +0 -0
  40. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/gpaw_wrapper.py +0 -0
  41. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/green.py +0 -0
  42. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/greentest.py +0 -0
  43. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/io_exchange/__init__.py +0 -0
  44. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/io_exchange/io_multibinit.py +0 -0
  45. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/io_exchange/io_tomsasd.py +0 -0
  46. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/io_exchange/io_txt.py +0 -0
  47. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/io_exchange/io_uppasd.py +0 -0
  48. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/io_exchange/io_vampire.py +0 -0
  49. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/kpoints.py +0 -0
  50. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/mathutils.py +0 -0
  51. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/orbmap.py +0 -0
  52. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/pauli.py +0 -0
  53. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/pert.py +0 -0
  54. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/plot.py +0 -0
  55. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/rotate_atoms.py +0 -0
  56. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/__init__.py +0 -0
  57. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/base_parser.py +0 -0
  58. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/constants.py +0 -0
  59. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/hamiltonian.py +0 -0
  60. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/hamiltonian_terms.py +0 -0
  61. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/plot.py +0 -0
  62. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/qsolver.py +0 -0
  63. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/spin_api.py +0 -0
  64. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/spin_xml.py +0 -0
  65. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/spinham/supercell.py +0 -0
  66. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/tensor_rotate.py +0 -0
  67. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/utest.py +0 -0
  68. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/utils.py +0 -0
  69. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J/versioninfo.py +0 -0
  70. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J.egg-info/dependency_links.txt +0 -0
  71. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J.egg-info/requires.txt +0 -0
  72. {TB2J-0.8.1 → TB2J-0.8.2.1}/TB2J.egg-info/top_level.txt +0 -0
  73. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/TB2J_downfold.py +0 -0
  74. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/TB2J_eigen.py +0 -0
  75. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/TB2J_magnon.py +0 -0
  76. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/TB2J_magnon_dos.py +0 -0
  77. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/TB2J_merge.py +0 -0
  78. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/TB2J_rotate.py +0 -0
  79. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/abacus2J.py +0 -0
  80. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/siesta2J.py +0 -0
  81. {TB2J-0.8.1 → TB2J-0.8.2.1}/scripts/wann2J.py +0 -0
  82. {TB2J-0.8.1 → TB2J-0.8.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: TB2J
3
- Version: 0.8.1
3
+ Version: 0.8.2.1
4
4
  Summary: TB2J: First principle to Heisenberg exchange J using tight-binding Green function method
5
5
  Home-page: UNKNOWN
6
6
  Author: Xu He
@@ -0,0 +1 @@
1
+ __version__ = "0.8.2.1"
@@ -4,6 +4,7 @@
4
4
  The abacus wrapper
5
5
  """
6
6
  from pathlib import Path
7
+ import os
7
8
  import numpy as np
8
9
  from scipy.linalg import eigh
9
10
  from TB2J.utils import symbol_number_list
@@ -123,7 +124,14 @@ class AbacusParser:
123
124
  raise ValueError("nspin should be either 1 or 4.")
124
125
 
125
126
  def read_atoms(self):
126
- self.atoms = read_abacus(str(Path(self.outpath) / "../Stru"))
127
+ path1=str(Path(self.outpath) / "../STRU")
128
+ path2=str(Path(self.outpath) / "../Stru")
129
+ if os.path.exists(path1):
130
+ self.atoms = read_abacus(path1)
131
+ elif os.path.exists(path2):
132
+ self.atoms = read_abacus(path2)
133
+ else:
134
+ raise Exception("The STRU or Stru file cannot be found.")
127
135
  return self.atoms
128
136
 
129
137
  def read_basis(self):
@@ -1,7 +1,8 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """
3
- Created on Wed Jun 13 10:31:30 2018
4
- @author: shenzx
3
+ @author: Zhen-Xiong Shen
4
+ @email: shenzx@iai.ustc.edu.cn
5
+ @workplace: Institute of Artificial Intelligence, Hefei Comprehensive National Science Center
5
6
 
6
7
  Modified on Wed Aug 01 11:44:51 2022
7
8
  @author: Ji Yu-yang
@@ -19,6 +19,7 @@ from datetime import datetime
19
19
  import matplotlib.pyplot as plt
20
20
  from TB2J.spinham.spin_api import SpinModel
21
21
  from TB2J.io_exchange.io_txt import write_Jq_info
22
+ from TB2J.utils import symbol_number
22
23
 
23
24
 
24
25
  class SpinIO(object):
@@ -212,7 +213,23 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
212
213
  def get_spin_ispin(self, i):
213
214
  return self.spinat[self.iatom(i)]
214
215
 
216
+ def get_symbol_number_ispin(self, symnum):
217
+ """
218
+ Return the spin index for a given symbol number.
219
+ """
220
+ symdict = symbol_number(self.atoms)
221
+ return self.index_spin[symdict[symnum]]
222
+
223
+ def i_spin(self, i):
224
+ if isinstance(i, int):
225
+ return i
226
+ elif isinstance(i, str):
227
+ return self.get_symbol_number_ispin(i)
228
+ else:
229
+ raise ValueError("i must be either an integer or a string.")
230
+
215
231
  def get_charge_ispin(self, i):
232
+ i = self.i_spin(i)
216
233
  return self.charges[self.iatom(i)]
217
234
 
218
235
  def get_spin_iatom(self, iatom):
@@ -222,6 +239,8 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
222
239
  return self.charges[iatom]
223
240
 
224
241
  def get_J(self, i, j, R, default=None):
242
+ i = self.i_spin(i)
243
+ j = self.i_spin(j)
225
244
  key = (
226
245
  tuple(R),
227
246
  i,
@@ -233,6 +252,8 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
233
252
  return default
234
253
 
235
254
  def get_Jiso(self, i, j, R, default=None):
255
+ i = self.i_spin(i)
256
+ j = self.i_spin(j)
236
257
  key = (
237
258
  tuple(R),
238
259
  i,
@@ -244,6 +265,8 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
244
265
  return default
245
266
 
246
267
  def get_DMI(self, i, j, R, default=None):
268
+ i = self.i_spin(i)
269
+ j = self.i_spin(j)
247
270
  key = (
248
271
  tuple(R),
249
272
  i,
@@ -261,6 +284,8 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
261
284
  param j: spin index j
262
285
  param R (tuple of integers): cell index R
263
286
  """
287
+ i = self.i_spin(i)
288
+ j = self.i_spin(j)
264
289
  key = (
265
290
  tuple(R),
266
291
  i,
@@ -278,6 +303,8 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
278
303
  param j: spin index j
279
304
  param R (tuple of integers): cell index R
280
305
  """
306
+ i = self.i_spin(i)
307
+ j = self.i_spin(j)
281
308
  if iso_only:
282
309
  Jtensor = np.eye(3) * self.get_J(i, j, R)
283
310
  else:
@@ -295,6 +322,8 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
295
322
  returns:
296
323
  Jmat: (3*nspin,3*nspin) matrix.
297
324
  """
325
+ i = self.i_spin(i)
326
+ j = self.i_spin(j)
298
327
  n3 = self.nspin * 3
299
328
  Jmat = np.zeros((n3, n3), dtype=float)
300
329
  for i in range(self.nspin):
@@ -215,9 +215,9 @@ class Merger2:
215
215
  )
216
216
  w = Jani_removed / Jani
217
217
  Jani_sum += Jani * w # Jani_removed
218
- print(f"{Jani* w=}")
218
+ #print(f"{Jani* w=}")
219
219
  weights += w
220
- print(f"{weights=}")
220
+ #print(f"{weights=}")
221
221
  if np.any(weights == 0):
222
222
  raise RuntimeError(
223
223
  "The data set to be merged does not give a complete anisotropic J tensor, please add more data"
@@ -284,6 +284,21 @@ def gen_exchange_siesta(
284
284
  raise ImportError(
285
285
  f"sisl version is {sisl.__version__}, but should be larger than 0.10.0."
286
286
  )
287
+
288
+ include_orbs = {}
289
+ if isinstance(magnetic_elements, str):
290
+ magnetic_elements = [magnetic_elements]
291
+ for element in magnetic_elements:
292
+ if "_" in element:
293
+ elem = element.split("_")[0]
294
+ orb = element.split("_")[1:]
295
+ include_orbs[elem] = orb
296
+ else:
297
+ include_orbs[element] = None
298
+ magnetic_elements = list(include_orbs.keys())
299
+ print(magnetic_elements)
300
+ print(include_orbs)
301
+
287
302
  fdf = sisl.get_sile(fdf_fname)
288
303
  geom = fdf.read_geometry()
289
304
  H = fdf.read_hamiltonian()
@@ -9,7 +9,7 @@ from collections import defaultdict
9
9
  # from tbmodels import Model
10
10
  from ase.atoms import Atoms
11
11
  from TB2J.utils import auto_assign_basis_name
12
- from TB2J.wannier import parse_ham, parse_xyz, parse_atoms
12
+ from TB2J.wannier import parse_ham, parse_xyz, parse_atoms, parse_tb
13
13
 
14
14
 
15
15
  class AbstractTB:
@@ -84,6 +84,7 @@ class MyTB(AbstractTB):
84
84
  self,
85
85
  nbasis,
86
86
  data=None,
87
+ R_degens=None,
87
88
  positions=None,
88
89
  sparse=False,
89
90
  ndim=3,
@@ -93,6 +94,7 @@ class MyTB(AbstractTB):
93
94
  """
94
95
  :param nbasis: number of basis.
95
96
  :param data: a dictionary of {R: matrix}. R is a tuple, matrix is a nbasis*nbasis matrix.
97
+ :param R_degens: degeneracy of R.
96
98
  :param positions: reduced positions.
97
99
  :param sparse: Bool, whether to use a sparse matrix.
98
100
  :param ndim: number of dimensions.
@@ -107,6 +109,10 @@ class MyTB(AbstractTB):
107
109
  self._nspin = nspin
108
110
  self._norb = nbasis // nspin
109
111
  self._ndim = ndim
112
+ if R_degens is not None:
113
+ self.R_degens = R_degens
114
+ else:
115
+ self.R_degens = np.ones(len(self.data.keys()), dtype=int)
110
116
  if positions is None:
111
117
  self._positions = np.zeros((nbasis, self.ndim))
112
118
  else:
@@ -121,7 +127,6 @@ class MyTB(AbstractTB):
121
127
  self.k2Rfactor = -2.0j * np.pi
122
128
  self.is_siesta = False
123
129
  self.is_orthogonal = True
124
-
125
130
  self._name = "Wannier"
126
131
 
127
132
  def set_atoms(self, atoms):
@@ -178,12 +183,17 @@ class MyTB(AbstractTB):
178
183
  :param path: path
179
184
  :param prefix: prefix to the wannier files, often wannier90, or wannier90_up, or wannier90_dn for vasp.
180
185
  """
181
- # tbmodel = Model.from_wannier_folder(
182
- # folder=path, prefix=prefix)
183
- # m = MyTB.from_tbmodel(tbmodel)
184
186
 
185
- nbasis, data = parse_ham(fname=os.path.join(path, prefix + "_hr.dat"))
186
- xcart, _, _ = parse_xyz(fname=os.path.join(path, prefix + "_centres.xyz"))
187
+ tb_fname = os.path.join(path, prefix + "_tb.dat")
188
+ hr_fname = os.path.join(path, prefix + "_hr.dat")
189
+ if os.path.exists(tb_fname):
190
+ xcart, nbasis, data, R_degens = parse_tb(fname=tb_fname)
191
+ else:
192
+ nbasis, data, R_degens = parse_ham(
193
+ fname=os.path.join(path, prefix + "_hr.dat")
194
+ )
195
+ xcart, _, _ = parse_xyz(fname=os.path.join(path, prefix + "_centres.xyz"))
196
+
187
197
  if atoms is None:
188
198
  atoms = parse_atoms(os.path.join(path, prefix + ".win"))
189
199
  cell = atoms.get_cell()
@@ -200,7 +210,7 @@ class MyTB(AbstractTB):
200
210
  data[key][1::2, ::2] = dtmp[norb:, :norb]
201
211
  data[key][1::2, 1::2] = dtmp[norb:, norb:]
202
212
  ind, positions = auto_assign_basis_name(xred, atoms)
203
- m = MyTB(nbasis=nbasis, data=data, positions=xred)
213
+ m = MyTB(nbasis=nbasis, data=data, positions=xred, R_degens=R_degens)
204
214
  nm = m.shift_position(positions)
205
215
  nm.set_atoms(atoms)
206
216
  return nm
@@ -243,13 +253,16 @@ class MyTB(AbstractTB):
243
253
  """
244
254
  Hk = np.zeros((self.nbasis, self.nbasis), dtype="complex")
245
255
  if convention == 2:
246
- for R, mat in self.data.items():
247
- phase = np.exp(self.R2kfactor * np.dot(k, R))
256
+ for iR, (R, mat) in enumerate(self.data.items()):
257
+ phase = np.exp(self.R2kfactor * np.dot(k, R)) # /self.R_degens[iR]
248
258
  H = mat * phase
249
259
  Hk += H + H.conjugate().T
250
260
  elif convention == 1:
251
- for R, mat in self.data.items():
252
- phase = np.exp(self.R2kfactor * np.dot(k, R + self.rjminusri))
261
+ for iR, (R, mat) in enumerate(self.data.items()):
262
+ phase = (
263
+ np.exp(self.R2kfactor * np.dot(k, R + self.rjminusri))
264
+ / self.R_degens[iR]
265
+ )
253
266
  H = mat * phase
254
267
  Hk += H + H.conjugate().T
255
268
  else:
@@ -369,7 +382,7 @@ class MyTB(AbstractTB):
369
382
  newpos = copy.deepcopy(pos)
370
383
  for i in range(self.nbasis):
371
384
  newpos[i] = pos[i] - shift[i]
372
- d = MyTB(self.nbasis, ndim=self.ndim, nspin=self.nspin)
385
+ d = MyTB(self.nbasis, ndim=self.ndim, nspin=self.nspin, R_degens=self.R_degens)
373
386
  d._positions = newpos
374
387
 
375
388
  for R, v in self.data.items():
@@ -258,6 +258,110 @@ class SislWrapper(AbstractTB):
258
258
  return 0.0
259
259
 
260
260
 
261
+ class SislWFSXWrapper(SislWrapper):
262
+ """Wrapper for retrieving eigenvalues and eigenvectors from siesta WFSX file
263
+
264
+ Parameters
265
+ ----------
266
+ geom : sisl.Geometry
267
+ the geometry containing information about atomic positions and orbitals
268
+ wfsx_sile: sisl.io.siesta.wfsxSileSiesta
269
+ file reader for WFSX file
270
+ spin : sisl.physics.Spin
271
+ spin object carrying information about spin configurations and spin component.
272
+ ispin : None or int
273
+ index of spin channel to be considered. Only takes effect for collinear spin calculations (UP: 0, DOWN: 1).
274
+ (default: None)
275
+ shift_fermi: None or float
276
+ energy shift to be applied to all energies. If `None` no shift is applied. (default: None)
277
+ """
278
+
279
+ def __init__(self, geom, wfsx_sile, spin, ispin=None, shift_fermi=None):
280
+ # super().__init__(geom, spin=spin, ispin=ispin, shift_fermi=shift_fermi)
281
+ super().__init__(sisl_hamiltonian, geom=None, spin=None)
282
+ self.geom = geom
283
+ self.wfsx_sile = wfsx_sile
284
+ self.read_all()
285
+
286
+ def read_all(self):
287
+ """Read all wavefunctions, eigenvalues, and k-points from WFSX file."""
288
+ evals = []
289
+ evecs = []
290
+ wfsx_kpts = []
291
+
292
+ def change_gauge(k, evec):
293
+ """Change the eigenvector gauge"""
294
+ phase = np.dot(
295
+ self.geom.xyz[self.geom.o2a(np.arange(self.geom.no)), :],
296
+ np.dot(k, self.geom.rcell),
297
+ )
298
+ if self.spin.has_noncolinear:
299
+ # for NC/SOC we have a 2x2 spin-box per orbital
300
+ phase = np.repeat(phase, 2)
301
+ # r -> R gauge tranformation.
302
+ return evec * np.exp(1j * phase).reshape(1, -1)
303
+
304
+ # Read wavefunctions and eigenvalues
305
+ for wfc in self.wfsx_sile.yield_eigenstate(parent=self.geom):
306
+ wfsx_kpts.append(wfc.info["k"])
307
+ evals.append(wfc.c)
308
+ # To get the same eigvecs as eigh returns we need to transpose the
309
+ # array and change the gauge from 'r' to 'R'
310
+ evecs.append(change_gauge(wfc.info["k"], wfc.state).T)
311
+
312
+ # If any k-point occurs more than once in the WaveFuncKPoints block,
313
+ # we discard the duplicates
314
+ is_duplicate = self._is_duplicate(wfsx_kpts)
315
+ self.wfsx_kpts = wfsx_kpts[~is_duplicate]
316
+ self.evals = np.array(evals, dtype=float)[~is_duplicate]
317
+ if self.shift_fermi is not None:
318
+ self.evals += self.shift_fermi
319
+ self.evecs = np.array(evecs, dtype=np.complex64, order="C")[~is_duplicate]
320
+
321
+ def _is_duplicate(self, array):
322
+ # TODO: Move into utils
323
+ # Find all matches (i,j): array[i] == array[j]
324
+ matches = np.all(np.isclose(array[None, :], array[:, None]), axis=-1)
325
+ # Remove double counting of matches: (i,j) and (j,i)
326
+ # Also, remove matches of elements with themselves: (i,i)
327
+ matches = np.triu(matches, 1)
328
+
329
+ # Finally determine elements which are duplicates
330
+ return np.any(matches, axis=0)
331
+
332
+ def find_all(self, kpts):
333
+ """Find the correct eigenvectors and eigenvalues and sort them
334
+ to match the order in kpts.
335
+
336
+ Parameters
337
+ ----------
338
+ kpts : list of float (3,) or (nk, 3)
339
+ list of k points
340
+
341
+ Returns
342
+ -------
343
+ evals : list of float (nk, n)
344
+ list of eigenvalues for every requested k point
345
+ evecs :
346
+ list of eiegnvector for every requested k point
347
+ """
348
+ kpts = np.asarray(kpts)
349
+ sort_idx = np.where(
350
+ np.all(np.isclose(self.wfsx_kpts[None, :], kpts[:, None]), axis=-1)
351
+ )[1]
352
+ if len(sort_idx) < len(kpts):
353
+ # k-point not found
354
+ raise ValueError(
355
+ f"{self.__class__.__name__}._read_all unable to find at least one "
356
+ "required k point in '{self.wfsx_sile.file}'. Please, ensure that "
357
+ "all k points listed below are included:\n"
358
+ + "\n".join([str(k) for k in kpts])
359
+ )
360
+ if not np.all(np.isclose(self.wfsx_kpts[sort_idx], kpts)):
361
+ # raise ValueError( wfsx_kpts = np.asarray(wfsx_kpts)
362
+ pass
363
+
364
+
261
365
  # def test():
262
366
  # fdf = sisl.get_sile('/home/hexu/projects/learn_siesta/SMO_Wannier/siesta.fdf')
263
367
  # H = fdf.read_hamiltonian(order='nc',dim=2)
@@ -1 +1 @@
1
- from TB2J.wannier.w90_parser import parse_xyz, parse_ham, parse_atoms
1
+ from TB2J.wannier.w90_parser import parse_xyz, parse_ham, parse_atoms, parse_tb
@@ -5,6 +5,7 @@ import numpy as np
5
5
  from ase.io import read
6
6
  from ase.atoms import Atoms
7
7
  from ase.units import Angstrom, Bohr
8
+ from .w90_tb_parser import parse_tb_file
8
9
 
9
10
  unit_dict = {"ANG": Angstrom, "BOHR": Bohr}
10
11
 
@@ -49,6 +50,7 @@ def parse_ham(fname="wannier90_hr.dat", cutoff=None):
49
50
  for i in range(3, 3 + nline):
50
51
  d = map(float, lines[i].strip().split())
51
52
  dlist += d
53
+ R_degens = np.array(dlist, dtype=int)
52
54
  H_mnR = defaultdict(lambda: np.zeros((n_wann, n_wann), dtype=complex))
53
55
  for i in range(3 + nline, 3 + nline + n_wann**2 * n_R):
54
56
  t = lines[i].strip().split()
@@ -65,7 +67,7 @@ def parse_ham(fname="wannier90_hr.dat", cutoff=None):
65
67
  H_mnR[R][m, n] = val / 2.0
66
68
  else:
67
69
  H_mnR[R][m, n] = val / 2.0
68
- return n_wann, H_mnR
70
+ return n_wann, H_mnR, R_degens
69
71
 
70
72
 
71
73
  def parse_cell(fname, unit=Angstrom):
@@ -138,3 +140,11 @@ def parse_atoms(fname):
138
140
  taus = taus * factor
139
141
  atoms = Atoms(symbols=symbols, cell=cell, positions=taus)
140
142
  return atoms
143
+
144
+
145
+ def parse_tb(fname):
146
+ """
147
+ return the wannier center, number of wannier functions and the Hamiltonian from the wannier90 _tb.dat file.
148
+ """
149
+ result = parse_tb_file(fname)
150
+ return result["centers"], result["n_wann"], result["H"], result["Rdegens"]
@@ -0,0 +1,129 @@
1
+ """
2
+ parse the tb files in Wannier90
3
+ """
4
+
5
+ import os
6
+ import re
7
+ import numpy as np
8
+ from typing import List, Tuple, Dict
9
+
10
+
11
+ def ssrline(file):
12
+ """
13
+ Read a line and split it into a list of strings"""
14
+ line = file.readline().strip()
15
+ return re.split(r"\s+", line)
16
+
17
+
18
+ def parse_vector(file, vector_type, size):
19
+ result = np.zeros((size,), dtype=vector_type)
20
+ for i in range(size):
21
+ result[i] = np.fromstring(file.readline().strip(), sep=" ")
22
+ result[i] = result[i].astype(vector_type)
23
+ return result
24
+
25
+
26
+ def find_Rvectors(Rvectors, R):
27
+ for i, Rvec in enumerate(Rvectors):
28
+ if np.all(Rvec == R):
29
+ return i
30
+ return -1
31
+
32
+
33
+ def parse_tb_file(filename):
34
+ with open(filename, "r") as io:
35
+ header = io.readline().strip()
36
+ a1 = np.fromstring(io.readline().strip(), sep=" ")
37
+ a2 = np.fromstring(io.readline().strip(), sep=" ")
38
+ a3 = np.fromstring(io.readline().strip(), sep=" ")
39
+ # a1, a2, a3 = np.array(list(map(float, [a1, a2, a3])), dtype=np.float64)
40
+ # lattice = np.vstack((a1.reshape(-1, 1), a2.reshape(-1, 1), a3.reshape(-1, 1))).T
41
+ lattice = np.vstack((a1, a2, a3))
42
+
43
+ n_wann = int(ssrline(io)[0])
44
+ n_Rvecs = int(ssrline(io)[0])
45
+
46
+ Rdegens = []
47
+ line = io.readline().strip()
48
+ while line != "":
49
+ Rdegens += [int(x) for x in line.split()]
50
+ line = io.readline().strip()
51
+ assert len(Rdegens) == n_Rvecs, "Rdegens length does not match n_Rvecs"
52
+
53
+ Rvectors = np.zeros((n_Rvecs, 3), dtype=np.int32)
54
+ # H = np.zeros((n_Rvecs,n_wann, n_wann), dtype=np.complex128)
55
+ H = {}
56
+ r_x = np.zeros((n_Rvecs, n_wann, n_wann), dtype=np.complex128)
57
+ r_y = np.zeros((n_Rvecs, n_wann, n_wann), dtype=np.complex128)
58
+ r_z = np.zeros((n_Rvecs, n_wann, n_wann), dtype=np.complex128)
59
+ pos_operator = np.zeros((n_Rvecs, n_wann, n_wann, 3), dtype=np.complex128)
60
+
61
+ for iR in range(n_Rvecs):
62
+ # read Rvectors
63
+ Rvectors[iR] = np.fromstring(io.readline().strip(), sep=" ")
64
+ Rvectors[iR] = Rvectors[iR].astype(np.int32)
65
+ R = tuple(Rvectors[iR])
66
+ H[R] = np.zeros((n_wann, n_wann), dtype=np.complex128)
67
+ # read H
68
+ for n in range(n_wann):
69
+ for m in range(n_wann):
70
+ line = ssrline(io)
71
+ assert (
72
+ m == int(line[0]) - 1 and n == int(line[1]) - 1
73
+ ), "Unexpected indices"
74
+ reH, imH = float(line[2]), float(line[3])
75
+ # H[iR][m, n] = reH + 1j * imH
76
+ H[R][m, n] = (reH + 1j * imH) / 2.0
77
+ io.readline() # empty line
78
+ # set the onsite term to half
79
+ # np.fill_diagonal(H[(0, 0, 0)], H[(0, 0, 0)].diagonal() / 2.0)
80
+ # print("onsite H from TB: ", H[(0, 0, 0)].diagonal())
81
+
82
+ iR0 = find_Rvectors(Rvectors, [0, 0, 0])
83
+ for iR in range(n_Rvecs):
84
+ # read Rvectors
85
+ line = io.readline().strip()
86
+ Rvectors[iR] = np.fromstring(line, sep=" ")
87
+ Rvectors[iR] = Rvectors[iR].astype(np.int32)
88
+ # read r_x, r_y, r_z
89
+ for n in range(n_wann):
90
+ for m in range(n_wann):
91
+ line = ssrline(io)
92
+ assert (
93
+ m == int(line[0]) - 1 and n == int(line[1]) - 1
94
+ ), f"Unexpected indices in line {line}"
95
+ pos_operator[iR, m, n, 0] = complex(float(line[2]), float(line[3]))
96
+ pos_operator[iR, m, n, 1] = complex(float(line[4]), float(line[5]))
97
+ pos_operator[iR, m, n, 2] = complex(float(line[6]), float(line[7]))
98
+ io.readline()
99
+
100
+ # print(
101
+ # f"Reading tb.dat file: {filename} | Header: {header} | n_wann: {n_wann} | n_Rvecs: {n_Rvecs}"
102
+ # )
103
+
104
+ centers = pos_operator[iR0, :, :, :].diagonal(offset=0, axis1=0, axis2=1).T.real
105
+
106
+ return {
107
+ "n_wann": n_wann,
108
+ "lattice": lattice,
109
+ "Rvectors": Rvectors,
110
+ "Rdegens": Rdegens,
111
+ "H": H,
112
+ "pos_operator": pos_operator,
113
+ "centers": centers,
114
+ "header": header,
115
+ }
116
+
117
+
118
+ def test_parse_tb_file():
119
+ filename = "cri3_up_tb.dat"
120
+ result = parse_tb_file(filename)
121
+ # rvecs=result["Rvectors"]
122
+ # iR=find_Rvectors(rvecs, [0, 0, 0])
123
+ # for i in range(30):
124
+ # print(np.real(result["pos_operator"][iR, i, i, :]))
125
+ print(result["centers"])
126
+
127
+
128
+ if __name__ == "__main__":
129
+ test_parse_tb_file()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: TB2J
3
- Version: 0.8.1
3
+ Version: 0.8.2.1
4
4
  Summary: TB2J: First principle to Heisenberg exchange J using tight-binding Green function method
5
5
  Home-page: UNKNOWN
6
6
  Author: Xu He
@@ -67,6 +67,7 @@ TB2J/spinham/spin_xml.py
67
67
  TB2J/spinham/supercell.py
68
68
  TB2J/wannier/__init__.py
69
69
  TB2J/wannier/w90_parser.py
70
+ TB2J/wannier/w90_tb_parser.py
70
71
  scripts/TB2J_downfold.py
71
72
  scripts/TB2J_eigen.py
72
73
  scripts/TB2J_magnon.py
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  from setuptools import setup, find_packages
3
3
 
4
- __version__ = "0.8.1"
4
+ __version__ = "0.8.2.1"
5
5
 
6
6
  long_description = """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. """
7
7
 
@@ -1 +0,0 @@
1
- __version__ = "0.8.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes