TB2J 0.9.9.13__tar.gz → 0.9.9.15__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 (118) hide show
  1. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/PKG-INFO +1 -1
  2. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/MAEGreen.py +1 -2
  3. tb2j-0.9.9.15/TB2J/__init__.py +1 -0
  4. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchange.py +1 -1
  5. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/stru_api.py +10 -17
  6. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/siesta_interface.py +1 -1
  7. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_exchange.py +4 -5
  8. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_vampire.py +1 -1
  9. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_merge.py +60 -45
  10. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/magnon3.py +105 -11
  11. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/magnon_band.py +3 -3
  12. tb2j-0.9.9.15/TB2J/magnon/magnon_dos.py +308 -0
  13. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/magnon_math.py +1 -1
  14. tb2j-0.9.9.15/TB2J/magnon/plot_magnon_dos_cli.py +99 -0
  15. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/PKG-INFO +1 -1
  16. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/SOURCES.txt +2 -0
  17. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/entry_points.txt +1 -0
  18. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_merge.py +9 -5
  19. tb2j-0.9.9.15/scripts/TB2J_plot_magnon_bands.py +27 -0
  20. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/setup.py +2 -1
  21. tb2j-0.9.9.13/TB2J/__init__.py +0 -1
  22. tb2j-0.9.9.13/scripts/TB2J_plot_magnon_bands.py +0 -7
  23. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/LICENSE +0 -0
  24. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/README.md +0 -0
  25. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/Jdownfolder.py +0 -0
  26. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/Jtensor.py +0 -0
  27. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/MAE.py +0 -0
  28. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/Oiju.py +0 -0
  29. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/Oiju_epc.py +0 -0
  30. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/anisotropy.py +0 -0
  31. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/basis.py +0 -0
  32. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/citation.py +0 -0
  33. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/contour.py +0 -0
  34. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/density_matrix.py +0 -0
  35. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/epc.py +0 -0
  36. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchangeCL2.py +0 -0
  37. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchange_params.py +0 -0
  38. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchange_pert.py +0 -0
  39. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchange_qspace.py +0 -0
  40. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/external/__init__.py +0 -0
  41. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/external/p_tqdm.py +0 -0
  42. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/gpaw_wrapper.py +0 -0
  43. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/green.py +0 -0
  44. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/greentest.py +0 -0
  45. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/__init__.py +0 -0
  46. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/__init__.py +0 -0
  47. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/abacus_api.py +0 -0
  48. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/abacus_wrapper.py +0 -0
  49. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/gen_exchange_abacus.py +0 -0
  50. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/orbital_api.py +0 -0
  51. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/test_density_matrix.py +0 -0
  52. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/test_read_HRSR.py +0 -0
  53. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/test_read_stru.py +0 -0
  54. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/gpaw_interface.py +0 -0
  55. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/lawaf_interface.py +0 -0
  56. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/manager.py +0 -0
  57. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/wannier90_interface.py +0 -0
  58. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/__init__.py +0 -0
  59. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_multibinit.py +0 -0
  60. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_tomsasd.py +0 -0
  61. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_txt.py +0 -0
  62. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_uppasd.py +0 -0
  63. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/kpoints.py +0 -0
  64. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/__init__.py +0 -0
  65. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/io_exchange2.py +0 -0
  66. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/magnon_io.py +0 -0
  67. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/plot.py +0 -0
  68. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/structure.py +0 -0
  69. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/__init__.py +0 -0
  70. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/auto_kpath.py +0 -0
  71. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/fermi.py +0 -0
  72. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/fibonacci_sphere.py +0 -0
  73. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/kR_convert.py +0 -0
  74. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/lowdin.py +0 -0
  75. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/rotate_spin.py +0 -0
  76. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/myTB.py +0 -0
  77. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mycfr.py +0 -0
  78. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/orbital_magmom.py +0 -0
  79. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/orbmap.py +0 -0
  80. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/pauli.py +0 -0
  81. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/pert.py +0 -0
  82. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/plot.py +0 -0
  83. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/rotate_atoms.py +0 -0
  84. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/rotate_siestaDM.py +0 -0
  85. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/sisl_wrapper.py +0 -0
  86. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/__init__.py +0 -0
  87. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/base_parser.py +0 -0
  88. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/constants.py +0 -0
  89. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/hamiltonian.py +0 -0
  90. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/hamiltonian_terms.py +0 -0
  91. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/plot.py +0 -0
  92. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/qsolver.py +0 -0
  93. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/spin_api.py +0 -0
  94. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/spin_xml.py +0 -0
  95. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/supercell.py +0 -0
  96. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/symmetrize_J.py +0 -0
  97. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/tensor_rotate.py +0 -0
  98. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/thetaphi.py +0 -0
  99. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/utest.py +0 -0
  100. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/utils.py +0 -0
  101. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/versioninfo.py +0 -0
  102. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/wannier/__init__.py +0 -0
  103. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/wannier/w90_parser.py +0 -0
  104. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/wannier/w90_tb_parser.py +0 -0
  105. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/dependency_links.txt +0 -0
  106. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/requires.txt +0 -0
  107. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/top_level.txt +0 -0
  108. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_downfold.py +0 -0
  109. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_eigen.py +0 -0
  110. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_magnon.py +0 -0
  111. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_magnon2.py +0 -0
  112. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_magnon_dos.py +0 -0
  113. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_rotate.py +0 -0
  114. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_rotateDM.py +0 -0
  115. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/abacus2J.py +0 -0
  116. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/siesta2J.py +0 -0
  117. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/wann2J.py +0 -0
  118. {tb2j-0.9.9.13 → tb2j-0.9.9.15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TB2J
3
- Version: 0.9.9.13
3
+ Version: 0.9.9.15
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
@@ -94,12 +94,11 @@ class MAEGreen(ExchangeNCL):
94
94
  self.thetas.append(i * np.pi / 180)
95
95
  self.phis.append(j * np.pi / 180)
96
96
 
97
- def set_angels_ztox(self, n=16):
97
+ def set_angels_ztox(self, n=16):
98
98
  """Set angles for a scan from z to x"""
99
99
  self.thetas = np.linspace(0, np.pi, n)
100
100
  self.phis = np.zeros(n)
101
101
 
102
-
103
102
  def set_angles_random(self, n=16):
104
103
  # n random pairs of theta, phi
105
104
  self.thetas = np.random.random(n) * np.pi
@@ -0,0 +1 @@
1
+ __version__ = "0.9.9.14"
@@ -48,7 +48,7 @@ class Exchange(ExchangeParams):
48
48
  self.emin = self.G.find_energy_ingap(rbound=self.efermi - 15.0) - self.efermi
49
49
  # self.emin = self.G.find_energy_ingap(rbound=self.efermi - 15.0) - self.efermi
50
50
  # self.emin = -42.0
51
- #print(f"A gap is found at {self.emin}, set emin to it.")
51
+ # print(f"A gap is found at {self.emin}, set emin to it.")
52
52
 
53
53
  def set_tbmodels(self, tbmodels):
54
54
  pass
@@ -7,21 +7,28 @@
7
7
  Modified on Wed Aug 01 11:44:51 2022
8
8
  @author: Ji Yu-yang
9
9
  """
10
- import warnings
11
- warnings.simplefilter(action='ignore', category=FutureWarning)
12
-
13
10
 
14
11
  import os
15
12
  import re
16
13
  import shutil
14
+ import warnings
15
+ from copy import deepcopy
16
+ from math import sqrt
17
17
  from pathlib import Path
18
18
 
19
19
  import numpy as np
20
20
  from ase import Atoms
21
+ from ase.calculators.calculator import kpts2ndarray, kpts2sizeandoffsets
21
22
  from ase.calculators.singlepoint import SinglePointDFTCalculator, arrays_to_kpoints
23
+ from ase.constraints import FixAtoms, FixCartesian
24
+ from ase.dft.kpoints import bandpath
25
+ from ase.stress import full_3x3_to_voigt_6_stress
22
26
  from ase.units import Bohr, GPa, Hartree, Rydberg, _me, mol
23
27
  from ase.utils import lazymethod, lazyproperty, reader, writer
24
28
 
29
+ warnings.simplefilter(action="ignore", category=FutureWarning)
30
+
31
+
25
32
  _re_float = r"[-+]?\d+\.*\d*(?:[Ee][-+]\d+)?"
26
33
  AU_to_MASS = mol * _me * 1e3
27
34
  UNIT_V = np.sqrt(Hartree / AU_to_MASS)
@@ -42,7 +49,6 @@ def write_input(fd, parameters=None):
42
49
  parameters: dict
43
50
  The dictionary of all paramters for the calculation.
44
51
  """
45
- from copy import deepcopy
46
52
 
47
53
  params = deepcopy(parameters)
48
54
  params["dft_functional"] = (
@@ -105,8 +111,6 @@ def write_kpt(fd=None, parameters=None, atoms=None):
105
111
  return
106
112
  elif kpts is not None:
107
113
  if isinstance(kpts, dict) and "path" not in kpts:
108
- from ase.calculators.calculator import kpts2sizeandoffsets
109
-
110
114
  kgrid, shift = kpts2sizeandoffsets(atoms=atoms, **kpts)
111
115
  koffset = []
112
116
  for i, x in enumerate(shift):
@@ -121,8 +125,6 @@ def write_kpt(fd=None, parameters=None, atoms=None):
121
125
  koffset = [koffset] * 3
122
126
 
123
127
  if isinstance(kgrid, dict) or hasattr(kgrid, "kpts"):
124
- from ase.calculators.calculator import kpts2ndarray
125
-
126
128
  kmode = "Direct"
127
129
  kgrid = kpts2ndarray(kgrid, atoms=atoms)
128
130
  elif isinstance(kgrid, str) and (kgrid == "gamma"):
@@ -272,8 +274,6 @@ def judge_exist_stru(stru=None):
272
274
 
273
275
 
274
276
  def read_ase_stru(stru=None, coordinates_type="Cartesian"):
275
- from ase.constraints import FixAtoms, FixCartesian
276
-
277
277
  fix_cart = np.ones((len(stru), 3), dtype=int).tolist()
278
278
  for constr in stru.constraints:
279
279
  for i in constr.index:
@@ -567,8 +567,6 @@ def read_kpt(fd, cell=None):
567
567
  else:
568
568
  knumbers = klines[:, 3].astype(int)
569
569
  if cell is not None:
570
- from ase.dft.kpoints import bandpath
571
-
572
570
  return bandpath(kpts, cell, npoints=knumbers.sum())
573
571
  else:
574
572
  return {
@@ -683,8 +681,6 @@ def read_abacus(fd, latname=None, verbose=False):
683
681
  If `verbose` is True, pseudo-potential, basis and other information along with the Atoms object will be output as a dict.
684
682
  """
685
683
 
686
- from ase.constraints import FixCartesian
687
-
688
684
  contents = fd.read()
689
685
  title_str = r"(?:LATTICE_CONSTANT|NUMERICAL_DESCRIPTOR|NUMERICAL_ORBITAL|ABFS_ORBITAL|LATTICE_VECTORS|LATTICE_PARAMETERS|ATOMIC_POSITIONS)"
690
686
 
@@ -887,8 +883,6 @@ def read_abacus(fd, latname=None, verbose=False):
887
883
 
888
884
 
889
885
  def get_lattice_from_latname(lines, latname=None):
890
- from math import sqrt
891
-
892
886
  if lines:
893
887
  lines = lines.group(1).split(" ")
894
888
 
@@ -1488,7 +1482,6 @@ class AbacusOutCalcChunk(AbacusOutChunk):
1488
1482
  @lazymethod
1489
1483
  def get_stress(self):
1490
1484
  """Get the stress from the output file according to index"""
1491
- from ase.stress import full_3x3_to_voigt_6_stress
1492
1485
 
1493
1486
  try:
1494
1487
  stress = (
@@ -165,7 +165,7 @@ Warning: The DMI component parallel to the spin orientation, the Jani which has
165
165
  # thetas = [0, np.pi / 2, np.pi, 3 * np.pi / 2]
166
166
  # phis = [0, 0, 0, 0]
167
167
  # MAE.set_angles(thetas=thetas, phis=phis)
168
- #MAE.set_xyz_angles()
168
+ # MAE.set_xyz_angles()
169
169
  MAE.run(output_path=f"{output_path}_anisotropy", with_eigen=False)
170
170
  # print(
171
171
  # f"MAE calculation finished. The results are in {output_path} directory."
@@ -340,20 +340,19 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
340
340
  """
341
341
  i = self.i_spin(i)
342
342
  j = self.i_spin(j)
343
- J=D=Ja = None
343
+ J = D = Ja = None
344
344
  if Jiso:
345
- J= self.get_Jiso(i, j, R)
345
+ J = self.get_Jiso(i, j, R)
346
346
  if DMI:
347
347
  D = self.get_DMI(i, j, R)
348
348
  if D is not None:
349
- D*=1
349
+ D *= 1
350
350
  if Jani:
351
351
  Ja = self.get_Jani(i, j, R)
352
352
  if Ja is not None:
353
- Ja*=1
353
+ Ja *= 1
354
354
  Jtensor = combine_J_tensor(Jiso=J, D=D, Jani=Ja)
355
355
 
356
-
357
356
  # if iso_only:
358
357
  # J = self.get_Jiso(i, j, R)
359
358
  # if J is not None:
@@ -60,7 +60,7 @@ def write_vampire_unitcell_file(cls, fname):
60
60
  val = np.real(Jtensor[i, j] * 2.0 / J)
61
61
  if np.abs(val) < 1e-30:
62
62
  val = 0.0
63
- myfile.write(f"{val:<012.5e} ")
63
+ myfile.write(f"{val:< 12.5e} ")
64
64
  myfile.write("\n")
65
65
 
66
66
 
@@ -1,31 +1,35 @@
1
- import os
2
1
  import copy
2
+ import os
3
3
  import warnings
4
+ from itertools import combinations_with_replacement
5
+
4
6
  import numpy as np
5
- from itertools import combinations_with_replacement, product
7
+
6
8
  from TB2J.io_exchange import SpinIO
7
9
 
8
10
  u0 = np.zeros(3)
9
11
  uy = np.array([0.0, 1.0, 0.0])
10
12
  uz = np.array([0.0, 0.0, 1.0])
11
13
 
12
- def get_Jani_coefficients(a, R=np.eye(3)):
13
14
 
15
+ def get_Jani_coefficients(a, R=np.eye(3)):
14
16
  if len(a) == 1:
15
17
  u = a
16
18
  v = a
17
19
  else:
18
20
  u = a[[0, 0, 1]]
19
21
  v = a[[0, 1, 1]]
20
-
22
+
21
23
  ur = u @ R.T
22
24
  vr = v @ R.T
23
- coefficients = np.hstack([ur*vr, np.roll(ur, -1, axis=-1)*vr + np.roll(vr, -1, axis=-1)*ur])
25
+ coefficients = np.hstack(
26
+ [ur * vr, np.roll(ur, -1, axis=-1) * vr + np.roll(vr, -1, axis=-1) * ur]
27
+ )
24
28
 
25
29
  return coefficients, u, v
26
30
 
27
- def get_projections(a, b, tol=1e-2):
28
31
 
32
+ def get_projections(a, b, tol=1e-2):
29
33
  projections = np.empty((2, 3))
30
34
  if np.linalg.matrix_rank([a, b], tol=tol) == 1:
31
35
  if np.linalg.matrix_rank([a, uy], tol=tol) == 1:
@@ -41,13 +45,13 @@ def get_projections(a, b, tol=1e-2):
41
45
 
42
46
  return projections
43
47
 
48
+
44
49
  class SpinIO_merge(SpinIO):
45
50
  def __init__(self, *args, **kwargs):
46
51
  super(SpinIO_merge, self).__init__(*args, **kwargs)
47
52
  self.projv = None
48
53
 
49
54
  def _set_projection_vectors(self):
50
-
51
55
  norm = np.linalg.norm(self.spinat, axis=-1).reshape(-1, 1)
52
56
  spinat = self.spinat / norm
53
57
  idx = [self.ind_atoms[i] for i in self.index_spin if i >= 0]
@@ -60,27 +64,29 @@ class SpinIO_merge(SpinIO):
60
64
  self.projv = projv
61
65
 
62
66
  @classmethod
63
- def load_pickle(cls, path='TB2J_results', fname='TB2J.pickle'):
67
+ def load_pickle(cls, path="TB2J_results", fname="TB2J.pickle"):
64
68
  obj = super(SpinIO_merge, cls).load_pickle(path=path, fname=fname)
65
69
  obj._set_projection_vectors()
66
70
 
67
71
  return obj
68
72
 
73
+
69
74
  def read_pickle(path):
70
- p1 = os.path.join(path, 'TB2J_results', 'TB2J.pickle')
71
- p2 = os.path.join(path, 'TB2J.pickle')
75
+ p1 = os.path.join(path, "TB2J_results", "TB2J.pickle")
76
+ p2 = os.path.join(path, "TB2J.pickle")
72
77
  if os.path.exists(p1) and os.path.exists(p2):
73
78
  print(f" WARNING!: Both file {p1} and {p2} exist. Use default {p1}.")
74
79
  if os.path.exists(p1):
75
- ret = SpinIO_merge.load_pickle(os.path.join(path, 'TB2J_results'))
80
+ ret = SpinIO_merge.load_pickle(os.path.join(path, "TB2J_results"))
76
81
  elif os.path.exists(p2):
77
82
  ret = SpinIO_merge.load_pickle(path)
78
83
  else:
79
84
  raise FileNotFoundError(f"Cannot find either file {p1} or {p2}")
80
85
  return ret
81
86
 
82
- class Merger():
83
- def __init__(self, *paths, main_path=None):
87
+
88
+ class Merger:
89
+ def __init__(self, *paths, main_path=None):
84
90
  self.dat = [read_pickle(path) for path in paths]
85
91
 
86
92
  if main_path is None:
@@ -90,32 +96,34 @@ class Merger():
90
96
  self.dat.append(copy.deepcopy(self.main_dat))
91
97
 
92
98
  self._set_projv()
93
-
99
+
94
100
  def _set_projv(self):
95
101
  cell = self.main_dat.atoms.cell.array
96
- rotated_cells = np.stack(
97
- [obj.atoms.cell.array for obj in self.dat], axis=0
98
- )
102
+ rotated_cells = np.stack([obj.atoms.cell.array for obj in self.dat], axis=0)
99
103
  R = np.linalg.solve(cell, rotated_cells)
100
104
  indices = range(len(self.dat))
101
105
 
102
- proju = {}; projv = {}; coeff_matrix = {}; projectors = {};
106
+ proju = {}
107
+ projv = {}
108
+ coeff_matrix = {}
109
+ projectors = {}
103
110
  for key in self.main_dat.projv.keys():
104
111
  vectors = [obj.projv[key] for obj in self.dat]
105
- coefficients, u, v = zip(*[get_Jani_coefficients(vectors[i], R=R[i]) for i in indices])
112
+ coefficients, u, v = zip(
113
+ *[get_Jani_coefficients(vectors[i], R=R[i]) for i in indices]
114
+ )
106
115
  projectors[key] = np.vstack([u[i] @ R[i].T for i in indices])
107
116
  coeff_matrix[key] = np.vstack(coefficients)
108
117
  proju[key] = np.stack(u)
109
118
  projv[key] = np.stack(v)
110
119
  if np.linalg.matrix_rank(coeff_matrix[key], tol=1e-2) < 6:
111
- warnings.warn('''
120
+ warnings.warn("""
112
121
  WARNING: The matrix of equations to reconstruct the exchange tensors is
113
122
  close to being singular. This happens when the magnetic moments between
114
123
  different configurations are cloes to being parallel. You need to consider
115
124
  more rotated spin configurations, otherwise the results might have a large
116
- error.'''
117
- )
118
-
125
+ error.""")
126
+
119
127
  self.proju = proju
120
128
  self.projv = projv
121
129
  self.coeff_matrix = coeff_matrix
@@ -123,71 +131,78 @@ class Merger():
123
131
 
124
132
  def merge_Jani(self):
125
133
  Jani_dict = {}
126
- proju = self.proju; projv = self.projv; coeff_matrix = self.coeff_matrix;
134
+ proju = self.proju
135
+ projv = self.projv
136
+ coeff_matrix = self.coeff_matrix
127
137
  for key in self.main_dat.Jani_dict.keys():
128
138
  try:
129
139
  R, i, j = key
130
140
  u = proju[i, j]
131
141
  v = projv[i, j]
132
142
  Jani = np.stack([sio.Jani_dict[key] for sio in self.dat])
133
- projections = np.einsum('nmi,nij,nmj->nm', u, Jani, v).flatten()
143
+ projections = np.einsum("nmi,nij,nmj->nm", u, Jani, v).flatten()
134
144
  except KeyError as err:
135
145
  raise KeyError(
136
146
  "Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
137
- % err)
147
+ % err
148
+ )
138
149
  newJani = np.linalg.lstsq(coeff_matrix[i, j], projections, rcond=1e-2)[0]
139
- Jani_dict[key] = np.array([
140
- [newJani[0], newJani[3], newJani[5]],
141
- [newJani[3], newJani[1], newJani[4]],
142
- [newJani[5], newJani[4], newJani[2]]
143
- ])
150
+ Jani_dict[key] = np.array(
151
+ [
152
+ [newJani[0], newJani[3], newJani[5]],
153
+ [newJani[3], newJani[1], newJani[4]],
154
+ [newJani[5], newJani[4], newJani[2]],
155
+ ]
156
+ )
144
157
  self.main_dat.Jani_dict = Jani_dict
145
158
 
146
159
  def merge_Jiso(self):
147
- Jdict={}
160
+ Jdict = {}
148
161
  for key in self.main_dat.exchange_Jdict.keys():
149
162
  try:
150
- J = np.mean([obj.exchange_Jdict[key] for obj in self.dat])
163
+ J = np.mean([obj.exchange_Jdict[key] for obj in self.dat])
151
164
  except KeyError as err:
152
165
  raise KeyError(
153
- "Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
154
- % err)
166
+ "Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
167
+ % err
168
+ )
155
169
  Jdict[key] = J
156
170
  self.main_dat.exchange_Jdict = Jdict
157
-
158
171
 
159
172
  def merge_DMI(self):
160
173
  dmi_ddict = {}
161
174
  if all(obj.has_dmi for obj in self.dat):
162
- projectors = self.projectors; proju = self.proju;
175
+ projectors = self.projectors
176
+ proju = self.proju
163
177
  for key in self.main_dat.dmi_ddict.keys():
164
178
  try:
165
179
  R, i, j = key
166
180
  u = proju[i, j]
167
181
  DMI = np.stack([sio.dmi_ddict[key] for sio in self.dat])
168
- projections = np.einsum('nmi,ni->nm', u, DMI).flatten()
182
+ projections = np.einsum("nmi,ni->nm", u, DMI).flatten()
169
183
  except KeyError as err:
170
184
  raise KeyError(
171
185
  "Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
172
- % err)
186
+ % err
187
+ )
173
188
  newDMI = np.linalg.lstsq(projectors[i, j], projections, rcond=4e-1)[0]
174
189
  dmi_ddict[key] = newDMI
175
190
  self.main_dat.dmi_ddict = dmi_ddict
176
191
 
177
192
  def standardize(self):
178
193
  # make sure that the Jani has the trace of zero
179
- Jdict=self.main_dat.exchange_Jdict
180
- Jani_dict=self.main_dat.Jani_dict
194
+ Jdict = self.main_dat.exchange_Jdict
195
+ Jani_dict = self.main_dat.Jani_dict
181
196
  for key in self.main_dat.Jani_dict.keys():
182
197
  Jani = self.main_dat.Jani_dict[key]
183
- shift = np.trace(Jani)/3.0
184
- Jani_dict[key] -= shift * np.eye(3)
198
+ shift = np.trace(Jani) / 3.0
199
+ Jani_dict[key] -= shift * np.eye(3)
185
200
  Jdict[key] += shift
186
201
  self.main_dat.Jani_dict = Jani_dict
187
202
  self.main_dat.exchange_Jdict = Jdict
188
-
189
203
 
190
- def merge(*paths, main_path=None, save=True, write_path='TB2J_results'):
204
+
205
+ def merge(*paths, main_path=None, save=True, write_path="TB2J_results"):
191
206
  m = Merger(*paths, main_path=main_path)
192
207
  m.merge_Jiso()
193
208
  m.merge_DMI()
@@ -2,12 +2,16 @@ from dataclasses import asdict, dataclass
2
2
  from pathlib import Path
3
3
  from typing import List, Optional, Tuple, Union
4
4
 
5
+ import matplotlib.pyplot as plt
5
6
  import numpy as np
6
7
  import tomli
7
8
  import tomli_w
9
+ from ase.dft.dos import DOS
10
+ from ase.units import J, eV
8
11
  from scipy.spatial.transform import Rotation
9
12
 
10
13
  from TB2J.io_exchange import SpinIO
14
+ from TB2J.kpoints import monkhorst_pack
11
15
  from TB2J.magnon.magnon_band import MagnonBand
12
16
  from TB2J.magnon.magnon_math import get_rotation_arrays
13
17
  from TB2J.mathutils.auto_kpath import auto_kpath
@@ -162,10 +166,10 @@ class Magnon:
162
166
  phase = 2 * np.pi * R @ qpt
163
167
  Jq[iqpt] += np.exp(1j * phase) * JRprime[iR]
164
168
 
165
- #Jq_copy = Jq.copy()
166
- #Jq.swapaxes(-1, -2) # swap xyz
167
- #Jq.swapaxes(-3, -4) # swap ij
168
- #Jq = (Jq.conj() + Jq_copy) / 2.0
169
+ # Jq_copy = Jq.copy()
170
+ # Jq.swapaxes(-1, -2) # swap xyz
171
+ # Jq.swapaxes(-3, -4) # swap ij
172
+ # Jq = (Jq.conj() + Jq_copy) / 2.0
169
173
  return Jq
170
174
 
171
175
  def Hq(self, kpoints):
@@ -187,7 +191,6 @@ class Magnon:
187
191
  magmoms = self.magmom.copy()
188
192
  magmoms /= np.linalg.norm(magmoms, axis=-1)[:, None]
189
193
 
190
-
191
194
  U, V = get_rotation_arrays(magmoms, u=self._uz)
192
195
 
193
196
  J0 = -self.Jq(np.zeros((1, 3)))[0]
@@ -195,7 +198,6 @@ class Magnon:
195
198
  # Jq = -Hermitize(self.Jq(kpoints, anisotropic=anisotropic))
196
199
 
197
200
  Jq = -self.Jq(kpoints)
198
- print(f"J0 shape: {J0.shape}")
199
201
 
200
202
  C = np.diag(np.einsum("ix,ijxy,jy->i", V, 2 * J0, V))
201
203
  B = np.einsum("ix,kijxy,jy->kij", U, Jq, U)
@@ -203,7 +205,6 @@ class Magnon:
203
205
  A2 = np.einsum("ix,kijxy,jy->kij", U.conj(), Jq, U)
204
206
 
205
207
  H = np.block([[A1 - C, B], [B.swapaxes(-1, -2).conj(), A2 - C]])
206
- print(f"H shape: {H.shape}")
207
208
  return H
208
209
 
209
210
  def _magnon_energies(self, kpoints, u=None):
@@ -801,12 +802,11 @@ def main():
801
802
  )
802
803
 
803
804
  parser.add_argument(
804
- "--show",
805
+ "--show",
805
806
  action="store_true",
806
807
  default=False,
807
808
  help="show figure on screen.",
808
- )
809
-
809
+ )
810
810
 
811
811
  args = parser.parse_args()
812
812
 
@@ -834,7 +834,7 @@ def main():
834
834
  Q=args.Q if args.Q is not None else None,
835
835
  uz_file=args.uz_file,
836
836
  n=args.n if args.n is not None else None,
837
- show=args.show
837
+ show=args.show,
838
838
  )
839
839
 
840
840
  plot_magnon_bands_from_TB2J(params)
@@ -842,3 +842,97 @@ def main():
842
842
 
843
843
  if __name__ == "__main__":
844
844
  main()
845
+
846
+
847
+ class MagnonASEWrapper:
848
+ def __init__(self, magnon: Magnon):
849
+ self.magnon = magnon
850
+ self.atoms = None
851
+ self.kpts = None
852
+ self.weights = None
853
+ self.dos_args = {}
854
+
855
+ def set(self, atoms=None, kmesh=[9, 9, 9], gamma=True, **kwargs):
856
+ self.atoms = atoms
857
+ self.dos_args = {
858
+ "kmesh": kmesh,
859
+ "gamma": gamma,
860
+ }
861
+ self.kpts = monkhorst_pack(
862
+ self.dos_args["kmesh"], gamma_center=self.dos_args["gamma"]
863
+ )
864
+ self.weights = np.ones(len(self.kpts)) / len(self.kpts)
865
+
866
+ def get_k_points_and_weights(self):
867
+ return self.kpts, self.weights
868
+
869
+ def get_k_point_weights(self):
870
+ return self.weights
871
+
872
+ def get_number_of_spins(self):
873
+ return 1
874
+
875
+ def get_eigenvalues(self, kpt, spin=0):
876
+ """
877
+ return the eigenvalues at a given k-point. The energy unit is eV
878
+ args:
879
+ kpt: k-point index.
880
+ spin: spin index.
881
+ """
882
+ kpoint = self.kpts[kpt]
883
+ # Magnon energies are already in eV, convert to meV for consistency with plot
884
+ evals = self.magnon._magnon_energies(np.array([kpoint]))[0]
885
+ evals = evals * J / eV # Convert to eV
886
+ return evals
887
+
888
+ def get_fermi_level(self):
889
+ return 0.0
890
+
891
+ def get_bz_k_points(self):
892
+ return self.kpts
893
+
894
+ def get_dos(self, width=0.1, window=None, npts=401):
895
+ dos = DOS(self, width=width, window=window, npts=npts)
896
+ energies = dos.get_energies()
897
+ tdos = dos.get_dos()
898
+ return energies, tdos
899
+
900
+ def plot_dos(
901
+ self,
902
+ smearing_width=0.0001,
903
+ window=None,
904
+ npts=401,
905
+ output="magnon_dos.pdf",
906
+ ax=None,
907
+ show=True,
908
+ dos_filename="magnon_dos.txt",
909
+ ):
910
+ """
911
+ plot total DOS.
912
+ :param width: width of Gaussian smearing
913
+ :param window: energy window
914
+ :param npts: number of points
915
+ :param output: output filename
916
+ :param ax: matplotlib axis
917
+ :return: ax
918
+ """
919
+ if ax is None:
920
+ _fig, ax = plt.subplots()
921
+ energies, tdos = self.get_dos(width=smearing_width, window=window, npts=npts)
922
+ energies = energies * 1000 # Convert to meV
923
+ tdos = tdos / 1000 # Convert to states/meV
924
+ if dos_filename is not None:
925
+ np.savetxt(
926
+ dos_filename,
927
+ np.array([energies, tdos]).T,
928
+ header="Energy(meV) DOS(state/meV)",
929
+ )
930
+ ax.plot(energies, tdos)
931
+ ax.set_xlabel("Energy (meV)")
932
+ ax.set_ylabel("DOS (states/meV)")
933
+ ax.set_title("Total DOS")
934
+ if output is not None:
935
+ plt.savefig(output)
936
+ if show:
937
+ plt.show()
938
+ return ax
@@ -40,7 +40,7 @@ class MagnonBand:
40
40
  if self.xcoords is None:
41
41
  self.xcoords = np.arange(len(self.kpoints))
42
42
 
43
- def plot(self, ax=None, filename=None, show=False, shift=0.0,**kwargs):
43
+ def plot(self, ax=None, filename=None, show=False, shift=0.0, **kwargs):
44
44
  """Plot the magnon band structure.
45
45
 
46
46
  Parameters
@@ -79,7 +79,7 @@ class MagnonBand:
79
79
  for band in segment_bands:
80
80
  ax.plot(
81
81
  x,
82
- band[start_idx : start_idx + nbands]+shift,
82
+ band[start_idx : start_idx + nbands] + shift,
83
83
  linewidth=linewidth,
84
84
  color=color,
85
85
  linestyle=linestyle,
@@ -91,7 +91,7 @@ class MagnonBand:
91
91
  for band in self.energies.T:
92
92
  ax.plot(
93
93
  self.xcoords,
94
- band+shift,
94
+ band + shift,
95
95
  linewidth=linewidth,
96
96
  color=color,
97
97
  linestyle=linestyle,