TB2J 0.9.12.18__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.
- 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 +238 -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 +11 -11
- 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.22.dist-info}/METADATA +7 -3
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.22.dist-info}/RECORD +32 -35
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.22.dist-info}/WHEEL +1 -1
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.22.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.22.dist-info}/licenses/LICENSE +0 -0
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.22.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,22 @@ 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, R, Jiso=True, Jani=True, DMI=True, SIA=True,
|
|
458
|
+
):
|
|
404
459
|
"""
|
|
405
460
|
Return the full exchange tensor of all i and j for cell R.
|
|
406
461
|
param R (tuple of integers): cell index R
|
|
407
462
|
returns:
|
|
408
463
|
Jmat: (nspin,nspin,3,3) matrix.
|
|
409
464
|
"""
|
|
410
|
-
if Jani or DMI:
|
|
465
|
+
if Jani or DMI or SIA:
|
|
411
466
|
raise ValueError(
|
|
412
|
-
"Jani and
|
|
467
|
+
"Jani, DMI and SIA are not supported for this method. Use get_full_Jtensor_for_one_R_ij33 instead."
|
|
413
468
|
)
|
|
414
469
|
n = self.nspin
|
|
415
470
|
Jmat = np.zeros((n, n), dtype=float)
|
|
@@ -421,7 +476,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
421
476
|
return Jmat
|
|
422
477
|
|
|
423
478
|
def get_full_Jtensor_for_Rlist(
|
|
424
|
-
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"
|
|
425
480
|
):
|
|
426
481
|
n = self.nspin
|
|
427
482
|
n3 = n * 3
|
|
@@ -430,7 +485,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
430
485
|
Jmat = np.zeros((nR, n3, n3), dtype=float)
|
|
431
486
|
for iR, R in enumerate(self.Rlist):
|
|
432
487
|
Jmat[iR] = self.get_full_Jtensor_for_one_R_i3j3(
|
|
433
|
-
R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
488
|
+
R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
434
489
|
)
|
|
435
490
|
if asr:
|
|
436
491
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
|
@@ -442,8 +497,9 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
442
497
|
elif order == "ij33":
|
|
443
498
|
Jmat = np.zeros((nR, n, n, 3, 3), dtype=float)
|
|
444
499
|
for iR, R in enumerate(self.Rlist):
|
|
500
|
+
print(f"R={R}")
|
|
445
501
|
Jmat[iR] = self.get_full_Jtensor_for_one_R_ij33(
|
|
446
|
-
R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
502
|
+
R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
447
503
|
)
|
|
448
504
|
if asr:
|
|
449
505
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
|
@@ -454,8 +510,9 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
454
510
|
elif order == "i3j3_2D":
|
|
455
511
|
Jmat = np.zeros((nR, n3, n3), dtype=float)
|
|
456
512
|
for iR, R in enumerate(self.Rlist):
|
|
513
|
+
print(f"R={R}")
|
|
457
514
|
Jmat[iR] = self.get_full_Jtensor_for_one_R_i3j3(
|
|
458
|
-
R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
515
|
+
R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
459
516
|
).reshape((n3, n3))
|
|
460
517
|
if asr:
|
|
461
518
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
|
@@ -468,7 +525,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
468
525
|
Jmat = np.zeros((nR, n, n), dtype=float)
|
|
469
526
|
for iR, R in enumerate(self.Rlist):
|
|
470
527
|
Jmat[iR] = self.get_full_Jtensor_for_one_R_ij(
|
|
471
|
-
R, Jiso=Jiso, Jani=Jani, DMI=DMI
|
|
528
|
+
R, Jiso=Jiso, Jani=Jani, DMI=DMI, SIA=SIA
|
|
472
529
|
)
|
|
473
530
|
if asr:
|
|
474
531
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
|
@@ -565,17 +622,41 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
565
622
|
marker="o",
|
|
566
623
|
fname=None,
|
|
567
624
|
show=False,
|
|
625
|
+
by_species=False,
|
|
568
626
|
**kwargs,
|
|
569
627
|
):
|
|
570
628
|
if ax is None:
|
|
571
629
|
fig, ax = plt.subplots()
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
+
|
|
579
660
|
ax.axhline(color="gray")
|
|
580
661
|
ax.set_xlabel(r"Distance ($\AA$)")
|
|
581
662
|
ax.set_ylabel("J (meV)")
|
|
@@ -585,25 +666,68 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
585
666
|
plt.show()
|
|
586
667
|
return ax
|
|
587
668
|
|
|
588
|
-
def plot_DvsR(self, ax=None, fname=None, show=False):
|
|
669
|
+
def plot_DvsR(self, ax=None, fname=None, show=False, by_species=False):
|
|
589
670
|
if ax is None:
|
|
590
671
|
fig, ax = plt.subplots()
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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)
|
|
605
730
|
ax.axhline(color="gray")
|
|
606
|
-
ax.legend(loc=1)
|
|
607
731
|
ax.set_ylabel("D (meV)")
|
|
608
732
|
ax.set_xlabel(r"Distance ($\AA$)")
|
|
609
733
|
if fname is not None:
|
|
@@ -612,31 +736,71 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
612
736
|
plt.show()
|
|
613
737
|
return ax
|
|
614
738
|
|
|
615
|
-
def plot_JanivsR(self, ax=None, fname=None, show=False):
|
|
739
|
+
def plot_JanivsR(self, ax=None, fname=None, show=False, by_species=False):
|
|
616
740
|
if ax is None:
|
|
617
741
|
fig, ax = plt.subplots()
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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)
|
|
638
803
|
ax.axhline(color="gray")
|
|
639
|
-
ax.legend(loc=1, ncol=2)
|
|
640
804
|
ax.set_xlabel(r"Distance ($\AA$)")
|
|
641
805
|
ax.set_ylabel("Jani (meV)")
|
|
642
806
|
if fname is not None:
|
|
@@ -645,7 +809,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
645
809
|
plt.show()
|
|
646
810
|
return ax
|
|
647
811
|
|
|
648
|
-
def plot_all(self, title=None, savefile=None, show=False):
|
|
812
|
+
def plot_all(self, title=None, savefile=None, show=False, by_species=False):
|
|
649
813
|
if self.has_dmi and self.has_bilinear:
|
|
650
814
|
naxis = 3
|
|
651
815
|
else:
|
|
@@ -653,11 +817,11 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
|
653
817
|
fig, axes = plt.subplots(naxis, 1, sharex=True, figsize=(5, 2.2 * naxis))
|
|
654
818
|
|
|
655
819
|
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])
|
|
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)
|
|
659
823
|
else:
|
|
660
|
-
self.plot_JvsR(axes)
|
|
824
|
+
self.plot_JvsR(axes, by_species=by_species)
|
|
661
825
|
|
|
662
826
|
if title is not None:
|
|
663
827
|
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,
|
TB2J/io_exchange/io_vampire.py
CHANGED
|
@@ -50,7 +50,7 @@ def write_vampire_unitcell_file(cls, fname):
|
|
|
50
50
|
counter = -1
|
|
51
51
|
for key in cls.exchange_Jdict:
|
|
52
52
|
R, ispin, jspin = key
|
|
53
|
-
Jtensor = cls.get_J_tensor(ispin, jspin, R)
|
|
53
|
+
Jtensor = cls.get_J_tensor(ispin, jspin, R, Jani=True, DMI=True)
|
|
54
54
|
counter += 1 # starts at 0
|
|
55
55
|
myfile.write(
|
|
56
56
|
f"{counter:5d} {ispin:3d} {jspin:3d} {R[0]:3d} {R[1]:3d} {R[2]:3d} "
|