TB2J 0.9.12.9__py3-none-any.whl → 0.9.12.22__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 (40) hide show
  1. TB2J/MAE.py +8 -1
  2. TB2J/MAEGreen.py +78 -61
  3. TB2J/contour.py +3 -2
  4. TB2J/exchange.py +346 -51
  5. TB2J/exchangeCL2.py +285 -47
  6. TB2J/exchange_params.py +48 -0
  7. TB2J/green.py +73 -36
  8. TB2J/interfaces/abacus/gen_exchange_abacus.py +2 -1
  9. TB2J/interfaces/wannier90_interface.py +4 -4
  10. TB2J/io_exchange/__init__.py +19 -1
  11. TB2J/io_exchange/edit.py +594 -0
  12. TB2J/io_exchange/io_espins.py +276 -0
  13. TB2J/io_exchange/io_exchange.py +248 -76
  14. TB2J/io_exchange/io_tomsasd.py +4 -3
  15. TB2J/io_exchange/io_txt.py +72 -7
  16. TB2J/io_exchange/io_vampire.py +4 -2
  17. TB2J/io_merge.py +60 -40
  18. TB2J/magnon/magnon3.py +27 -2
  19. TB2J/mathutils/rotate_spin.py +7 -7
  20. TB2J/myTB.py +11 -11
  21. TB2J/mycfr.py +11 -11
  22. TB2J/pauli.py +32 -2
  23. TB2J/plot.py +26 -0
  24. TB2J/rotate_atoms.py +9 -6
  25. TB2J/scripts/TB2J_edit.py +403 -0
  26. TB2J/scripts/TB2J_plot_exchange.py +48 -0
  27. TB2J/spinham/hamiltonian.py +156 -13
  28. TB2J/spinham/hamiltonian_terms.py +40 -1
  29. TB2J/spinham/spin_xml.py +40 -8
  30. TB2J/symmetrize_J.py +140 -9
  31. TB2J/tests/test_cli_remove_sublattice.py +33 -0
  32. TB2J/tests/test_cli_toggle_exchange.py +50 -0
  33. {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/METADATA +10 -7
  34. {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/RECORD +38 -34
  35. {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/WHEEL +1 -1
  36. {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/entry_points.txt +2 -0
  37. TB2J/interfaces/abacus/test_read_HRSR.py +0 -43
  38. TB2J/interfaces/abacus/test_read_stru.py +0 -32
  39. {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/licenses/LICENSE +0 -0
  40. {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/top_level.txt +0 -0
@@ -55,6 +55,7 @@ class SpinIO(object):
55
55
  write_experimental=True,
56
56
  description=None,
57
57
  standardize_Jani=False,
58
+ sia_tensor=None,
58
59
  ):
59
60
  """
60
61
  :param atoms: Ase atoms structure.
@@ -80,6 +81,7 @@ class SpinIO(object):
80
81
  :param gyro_ratio: gyromagnetic ratio
81
82
  :param write_experimental: write_experimental data to output files
82
83
  :param description: add some description into the xml file.
84
+ :param sia_tensor: single ion anisotropy tensor, dictionary {i: 3x3 tensor}
83
85
  """
84
86
  self.atoms = atoms #: atomic structures, ase.Atoms object
85
87
  self.index_spin = index_spin
@@ -103,12 +105,10 @@ class SpinIO(object):
103
105
  self._ind_atoms[ispin] = iatom
104
106
 
105
107
  if exchange_Jdict is not None:
106
- self.has_exchange = True #: whether there is isotropic exchange
107
108
  #: The dictionary of :math:`J_{ij}(R)`, the keys are (i,j, R),
108
109
  # where R is a tuple, and the value is the isotropic exchange
109
110
  self.exchange_Jdict = exchange_Jdict
110
111
  else:
111
- self.has_exchange = False
112
112
  self.exchange_Jdict = None
113
113
 
114
114
  self.Jiso_orb = Jiso_orb
@@ -120,21 +120,37 @@ class SpinIO(object):
120
120
  self.dJdx2 = dJdx2
121
121
 
122
122
  if dmi_ddict is not None:
123
- self.has_dmi = True #: Whether there is DMI.
124
123
  #: The dictionary of DMI. the key is the same as exchange_Jdict, the values are 3-d vectors (Dx, Dy, Dz).
125
124
  self.dmi_ddict = dmi_ddict
126
125
  else:
127
- self.has_dmi = False
128
126
  self.dmi_ddict = None
129
127
 
130
128
  if Jani_dict is not None:
131
- self.has_bilinear = True #: Whether there is anisotropic exchange term
132
129
  #: The dictionary of anisotropic exchange. The vlaues are matrices of shape (3,3).
133
130
  self.Jani_dict = Jani_dict
134
131
  else:
135
- self.has_bilinear = False
136
132
  self.Jani_dict = None
137
133
 
134
+ if k1 is not None and k1dir is not None:
135
+ # Convert uniaxial anisotropy to SIA tensor
136
+ if sia_tensor is None:
137
+ sia_tensor = {}
138
+ for i, (K, axis) in enumerate(zip(k1, k1dir)):
139
+ if abs(K) > 1e-10:
140
+ e = np.array(axis)
141
+ norm = np.linalg.norm(e)
142
+ if norm > 1e-6:
143
+ e = e / norm
144
+ # A = K * e * e^T
145
+ A = K * np.outer(e, e)
146
+ if i in sia_tensor:
147
+ sia_tensor[i] += A
148
+ else:
149
+ sia_tensor[i] = A
150
+ # Clear k1 and k1dir as they are now integrated into sia_tensor
151
+ k1 = None
152
+ k1dir = None
153
+
138
154
  if k1 is not None and k1dir is not None:
139
155
  self.has_uniaxial_anistropy = True
140
156
  self.k1 = k1
@@ -144,13 +160,19 @@ class SpinIO(object):
144
160
  self.k1 = None
145
161
  self.k1dir = None
146
162
 
147
- self.has_bilinear = not (Jani_dict == {} or Jani_dict is None)
148
-
149
163
  self.has_biquadratic = not (
150
164
  biquadratic_Jdict == {} or biquadratic_Jdict is None
151
165
  )
166
+
152
167
  self.biquadratic_Jdict = biquadratic_Jdict
153
168
 
169
+ if sia_tensor is not None:
170
+ self.has_sia_tensor = True
171
+ self.sia_tensor = sia_tensor
172
+ else:
173
+ self.has_sia_tensor = False
174
+ self.sia_tensor = None
175
+
154
176
  if NJT_ddict is not None:
155
177
  self.has_NJT_dmi = True
156
178
  self.NJT_ddict = NJT_ddict
@@ -188,6 +210,18 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
188
210
  self.orbital_names = orbital_names
189
211
  self.TB2J_version = __version__
190
212
 
213
+ @property
214
+ def has_exchange(self):
215
+ return self.exchange_Jdict is not None
216
+
217
+ @property
218
+ def has_dmi(self):
219
+ return self.dmi_ddict is not None
220
+
221
+ @property
222
+ def has_bilinear(self):
223
+ return self.Jani_dict is not None
224
+
191
225
  def _build_Rlist(self):
192
226
  Rset = set()
193
227
  ispin_set = set()
@@ -227,12 +261,14 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
227
261
  return self.index_spin[symdict[symnum]]
228
262
 
229
263
  def i_spin(self, i):
230
- if isinstance(i, int):
231
- return i
264
+ if isinstance(i, (int, np.integer)):
265
+ return int(i)
232
266
  elif isinstance(i, str):
233
267
  return self.get_symbol_number_ispin(i)
234
268
  else:
235
- raise ValueError("i must be either an integer or a string.")
269
+ raise ValueError(
270
+ f"i must be either an integer or a string. Got {type(i)}: {i}"
271
+ )
236
272
 
237
273
  def get_charge_ispin(self, i):
238
274
  i = self.i_spin(i)
@@ -331,7 +367,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
331
367
  else:
332
368
  return default
333
369
 
334
- def get_J_tensor(self, i, j, R, Jiso=True, Jani=False, DMI=False):
370
+ def get_J_tensor(self, i, j, R, Jiso=True, Jani=True, DMI=True, SIA=True):
335
371
  """
336
372
  Return the full exchange tensor for atom i and j, and cell R.
337
373
  param i : spin index i
@@ -352,6 +388,20 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
352
388
  if Ja is not None:
353
389
  Ja *= 1
354
390
  Jtensor = combine_J_tensor(Jiso=J, D=D, Jani=Ja)
391
+ if np.linalg.norm(R) < 0.001 and i == j:
392
+ print(f"{SIA=} , {i=}")
393
+ print(f"{self.has_sia_tensor=}")
394
+
395
+ if (
396
+ SIA
397
+ and self.has_sia_tensor
398
+ and self.sia_tensor is not None
399
+ and i == j
400
+ and np.linalg.norm(R) < 0.001
401
+ ):
402
+ if i in self.sia_tensor:
403
+ print(f"Adding SIA tensor for {i}, with {self.sia_tensor[i]}")
404
+ Jtensor += self.sia_tensor[i]
355
405
 
356
406
  # if iso_only:
357
407
  # J = self.get_Jiso(i, j, R)
@@ -367,7 +417,9 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
367
417
  # )
368
418
  return Jtensor
369
419
 
370
- def get_full_Jtensor_for_one_R_i3j3(self, R, Jiso=True, Jani=True, DMI=True):
420
+ def get_full_Jtensor_for_one_R_i3j3(
421
+ self, R, Jiso=True, Jani=True, DMI=True, SIA=True
422
+ ):
371
423
  """
372
424
  Return the full exchange tensor of all i and j for cell R.
373
425
  param R (tuple of integers): cell index R
@@ -379,11 +431,13 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
379
431
  for i in range(self.nspin):
380
432
  for j in range(self.nspin):
381
433
  Jmat[i * 3 : i * 3 + 3, j * 3 : j * 3 + 3] = self.get_J_tensor(
382
- i, j, R, Jiso=Jiso, Jani=Jani, DMI=DMI
434
+ i, j, R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
383
435
  )
384
436
  return Jmat
385
437
 
386
- def get_full_Jtensor_for_one_R_ij33(self, R, Jiso=True, Jani=True, DMI=True):
438
+ def get_full_Jtensor_for_one_R_ij33(
439
+ self, R, Jiso=True, Jani=True, DMI=True, SIA=True
440
+ ):
387
441
  """
388
442
  Return the full exchange tensor of all i and j for cell R.
389
443
  param R (tuple of integers): cell index R
@@ -395,20 +449,22 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
395
449
  for i in range(self.nspin):
396
450
  for j in range(self.nspin):
397
451
  Jmat[i, j, :, :] = self.get_J_tensor(
398
- i, j, R, Jiso=Jiso, Jani=Jani, DMI=DMI
452
+ i, j, R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
399
453
  )
400
454
  return Jmat
401
455
 
402
- def get_full_Jtensor_for_one_R_ij(self, R, Jiso=True, Jani=True, DMI=True):
456
+ def get_full_Jtensor_for_one_R_ij(
457
+ self, R, Jiso=True, Jani=True, DMI=True, SIA=True,
458
+ ):
403
459
  """
404
460
  Return the full exchange tensor of all i and j for cell R.
405
461
  param R (tuple of integers): cell index R
406
462
  returns:
407
463
  Jmat: (nspin,nspin,3,3) matrix.
408
464
  """
409
- if Jani or DMI:
465
+ if Jani or DMI or SIA:
410
466
  raise ValueError(
411
- "Jani and DMI are not supported for this method. Use get_full_Jtensor_for_one_R_ij33 instead."
467
+ "Jani, DMI and SIA are not supported for this method. Use get_full_Jtensor_for_one_R_ij33 instead."
412
468
  )
413
469
  n = self.nspin
414
470
  Jmat = np.zeros((n, n), dtype=float)
@@ -420,7 +476,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
420
476
  return Jmat
421
477
 
422
478
  def get_full_Jtensor_for_Rlist(
423
- self, asr=False, Jiso=True, Jani=True, DMI=True, order="i3j3"
479
+ self, asr=False, Jiso=True, Jani=True, DMI=True, SIA=True, order="i3j3"
424
480
  ):
425
481
  n = self.nspin
426
482
  n3 = n * 3
@@ -429,7 +485,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
429
485
  Jmat = np.zeros((nR, n3, n3), dtype=float)
430
486
  for iR, R in enumerate(self.Rlist):
431
487
  Jmat[iR] = self.get_full_Jtensor_for_one_R_i3j3(
432
- R, Jiso=Jiso, Jani=Jani, DMI=DMI
488
+ R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
433
489
  )
434
490
  if asr:
435
491
  iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
@@ -441,8 +497,9 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
441
497
  elif order == "ij33":
442
498
  Jmat = np.zeros((nR, n, n, 3, 3), dtype=float)
443
499
  for iR, R in enumerate(self.Rlist):
500
+ print(f"R={R}")
444
501
  Jmat[iR] = self.get_full_Jtensor_for_one_R_ij33(
445
- R, Jiso=Jiso, Jani=Jani, DMI=DMI
502
+ R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
446
503
  )
447
504
  if asr:
448
505
  iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
@@ -453,20 +510,22 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
453
510
  elif order == "i3j3_2D":
454
511
  Jmat = np.zeros((nR, n3, n3), dtype=float)
455
512
  for iR, R in enumerate(self.Rlist):
513
+ print(f"R={R}")
456
514
  Jmat[iR] = self.get_full_Jtensor_for_one_R_i3j3(
457
- R, Jiso=Jiso, Jani=Jani, DMI=DMI
515
+ R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
458
516
  ).reshape((n3, n3))
459
517
  if asr:
460
518
  iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
461
519
  assert np.linalg.norm(self.Rlist[iR0]) == 0
462
- sum_JR = np.sum(np.sum(Jmat, axis=0))
520
+ sum_JR = np.sum(np.sum(Jmat, axis=0), axis=0)
521
+ print(sum_JR)
463
522
  for i in range(n3):
464
- Jmat[iR0][i, i] -= sum_JRi[i]
523
+ Jmat[iR0][i, i] -= sum_JR[i]
465
524
  elif order == "ij":
466
525
  Jmat = np.zeros((nR, n, n), dtype=float)
467
526
  for iR, R in enumerate(self.Rlist):
468
527
  Jmat[iR] = self.get_full_Jtensor_for_one_R_ij(
469
- R, Jiso=Jiso, Jani=Jani, DMI=DMI
528
+ R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
470
529
  )
471
530
  if asr:
472
531
  iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
@@ -521,6 +580,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
521
580
  self.write_multibinit(path=os.path.join(path, "Multibinit"))
522
581
  self.write_tom_format(path=os.path.join(path, "TomASD"))
523
582
  self.write_vampire(path=os.path.join(path, "Vampire"))
583
+ self.write_espins(path=os.path.join(path, "ESPInS"))
524
584
 
525
585
  self.plot_all(savefile=os.path.join(path, "JvsR.pdf"))
526
586
  # self.write_Jq(kmesh=[9, 9, 9], path=path)
@@ -562,17 +622,41 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
562
622
  marker="o",
563
623
  fname=None,
564
624
  show=False,
625
+ by_species=False,
565
626
  **kwargs,
566
627
  ):
567
628
  if ax is None:
568
629
  fig, ax = plt.subplots()
569
- ds = []
570
- Js = []
571
- for key, val in self.exchange_Jdict.items():
572
- d = self.distance_dict[key][1]
573
- ds.append(d)
574
- Js.append(val * 1e3)
575
- ax.scatter(ds, Js, marker=marker, color=color, **kwargs)
630
+ if by_species:
631
+ groups = {}
632
+ for key, val in self.exchange_Jdict.items():
633
+ R, i, j = key
634
+ idx_i = self.ind_atoms[i]
635
+ idx_j = self.ind_atoms[j]
636
+ spec_i = self.atoms[idx_i].symbol
637
+ spec_j = self.atoms[idx_j].symbol
638
+ pair = tuple(sorted([spec_i, spec_j]))
639
+ pair_name = "-".join(pair)
640
+ if pair_name not in groups:
641
+ groups[pair_name] = {"ds": [], "Js": []}
642
+ d = self.distance_dict[key][1]
643
+ groups[pair_name]["ds"].append(d)
644
+ groups[pair_name]["Js"].append(val * 1e3)
645
+
646
+ for pair_name, data in groups.items():
647
+ ax.scatter(
648
+ data["ds"], data["Js"], marker=marker, label=pair_name, **kwargs
649
+ )
650
+ ax.legend()
651
+ else:
652
+ ds = []
653
+ Js = []
654
+ for key, val in self.exchange_Jdict.items():
655
+ d = self.distance_dict[key][1]
656
+ ds.append(d)
657
+ Js.append(val * 1e3)
658
+ ax.scatter(ds, Js, marker=marker, color=color, **kwargs)
659
+
576
660
  ax.axhline(color="gray")
577
661
  ax.set_xlabel(r"Distance ($\AA$)")
578
662
  ax.set_ylabel("J (meV)")
@@ -582,25 +666,68 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
582
666
  plt.show()
583
667
  return ax
584
668
 
585
- def plot_DvsR(self, ax=None, fname=None, show=False):
669
+ def plot_DvsR(self, ax=None, fname=None, show=False, by_species=False):
586
670
  if ax is None:
587
671
  fig, ax = plt.subplots()
588
- ds = []
589
- Ds = []
590
- for key, val in self.dmi_ddict.items():
591
- d = self.distance_dict[key][1]
592
- ds.append(d)
593
- Ds.append(val * 1e3)
594
- Ds = np.array(Ds)
595
- ax.scatter(ds, Ds[:, 0], marker="s", color="r", label="Dx")
596
- ax.scatter(
597
- ds, Ds[:, 1], marker="o", edgecolors="g", facecolors="none", label="Dy"
598
- )
599
- ax.scatter(
600
- ds, Ds[:, 2], marker="D", edgecolors="b", facecolors="none", label="Dz"
601
- )
672
+ if by_species:
673
+ groups = {}
674
+ for key, val in self.dmi_ddict.items():
675
+ R, i, j = key
676
+ idx_i = self.ind_atoms[i]
677
+ idx_j = self.ind_atoms[j]
678
+ spec_i = self.atoms[idx_i].symbol
679
+ spec_j = self.atoms[idx_j].symbol
680
+ pair = tuple(sorted([spec_i, spec_j]))
681
+ pair_name = "-".join(pair)
682
+ if pair_name not in groups:
683
+ groups[pair_name] = {"ds": [], "Ds": []}
684
+ d = self.distance_dict[key][1]
685
+ groups[pair_name]["ds"].append(d)
686
+ groups[pair_name]["Ds"].append(val * 1e3)
687
+
688
+ markers = ["s", "o", "D", "v", "^", "<", ">", "p", "*", "h", "H"]
689
+ for idx, (pair_name, data) in enumerate(groups.items()):
690
+ Ds = np.array(data["Ds"])
691
+ marker = markers[idx % len(markers)]
692
+ ax.scatter(
693
+ data["ds"],
694
+ Ds[:, 0],
695
+ marker=marker,
696
+ color="r",
697
+ label=f"{pair_name} Dx",
698
+ )
699
+ ax.scatter(
700
+ data["ds"],
701
+ Ds[:, 1],
702
+ marker=marker,
703
+ color="g",
704
+ label=f"{pair_name} Dy",
705
+ )
706
+ ax.scatter(
707
+ data["ds"],
708
+ Ds[:, 2],
709
+ marker=marker,
710
+ color="b",
711
+ label=f"{pair_name} Dz",
712
+ )
713
+ ax.legend(ncol=3, fontsize="small")
714
+ else:
715
+ ds = []
716
+ Ds = []
717
+ for key, val in self.dmi_ddict.items():
718
+ d = self.distance_dict[key][1]
719
+ ds.append(d)
720
+ Ds.append(val * 1e3)
721
+ Ds = np.array(Ds)
722
+ ax.scatter(ds, Ds[:, 0], marker="s", color="r", label="Dx")
723
+ ax.scatter(
724
+ ds, Ds[:, 1], marker="o", edgecolors="g", facecolors="none", label="Dy"
725
+ )
726
+ ax.scatter(
727
+ ds, Ds[:, 2], marker="D", edgecolors="b", facecolors="none", label="Dz"
728
+ )
729
+ ax.legend(loc=1)
602
730
  ax.axhline(color="gray")
603
- ax.legend(loc=1)
604
731
  ax.set_ylabel("D (meV)")
605
732
  ax.set_xlabel(r"Distance ($\AA$)")
606
733
  if fname is not None:
@@ -609,31 +736,71 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
609
736
  plt.show()
610
737
  return ax
611
738
 
612
- def plot_JanivsR(self, ax=None, fname=None, show=False):
739
+ def plot_JanivsR(self, ax=None, fname=None, show=False, by_species=False):
613
740
  if ax is None:
614
741
  fig, ax = plt.subplots()
615
- ds = []
616
- Jani = []
617
- for key, val in self.Jani_dict.items():
618
- d = self.distance_dict[key][1]
619
- ds.append(d)
620
- # val = val - np.diag([np.trace(val) / 3] * 3)
621
- Jani.append(val * 1e3)
622
- Jani = np.array(Jani)
623
- s = "xyz"
624
- for i in range(3):
625
- ax.scatter(ds, Jani[:, i, i], marker="s", label=f"J{s[i]}{s[i]}")
626
- c = "rgb"
627
- for ic, (i, j) in enumerate([(0, 1), (0, 2), (1, 2)]):
628
- ax.scatter(
629
- ds,
630
- Jani[:, i, j],
631
- edgecolors=c[ic],
632
- facecolors="none",
633
- label=f"J{s[i]}{s[j]}",
634
- )
742
+ if by_species:
743
+ groups = {}
744
+ for key, val in self.Jani_dict.items():
745
+ R, i, j = key
746
+ idx_i = self.ind_atoms[i]
747
+ idx_j = self.ind_atoms[j]
748
+ spec_i = self.atoms[idx_i].symbol
749
+ spec_j = self.atoms[idx_j].symbol
750
+ pair = tuple(sorted([spec_i, spec_j]))
751
+ pair_name = "-".join(pair)
752
+ if pair_name not in groups:
753
+ groups[pair_name] = {"ds": [], "Jani": []}
754
+ d = self.distance_dict[key][1]
755
+ groups[pair_name]["ds"].append(d)
756
+ groups[pair_name]["Jani"].append(val * 1e3)
757
+
758
+ markers = ["s", "o", "D", "v", "^", "<", ">", "p", "*", "h", "H"]
759
+ s = "xyz"
760
+ for idx, (pair_name, data) in enumerate(groups.items()):
761
+ Jani = np.array(data["Jani"])
762
+ marker = markers[idx % len(markers)]
763
+ # Diagonal
764
+ for i in range(3):
765
+ ax.scatter(
766
+ data["ds"],
767
+ Jani[:, i, i],
768
+ marker=marker,
769
+ label=f"{pair_name} J{s[i]}{s[i]}",
770
+ )
771
+ # Off-diagonal
772
+ for i, j in [(0, 1), (0, 2), (1, 2)]:
773
+ ax.scatter(
774
+ data["ds"],
775
+ Jani[:, i, j],
776
+ marker=marker,
777
+ facecolors="none",
778
+ label=f"{pair_name} J{s[i]}{s[j]}",
779
+ )
780
+ ax.legend(ncol=3, fontsize="small")
781
+ else:
782
+ ds = []
783
+ Jani = []
784
+ for key, val in self.Jani_dict.items():
785
+ d = self.distance_dict[key][1]
786
+ ds.append(d)
787
+ # val = val - np.diag([np.trace(val) / 3] * 3)
788
+ Jani.append(val * 1e3)
789
+ Jani = np.array(Jani)
790
+ s = "xyz"
791
+ for i in range(3):
792
+ ax.scatter(ds, Jani[:, i, i], marker="s", label=f"J{s[i]}{s[i]}")
793
+ c = "rgb"
794
+ for ic, (i, j) in enumerate([(0, 1), (0, 2), (1, 2)]):
795
+ ax.scatter(
796
+ ds,
797
+ Jani[:, i, j],
798
+ edgecolors=c[ic],
799
+ facecolors="none",
800
+ label=f"J{s[i]}{s[j]}",
801
+ )
802
+ ax.legend(loc=1, ncol=2)
635
803
  ax.axhline(color="gray")
636
- ax.legend(loc=1, ncol=2)
637
804
  ax.set_xlabel(r"Distance ($\AA$)")
638
805
  ax.set_ylabel("Jani (meV)")
639
806
  if fname is not None:
@@ -642,7 +809,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
642
809
  plt.show()
643
810
  return ax
644
811
 
645
- def plot_all(self, title=None, savefile=None, show=False):
812
+ def plot_all(self, title=None, savefile=None, show=False, by_species=False):
646
813
  if self.has_dmi and self.has_bilinear:
647
814
  naxis = 3
648
815
  else:
@@ -650,11 +817,11 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
650
817
  fig, axes = plt.subplots(naxis, 1, sharex=True, figsize=(5, 2.2 * naxis))
651
818
 
652
819
  if self.has_dmi and self.has_bilinear:
653
- self.plot_JvsR(axes[0])
654
- self.plot_DvsR(axes[1])
655
- self.plot_JanivsR(axes[2])
820
+ self.plot_JvsR(axes[0], by_species=by_species)
821
+ self.plot_DvsR(axes[1], by_species=by_species)
822
+ self.plot_JanivsR(axes[2], by_species=by_species)
656
823
  else:
657
- self.plot_JvsR(axes)
824
+ self.plot_JvsR(axes, by_species=by_species)
658
825
 
659
826
  if title is not None:
660
827
  fig.suptitle(title)
@@ -684,6 +851,11 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
684
851
 
685
852
  write_uppasd(self, path=path)
686
853
 
854
+ def write_espins(self, path):
855
+ from TB2J.io_exchange.io_espins import write_espins
856
+
857
+ write_espins(self, path=path)
858
+
687
859
 
688
860
  def gen_distance_dict(ind_mag_atoms, atoms, Rlist):
689
861
  distance_dict = {}
@@ -1,7 +1,8 @@
1
- import numpy as np
2
1
  import os
3
2
  from itertools import groupby
4
3
 
4
+ import numpy as np
5
+
5
6
 
6
7
  def write_tom_ucf(cls, fname):
7
8
  """
@@ -25,8 +26,8 @@ def write_tom_ucf(cls, fname):
25
26
  gyro_ratio = cls.gyro_ratio[i]
26
27
  symbol = cls.atoms.get_chemical_symbols()[i]
27
28
  if cls.has_uniaxial_anistropy:
28
- k1 = cls.k1[i]
29
- k1dir = cls.k1dir[i]
29
+ k1 = cls.k1[id_spin]
30
+ k1dir = cls.k1dir[id_spin]
30
31
  else:
31
32
  k1 = 0.0
32
33
  k1dir = [0.0, 0.0, 1.0]