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
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Debug script for spin-phonon coupling using double-sided finite difference method.
|
|
3
|
-
|
|
4
|
-
PURPOSE:
|
|
5
|
-
This script uses the double-sided finite difference method from Oiju_FD2.py to
|
|
6
|
-
calculate spin-phonon coupling parameters and their derivatives dJ/dx. It computes
|
|
7
|
-
exchange parameters at both positive and negative displacements, then calculates
|
|
8
|
-
dJ/dx = (J(+dx) - J(-dx)) / (2*dx).
|
|
9
|
-
|
|
10
|
-
USAGE:
|
|
11
|
-
uv run python agent_files/debug_spinphon_fd/debug_main.py
|
|
12
|
-
|
|
13
|
-
EXPECTED OUTPUT:
|
|
14
|
-
The script will create output directories with subdirectories:
|
|
15
|
-
- original/: Exchange parameters at zero displacement (only if compute_d2J=True)
|
|
16
|
-
- negative/: Exchange parameters at -amplitude displacement
|
|
17
|
-
- positive/: Exchange parameters at +amplitude displacement
|
|
18
|
-
- dJdx/: Computed derivatives dJ/dx in both text and pickle formats
|
|
19
|
-
|
|
20
|
-
PERFORMANCE OPTIONS:
|
|
21
|
-
- compute_d2J: Set False to skip J(0) calculation and d2J/dx2 (~33% faster)
|
|
22
|
-
- ispin0_only: Set True to only compute pairs with ispin=0 or jspin=0 (~93% faster)
|
|
23
|
-
- Combined: ~95% reduction in computation time!
|
|
24
|
-
|
|
25
|
-
FILES USED:
|
|
26
|
-
- Oiju_FD2.py: Source of double-sided finite difference implementation
|
|
27
|
-
- Oiju_epw2.py: Reference for data path structure and interface
|
|
28
|
-
|
|
29
|
-
DEBUG NOTES:
|
|
30
|
-
- Uses double-sided finite difference for better numerical accuracy
|
|
31
|
-
- Computes full exchange tensor derivatives including DMI components
|
|
32
|
-
- Results include isotropic, anisotropic, and DMI contributions to dJ/dx
|
|
33
|
-
- Default settings use both optimizations for maximum speed
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# Add the TB2J directory to the Python path
|
|
38
|
-
# sys.path.insert(0, '/home_phythema/hexu/projects/TB2J/TB2J')
|
|
39
|
-
|
|
40
|
-
import numpy as np
|
|
41
|
-
|
|
42
|
-
from TB2J.Oiju_FD2 import gen_exchange_Oiju_FD_double_sided
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def main():
|
|
46
|
-
"""Main function to run spin-phonon coupling calculation using double-sided finite difference method."""
|
|
47
|
-
|
|
48
|
-
# Use the same data path structure as Oiju_epw2.py
|
|
49
|
-
path = "/home_phythema/hexu/spinphon/2025-10-02_newdata/k555q555"
|
|
50
|
-
nsc=2
|
|
51
|
-
nkpt = 5
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# Configuration parameters matching Oiju_epw2.py example
|
|
55
|
-
config = {
|
|
56
|
-
"path": path,
|
|
57
|
-
"colinear": True,
|
|
58
|
-
"posfile": "scf.pwi",
|
|
59
|
-
"prefix_up": "up/SrMnO3",
|
|
60
|
-
"prefix_dn": "down/SrMnO3.down",
|
|
61
|
-
"prefix_SOC": "wannier90",
|
|
62
|
-
"epw_up_path": f"{path}/up",
|
|
63
|
-
"epw_down_path": f"{path}/down",
|
|
64
|
-
"epw_prefix_up": "epmat",
|
|
65
|
-
"epw_prefix_dn": "epmat",
|
|
66
|
-
"Ru": (0, 0, 0),
|
|
67
|
-
"Rcut": 5,
|
|
68
|
-
"efermi": 11.26,
|
|
69
|
-
"magnetic_elements": ["Mn"],
|
|
70
|
-
"kmesh": [3, 3, 3],
|
|
71
|
-
"emin": -7.3363330034071295,
|
|
72
|
-
"emax": 0.0,
|
|
73
|
-
"nz": 70,
|
|
74
|
-
"np": 1,
|
|
75
|
-
"exclude_orbs": [],
|
|
76
|
-
"description": "Double-sided finite difference calculation for dJ/dx",
|
|
77
|
-
"list_iatom": None,
|
|
78
|
-
"output_path": f"FD_spinphon_results_sc{nsc}_k{nkpt}",
|
|
79
|
-
# Additional parameters for finite difference method
|
|
80
|
-
"supercell_matrix": np.eye(3, dtype=int)*nsc,
|
|
81
|
-
"amplitude": 0.003,
|
|
82
|
-
"max_distance": None,
|
|
83
|
-
# Performance optimization options
|
|
84
|
-
"compute_d2J": False, # Set True to compute second derivative d2J/dx2
|
|
85
|
-
"ispin0_only": True, # Set True to only compute pairs with ispin=0 or jspin=0
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# Run calculation for a single displacement pattern (idisp=0)
|
|
89
|
-
# You can modify this to loop over multiple displacement patterns
|
|
90
|
-
displacement_patterns = [5, 6, 8, 0] # Change to range(15) for all patterns
|
|
91
|
-
|
|
92
|
-
print("=" * 80)
|
|
93
|
-
print("Starting double-sided finite difference spin-phonon coupling calculation")
|
|
94
|
-
print("=" * 80)
|
|
95
|
-
print(f"Data path: {path}")
|
|
96
|
-
print(f"Output path: {config['output_path']}")
|
|
97
|
-
print(f"Displacement patterns: {displacement_patterns}")
|
|
98
|
-
print(f"Amplitude: {config['amplitude']}")
|
|
99
|
-
print(
|
|
100
|
-
f"Method: dJ/dx = (J(+{config['amplitude']}) - J(-{config['amplitude']})) / (2*{config['amplitude']})"
|
|
101
|
-
)
|
|
102
|
-
print("\nPerformance optimizations:")
|
|
103
|
-
print(f" - compute_d2J: {config['compute_d2J']} {'(computes d2J/dx2)' if config['compute_d2J'] else '(skips J(0) calculation, ~33% faster)'}")
|
|
104
|
-
print(f" - ispin0_only: {config['ispin0_only']} {'(only pairs with ispin=0 or jspin=0, ~93% faster)' if config['ispin0_only'] else '(all pairs)'}")
|
|
105
|
-
if not config['compute_d2J'] and config['ispin0_only']:
|
|
106
|
-
print(f" - Combined speedup: ~95% reduction in computation time!")
|
|
107
|
-
print("=" * 80)
|
|
108
|
-
|
|
109
|
-
for idisp in displacement_patterns:
|
|
110
|
-
print(f"\n{'='*80}")
|
|
111
|
-
print(f"Processing displacement pattern {idisp}")
|
|
112
|
-
print(f"{'='*80}")
|
|
113
|
-
|
|
114
|
-
try:
|
|
115
|
-
gen_exchange_Oiju_FD_double_sided(idisp=idisp, **config)
|
|
116
|
-
print(f"\n{'='*80}")
|
|
117
|
-
print(f"Successfully completed displacement pattern {idisp}")
|
|
118
|
-
print("Results saved in:")
|
|
119
|
-
if config['compute_d2J']:
|
|
120
|
-
print(f" - {config['output_path']}/idisp{idisp}_Ru{config['Ru'][0]}_{config['Ru'][1]}_{config['Ru'][2]}/original/")
|
|
121
|
-
print(f" - {config['output_path']}/idisp{idisp}_Ru{config['Ru'][0]}_{config['Ru'][1]}_{config['Ru'][2]}/negative/")
|
|
122
|
-
print(f" - {config['output_path']}/idisp{idisp}_Ru{config['Ru'][0]}_{config['Ru'][1]}_{config['Ru'][2]}/positive/")
|
|
123
|
-
print(f" - {config['output_path']}/idisp{idisp}_Ru{config['Ru'][0]}_{config['Ru'][1]}_{config['Ru'][2]}/dJdx/")
|
|
124
|
-
print(f"{'='*80}")
|
|
125
|
-
|
|
126
|
-
except Exception as e:
|
|
127
|
-
print(f"\nError processing displacement pattern {idisp}: {e}")
|
|
128
|
-
import traceback
|
|
129
|
-
|
|
130
|
-
traceback.print_exc()
|
|
131
|
-
|
|
132
|
-
print("\n" + "=" * 80)
|
|
133
|
-
print("Calculation completed!")
|
|
134
|
-
print("=" * 80)
|
|
135
|
-
print("Results structure:")
|
|
136
|
-
print(f" {config['output_path']}/")
|
|
137
|
-
print(f" └── idisp{{N}}_Ru{{X}}_{{Y}}_{{Z}}/")
|
|
138
|
-
if config['compute_d2J']:
|
|
139
|
-
print(" ├── original/ # Exchange at zero displacement")
|
|
140
|
-
print(" ├── negative/ # Exchange at -amplitude")
|
|
141
|
-
print(" ├── positive/ # Exchange at +amplitude")
|
|
142
|
-
print(" └── dJdx/ # Computed dJ/dx derivatives")
|
|
143
|
-
print(" ├── exchange.out # Human-readable format")
|
|
144
|
-
print(" └── dJdx.pickle # Python dictionary format")
|
|
145
|
-
print("\nOutput format:")
|
|
146
|
-
if config['compute_d2J']:
|
|
147
|
-
print(" - 14 columns: includes J_0, J_neg, J_pos, dJ/dx, d2J/dx2")
|
|
148
|
-
else:
|
|
149
|
-
print(" - 12 columns: includes J_neg, J_pos, dJ/dx (no J_0, no d2J/dx2)")
|
|
150
|
-
if config['ispin0_only']:
|
|
151
|
-
print(" - Only pairs with ispin=0 or jspin=0 included")
|
|
152
|
-
print("=" * 80)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if __name__ == "__main__":
|
|
156
|
-
main()
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Test script for compute_dJdx_from_exchanges function with distance-based sorting.
|
|
3
|
-
|
|
4
|
-
PURPOSE:
|
|
5
|
-
Tests the compute_dJdx_from_exchanges function using existing exchange data
|
|
6
|
-
from FD_spinphon_results to verify:
|
|
7
|
-
1. Distance-based sorting works correctly
|
|
8
|
-
2. Output format matches TB2J exchange output order
|
|
9
|
-
3. dJ/dx and d²J/dx² values are reasonable
|
|
10
|
-
|
|
11
|
-
USAGE:
|
|
12
|
-
uv run python agent_files/debug_spinphon_fd/test_compute_dJdx.py
|
|
13
|
-
|
|
14
|
-
EXPECTED OUTPUT:
|
|
15
|
-
Creates dJdx/exchange.out with sorted exchange interactions by distance
|
|
16
|
-
and prints verification information.
|
|
17
|
-
|
|
18
|
-
FILES USED:
|
|
19
|
-
- FD_spinphon_results/original/TB2J.pickle
|
|
20
|
-
- FD_spinphon_results/negative/TB2J.pickle
|
|
21
|
-
- FD_spinphon_results/positive/TB2J.pickle
|
|
22
|
-
|
|
23
|
-
DEBUG NOTES:
|
|
24
|
-
- Uses pickle.load to read existing ExchangeNCL objects
|
|
25
|
-
- Does not require supercellmap module
|
|
26
|
-
- Tests only the derivative computation and output formatting
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
import os
|
|
30
|
-
import pickle
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def load_exchange(pickle_path):
|
|
34
|
-
"""Load exchange object from pickle file."""
|
|
35
|
-
with open(pickle_path, "rb") as f:
|
|
36
|
-
return pickle.load(f)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def compute_dJdx_from_exchanges(
|
|
40
|
-
exchange_orig, exchange_neg, exchange_pos, amplitude, output_path, compute_d2J=True
|
|
41
|
-
):
|
|
42
|
-
"""
|
|
43
|
-
Compute dJ/dx and optionally d²J/dx² from exchanges at three positions using finite difference.
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
exchange_orig: Exchange dict at zero displacement (can be None if compute_d2J=False)
|
|
47
|
-
exchange_neg: Exchange dict at -amplitude
|
|
48
|
-
exchange_pos: Exchange dict at +amplitude
|
|
49
|
-
amplitude: Displacement amplitude in Angstrom
|
|
50
|
-
output_path: Directory to save dJdx results
|
|
51
|
-
compute_d2J: Whether to compute second derivative (default=True)
|
|
52
|
-
"""
|
|
53
|
-
os.makedirs(output_path, exist_ok=True)
|
|
54
|
-
|
|
55
|
-
# Extract isotropic exchange values (dict already contains the data)
|
|
56
|
-
Jiso_orig = exchange_orig["exchange_Jdict"] if exchange_orig is not None else None
|
|
57
|
-
Jiso_neg = exchange_neg["exchange_Jdict"]
|
|
58
|
-
Jiso_pos = exchange_pos["exchange_Jdict"]
|
|
59
|
-
|
|
60
|
-
# Get distance information (use neg or pos if orig not available)
|
|
61
|
-
if exchange_orig is not None:
|
|
62
|
-
distance_dict = exchange_orig["distance_dict"]
|
|
63
|
-
else:
|
|
64
|
-
distance_dict = exchange_neg["distance_dict"]
|
|
65
|
-
|
|
66
|
-
# Compute first derivative: dJ/dx = (J_pos - J_neg) / (2*dx)
|
|
67
|
-
# Note: multiply by 1e3 to convert from eV/A to meV/A
|
|
68
|
-
dJiso_dx = {}
|
|
69
|
-
keys_to_use = Jiso_orig.keys() if Jiso_orig is not None else Jiso_neg.keys()
|
|
70
|
-
for key in keys_to_use:
|
|
71
|
-
if key in Jiso_pos and key in Jiso_neg:
|
|
72
|
-
dJiso_dx[key] = (Jiso_pos[key] - Jiso_neg[key]) / (2.0 * amplitude) * 1e3
|
|
73
|
-
|
|
74
|
-
# Compute second derivative: d²J/dx² = (J_pos - 2*J_orig + J_neg) / (dx²)
|
|
75
|
-
# Note: multiply by 1e3 to convert from eV/A² to meV/A²
|
|
76
|
-
d2Jiso_dx2 = {}
|
|
77
|
-
if compute_d2J and Jiso_orig is not None:
|
|
78
|
-
for key in Jiso_orig.keys():
|
|
79
|
-
if key in Jiso_pos and key in Jiso_neg:
|
|
80
|
-
d2Jiso_dx2[key] = (
|
|
81
|
-
(Jiso_pos[key] - 2.0 * Jiso_orig[key] + Jiso_neg[key])
|
|
82
|
-
/ (amplitude**2)
|
|
83
|
-
* 1e3
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
# Sort keys by first atom index (ispin), then by distance
|
|
87
|
-
sorted_keys = sorted(dJiso_dx.keys(), key=lambda x: (x[1], distance_dict[x][1]))
|
|
88
|
-
|
|
89
|
-
# Save results in text format (sorted by ispin then distance)
|
|
90
|
-
output_file = os.path.join(output_path, "exchange.out")
|
|
91
|
-
with open(output_file, "w") as f:
|
|
92
|
-
if compute_d2J:
|
|
93
|
-
f.write(
|
|
94
|
-
"# Derivatives of isotropic exchange computed from finite difference\n"
|
|
95
|
-
)
|
|
96
|
-
else:
|
|
97
|
-
f.write(
|
|
98
|
-
"# First derivative of isotropic exchange computed from finite difference\n"
|
|
99
|
-
)
|
|
100
|
-
f.write(f"# dJ/dx = (J(+{amplitude}) - J(-{amplitude})) / (2*{amplitude})\n")
|
|
101
|
-
if compute_d2J:
|
|
102
|
-
f.write(
|
|
103
|
-
f"# d²J/dx² = (J(+{amplitude}) - 2*J(0) + J(-{amplitude})) / ({amplitude}²)\n"
|
|
104
|
-
)
|
|
105
|
-
if compute_d2J:
|
|
106
|
-
f.write(
|
|
107
|
-
"# Units: J in meV, distances in Angstrom, dJ/dx in meV/Angstrom, d²J/dx² in meV/Angstrom²\n"
|
|
108
|
-
)
|
|
109
|
-
else:
|
|
110
|
-
f.write("# Units: J in meV, distances in Angstrom, dJ/dx in meV/Angstrom\n")
|
|
111
|
-
f.write("# Sorted by: first atom index (ispin), then by distance\n")
|
|
112
|
-
if compute_d2J and Jiso_orig is not None:
|
|
113
|
-
f.write(
|
|
114
|
-
"# ispin jspin Rx Ry Rz distance(A) dx(A) dy(A) dz(A) J_0(meV) J_neg(meV) J_pos(meV) dJ/dx d2J/dx2\n"
|
|
115
|
-
)
|
|
116
|
-
elif Jiso_orig is None:
|
|
117
|
-
if compute_d2J:
|
|
118
|
-
f.write(
|
|
119
|
-
"# ispin jspin Rx Ry Rz distance(A) dx(A) dy(A) dz(A) J_neg(meV) J_pos(meV) dJ/dx d2J/dx2\n"
|
|
120
|
-
)
|
|
121
|
-
else:
|
|
122
|
-
f.write(
|
|
123
|
-
"# ispin jspin Rx Ry Rz distance(A) dx(A) dy(A) dz(A) J_neg(meV) J_pos(meV) dJ/dx\n"
|
|
124
|
-
)
|
|
125
|
-
else:
|
|
126
|
-
f.write(
|
|
127
|
-
"# ispin jspin Rx Ry Rz distance(A) dx(A) dy(A) dz(A) J_0(meV) J_neg(meV) J_pos(meV) dJ/dx\n"
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
for key in sorted_keys:
|
|
131
|
-
R, ispin, jspin = key
|
|
132
|
-
Rx, Ry, Rz = R
|
|
133
|
-
|
|
134
|
-
# Get distance vector and norm
|
|
135
|
-
vec, distance = distance_dict[key]
|
|
136
|
-
|
|
137
|
-
# Get J values (convert from eV to meV)
|
|
138
|
-
Jneg = Jiso_neg[key] * 1e3
|
|
139
|
-
Jpos = Jiso_pos[key] * 1e3
|
|
140
|
-
|
|
141
|
-
# Write output line
|
|
142
|
-
line = (
|
|
143
|
-
f"{ispin:3d} {jspin:3d} {Rx:3d} {Ry:3d} {Rz:3d} "
|
|
144
|
-
f"{distance:12.8f} {vec[0]:10.6f} {vec[1]:10.6f} {vec[2]:10.6f} "
|
|
145
|
-
)
|
|
146
|
-
if Jiso_orig is not None:
|
|
147
|
-
J0 = Jiso_orig[key] * 1e3
|
|
148
|
-
line += f"{J0:16.8f} "
|
|
149
|
-
line += f"{Jneg:16.8f} {Jpos:16.8f} {dJiso_dx[key]:16.8f}"
|
|
150
|
-
if compute_d2J and key in d2Jiso_dx2:
|
|
151
|
-
line += f" {d2Jiso_dx2[key]:16.8f}"
|
|
152
|
-
line += "\n"
|
|
153
|
-
f.write(line)
|
|
154
|
-
|
|
155
|
-
# Save in pickle format
|
|
156
|
-
pickle_file = os.path.join(output_path, "dJdx.pickle")
|
|
157
|
-
results = {
|
|
158
|
-
"dJiso_dx": dJiso_dx,
|
|
159
|
-
"Jiso_neg": Jiso_neg,
|
|
160
|
-
"Jiso_pos": Jiso_pos,
|
|
161
|
-
"amplitude": amplitude,
|
|
162
|
-
}
|
|
163
|
-
if Jiso_orig is not None:
|
|
164
|
-
results["Jiso_orig"] = Jiso_orig
|
|
165
|
-
if compute_d2J:
|
|
166
|
-
results["d2Jiso_dx2"] = d2Jiso_dx2
|
|
167
|
-
|
|
168
|
-
with open(pickle_file, "wb") as f:
|
|
169
|
-
pickle.dump(results, f)
|
|
170
|
-
|
|
171
|
-
print("Results saved to:")
|
|
172
|
-
print(f" {output_file}")
|
|
173
|
-
print(f" {pickle_file}")
|
|
174
|
-
print(f"\nNumber of exchange interactions: {len(dJiso_dx)}")
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def main():
|
|
178
|
-
"""Test compute_dJdx_from_exchanges with existing data."""
|
|
179
|
-
|
|
180
|
-
# Define paths
|
|
181
|
-
results_dir = "FD_spinphon_results"
|
|
182
|
-
orig_pickle = os.path.join(results_dir, "original", "TB2J.pickle")
|
|
183
|
-
neg_pickle = os.path.join(results_dir, "negative", "TB2J.pickle")
|
|
184
|
-
pos_pickle = os.path.join(results_dir, "positive", "TB2J.pickle")
|
|
185
|
-
dJdx_dir = os.path.join(results_dir, "dJdx")
|
|
186
|
-
|
|
187
|
-
print("=" * 80)
|
|
188
|
-
print("Testing compute_dJdx_from_exchanges with distance-based sorting")
|
|
189
|
-
print("=" * 80)
|
|
190
|
-
|
|
191
|
-
# Load exchange objects
|
|
192
|
-
print("\nLoading exchange data...")
|
|
193
|
-
print(f" Original: {orig_pickle}")
|
|
194
|
-
print(f" Negative: {neg_pickle}")
|
|
195
|
-
print(f" Positive: {pos_pickle}")
|
|
196
|
-
|
|
197
|
-
exchange_orig = load_exchange(orig_pickle)
|
|
198
|
-
exchange_neg = load_exchange(neg_pickle)
|
|
199
|
-
exchange_pos = load_exchange(pos_pickle)
|
|
200
|
-
|
|
201
|
-
print("\nExchange objects loaded successfully!")
|
|
202
|
-
print(f" Original has {len(exchange_orig['exchange_Jdict'])} interactions")
|
|
203
|
-
print(f" Negative has {len(exchange_neg['exchange_Jdict'])} interactions")
|
|
204
|
-
print(f" Positive has {len(exchange_pos['exchange_Jdict'])} interactions")
|
|
205
|
-
|
|
206
|
-
# Compute derivatives
|
|
207
|
-
amplitude = 0.01 # Angstrom
|
|
208
|
-
print(f"\nComputing dJ/dx with amplitude = {amplitude} Angstrom...")
|
|
209
|
-
|
|
210
|
-
compute_dJdx_from_exchanges(
|
|
211
|
-
exchange_orig, exchange_neg, exchange_pos, amplitude, dJdx_dir
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
# Read and display first few lines of output
|
|
215
|
-
output_file = os.path.join(dJdx_dir, "exchange.out")
|
|
216
|
-
print("\n" + "=" * 80)
|
|
217
|
-
print("First 10 lines of output (sorted by distance):")
|
|
218
|
-
print("=" * 80)
|
|
219
|
-
|
|
220
|
-
with open(output_file, "r") as f:
|
|
221
|
-
lines = f.readlines()
|
|
222
|
-
for i, line in enumerate(lines[:15]): # Show header + first 10 data lines
|
|
223
|
-
print(line.rstrip())
|
|
224
|
-
|
|
225
|
-
# Verify manual calculation for first non-header line
|
|
226
|
-
print("\n" + "=" * 80)
|
|
227
|
-
print("Manual verification of first interaction:")
|
|
228
|
-
print("=" * 80)
|
|
229
|
-
|
|
230
|
-
# Find first data line
|
|
231
|
-
with open(output_file, "r") as f:
|
|
232
|
-
for line in f:
|
|
233
|
-
if not line.startswith("#"):
|
|
234
|
-
parts = line.split()
|
|
235
|
-
if len(parts) == 14: # Updated: now has distance vector components
|
|
236
|
-
ispin, jspin, Rx, Ry, Rz = map(int, parts[:5])
|
|
237
|
-
distance, dx, dy, dz, J0, Jneg, Jpos, dJdx, d2Jdx2 = map(
|
|
238
|
-
float, parts[5:]
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
print(
|
|
242
|
-
f"Interaction: ispin={ispin}, jspin={jspin}, R=({Rx},{Ry},{Rz})"
|
|
243
|
-
)
|
|
244
|
-
print(f"Distance: {distance:.8f} Å")
|
|
245
|
-
print(f"Distance vector: ({dx:.8f}, {dy:.8f}, {dz:.8f}) Å")
|
|
246
|
-
print(f"J(0) = {J0:.8f} meV")
|
|
247
|
-
print(f"J(-dx) = {Jneg:.8f} meV")
|
|
248
|
-
print(f"J(+dx) = {Jpos:.8f} meV")
|
|
249
|
-
print(f"\nComputed dJ/dx = {dJdx:.8f} meV/Å")
|
|
250
|
-
|
|
251
|
-
# Manual calculation
|
|
252
|
-
manual_dJdx = (Jpos - Jneg) / (2.0 * amplitude)
|
|
253
|
-
print(f"Manual dJ/dx = (J(+dx) - J(-dx)) / (2*{amplitude})")
|
|
254
|
-
print(f" = ({Jpos:.8f} - {Jneg:.8f}) / {2*amplitude}")
|
|
255
|
-
print(f" = {manual_dJdx:.8f} meV/Å")
|
|
256
|
-
|
|
257
|
-
if abs(dJdx - manual_dJdx) < 1e-6:
|
|
258
|
-
print("\n✓ Manual calculation matches!")
|
|
259
|
-
else:
|
|
260
|
-
print(
|
|
261
|
-
f"\n✗ Manual calculation differs by {abs(dJdx - manual_dJdx):.2e}"
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
break
|
|
265
|
-
|
|
266
|
-
print("\n" + "=" * 80)
|
|
267
|
-
print("Test completed successfully!")
|
|
268
|
-
print("=" * 80)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if __name__ == "__main__":
|
|
272
|
-
main()
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Test script for ispin0_only parameter.
|
|
3
|
-
|
|
4
|
-
PURPOSE:
|
|
5
|
-
Tests that ispin0_only=True correctly filters exchange pairs to only include
|
|
6
|
-
those where the first spin index (ispin) is 0, significantly reducing computation time.
|
|
7
|
-
|
|
8
|
-
USAGE:
|
|
9
|
-
uv run python TB2J/agent_files/debug_spinphon_fd/test_ispin0_only.py
|
|
10
|
-
|
|
11
|
-
EXPECTED OUTPUT:
|
|
12
|
-
- Comparison of number of pairs computed with ispin0_only=False vs True
|
|
13
|
-
- Verification that all pairs in ispin0_only=True output have ispin=0
|
|
14
|
-
- Demonstration of computational savings
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
import sys
|
|
18
|
-
import os
|
|
19
|
-
sys.path.insert(0, '../..')
|
|
20
|
-
from test_compute_dJdx import compute_dJdx_from_exchanges, load_exchange
|
|
21
|
-
|
|
22
|
-
print("Testing ispin0_only parameter...")
|
|
23
|
-
print("=" * 80)
|
|
24
|
-
|
|
25
|
-
# Use available test data
|
|
26
|
-
base_path = 'FD_spinphon_results_0.001_k333_nocenter/idisp0_Ru0_0_0'
|
|
27
|
-
|
|
28
|
-
print("\n1. Testing with ispin0_only=False (default - all pairs)")
|
|
29
|
-
print("-" * 80)
|
|
30
|
-
neg = load_exchange(os.path.join(base_path, 'negative/TB2J.pickle'))
|
|
31
|
-
pos = load_exchange(os.path.join(base_path, 'positive/TB2J.pickle'))
|
|
32
|
-
|
|
33
|
-
compute_dJdx_from_exchanges(None, neg, pos, 0.001, 'test_all_pairs_output', compute_d2J=False)
|
|
34
|
-
|
|
35
|
-
# Count total pairs
|
|
36
|
-
with open('test_all_pairs_output/exchange.out', 'r') as f:
|
|
37
|
-
all_pairs_count = sum(1 for line in f if not line.startswith('#'))
|
|
38
|
-
|
|
39
|
-
print(f"Total number of exchange pairs computed: {all_pairs_count}")
|
|
40
|
-
|
|
41
|
-
# Check ispin distribution
|
|
42
|
-
ispin_counts = {}
|
|
43
|
-
with open('test_all_pairs_output/exchange.out', 'r') as f:
|
|
44
|
-
for line in f:
|
|
45
|
-
if not line.startswith('#'):
|
|
46
|
-
parts = line.split()
|
|
47
|
-
ispin = int(parts[0])
|
|
48
|
-
ispin_counts[ispin] = ispin_counts.get(ispin, 0) + 1
|
|
49
|
-
|
|
50
|
-
print(f"Distribution by ispin: {dict(sorted(ispin_counts.items()))}")
|
|
51
|
-
|
|
52
|
-
print("\n2. Testing with ispin0_only=True (only ispin=0 or jspin=0 pairs)")
|
|
53
|
-
print("-" * 80)
|
|
54
|
-
|
|
55
|
-
# Note: In actual usage, we would create new Exchange objects with ispin0_only=True
|
|
56
|
-
# Here we're just testing the filtering in compute_dJdx_from_exchanges
|
|
57
|
-
# The real performance gain comes from Exchange class not computing the other pairs
|
|
58
|
-
|
|
59
|
-
# For this test, we manually filter to simulate the behavior
|
|
60
|
-
neg_filtered = load_exchange(os.path.join(base_path, 'negative/TB2J.pickle'))
|
|
61
|
-
pos_filtered = load_exchange(os.path.join(base_path, 'positive/TB2J.pickle'))
|
|
62
|
-
|
|
63
|
-
# Filter the dictionaries
|
|
64
|
-
def filter_ispin0(exchange_dict):
|
|
65
|
-
"""Filter exchange dict to only include pairs where ispin=0 or jspin=0"""
|
|
66
|
-
filtered = {}
|
|
67
|
-
filtered['exchange_Jdict'] = {k: v for k, v in exchange_dict['exchange_Jdict'].items() if k[1] == 0 or k[2] == 0}
|
|
68
|
-
filtered['distance_dict'] = {k: v for k, v in exchange_dict['distance_dict'].items() if k[1] == 0 or k[2] == 0}
|
|
69
|
-
return filtered
|
|
70
|
-
|
|
71
|
-
neg_filtered = filter_ispin0(neg_filtered)
|
|
72
|
-
pos_filtered = filter_ispin0(pos_filtered)
|
|
73
|
-
|
|
74
|
-
compute_dJdx_from_exchanges(None, neg_filtered, pos_filtered, 0.001, 'test_ispin0_output', compute_d2J=False)
|
|
75
|
-
|
|
76
|
-
# Count ispin0 pairs
|
|
77
|
-
with open('test_ispin0_output/exchange.out', 'r') as f:
|
|
78
|
-
ispin0_pairs_count = sum(1 for line in f if not line.startswith('#'))
|
|
79
|
-
|
|
80
|
-
print(f"Number of exchange pairs with ispin=0 or jspin=0: {ispin0_pairs_count}")
|
|
81
|
-
|
|
82
|
-
# Verify all pairs have ispin=0 or jspin=0
|
|
83
|
-
all_valid = True
|
|
84
|
-
ispin0_count = 0
|
|
85
|
-
jspin0_count = 0
|
|
86
|
-
both0_count = 0
|
|
87
|
-
with open('test_ispin0_output/exchange.out', 'r') as f:
|
|
88
|
-
for line in f:
|
|
89
|
-
if not line.startswith('#'):
|
|
90
|
-
parts = line.split()
|
|
91
|
-
ispin = int(parts[0])
|
|
92
|
-
jspin = int(parts[1])
|
|
93
|
-
if ispin != 0 and jspin != 0:
|
|
94
|
-
all_valid = False
|
|
95
|
-
print(f"ERROR: Found pair with ispin={ispin}, jspin={jspin}")
|
|
96
|
-
break
|
|
97
|
-
if ispin == 0 and jspin == 0:
|
|
98
|
-
both0_count += 1
|
|
99
|
-
elif ispin == 0:
|
|
100
|
-
ispin0_count += 1
|
|
101
|
-
elif jspin == 0:
|
|
102
|
-
jspin0_count += 1
|
|
103
|
-
|
|
104
|
-
if all_valid:
|
|
105
|
-
print("✓ All pairs have ispin=0 or jspin=0")
|
|
106
|
-
print(f" - Pairs with both ispin=0 and jspin=0: {both0_count}")
|
|
107
|
-
print(f" - Pairs with ispin=0 only: {ispin0_count}")
|
|
108
|
-
print(f" - Pairs with jspin=0 only: {jspin0_count}")
|
|
109
|
-
|
|
110
|
-
print("\n3. Performance comparison")
|
|
111
|
-
print("-" * 80)
|
|
112
|
-
reduction = (all_pairs_count - ispin0_pairs_count) / all_pairs_count * 100
|
|
113
|
-
print(f"Pairs reduced: {all_pairs_count} → {ispin0_pairs_count}")
|
|
114
|
-
print(f"Reduction: {reduction:.1f}%")
|
|
115
|
-
print(f"Speedup factor: ~{all_pairs_count / ispin0_pairs_count:.1f}x")
|
|
116
|
-
|
|
117
|
-
print("\n" + "=" * 80)
|
|
118
|
-
print("Test completed successfully!")
|
|
119
|
-
print("\nNOTE: In actual usage with Exchange class, the performance gain comes from")
|
|
120
|
-
print("not computing the filtered pairs at all, not just filtering the output.")
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"""Test script with compute_d2J=False"""
|
|
2
|
-
import sys
|
|
3
|
-
import os
|
|
4
|
-
sys.path.insert(0, '../..')
|
|
5
|
-
from test_compute_dJdx import compute_dJdx_from_exchanges, load_exchange
|
|
6
|
-
|
|
7
|
-
print("Testing with compute_d2J=False (skips J_0 computation)...")
|
|
8
|
-
|
|
9
|
-
# Use available test data
|
|
10
|
-
base_path = 'FD_spinphon_results_0.001_k333_nocenter/idisp0_Ru0_0_0'
|
|
11
|
-
neg = load_exchange(os.path.join(base_path, 'negative/TB2J.pickle'))
|
|
12
|
-
pos = load_exchange(os.path.join(base_path, 'positive/TB2J.pickle'))
|
|
13
|
-
|
|
14
|
-
# Note: passing orig=None when compute_d2J=False
|
|
15
|
-
compute_dJdx_from_exchanges(None, neg, pos, 0.001, 'test_no_d2j_output', compute_d2J=False)
|
|
16
|
-
|
|
17
|
-
print("\nFirst 12 lines of output:")
|
|
18
|
-
with open('test_no_d2j_output/exchange.out', 'r') as f:
|
|
19
|
-
for i, line in enumerate(f):
|
|
20
|
-
if i < 12:
|
|
21
|
-
print(line.rstrip())
|
|
22
|
-
|
|
23
|
-
print("\nChecking number of columns:")
|
|
24
|
-
with open('test_no_d2j_output/exchange.out', 'r') as f:
|
|
25
|
-
for line in f:
|
|
26
|
-
if not line.startswith('#'):
|
|
27
|
-
parts = line.split()
|
|
28
|
-
print(f"Found {len(parts)} columns (expected 12: no J_0, no d2J/dx2)")
|
|
29
|
-
break
|
|
30
|
-
|
|
31
|
-
print("\nTest completed!")
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""Test script with compute_d2J=True (default)"""
|
|
2
|
-
import sys
|
|
3
|
-
sys.path.insert(0, '../..')
|
|
4
|
-
from test_compute_dJdx import compute_dJdx_from_exchanges, load_exchange
|
|
5
|
-
|
|
6
|
-
print("Testing with compute_d2J=True (default)...")
|
|
7
|
-
|
|
8
|
-
orig = load_exchange('FD_spinphon_results_222/original/TB2J.pickle')
|
|
9
|
-
neg = load_exchange('FD_spinphon_results_222/negative/TB2J.pickle')
|
|
10
|
-
pos = load_exchange('FD_spinphon_results_222/positive/TB2J.pickle')
|
|
11
|
-
|
|
12
|
-
compute_dJdx_from_exchanges(orig, neg, pos, 0.01, 'test_with_d2j_output')
|
|
13
|
-
|
|
14
|
-
print("\nFirst 12 lines of output:")
|
|
15
|
-
with open('test_with_d2j_output/exchange.out', 'r') as f:
|
|
16
|
-
for i, line in enumerate(f):
|
|
17
|
-
if i < 12:
|
|
18
|
-
print(line.rstrip())
|
|
19
|
-
|
|
20
|
-
print("\nChecking number of columns:")
|
|
21
|
-
with open('test_with_d2j_output/exchange.out', 'r') as f:
|
|
22
|
-
for line in f:
|
|
23
|
-
if not line.startswith('#'):
|
|
24
|
-
parts = line.split()
|
|
25
|
-
print(f"Found {len(parts)} columns (expected 14 with d2J)")
|
|
26
|
-
break
|
|
27
|
-
|
|
28
|
-
print("\nTest completed!")
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from abacus_api import read_HR_SR
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def main():
|
|
5
|
-
# nspin = 2 case
|
|
6
|
-
nspin = 2
|
|
7
|
-
binary = False
|
|
8
|
-
file_path = "../abacus_example/case_Sr2Mn2O6/1_no_soc/OUT.Sr2Mn2O6/"
|
|
9
|
-
HR_file = [
|
|
10
|
-
file_path + "data-HR-sparse_SPIN0.csr",
|
|
11
|
-
file_path + "data-HR-sparse_SPIN1.csr",
|
|
12
|
-
]
|
|
13
|
-
SR_file = file_path + "data-SR-sparse_SPIN0.csr"
|
|
14
|
-
|
|
15
|
-
basis_num, R_direct_coor, HR_up, HR_dn, SR = read_HR_SR(
|
|
16
|
-
nspin, binary, HR_file, SR_file
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
print("basis_num =", basis_num)
|
|
20
|
-
print("R_direct_coor =", R_direct_coor)
|
|
21
|
-
print("HR_up[1, 0, 15:22] =", HR_up[1, 0, 15:22])
|
|
22
|
-
print("HR_dn[1, 0, 15:22] =", HR_dn[1, 0, 15:22])
|
|
23
|
-
print("SR[1, 0, 15:22] =", SR[1, 0, 15:22])
|
|
24
|
-
|
|
25
|
-
# nspin = 4 case
|
|
26
|
-
nspin = 4
|
|
27
|
-
binary = False
|
|
28
|
-
file_path = "../abacus_example/case_Sr2Mn2O6/2_soc/OUT.Sr2Mn2O6/"
|
|
29
|
-
HR_file = file_path + "data-HR-sparse_SPIN0.csr"
|
|
30
|
-
SR_file = file_path + "data-SR-sparse_SPIN0.csr"
|
|
31
|
-
|
|
32
|
-
basis_num, R_direct_coor, HR, SR = read_HR_SR(nspin, binary, HR_file, SR_file)
|
|
33
|
-
|
|
34
|
-
print("basis_num =", basis_num)
|
|
35
|
-
print("R_direct_coor =", R_direct_coor)
|
|
36
|
-
print("HR[1, 0, 30:37] =", HR[1, 0, 30:37])
|
|
37
|
-
print("SR[1, 0, 30:37] =", SR[1, 0, 30:37])
|
|
38
|
-
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if __name__ == "__main__":
|
|
43
|
-
main()
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
@File : test_read_stru.py
|
|
5
|
-
@Time : 2024/02/02 09:52:23
|
|
6
|
-
@Author : Shen Zhen-Xiong
|
|
7
|
-
@Email : shenzx@iai.ustc.edu.cn
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
import os
|
|
11
|
-
|
|
12
|
-
from stru_api import read_abacus, read_input, write_abacus
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def main():
|
|
16
|
-
# stru_fe = read_abacus(os.path.join(os.getcwd(), "input/Fe.STRU"))
|
|
17
|
-
stru_sr2mn2o6 = read_abacus(
|
|
18
|
-
os.path.join(os.getcwd(), "input/Sr2Mn2O6.STRU"), verbose=True
|
|
19
|
-
)
|
|
20
|
-
write_abacus(
|
|
21
|
-
file=os.path.join(os.getcwd(), "STRU"),
|
|
22
|
-
atoms=stru_sr2mn2o6,
|
|
23
|
-
pp=stru_sr2mn2o6.info["pp"],
|
|
24
|
-
basis=stru_sr2mn2o6.info["basis"],
|
|
25
|
-
)
|
|
26
|
-
input_file = read_input(os.path.join(os.getcwd(), "input/INPUT"))
|
|
27
|
-
print(input_file["pseudo_dir"])
|
|
28
|
-
return
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if __name__ == "__main__":
|
|
32
|
-
main()
|