TB2J 0.9.12.18__py3-none-any.whl → 0.9.12.23__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.
- TB2J/MAE.py +8 -1
- TB2J/MAEGreen.py +0 -2
- TB2J/exchange.py +11 -4
- TB2J/exchangeCL2.py +2 -0
- TB2J/exchange_params.py +24 -0
- TB2J/green.py +15 -3
- TB2J/interfaces/abacus/gen_exchange_abacus.py +2 -1
- TB2J/io_exchange/__init__.py +19 -1
- TB2J/io_exchange/edit.py +594 -0
- TB2J/io_exchange/io_exchange.py +243 -74
- TB2J/io_exchange/io_tomsasd.py +4 -3
- TB2J/io_exchange/io_txt.py +72 -7
- TB2J/io_exchange/io_vampire.py +1 -1
- TB2J/io_merge.py +60 -40
- TB2J/magnon/magnon3.py +27 -2
- TB2J/mathutils/rotate_spin.py +7 -7
- TB2J/mycfr.py +16 -16
- TB2J/plot.py +26 -0
- TB2J/scripts/TB2J_edit.py +403 -0
- TB2J/scripts/TB2J_plot_exchange.py +48 -0
- TB2J/spinham/hamiltonian.py +156 -13
- TB2J/spinham/hamiltonian_terms.py +40 -1
- TB2J/spinham/spin_xml.py +40 -8
- TB2J/symmetrize_J.py +138 -7
- TB2J/tests/test_cli_remove_sublattice.py +33 -0
- TB2J/tests/test_cli_toggle_exchange.py +50 -0
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/METADATA +7 -3
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/RECORD +32 -35
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/WHEEL +1 -1
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/entry_points.txt +2 -0
- TB2J/.gitignore +0 -5
- TB2J/agent_files/debug_spinphon_fd/debug_main.py +0 -156
- TB2J/agent_files/debug_spinphon_fd/test_compute_dJdx.py +0 -272
- TB2J/agent_files/debug_spinphon_fd/test_ispin0_only.py +0 -120
- TB2J/agent_files/debug_spinphon_fd/test_no_d2j.py +0 -31
- TB2J/agent_files/debug_spinphon_fd/test_with_d2j.py +0 -28
- TB2J/interfaces/abacus/test_read_HRSR.py +0 -43
- TB2J/interfaces/abacus/test_read_stru.py +0 -32
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/licenses/LICENSE +0 -0
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/top_level.txt +0 -0
TB2J/io_exchange/io_exchange.py
CHANGED
|
@@ -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,14 +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
|
)
|
|
152
166
|
|
|
153
167
|
self.biquadratic_Jdict = biquadratic_Jdict
|
|
154
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
|
+
|
|
155
176
|
if NJT_ddict is not None:
|
|
156
177
|
self.has_NJT_dmi = True
|
|
157
178
|
self.NJT_ddict = NJT_ddict
|
|
@@ -189,6 +210,18 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
189
210
|
self.orbital_names = orbital_names
|
|
190
211
|
self.TB2J_version = __version__
|
|
191
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
|
+
|
|
192
225
|
def _build_Rlist(self):
|
|
193
226
|
Rset = set()
|
|
194
227
|
ispin_set = set()
|
|
@@ -228,12 +261,14 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
228
261
|
return self.index_spin[symdict[symnum]]
|
|
229
262
|
|
|
230
263
|
def i_spin(self, i):
|
|
231
|
-
if isinstance(i, int):
|
|
232
|
-
return i
|
|
264
|
+
if isinstance(i, (int, np.integer)):
|
|
265
|
+
return int(i)
|
|
233
266
|
elif isinstance(i, str):
|
|
234
267
|
return self.get_symbol_number_ispin(i)
|
|
235
268
|
else:
|
|
236
|
-
raise ValueError(
|
|
269
|
+
raise ValueError(
|
|
270
|
+
f"i must be either an integer or a string. Got {type(i)}: {i}"
|
|
271
|
+
)
|
|
237
272
|
|
|
238
273
|
def get_charge_ispin(self, i):
|
|
239
274
|
i = self.i_spin(i)
|
|
@@ -332,7 +367,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
332
367
|
else:
|
|
333
368
|
return default
|
|
334
369
|
|
|
335
|
-
def get_J_tensor(self, i, j, R, Jiso=True, Jani=
|
|
370
|
+
def get_J_tensor(self, i, j, R, Jiso=True, Jani=True, DMI=True, SIA=True):
|
|
336
371
|
"""
|
|
337
372
|
Return the full exchange tensor for atom i and j, and cell R.
|
|
338
373
|
param i : spin index i
|
|
@@ -353,6 +388,20 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
353
388
|
if Ja is not None:
|
|
354
389
|
Ja *= 1
|
|
355
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]
|
|
356
405
|
|
|
357
406
|
# if iso_only:
|
|
358
407
|
# J = self.get_Jiso(i, j, R)
|
|
@@ -368,7 +417,9 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
368
417
|
# )
|
|
369
418
|
return Jtensor
|
|
370
419
|
|
|
371
|
-
def get_full_Jtensor_for_one_R_i3j3(
|
|
420
|
+
def get_full_Jtensor_for_one_R_i3j3(
|
|
421
|
+
self, R, Jiso=True, Jani=True, DMI=True, SIA=True
|
|
422
|
+
):
|
|
372
423
|
"""
|
|
373
424
|
Return the full exchange tensor of all i and j for cell R.
|
|
374
425
|
param R (tuple of integers): cell index R
|
|
@@ -380,11 +431,13 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
380
431
|
for i in range(self.nspin):
|
|
381
432
|
for j in range(self.nspin):
|
|
382
433
|
Jmat[i * 3 : i * 3 + 3, j * 3 : j * 3 + 3] = self.get_J_tensor(
|
|
383
|
-
i, j, R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
434
|
+
i, j, R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
384
435
|
)
|
|
385
436
|
return Jmat
|
|
386
437
|
|
|
387
|
-
def get_full_Jtensor_for_one_R_ij33(
|
|
438
|
+
def get_full_Jtensor_for_one_R_ij33(
|
|
439
|
+
self, R, Jiso=True, Jani=True, DMI=True, SIA=True
|
|
440
|
+
):
|
|
388
441
|
"""
|
|
389
442
|
Return the full exchange tensor of all i and j for cell R.
|
|
390
443
|
param R (tuple of integers): cell index R
|
|
@@ -396,20 +449,27 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
396
449
|
for i in range(self.nspin):
|
|
397
450
|
for j in range(self.nspin):
|
|
398
451
|
Jmat[i, j, :, :] = self.get_J_tensor(
|
|
399
|
-
i, j, R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
452
|
+
i, j, R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
400
453
|
)
|
|
401
454
|
return Jmat
|
|
402
455
|
|
|
403
|
-
def get_full_Jtensor_for_one_R_ij(
|
|
456
|
+
def get_full_Jtensor_for_one_R_ij(
|
|
457
|
+
self,
|
|
458
|
+
R,
|
|
459
|
+
Jiso=True,
|
|
460
|
+
Jani=True,
|
|
461
|
+
DMI=True,
|
|
462
|
+
SIA=True,
|
|
463
|
+
):
|
|
404
464
|
"""
|
|
405
465
|
Return the full exchange tensor of all i and j for cell R.
|
|
406
466
|
param R (tuple of integers): cell index R
|
|
407
467
|
returns:
|
|
408
468
|
Jmat: (nspin,nspin,3,3) matrix.
|
|
409
469
|
"""
|
|
410
|
-
if Jani or DMI:
|
|
470
|
+
if Jani or DMI or SIA:
|
|
411
471
|
raise ValueError(
|
|
412
|
-
"Jani and
|
|
472
|
+
"Jani, DMI and SIA are not supported for this method. Use get_full_Jtensor_for_one_R_ij33 instead."
|
|
413
473
|
)
|
|
414
474
|
n = self.nspin
|
|
415
475
|
Jmat = np.zeros((n, n), dtype=float)
|
|
@@ -421,7 +481,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
421
481
|
return Jmat
|
|
422
482
|
|
|
423
483
|
def get_full_Jtensor_for_Rlist(
|
|
424
|
-
self, asr=False, Jiso=True, Jani=True, DMI=True, order="i3j3"
|
|
484
|
+
self, asr=False, Jiso=True, Jani=True, DMI=True, SIA=True, order="i3j3"
|
|
425
485
|
):
|
|
426
486
|
n = self.nspin
|
|
427
487
|
n3 = n * 3
|
|
@@ -430,7 +490,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
430
490
|
Jmat = np.zeros((nR, n3, n3), dtype=float)
|
|
431
491
|
for iR, R in enumerate(self.Rlist):
|
|
432
492
|
Jmat[iR] = self.get_full_Jtensor_for_one_R_i3j3(
|
|
433
|
-
R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
493
|
+
R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
434
494
|
)
|
|
435
495
|
if asr:
|
|
436
496
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
|
@@ -442,8 +502,9 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
442
502
|
elif order == "ij33":
|
|
443
503
|
Jmat = np.zeros((nR, n, n, 3, 3), dtype=float)
|
|
444
504
|
for iR, R in enumerate(self.Rlist):
|
|
505
|
+
print(f"R={R}")
|
|
445
506
|
Jmat[iR] = self.get_full_Jtensor_for_one_R_ij33(
|
|
446
|
-
R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
507
|
+
R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
447
508
|
)
|
|
448
509
|
if asr:
|
|
449
510
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
|
@@ -454,8 +515,9 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
454
515
|
elif order == "i3j3_2D":
|
|
455
516
|
Jmat = np.zeros((nR, n3, n3), dtype=float)
|
|
456
517
|
for iR, R in enumerate(self.Rlist):
|
|
518
|
+
print(f"R={R}")
|
|
457
519
|
Jmat[iR] = self.get_full_Jtensor_for_one_R_i3j3(
|
|
458
|
-
R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
520
|
+
R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
459
521
|
).reshape((n3, n3))
|
|
460
522
|
if asr:
|
|
461
523
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
|
@@ -468,7 +530,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
468
530
|
Jmat = np.zeros((nR, n, n), dtype=float)
|
|
469
531
|
for iR, R in enumerate(self.Rlist):
|
|
470
532
|
Jmat[iR] = self.get_full_Jtensor_for_one_R_ij(
|
|
471
|
-
R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
533
|
+
R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
472
534
|
)
|
|
473
535
|
if asr:
|
|
474
536
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
|
@@ -565,17 +627,41 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
565
627
|
marker="o",
|
|
566
628
|
fname=None,
|
|
567
629
|
show=False,
|
|
630
|
+
by_species=False,
|
|
568
631
|
**kwargs,
|
|
569
632
|
):
|
|
570
633
|
if ax is None:
|
|
571
634
|
fig, ax = plt.subplots()
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
635
|
+
if by_species:
|
|
636
|
+
groups = {}
|
|
637
|
+
for key, val in self.exchange_Jdict.items():
|
|
638
|
+
R, i, j = key
|
|
639
|
+
idx_i = self.ind_atoms[i]
|
|
640
|
+
idx_j = self.ind_atoms[j]
|
|
641
|
+
spec_i = self.atoms[idx_i].symbol
|
|
642
|
+
spec_j = self.atoms[idx_j].symbol
|
|
643
|
+
pair = tuple(sorted([spec_i, spec_j]))
|
|
644
|
+
pair_name = "-".join(pair)
|
|
645
|
+
if pair_name not in groups:
|
|
646
|
+
groups[pair_name] = {"ds": [], "Js": []}
|
|
647
|
+
d = self.distance_dict[key][1]
|
|
648
|
+
groups[pair_name]["ds"].append(d)
|
|
649
|
+
groups[pair_name]["Js"].append(val * 1e3)
|
|
650
|
+
|
|
651
|
+
for pair_name, data in groups.items():
|
|
652
|
+
ax.scatter(
|
|
653
|
+
data["ds"], data["Js"], marker=marker, label=pair_name, **kwargs
|
|
654
|
+
)
|
|
655
|
+
ax.legend()
|
|
656
|
+
else:
|
|
657
|
+
ds = []
|
|
658
|
+
Js = []
|
|
659
|
+
for key, val in self.exchange_Jdict.items():
|
|
660
|
+
d = self.distance_dict[key][1]
|
|
661
|
+
ds.append(d)
|
|
662
|
+
Js.append(val * 1e3)
|
|
663
|
+
ax.scatter(ds, Js, marker=marker, color=color, **kwargs)
|
|
664
|
+
|
|
579
665
|
ax.axhline(color="gray")
|
|
580
666
|
ax.set_xlabel(r"Distance ($\AA$)")
|
|
581
667
|
ax.set_ylabel("J (meV)")
|
|
@@ -585,25 +671,68 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
585
671
|
plt.show()
|
|
586
672
|
return ax
|
|
587
673
|
|
|
588
|
-
def plot_DvsR(self, ax=None, fname=None, show=False):
|
|
674
|
+
def plot_DvsR(self, ax=None, fname=None, show=False, by_species=False):
|
|
589
675
|
if ax is None:
|
|
590
676
|
fig, ax = plt.subplots()
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
677
|
+
if by_species:
|
|
678
|
+
groups = {}
|
|
679
|
+
for key, val in self.dmi_ddict.items():
|
|
680
|
+
R, i, j = key
|
|
681
|
+
idx_i = self.ind_atoms[i]
|
|
682
|
+
idx_j = self.ind_atoms[j]
|
|
683
|
+
spec_i = self.atoms[idx_i].symbol
|
|
684
|
+
spec_j = self.atoms[idx_j].symbol
|
|
685
|
+
pair = tuple(sorted([spec_i, spec_j]))
|
|
686
|
+
pair_name = "-".join(pair)
|
|
687
|
+
if pair_name not in groups:
|
|
688
|
+
groups[pair_name] = {"ds": [], "Ds": []}
|
|
689
|
+
d = self.distance_dict[key][1]
|
|
690
|
+
groups[pair_name]["ds"].append(d)
|
|
691
|
+
groups[pair_name]["Ds"].append(val * 1e3)
|
|
692
|
+
|
|
693
|
+
markers = ["s", "o", "D", "v", "^", "<", ">", "p", "*", "h", "H"]
|
|
694
|
+
for idx, (pair_name, data) in enumerate(groups.items()):
|
|
695
|
+
Ds = np.array(data["Ds"])
|
|
696
|
+
marker = markers[idx % len(markers)]
|
|
697
|
+
ax.scatter(
|
|
698
|
+
data["ds"],
|
|
699
|
+
Ds[:, 0],
|
|
700
|
+
marker=marker,
|
|
701
|
+
color="r",
|
|
702
|
+
label=f"{pair_name} Dx",
|
|
703
|
+
)
|
|
704
|
+
ax.scatter(
|
|
705
|
+
data["ds"],
|
|
706
|
+
Ds[:, 1],
|
|
707
|
+
marker=marker,
|
|
708
|
+
color="g",
|
|
709
|
+
label=f"{pair_name} Dy",
|
|
710
|
+
)
|
|
711
|
+
ax.scatter(
|
|
712
|
+
data["ds"],
|
|
713
|
+
Ds[:, 2],
|
|
714
|
+
marker=marker,
|
|
715
|
+
color="b",
|
|
716
|
+
label=f"{pair_name} Dz",
|
|
717
|
+
)
|
|
718
|
+
ax.legend(ncol=3, fontsize="small")
|
|
719
|
+
else:
|
|
720
|
+
ds = []
|
|
721
|
+
Ds = []
|
|
722
|
+
for key, val in self.dmi_ddict.items():
|
|
723
|
+
d = self.distance_dict[key][1]
|
|
724
|
+
ds.append(d)
|
|
725
|
+
Ds.append(val * 1e3)
|
|
726
|
+
Ds = np.array(Ds)
|
|
727
|
+
ax.scatter(ds, Ds[:, 0], marker="s", color="r", label="Dx")
|
|
728
|
+
ax.scatter(
|
|
729
|
+
ds, Ds[:, 1], marker="o", edgecolors="g", facecolors="none", label="Dy"
|
|
730
|
+
)
|
|
731
|
+
ax.scatter(
|
|
732
|
+
ds, Ds[:, 2], marker="D", edgecolors="b", facecolors="none", label="Dz"
|
|
733
|
+
)
|
|
734
|
+
ax.legend(loc=1)
|
|
605
735
|
ax.axhline(color="gray")
|
|
606
|
-
ax.legend(loc=1)
|
|
607
736
|
ax.set_ylabel("D (meV)")
|
|
608
737
|
ax.set_xlabel(r"Distance ($\AA$)")
|
|
609
738
|
if fname is not None:
|
|
@@ -612,31 +741,71 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
612
741
|
plt.show()
|
|
613
742
|
return ax
|
|
614
743
|
|
|
615
|
-
def plot_JanivsR(self, ax=None, fname=None, show=False):
|
|
744
|
+
def plot_JanivsR(self, ax=None, fname=None, show=False, by_species=False):
|
|
616
745
|
if ax is None:
|
|
617
746
|
fig, ax = plt.subplots()
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
747
|
+
if by_species:
|
|
748
|
+
groups = {}
|
|
749
|
+
for key, val in self.Jani_dict.items():
|
|
750
|
+
R, i, j = key
|
|
751
|
+
idx_i = self.ind_atoms[i]
|
|
752
|
+
idx_j = self.ind_atoms[j]
|
|
753
|
+
spec_i = self.atoms[idx_i].symbol
|
|
754
|
+
spec_j = self.atoms[idx_j].symbol
|
|
755
|
+
pair = tuple(sorted([spec_i, spec_j]))
|
|
756
|
+
pair_name = "-".join(pair)
|
|
757
|
+
if pair_name not in groups:
|
|
758
|
+
groups[pair_name] = {"ds": [], "Jani": []}
|
|
759
|
+
d = self.distance_dict[key][1]
|
|
760
|
+
groups[pair_name]["ds"].append(d)
|
|
761
|
+
groups[pair_name]["Jani"].append(val * 1e3)
|
|
762
|
+
|
|
763
|
+
markers = ["s", "o", "D", "v", "^", "<", ">", "p", "*", "h", "H"]
|
|
764
|
+
s = "xyz"
|
|
765
|
+
for idx, (pair_name, data) in enumerate(groups.items()):
|
|
766
|
+
Jani = np.array(data["Jani"])
|
|
767
|
+
marker = markers[idx % len(markers)]
|
|
768
|
+
# Diagonal
|
|
769
|
+
for i in range(3):
|
|
770
|
+
ax.scatter(
|
|
771
|
+
data["ds"],
|
|
772
|
+
Jani[:, i, i],
|
|
773
|
+
marker=marker,
|
|
774
|
+
label=f"{pair_name} J{s[i]}{s[i]}",
|
|
775
|
+
)
|
|
776
|
+
# Off-diagonal
|
|
777
|
+
for i, j in [(0, 1), (0, 2), (1, 2)]:
|
|
778
|
+
ax.scatter(
|
|
779
|
+
data["ds"],
|
|
780
|
+
Jani[:, i, j],
|
|
781
|
+
marker=marker,
|
|
782
|
+
facecolors="none",
|
|
783
|
+
label=f"{pair_name} J{s[i]}{s[j]}",
|
|
784
|
+
)
|
|
785
|
+
ax.legend(ncol=3, fontsize="small")
|
|
786
|
+
else:
|
|
787
|
+
ds = []
|
|
788
|
+
Jani = []
|
|
789
|
+
for key, val in self.Jani_dict.items():
|
|
790
|
+
d = self.distance_dict[key][1]
|
|
791
|
+
ds.append(d)
|
|
792
|
+
# val = val - np.diag([np.trace(val) / 3] * 3)
|
|
793
|
+
Jani.append(val * 1e3)
|
|
794
|
+
Jani = np.array(Jani)
|
|
795
|
+
s = "xyz"
|
|
796
|
+
for i in range(3):
|
|
797
|
+
ax.scatter(ds, Jani[:, i, i], marker="s", label=f"J{s[i]}{s[i]}")
|
|
798
|
+
c = "rgb"
|
|
799
|
+
for ic, (i, j) in enumerate([(0, 1), (0, 2), (1, 2)]):
|
|
800
|
+
ax.scatter(
|
|
801
|
+
ds,
|
|
802
|
+
Jani[:, i, j],
|
|
803
|
+
edgecolors=c[ic],
|
|
804
|
+
facecolors="none",
|
|
805
|
+
label=f"J{s[i]}{s[j]}",
|
|
806
|
+
)
|
|
807
|
+
ax.legend(loc=1, ncol=2)
|
|
638
808
|
ax.axhline(color="gray")
|
|
639
|
-
ax.legend(loc=1, ncol=2)
|
|
640
809
|
ax.set_xlabel(r"Distance ($\AA$)")
|
|
641
810
|
ax.set_ylabel("Jani (meV)")
|
|
642
811
|
if fname is not None:
|
|
@@ -645,7 +814,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
645
814
|
plt.show()
|
|
646
815
|
return ax
|
|
647
816
|
|
|
648
|
-
def plot_all(self, title=None, savefile=None, show=False):
|
|
817
|
+
def plot_all(self, title=None, savefile=None, show=False, by_species=False):
|
|
649
818
|
if self.has_dmi and self.has_bilinear:
|
|
650
819
|
naxis = 3
|
|
651
820
|
else:
|
|
@@ -653,11 +822,11 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
653
822
|
fig, axes = plt.subplots(naxis, 1, sharex=True, figsize=(5, 2.2 * naxis))
|
|
654
823
|
|
|
655
824
|
if self.has_dmi and self.has_bilinear:
|
|
656
|
-
self.plot_JvsR(axes[0])
|
|
657
|
-
self.plot_DvsR(axes[1])
|
|
658
|
-
self.plot_JanivsR(axes[2])
|
|
825
|
+
self.plot_JvsR(axes[0], by_species=by_species)
|
|
826
|
+
self.plot_DvsR(axes[1], by_species=by_species)
|
|
827
|
+
self.plot_JanivsR(axes[2], by_species=by_species)
|
|
659
828
|
else:
|
|
660
|
-
self.plot_JvsR(axes)
|
|
829
|
+
self.plot_JvsR(axes, by_species=by_species)
|
|
661
830
|
|
|
662
831
|
if title is not None:
|
|
663
832
|
fig.suptitle(title)
|
TB2J/io_exchange/io_tomsasd.py
CHANGED
|
@@ -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[
|
|
29
|
-
k1dir = cls.k1dir[
|
|
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]
|
TB2J/io_exchange/io_txt.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import sys
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
4
5
|
from numpy import array_str
|
|
@@ -9,6 +10,7 @@ from TB2J.utils import symbol_number
|
|
|
9
10
|
def write_info_section(cls, myfile):
|
|
10
11
|
myfile.write("=" * 90 + "\n")
|
|
11
12
|
myfile.write("Information: \n")
|
|
13
|
+
myfile.write(f"Executed: \n{' '.join(sys.argv)}\n")
|
|
12
14
|
# myfile.write("Exchange parameters generated by TB2J %s\n" % (__version__))
|
|
13
15
|
# myfile.write("Generation time: %s\n" % (now.strftime("%y/%m/%d %H:%M:%S")))
|
|
14
16
|
myfile.write(cls.description)
|
|
@@ -82,6 +84,24 @@ def write_atom_section(cls, myfile):
|
|
|
82
84
|
)
|
|
83
85
|
)
|
|
84
86
|
|
|
87
|
+
# write single ion anisotropy
|
|
88
|
+
if cls.k1 is not None and cls.k1dir is not None:
|
|
89
|
+
myfile.write("\n")
|
|
90
|
+
myfile.write("-" * 90 + "\n")
|
|
91
|
+
myfile.write("Single Ion Anisotropy (meV): \n")
|
|
92
|
+
myfile.write("(k1 is the anisotropy constant, k1dir is the direction vector)\n")
|
|
93
|
+
myfile.write("{:^12s} {:^12s} {:^24s}\n".format("Atom number", "k1", "k1dir"))
|
|
94
|
+
for i, s in enumerate(symnum):
|
|
95
|
+
ispin = cls.index_spin[i]
|
|
96
|
+
if ispin >= 0:
|
|
97
|
+
k1 = cls.k1[ispin] * 1e3
|
|
98
|
+
k1dir = cls.k1dir[ispin]
|
|
99
|
+
myfile.write(
|
|
100
|
+
"{:<12s} {:12.4f} ({:7.4f}, {:7.4f}, {:7.4f})\n".format(
|
|
101
|
+
s, k1, k1dir[0], k1dir[1], k1dir[2]
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
|
|
85
105
|
myfile.write("\n")
|
|
86
106
|
|
|
87
107
|
|
|
@@ -159,17 +179,17 @@ def write_exchange_section(
|
|
|
159
179
|
|
|
160
180
|
if cls.has_biquadratic and write_experimental:
|
|
161
181
|
Jprime, B = cls.biquadratic_Jdict[ll]
|
|
162
|
-
myfile.write(f"[Testing!] Jprime: {Jprime*1e3:.3f}, B: {B*1e3:.3f}\n")
|
|
182
|
+
myfile.write(f"[Testing!] Jprime: {Jprime * 1e3:.3f}, B: {B * 1e3:.3f}\n")
|
|
163
183
|
|
|
164
184
|
if cls.dJdx is not None:
|
|
165
185
|
dJdx = cls.dJdx[ll]
|
|
166
|
-
myfile.write(f"dJ/dx: {dJdx*1e3:.3f}\n")
|
|
186
|
+
myfile.write(f"dJ/dx: {dJdx * 1e3:.3f}\n")
|
|
167
187
|
|
|
168
188
|
if cls.dJdx2 is not None:
|
|
169
189
|
dJdx2 = cls.dJdx2[ll]
|
|
170
|
-
myfile.write(f"d2J/dx2: {dJdx2*1e3:.3f}\n")
|
|
190
|
+
myfile.write(f"d2J/dx2: {dJdx2 * 1e3:.3f}\n")
|
|
171
191
|
|
|
172
|
-
if cls.dmi_ddict is not None:
|
|
192
|
+
if cls.dmi_ddict is not None and ll in cls.dmi_ddict:
|
|
173
193
|
DMI = cls.dmi_ddict[ll] * 1e3
|
|
174
194
|
myfile.write(
|
|
175
195
|
"[Testing!] DMI: ({:7.4f} {:7.4f} {:7.4f})\n".format(
|
|
@@ -188,13 +208,13 @@ def write_exchange_section(
|
|
|
188
208
|
except Exception as e:
|
|
189
209
|
myfile.write(f"[Debug!] DMI2 not available: {e}\n")
|
|
190
210
|
|
|
191
|
-
if cls.Jani_dict is not None:
|
|
211
|
+
if cls.Jani_dict is not None and ll in cls.Jani_dict:
|
|
192
212
|
J = cls.Jani_dict[ll] * 1e3
|
|
193
213
|
myfile.write(
|
|
194
214
|
f"[Testing!]J_ani:\n{array_str(J, precision=3, suppress_small=True)}\n"
|
|
195
215
|
)
|
|
196
216
|
|
|
197
|
-
if cls.NJT_ddict is not None:
|
|
217
|
+
if cls.NJT_ddict is not None and ll in cls.NJT_ddict:
|
|
198
218
|
DMI = cls.NJT_ddict[ll] * 1e3
|
|
199
219
|
myfile.write(
|
|
200
220
|
"[Experimental!] DMI_NJt: ({:7.4f} {:7.4f} {:7.4f})\n".format(
|
|
@@ -202,7 +222,7 @@ def write_exchange_section(
|
|
|
202
222
|
)
|
|
203
223
|
)
|
|
204
224
|
|
|
205
|
-
if cls.NJT_Jdict is not None:
|
|
225
|
+
if cls.NJT_Jdict is not None and ll in cls.NJT_Jdict:
|
|
206
226
|
J = cls.NJT_Jdict[ll] * 1e3
|
|
207
227
|
myfile.write(
|
|
208
228
|
"[Testing!] Jani_NJt: ({:7.4f} {:7.4f} {:7.4f})\n".format(
|
|
@@ -305,6 +325,50 @@ def write_Jq_info(cls, kpts, evals, evecs, myfile, special_kpoints={}):
|
|
|
305
325
|
print(f"{sns[cls.ind_atoms[i]]}, {v[0]: 8.3f}, {v[2]: 8.3f}, {v[2]: 8.3f}\n")
|
|
306
326
|
|
|
307
327
|
|
|
328
|
+
def write_sia_section(cls, myfile):
|
|
329
|
+
"""
|
|
330
|
+
Write single ion anisotropy tensor section.
|
|
331
|
+
"""
|
|
332
|
+
if cls.has_sia_tensor and cls.sia_tensor is not None:
|
|
333
|
+
myfile.write("\n")
|
|
334
|
+
myfile.write("=" * 90 + "\n")
|
|
335
|
+
myfile.write("Single Ion Anisotropy: \n")
|
|
336
|
+
symnum = symbol_number(cls.atoms)
|
|
337
|
+
sns = list(symnum.keys())
|
|
338
|
+
myfile.write(
|
|
339
|
+
"{:^12s} {:^13s} {:^13s} {:^13s}\n".format(
|
|
340
|
+
"Atom_number", "A_xx", "A_xy", "A_xz"
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
myfile.write(
|
|
344
|
+
"{:^12s} {:^13s} {:^13s} {:^13s}\n".format("", "A_yx", "A_yy", "A_yz")
|
|
345
|
+
)
|
|
346
|
+
myfile.write(
|
|
347
|
+
"{:^12s} {:^13s} {:^13s} {:^13s}\n".format("", "A_zx", "A_zy", "A_zz")
|
|
348
|
+
)
|
|
349
|
+
for i, tensor in cls.sia_tensor.items():
|
|
350
|
+
iatom = cls.ind_atoms[i]
|
|
351
|
+
myfile.write(
|
|
352
|
+
"{:<12s} {:13.8f} {:13.8f} {:13.8f}\n".format(
|
|
353
|
+
sns[iatom],
|
|
354
|
+
tensor[0, 0] * 1e3,
|
|
355
|
+
tensor[0, 1] * 1e3,
|
|
356
|
+
tensor[0, 2] * 1e3,
|
|
357
|
+
)
|
|
358
|
+
)
|
|
359
|
+
myfile.write(
|
|
360
|
+
"{:<12s} {:13.8f} {:13.8f} {:13.8f}\n".format(
|
|
361
|
+
"", tensor[1, 0] * 1e3, tensor[1, 1] * 1e3, tensor[1, 2] * 1e3
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
myfile.write(
|
|
365
|
+
"{:<12s} {:13.8f} {:13.8f} {:13.8f}\n".format(
|
|
366
|
+
"", tensor[2, 0] * 1e3, tensor[2, 1] * 1e3, tensor[2, 2] * 1e3
|
|
367
|
+
)
|
|
368
|
+
)
|
|
369
|
+
myfile.write("-" * 88 + "\n")
|
|
370
|
+
|
|
371
|
+
|
|
308
372
|
def write_txt(
|
|
309
373
|
cls,
|
|
310
374
|
path="TB2J_results",
|
|
@@ -321,6 +385,7 @@ def write_txt(
|
|
|
321
385
|
write_info_section(cls, myfile)
|
|
322
386
|
write_atom_section(cls, myfile)
|
|
323
387
|
write_orbital_section(cls, myfile)
|
|
388
|
+
write_sia_section(cls, myfile)
|
|
324
389
|
write_exchange_section(
|
|
325
390
|
cls,
|
|
326
391
|
myfile,
|