TB2Jflows 0.1.0__py3-none-any.whl → 0.2.0__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.
- TB2Jflows/__init__.py +5 -2
- TB2Jflows/ase_siesta.py +3 -1
- TB2Jflows/find_pp.py +61 -37
- TB2Jflows/mysiesta.py +280 -231
- TB2Jflows/siesta_basis.py +8 -11
- TB2Jflows/siesta_spinphonon.py +255 -0
- {tb2jflows-0.1.0.dist-info → tb2jflows-0.2.0.dist-info}/METADATA +1 -1
- tb2jflows-0.2.0.dist-info/RECORD +12 -0
- {tb2jflows-0.1.0.dist-info → tb2jflows-0.2.0.dist-info}/WHEEL +1 -1
- tb2jflows-0.1.0.dist-info/RECORD +0 -11
- {tb2jflows-0.1.0.dist-info → tb2jflows-0.2.0.dist-info}/licenses/LICENSE +0 -0
TB2Jflows/__init__.py
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
from TB2Jflows.ase_siesta import SiestaFlow
|
|
2
|
-
from TB2Jflows.auto_siesta_TB2J import auto_siesta_TB2J
|
|
1
|
+
from TB2Jflows.ase_siesta import SiestaFlow as SiestaFlow
|
|
2
|
+
from TB2Jflows.auto_siesta_TB2J import auto_siesta_TB2J as auto_siesta_TB2J
|
|
3
|
+
from TB2Jflows.siesta_spinphonon import (
|
|
4
|
+
compute_spinphonon_coupling as compute_spinphonon_coupling,
|
|
5
|
+
)
|
TB2Jflows/ase_siesta.py
CHANGED
|
@@ -5,11 +5,12 @@ from pathlib import Path
|
|
|
5
5
|
|
|
6
6
|
import ase
|
|
7
7
|
from ase.io.jsonio import decode, encode
|
|
8
|
-
#from pyDFTutils.siesta import MySiesta
|
|
9
8
|
from TB2J.interfaces import gen_exchange_siesta
|
|
10
9
|
from TB2J.io_merge import merge
|
|
11
10
|
from TB2J.rotate_atoms import rotate_atom_spin, rotate_atom_xyz
|
|
12
11
|
|
|
12
|
+
from .mysiesta import MySiesta
|
|
13
|
+
|
|
13
14
|
|
|
14
15
|
class SiestaFlow:
|
|
15
16
|
def __init__(
|
|
@@ -32,6 +33,7 @@ class SiestaFlow:
|
|
|
32
33
|
self.xc = xc
|
|
33
34
|
self.kpts = kpts
|
|
34
35
|
self.Udict = Udict
|
|
36
|
+
|
|
35
37
|
# default fdf arguments
|
|
36
38
|
self.fdf_arguments = {
|
|
37
39
|
"MaxSCFIterations": 350,
|
TB2Jflows/find_pp.py
CHANGED
|
@@ -1,67 +1,91 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from ase.data import chemical_symbols, atomic_numbers
|
|
3
2
|
|
|
3
|
+
from ase.data import atomic_numbers
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
class PPFinder:
|
|
6
7
|
def __init__(self):
|
|
7
8
|
pass
|
|
8
9
|
|
|
9
10
|
def get_pp_path(self, element, xc, label, rel):
|
|
10
11
|
pass
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
|
|
14
|
+
class DojoFinder:
|
|
13
15
|
def __init__(self, path=None):
|
|
14
16
|
if path is None:
|
|
15
|
-
self.path = os.environ[
|
|
17
|
+
self.path = os.environ["DOJO_PATH"]
|
|
16
18
|
else:
|
|
17
19
|
self.path = path
|
|
18
20
|
|
|
19
|
-
def get_pp_path(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
xc="pw"
|
|
30
|
-
dirname = os.path.join(self.path, f"{typ}-{rel}-{version}_{xc}_{accuracy}_{fmt}")
|
|
21
|
+
def get_pp_path(
|
|
22
|
+
self, xc: str, typ="NC", rel="sr", version="04", accuracy="standard", fmt="psml"
|
|
23
|
+
):
|
|
24
|
+
typ = typ.lower()
|
|
25
|
+
xc = xc.lower()
|
|
26
|
+
if xc == "lda":
|
|
27
|
+
xc = "pbe"
|
|
28
|
+
dirname = os.path.join(
|
|
29
|
+
self.path, f"{typ}-{rel}-{version}_{xc}_{accuracy}_{fmt}"
|
|
30
|
+
)
|
|
31
31
|
if not os.path.exists(dirname):
|
|
32
32
|
raise FileNotFoundError(f"File Not found: {dirname}")
|
|
33
33
|
return dirname
|
|
34
34
|
|
|
35
|
-
def get_pp_fname(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
def get_pp_fname(
|
|
36
|
+
self,
|
|
37
|
+
element,
|
|
38
|
+
xc: str,
|
|
39
|
+
typ="NC",
|
|
40
|
+
rel="sr",
|
|
41
|
+
version="04",
|
|
42
|
+
accuracy="standard",
|
|
43
|
+
fincore=False,
|
|
44
|
+
fmt="psml",
|
|
45
|
+
):
|
|
46
|
+
if 57 < atomic_numbers[element] <= 70:
|
|
45
47
|
if fincore:
|
|
46
|
-
fname = os.path.join(
|
|
47
|
-
|
|
48
|
+
fname = os.path.join(
|
|
49
|
+
self.get_pp_path(
|
|
50
|
+
xc=xc,
|
|
51
|
+
typ=typ,
|
|
52
|
+
rel=rel,
|
|
53
|
+
version=version,
|
|
54
|
+
accuracy=accuracy,
|
|
55
|
+
fmt=fmt,
|
|
56
|
+
),
|
|
57
|
+
f"fincore/{element}.{fmt}",
|
|
58
|
+
)
|
|
48
59
|
else:
|
|
49
|
-
fname = os.path.join(
|
|
50
|
-
|
|
60
|
+
fname = os.path.join(
|
|
61
|
+
self.get_pp_path(
|
|
62
|
+
xc=xc,
|
|
63
|
+
typ=typ,
|
|
64
|
+
rel=rel,
|
|
65
|
+
version=version,
|
|
66
|
+
accuracy=accuracy,
|
|
67
|
+
fmt=fmt,
|
|
68
|
+
),
|
|
69
|
+
f"withf/{element}.{fmt}",
|
|
70
|
+
)
|
|
51
71
|
|
|
52
72
|
else:
|
|
53
|
-
fname = os.path.join(
|
|
54
|
-
|
|
73
|
+
fname = os.path.join(
|
|
74
|
+
self.get_pp_path(
|
|
75
|
+
xc=xc, typ=typ, rel=rel, version=version, accuracy=accuracy, fmt=fmt
|
|
76
|
+
),
|
|
77
|
+
f"{element}.{fmt}",
|
|
78
|
+
)
|
|
55
79
|
if not os.path.exists(fname):
|
|
56
80
|
raise FileNotFoundError(f"File Not found: {fname}")
|
|
57
81
|
return fname
|
|
58
82
|
|
|
59
83
|
|
|
60
|
-
|
|
61
84
|
def test():
|
|
62
|
-
finder=DojoFinder(path=os.path.expanduser(
|
|
63
|
-
|
|
64
|
-
|
|
85
|
+
finder = DojoFinder(path=os.path.expanduser("~/projects/pp/dojo"))
|
|
86
|
+
finder.get_pp_path(element="Sr", xc="pbesol")
|
|
87
|
+
finder.get_pp_path(element="Srg", xc="pbe")
|
|
88
|
+
|
|
65
89
|
|
|
66
|
-
if __name__==
|
|
90
|
+
if __name__ == "__main__":
|
|
67
91
|
test()
|
TB2Jflows/mysiesta.py
CHANGED
|
@@ -1,35 +1,52 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import math
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
from os.path import isfile, islink, join
|
|
5
|
+
|
|
3
6
|
import numpy as np
|
|
4
7
|
from ase import Atoms
|
|
5
8
|
from ase.calculators.siesta import Siesta
|
|
6
|
-
from ase.calculators.
|
|
7
|
-
from ase.
|
|
8
|
-
from ase.calculators.siesta.parameters import Species, PAOBasisBlock
|
|
9
|
+
from ase.calculators.siesta.parameters import PAOBasisBlock, Species, format_fdf
|
|
10
|
+
from ase.data import atomic_numbers
|
|
9
11
|
from ase.io import write
|
|
10
|
-
import
|
|
12
|
+
from ase.units import Bohr
|
|
11
13
|
|
|
12
|
-
import
|
|
13
|
-
from os.path import join, isfile, islink
|
|
14
|
-
import numpy as np
|
|
15
|
-
from ase.units import Ry, eV, Bohr
|
|
16
|
-
from ase.data import atomic_numbers
|
|
17
|
-
#from ase.io.siesta import read_siesta_xv
|
|
18
|
-
#from ase.calculators.siesta.import_functions import \
|
|
19
|
-
# get_valence_charge, read_vca_synth_block
|
|
20
|
-
from ase.calculators.calculator import FileIOCalculator, ReadError
|
|
21
|
-
from ase.calculators.calculator import Parameters, all_changes
|
|
22
|
-
from ase.calculators.siesta.parameters import PAOBasisBlock, Species
|
|
23
|
-
from ase.calculators.siesta.parameters import format_fdf
|
|
24
|
-
#from pyDFTutils.siesta.pdos import gen_pdos_figure, plot_layer_pdos
|
|
14
|
+
from .find_pp import DojoFinder
|
|
25
15
|
|
|
26
|
-
from .
|
|
27
|
-
from .
|
|
16
|
+
# from pyDFTutils.siesta.pdos import gen_pdos_figure, plot_layer_pdos
|
|
17
|
+
from .siesta_basis import fincore_basis, withf_basis
|
|
28
18
|
|
|
29
19
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
def get_valence_charge(pseudopotential):
|
|
21
|
+
raise NotImplementedError(
|
|
22
|
+
"get_valence_charge is not available in the current environment."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def read_vca_synth_block(synth_block_filename, species_number):
|
|
27
|
+
raise NotImplementedError(
|
|
28
|
+
"read_vca_synth_block is not available in the current environment."
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
synthetic_atoms_dict_fincore = {
|
|
33
|
+
# Ce-Lu
|
|
34
|
+
"Ce": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
35
|
+
"Pr": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
36
|
+
"Nd": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
37
|
+
"Pm": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
38
|
+
"Sm": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
39
|
+
"Eu": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
40
|
+
"Gd": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
41
|
+
"Tb": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
42
|
+
"Dy": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
43
|
+
"Ho": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
44
|
+
"Er": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
45
|
+
"Tm": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
46
|
+
"Yb": ((6, 5, 5, 5), (2, 6, 1, 0)),
|
|
47
|
+
"Lu": ((5, 5, 5, 4), (2, 6, 3, 0)),
|
|
48
|
+
}
|
|
49
|
+
|
|
33
50
|
|
|
34
51
|
def read_siesta_xv(fd):
|
|
35
52
|
vectors = []
|
|
@@ -46,69 +63,80 @@ def read_siesta_xv(fd):
|
|
|
46
63
|
if len(line) > 5: # Ignore blank lines
|
|
47
64
|
data = line.split()
|
|
48
65
|
speciesnumber.append(int(data[0]))
|
|
49
|
-
atomnumbers.append(int(data[1])%200)
|
|
66
|
+
atomnumbers.append(int(data[1]) % 200)
|
|
50
67
|
xyz.append([float(data[2 + j]) * Bohr for j in range(3)])
|
|
51
68
|
V.append([float(data[5 + j]) * Bohr for j in range(3)])
|
|
52
69
|
|
|
53
70
|
vectors = np.array(vectors)
|
|
54
71
|
atomnumbers = np.array(atomnumbers)
|
|
55
72
|
xyz = np.array(xyz)
|
|
56
|
-
atoms = Atoms(numbers=atomnumbers, positions=xyz, cell=vectors,
|
|
57
|
-
pbc=True)
|
|
73
|
+
atoms = Atoms(numbers=atomnumbers, positions=xyz, cell=vectors, pbc=True)
|
|
58
74
|
assert natoms == len(atoms)
|
|
59
75
|
return atoms
|
|
60
76
|
|
|
77
|
+
|
|
61
78
|
def read_xv(fname):
|
|
62
79
|
with open(fname) as myfile:
|
|
63
|
-
atoms=read_siesta_xv(myfile)
|
|
80
|
+
atoms = read_siesta_xv(myfile)
|
|
64
81
|
return atoms
|
|
65
82
|
|
|
66
83
|
|
|
67
|
-
def get_species(atoms, xc, rel=
|
|
84
|
+
def get_species(atoms, xc, rel="sr", accuracy="standard", fincore=False):
|
|
68
85
|
finder = DojoFinder()
|
|
69
86
|
elems = list(dict.fromkeys(atoms.get_chemical_symbols()).keys())
|
|
70
87
|
elem_dict = dict(zip(elems, range(1, len(elems) + 1)))
|
|
71
88
|
pseudo_path = finder.get_pp_path(xc=xc)
|
|
72
89
|
species = [
|
|
73
|
-
Species(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
90
|
+
Species(
|
|
91
|
+
symbol=elem,
|
|
92
|
+
pseudopotential=finder.get_pp_fname(
|
|
93
|
+
elem, xc=xc, rel=rel, accuracy=accuracy, fincore=fincore
|
|
94
|
+
),
|
|
95
|
+
ghost=False,
|
|
96
|
+
)
|
|
97
|
+
for elem in elem_dict.keys()
|
|
77
98
|
]
|
|
78
99
|
return pseudo_path, species
|
|
79
100
|
|
|
80
101
|
|
|
81
102
|
def cart2sph(vec):
|
|
82
103
|
x, y, z = vec
|
|
83
|
-
r = np.linalg.norm(vec)
|
|
104
|
+
r = np.linalg.norm(vec) # r
|
|
84
105
|
if r < 1e-10:
|
|
85
106
|
theta, phi = 0.0, 0.0
|
|
86
107
|
else:
|
|
87
108
|
# note that there are many conventions, here is the ISO convention.
|
|
88
|
-
phi = math.atan2(y, x) * 180/math.pi
|
|
89
|
-
theta = math.acos(z/r) * 180/math.pi
|
|
109
|
+
phi = math.atan2(y, x) * 180 / math.pi # phi
|
|
110
|
+
theta = math.acos(z / r) * 180 / math.pi # theta
|
|
90
111
|
return r, theta, phi
|
|
91
112
|
|
|
92
113
|
|
|
93
114
|
class MySiesta(Siesta):
|
|
94
|
-
def __init__(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
def __init__(
|
|
116
|
+
self,
|
|
117
|
+
atoms=None,
|
|
118
|
+
command=None,
|
|
119
|
+
xc="LDA",
|
|
120
|
+
spin="non-polarized",
|
|
121
|
+
basis_set="DZP",
|
|
122
|
+
species=None,
|
|
123
|
+
ghosts=[],
|
|
124
|
+
synthetic_atoms={},
|
|
125
|
+
input_basis_set={},
|
|
126
|
+
pseudo_path=None,
|
|
127
|
+
input_pp={},
|
|
128
|
+
pp_accuracy="standard",
|
|
129
|
+
fincore=False,
|
|
130
|
+
**kwargs,
|
|
131
|
+
):
|
|
109
132
|
# non-perturnbative polarized orbital.
|
|
110
133
|
self.npt_elems = set()
|
|
111
|
-
|
|
134
|
+
|
|
135
|
+
if fincore:
|
|
136
|
+
self.synthetic_atoms = synthetic_atoms_dict_fincore.copy()
|
|
137
|
+
self.synthetic_atoms.update(synthetic_atoms)
|
|
138
|
+
else:
|
|
139
|
+
self.synthetic_atoms = synthetic_atoms
|
|
112
140
|
|
|
113
141
|
if fincore:
|
|
114
142
|
input_basis_set.update(fincore_basis)
|
|
@@ -130,52 +158,60 @@ class MySiesta(Siesta):
|
|
|
130
158
|
if pseudo_path is None:
|
|
131
159
|
pseudo_path = finder.get_pp_path(xc=xc, accuracy=pp_accuracy)
|
|
132
160
|
|
|
133
|
-
if spin ==
|
|
134
|
-
rel =
|
|
161
|
+
if spin == "spin-orbit":
|
|
162
|
+
rel = "fr"
|
|
135
163
|
else:
|
|
136
|
-
rel =
|
|
164
|
+
rel = "sr"
|
|
137
165
|
species = []
|
|
138
166
|
for elem, index in self.elem_dict.items():
|
|
139
167
|
if elem not in input_basis_set:
|
|
140
168
|
bselem = basis_set
|
|
141
|
-
if elem in [
|
|
169
|
+
if elem in ["Li", "Be", "Na", "Mg", "Sm"]:
|
|
142
170
|
self.npt_elems.add(f"{elem}.{index}")
|
|
143
171
|
else:
|
|
144
172
|
bselem = PAOBasisBlock(input_basis_set[elem])
|
|
145
173
|
if elem not in input_pp:
|
|
146
174
|
pseudopotential = finder.get_pp_fname(
|
|
147
|
-
elem, xc=xc, rel=rel, accuracy=pp_accuracy, fincore=fincore
|
|
175
|
+
elem, xc=xc, rel=rel, accuracy=pp_accuracy, fincore=fincore
|
|
176
|
+
)
|
|
148
177
|
else:
|
|
149
|
-
pseudopotential = os.path.join(
|
|
150
|
-
pseudo_path, input_pp[elem])
|
|
178
|
+
pseudopotential = os.path.join(pseudo_path, input_pp[elem])
|
|
151
179
|
|
|
152
180
|
if elem in self.synthetic_atoms:
|
|
153
181
|
excess_charge = 0
|
|
154
182
|
else:
|
|
155
183
|
excess_charge = None
|
|
156
184
|
|
|
157
|
-
species.append(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
185
|
+
species.append(
|
|
186
|
+
Species(
|
|
187
|
+
symbol=elem,
|
|
188
|
+
pseudopotential=pseudopotential,
|
|
189
|
+
basis_set=bselem,
|
|
190
|
+
ghost=False,
|
|
191
|
+
excess_charge=excess_charge,
|
|
192
|
+
)
|
|
193
|
+
)
|
|
162
194
|
for elem in ghost_elems:
|
|
163
195
|
species.append(
|
|
164
|
-
Species(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
196
|
+
Species(
|
|
197
|
+
symbol=elem,
|
|
198
|
+
pseudopotential=finder.get_pp_fname(
|
|
199
|
+
elem, xc=xc, rel=rel, accuracy=pp_accuracy, fincore=fincore
|
|
200
|
+
),
|
|
201
|
+
tag=1,
|
|
202
|
+
ghost=True,
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
Siesta.__init__(
|
|
207
|
+
self,
|
|
208
|
+
xc=xc,
|
|
209
|
+
spin=spin,
|
|
210
|
+
atoms=atoms,
|
|
211
|
+
pseudo_path=pseudo_path,
|
|
212
|
+
species=species,
|
|
213
|
+
**kwargs,
|
|
214
|
+
)
|
|
179
215
|
self.set_npt_elements()
|
|
180
216
|
self.set_synthetic_atoms()
|
|
181
217
|
|
|
@@ -188,16 +224,16 @@ class MySiesta(Siesta):
|
|
|
188
224
|
"""
|
|
189
225
|
species, species_numbers = self.species(atoms)
|
|
190
226
|
|
|
191
|
-
if self[
|
|
192
|
-
pseudo_path = self[
|
|
193
|
-
elif
|
|
194
|
-
pseudo_path = os.environ[
|
|
227
|
+
if self["pseudo_path"] is not None:
|
|
228
|
+
pseudo_path = self["pseudo_path"]
|
|
229
|
+
elif "SIESTA_PP_PATH" in os.environ:
|
|
230
|
+
pseudo_path = os.environ["SIESTA_PP_PATH"]
|
|
195
231
|
else:
|
|
196
232
|
mess = "Please set the environment variable 'SIESTA_PP_PATH'"
|
|
197
233
|
raise Exception(mess)
|
|
198
234
|
|
|
199
|
-
fd.write(format_fdf(
|
|
200
|
-
fd.write(format_fdf(
|
|
235
|
+
fd.write(format_fdf("NumberOfSpecies", len(species)))
|
|
236
|
+
fd.write(format_fdf("NumberOfAtoms", len(atoms)))
|
|
201
237
|
|
|
202
238
|
pao_basis = []
|
|
203
239
|
chemical_labels = []
|
|
@@ -205,20 +241,20 @@ class MySiesta(Siesta):
|
|
|
205
241
|
synth_blocks = []
|
|
206
242
|
for species_number, spec in enumerate(species):
|
|
207
243
|
species_number += 1
|
|
208
|
-
symbol = spec[
|
|
244
|
+
symbol = spec["symbol"]
|
|
209
245
|
atomic_number = atomic_numbers[symbol]
|
|
210
246
|
|
|
211
|
-
if spec[
|
|
212
|
-
if self.pseudo_qualifier() ==
|
|
247
|
+
if spec["pseudopotential"] is None:
|
|
248
|
+
if self.pseudo_qualifier() == "":
|
|
213
249
|
label = symbol
|
|
214
|
-
pseudopotential = label +
|
|
250
|
+
pseudopotential = label + ".psf"
|
|
215
251
|
else:
|
|
216
|
-
label =
|
|
217
|
-
pseudopotential = label +
|
|
252
|
+
label = ".".join([symbol, self.pseudo_qualifier()])
|
|
253
|
+
pseudopotential = label + ".psf"
|
|
218
254
|
else:
|
|
219
|
-
pseudopotential = spec[
|
|
255
|
+
pseudopotential = spec["pseudopotential"]
|
|
220
256
|
label = os.path.basename(pseudopotential)
|
|
221
|
-
label =
|
|
257
|
+
label = ".".join(label.split(".")[:-1])
|
|
222
258
|
|
|
223
259
|
if not os.path.isabs(pseudopotential):
|
|
224
260
|
pseudopotential = join(pseudo_path, pseudopotential)
|
|
@@ -228,138 +264,138 @@ class MySiesta(Siesta):
|
|
|
228
264
|
raise RuntimeError(mess)
|
|
229
265
|
|
|
230
266
|
name = os.path.basename(pseudopotential)
|
|
231
|
-
name = name.split(
|
|
267
|
+
name = name.split(".")
|
|
232
268
|
name.insert(-1, str(species_number))
|
|
233
|
-
if spec[
|
|
234
|
-
name.insert(-1,
|
|
269
|
+
if spec["ghost"]:
|
|
270
|
+
name.insert(-1, "ghost")
|
|
235
271
|
atomic_number = -atomic_number
|
|
236
272
|
|
|
237
|
-
name =
|
|
273
|
+
name = ".".join(name)
|
|
238
274
|
pseudo_targetpath = self.getpath(name)
|
|
239
275
|
|
|
240
276
|
if join(os.getcwd(), name) != pseudopotential:
|
|
241
277
|
if islink(pseudo_targetpath) or isfile(pseudo_targetpath):
|
|
242
278
|
os.remove(pseudo_targetpath)
|
|
243
|
-
symlink_pseudos = self[
|
|
279
|
+
symlink_pseudos = self["symlink_pseudos"]
|
|
244
280
|
|
|
245
281
|
symlink_pseudos = False
|
|
246
282
|
|
|
247
283
|
if symlink_pseudos is None:
|
|
248
|
-
symlink_pseudos = not os.name ==
|
|
284
|
+
symlink_pseudos = not os.name == "nt"
|
|
249
285
|
|
|
250
286
|
if symlink_pseudos:
|
|
251
287
|
os.symlink(pseudopotential, pseudo_targetpath)
|
|
252
288
|
else:
|
|
253
289
|
shutil.copy(pseudopotential, pseudo_targetpath)
|
|
254
|
-
if
|
|
290
|
+
if spec["excess_charge"] is not None:
|
|
255
291
|
atomic_number += 200
|
|
256
|
-
n_atoms = sum(np.array(species_numbers) == species_number)
|
|
257
|
-
|
|
258
|
-
if spec[
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
292
|
+
# n_atoms = sum(np.array(species_numbers) == species_number)
|
|
293
|
+
|
|
294
|
+
# if spec["excess_charge"] != 0:
|
|
295
|
+
# paec = float(spec["excess_charge"]) / n_atoms
|
|
296
|
+
# vc = get_valence_charge(pseudopotential)
|
|
297
|
+
# fraction = float(vc + paec) / vc
|
|
298
|
+
# pseudo_head = name[:-4]
|
|
299
|
+
# fractional_command = os.environ["SIESTA_UTIL_FRACTIONAL"]
|
|
300
|
+
# cmd = "%s %s %.7f" % (fractional_command, pseudo_head, fraction)
|
|
301
|
+
# os.system(cmd)
|
|
302
|
+
#
|
|
303
|
+
# pseudo_head += "-Fraction-%.5f" % fraction
|
|
304
|
+
# synth_pseudo = pseudo_head + ".psf"
|
|
305
|
+
# synth_block_filename = pseudo_head + ".synth"
|
|
306
|
+
# os.remove(name)
|
|
307
|
+
# shutil.copyfile(synth_pseudo, name)
|
|
308
|
+
# synth_block = read_vca_synth_block(
|
|
309
|
+
# synth_block_filename, species_number=species_number
|
|
310
|
+
# )
|
|
311
|
+
# synth_blocks.append(synth_block)
|
|
312
|
+
# else:
|
|
313
|
+
# synth_block = self.synthetic_atoms[symbol]
|
|
314
|
+
if symbol in self.synthetic_atoms:
|
|
279
315
|
synth_block = self.synthetic_atoms[symbol]
|
|
280
|
-
|
|
281
|
-
|
|
316
|
+
synth_blocks.append(synth_block)
|
|
282
317
|
|
|
283
318
|
if len(synth_blocks) > 0:
|
|
284
|
-
fd.write(format_fdf(
|
|
319
|
+
fd.write(format_fdf("SyntheticAtoms", list(synth_blocks)))
|
|
285
320
|
|
|
286
|
-
label =
|
|
287
|
-
string =
|
|
321
|
+
label = ".".join(np.array(name.split("."))[:-1])
|
|
322
|
+
string = " %d %d %s" % (species_number, atomic_number, label)
|
|
288
323
|
chemical_labels.append(string)
|
|
289
|
-
if isinstance(spec[
|
|
290
|
-
pao_basis.append(spec[
|
|
324
|
+
if isinstance(spec["basis_set"], PAOBasisBlock):
|
|
325
|
+
pao_basis.append(spec["basis_set"].script(label))
|
|
291
326
|
else:
|
|
292
|
-
basis_sizes.append((" " + label, spec[
|
|
293
|
-
fd.write((format_fdf(
|
|
294
|
-
fd.write(
|
|
295
|
-
fd.write((format_fdf(
|
|
296
|
-
fd.write((format_fdf(
|
|
297
|
-
fd.write(
|
|
298
|
-
|
|
327
|
+
basis_sizes.append((" " + label, spec["basis_set"]))
|
|
328
|
+
fd.write((format_fdf("ChemicalSpecieslabel", chemical_labels)))
|
|
329
|
+
fd.write("\n")
|
|
330
|
+
fd.write((format_fdf("PAO.Basis", pao_basis)))
|
|
331
|
+
fd.write((format_fdf("PAO.BasisSizes", basis_sizes)))
|
|
332
|
+
fd.write("\n")
|
|
299
333
|
|
|
300
334
|
def set_npt_elements(self):
|
|
301
335
|
if len(self.npt_elems) > 0:
|
|
302
336
|
npt_text = []
|
|
303
337
|
for name in self.npt_elems:
|
|
304
|
-
npt_text.append(
|
|
305
|
-
f"{name} non-perturbative ")
|
|
338
|
+
npt_text.append(f"{name} non-perturbative ")
|
|
306
339
|
# npt_text += "%endblock PAO.PolarizationScheme\n"
|
|
307
|
-
self[
|
|
340
|
+
self["fdf_arguments"].update({"PAO.PolarizationScheme": npt_text})
|
|
308
341
|
|
|
309
342
|
def set_synthetic_atoms(self):
|
|
310
343
|
print("setting syn")
|
|
311
|
-
nsyn=len(self.synthetic_atoms)
|
|
312
|
-
if
|
|
344
|
+
nsyn = len(self.synthetic_atoms)
|
|
345
|
+
if nsyn > 0:
|
|
313
346
|
syntext = []
|
|
314
|
-
#syntext.append(f"{nsyn}")
|
|
347
|
+
# syntext.append(f"{nsyn}")
|
|
315
348
|
for name, content in self.synthetic_atoms.items():
|
|
316
|
-
|
|
317
|
-
f"{self.elem_dict[name]}")
|
|
318
|
-
|
|
319
|
-
" ".join([str(x) for x in content[
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
self['fdf_arguments'].update({"SyntheticAtoms": syntext})
|
|
349
|
+
if name in self.elem_dict:
|
|
350
|
+
syntext.append(f"{self.elem_dict[name]}")
|
|
351
|
+
syntext.append(" ".join([str(x) for x in content[0]]))
|
|
352
|
+
syntext.append(" ".join([str(x) for x in content[1]]))
|
|
353
|
+
if len(syntext) > 0:
|
|
354
|
+
self["fdf_arguments"].update({"SyntheticAtoms": syntext})
|
|
323
355
|
|
|
324
|
-
#print("setting syn:", syntext)
|
|
356
|
+
# print("setting syn:", syntext)
|
|
325
357
|
|
|
326
358
|
def set_fdf_arguments(self, fdf_arguments):
|
|
327
|
-
self[
|
|
328
|
-
|
|
329
|
-
def set_mixer(
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
359
|
+
self["fdf_arguments"].update(fdf_arguments)
|
|
360
|
+
|
|
361
|
+
def set_mixer(
|
|
362
|
+
self,
|
|
363
|
+
method="pulay",
|
|
364
|
+
weight=0.05,
|
|
365
|
+
history=10,
|
|
366
|
+
restart=25,
|
|
367
|
+
restart_save=4,
|
|
368
|
+
linear_after=0,
|
|
369
|
+
linear_after_weight=0.1,
|
|
370
|
+
):
|
|
337
371
|
pass
|
|
338
372
|
|
|
339
373
|
def update_fdf_arguments(self, fdf_arguments):
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
def add_Hubbard_U(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
374
|
+
self["fdf_arguments"].update(fdf_arguments)
|
|
375
|
+
|
|
376
|
+
def add_Hubbard_U(
|
|
377
|
+
self,
|
|
378
|
+
specy,
|
|
379
|
+
n=3,
|
|
380
|
+
l=2, # noqa: E741
|
|
381
|
+
U=0,
|
|
382
|
+
J=0,
|
|
383
|
+
rc=0.0,
|
|
384
|
+
Fermi_cut=0.0,
|
|
385
|
+
scale_factor="0.95",
|
|
386
|
+
):
|
|
387
|
+
if "Udict" not in self.__dict__:
|
|
352
388
|
self.Udict = dict()
|
|
353
|
-
idx=self.elem_dict[specy]
|
|
354
|
-
specy_label=f"{specy}.{idx}"
|
|
389
|
+
idx = self.elem_dict[specy]
|
|
390
|
+
specy_label = f"{specy}.{idx}"
|
|
355
391
|
self.Udict[specy_label] = {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
392
|
+
"n": n,
|
|
393
|
+
"l": l,
|
|
394
|
+
"U": U,
|
|
395
|
+
"J": J,
|
|
396
|
+
"rc": rc,
|
|
397
|
+
"Fermi_cut": Fermi_cut,
|
|
398
|
+
"scale_factor": scale_factor,
|
|
363
399
|
}
|
|
364
400
|
self.set_Hubbard_U(self.Udict)
|
|
365
401
|
|
|
@@ -369,18 +405,19 @@ class MySiesta(Siesta):
|
|
|
369
405
|
"""
|
|
370
406
|
Ublock = []
|
|
371
407
|
for key, val in Udict.items():
|
|
372
|
-
Ublock.append(
|
|
373
|
-
if val[
|
|
374
|
-
Ublock.append(
|
|
408
|
+
Ublock.append(" %s %s " % (key, 1))
|
|
409
|
+
if val["n"] is not None:
|
|
410
|
+
Ublock.append(" n=%s %s" % (val["n"], val["l"]))
|
|
375
411
|
else:
|
|
376
|
-
Ublock.append(
|
|
377
|
-
Ublock.append(
|
|
378
|
-
if
|
|
379
|
-
Ublock.append(
|
|
380
|
-
Ublock.append(
|
|
412
|
+
Ublock.append("%s" % (val["l"]))
|
|
413
|
+
Ublock.append(" %s %s" % (val["U"], val["J"]))
|
|
414
|
+
if "rc" in val:
|
|
415
|
+
Ublock.append(" %s %s" % (val["rc"], val["Fermi_cut"]))
|
|
416
|
+
Ublock.append(" %s" % val["scale_factor"])
|
|
381
417
|
|
|
382
418
|
self.update_fdf_arguments(
|
|
383
|
-
{
|
|
419
|
+
{"LDAU.Proj": Ublock, "LDAU.ProjectorGenerationMethod": 2}
|
|
420
|
+
)
|
|
384
421
|
|
|
385
422
|
def set_Udict(self, Udict):
|
|
386
423
|
"""
|
|
@@ -400,47 +437,57 @@ class MySiesta(Siesta):
|
|
|
400
437
|
def relax(
|
|
401
438
|
self,
|
|
402
439
|
atoms,
|
|
403
|
-
TypeOfRun=
|
|
440
|
+
TypeOfRun="Broyden",
|
|
404
441
|
VariableCell=True,
|
|
405
442
|
ConstantVolume=False,
|
|
406
443
|
RelaxCellOnly=False,
|
|
407
444
|
MaxForceTol=0.001,
|
|
408
445
|
MaxStressTol=1,
|
|
409
446
|
NumCGSteps=40,
|
|
410
|
-
relaxed_file="relaxed.vasp"
|
|
447
|
+
relaxed_file="relaxed.vasp",
|
|
411
448
|
):
|
|
412
449
|
pbc = atoms.get_pbc()
|
|
413
450
|
initial_magnetic_moments = atoms.get_initial_magnetic_moments()
|
|
414
|
-
self.update_fdf_arguments(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
451
|
+
self.update_fdf_arguments(
|
|
452
|
+
{
|
|
453
|
+
"MD.TypeOfRun": TypeOfRun,
|
|
454
|
+
"MD.VariableCell": VariableCell,
|
|
455
|
+
"MD.ConstantVolume": ConstantVolume,
|
|
456
|
+
"MD.RelaxCellOnly": RelaxCellOnly,
|
|
457
|
+
"MD.MaxForceTol": "%s eV/Ang" % MaxForceTol,
|
|
458
|
+
"MD.MaxStressTol": "%s GPa" % MaxStressTol,
|
|
459
|
+
"MD.NumCGSteps": NumCGSteps,
|
|
460
|
+
}
|
|
461
|
+
)
|
|
423
462
|
self.calculate(atoms)
|
|
424
|
-
#self.read(self.prefix + '.XV')
|
|
425
|
-
self.atoms=read_xv(os.path.join(self.directory, self.prefix +
|
|
463
|
+
# self.read(self.prefix + '.XV')
|
|
464
|
+
self.atoms = read_xv(os.path.join(self.directory, self.prefix + ".XV"))
|
|
426
465
|
self.atoms.set_pbc(pbc)
|
|
427
466
|
self.atoms.set_initial_magnetic_moments(initial_magnetic_moments)
|
|
428
467
|
atoms = self.atoms
|
|
429
|
-
self.update_fdf_arguments(
|
|
430
|
-
|
|
431
|
-
|
|
468
|
+
self.update_fdf_arguments(
|
|
469
|
+
{
|
|
470
|
+
"MD.NumCGSteps": 0,
|
|
471
|
+
}
|
|
472
|
+
)
|
|
432
473
|
if relaxed_file is not None:
|
|
433
474
|
write(relaxed_file, atoms, vasp5=True, sort=False)
|
|
434
475
|
return self.atoms
|
|
435
476
|
|
|
436
|
-
def scf_calculation(self, atoms, dos=True, kpts=[7,7,7], **kwargs):
|
|
477
|
+
def scf_calculation(self, atoms, dos=True, kpts=[7, 7, 7], **kwargs):
|
|
437
478
|
if dos:
|
|
438
479
|
k1, k2, k3 = kpts
|
|
439
|
-
self.update_fdf_arguments(
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
480
|
+
self.update_fdf_arguments(
|
|
481
|
+
{
|
|
482
|
+
"WriteEigenvalues": ".true.",
|
|
483
|
+
"ProjectedDensityOfStates": ["-70.00 30.0 0.015 3000 eV"],
|
|
484
|
+
"PDOS.kgrid_Monkhorst_Pack": [
|
|
485
|
+
f"{k1} 0 0 0.0",
|
|
486
|
+
f"0 {k2} 0 0.0",
|
|
487
|
+
f"0 0 {k3} 0.0",
|
|
488
|
+
],
|
|
489
|
+
}
|
|
490
|
+
)
|
|
444
491
|
self.calculate(atoms, **kwargs)
|
|
445
492
|
|
|
446
493
|
def _write_structure(self, f, atoms):
|
|
@@ -451,23 +498,25 @@ class MySiesta(Siesta):
|
|
|
451
498
|
- atoms: An atoms object.
|
|
452
499
|
"""
|
|
453
500
|
cell = atoms.cell
|
|
454
|
-
f.write(
|
|
501
|
+
f.write("\n")
|
|
455
502
|
|
|
456
503
|
if cell.rank in [1, 2]:
|
|
457
|
-
raise ValueError(
|
|
458
|
-
|
|
504
|
+
raise ValueError(
|
|
505
|
+
"Expected 3D unit cell or no unit cell. You may "
|
|
506
|
+
"wish to add vacuum along some directions."
|
|
507
|
+
)
|
|
459
508
|
|
|
460
509
|
# Write lattice vectors
|
|
461
510
|
if np.any(cell):
|
|
462
|
-
f.write(format_fdf(
|
|
463
|
-
f.write(
|
|
511
|
+
f.write(format_fdf("LatticeConstant", "1.0 Ang"))
|
|
512
|
+
f.write("%block LatticeVectors\n")
|
|
464
513
|
for i in range(3):
|
|
465
514
|
for j in range(3):
|
|
466
|
-
s = (
|
|
515
|
+
s = (" %.15f" % cell[i, j]).rjust(16) + " "
|
|
467
516
|
f.write(s)
|
|
468
|
-
f.write(
|
|
469
|
-
f.write(
|
|
470
|
-
f.write(
|
|
517
|
+
f.write("\n")
|
|
518
|
+
f.write("%endblock LatticeVectors\n")
|
|
519
|
+
f.write("\n")
|
|
471
520
|
|
|
472
521
|
self._write_atomic_coordinates(f, atoms)
|
|
473
522
|
|
|
@@ -480,26 +529,26 @@ class MySiesta(Siesta):
|
|
|
480
529
|
# atoms object.
|
|
481
530
|
if magmoms is not None:
|
|
482
531
|
if len(magmoms) == 0:
|
|
483
|
-
f.write(
|
|
532
|
+
f.write("#Empty block forces ASE initialization.\n")
|
|
484
533
|
|
|
485
|
-
f.write(
|
|
534
|
+
f.write("%block DM.InitSpin\n")
|
|
486
535
|
if len(magmoms) != 0 and isinstance(magmoms[0], np.ndarray):
|
|
487
536
|
for n, Mcart in enumerate(magmoms):
|
|
488
537
|
M = cart2sph(Mcart)
|
|
489
538
|
if M[0] != 0:
|
|
490
|
-
f.write(
|
|
491
|
-
|
|
539
|
+
f.write(
|
|
540
|
+
" %d %.14f %.14f %.14f \n" % (n + 1, M[0], M[1], M[2])
|
|
541
|
+
)
|
|
492
542
|
elif len(magmoms) != 0 and isinstance(magmoms[0], float):
|
|
493
543
|
for n, M in enumerate(magmoms):
|
|
494
544
|
if M != 0:
|
|
495
|
-
f.write(
|
|
496
|
-
f.write(
|
|
497
|
-
f.write(
|
|
545
|
+
f.write(" %d %.14f \n" % (n + 1, M))
|
|
546
|
+
f.write("%endblock DM.InitSpin\n")
|
|
547
|
+
f.write("\n")
|
|
498
548
|
|
|
499
549
|
def my_read_results(self):
|
|
500
|
-
"""Read the results.
|
|
501
|
-
|
|
502
|
-
#self.read_number_of_grid_points()
|
|
550
|
+
"""Read the results."""
|
|
551
|
+
# self.read_number_of_grid_points()
|
|
503
552
|
self.read_energy()
|
|
504
553
|
self.read_forces_stress()
|
|
505
554
|
# self.read_eigenvalues()
|
TB2Jflows/siesta_basis.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
1
|
withf_basis = {
|
|
3
|
-
#"La": "5\nn=5 0 1\n3.889 \n1.0 \nn=6 0 2\n9.113 6.906 \n1.0 1.0 \nn=5 1 1\n4.548 \n1.0 \nn=6 1 1\n9.113 \n1.0 \nn=5 2 2\n7.55 5.398 \n1.0 1.0 \n",
|
|
2
|
+
# "La": "5\nn=5 0 1\n3.889 \n1.0 \nn=6 0 2\n9.113 6.906 \n1.0 1.0 \nn=5 1 1\n4.548 \n1.0 \nn=6 1 1\n9.113 \n1.0 \nn=5 2 2\n7.55 5.398 \n1.0 1.0 \n",
|
|
4
3
|
"Ce": "6\nn=5 0 1\n3.805 \n1.0 \nn=6 0 2\n9.041 6.837 \n1.0 1.0 \nn=5 1 1\n4.458 \n1.0 \nn=6 1 1\n9.041 \n1.0 \nn=5 2 2\n7.489 5.339 \n1.0 1.0 \nn=4 3 2\n4.114 2.494 \n1.0 1.0 \n",
|
|
5
4
|
"Pr": "6\nn=5 0 1\n3.737 \n1.0 \nn=6 0 2\n8.879 6.702 \n1.0 1.0 \nn=5 1 1\n4.387 \n1.0 \nn=6 1 1\n8.879 \n1.0 \nn=5 2 2\n7.46 5.307 \n1.0 1.0 \nn=4 3 2\n3.944 2.369 \n1.0 1.0 \n",
|
|
6
5
|
"Nd": "6\nn=5 0 1\n3.67 \n1.0 \nn=6 0 2\n8.808 6.641 \n1.0 1.0 \nn=5 1 1\n4.317 \n1.0 \nn=6 1 1\n8.808 \n1.0 \nn=5 2 2\n7.445 5.285 \n1.0 1.0 \nn=4 3 2\n3.805 2.271 \n1.0 1.0 \n",
|
|
@@ -13,11 +12,11 @@ withf_basis = {
|
|
|
13
12
|
"Ho": "6\nn=5 0 1\n3.293 \n1.0 \nn=6 0 2\n8.179 6.093 \n1.0 1.0 \nn=5 1 1\n3.944 \n1.0 \nn=6 1 1\n8.179 \n1.0 \nn=5 2 2\n7.61 5.349 \n1.0 1.0 \nn=4 3 2\n3.254 1.86 \n1.0 1.0 \n",
|
|
14
13
|
"Er": "6\nn=5 0 1\n3.247 \n1.0 \nn=6 0 2\n8.049 5.972 \n1.0 1.0 \nn=5 1 1\n3.897 \n1.0 \nn=6 1 1\n8.049 \n1.0 \nn=5 2 2\n7.656 5.387 \n1.0 1.0 \nn=4 3 2\n3.208 1.821 \n1.0 1.0 \n",
|
|
15
14
|
"Tm": "6\nn=5 0 1\n3.202 \n1.0 \nn=6 0 2\n7.921 5.872 \n1.0 1.0 \nn=5 1 1\n3.858 \n1.0 \nn=6 1 1\n7.921 \n1.0 \nn=5 2 2\n7.718 5.419 \n1.0 1.0 \nn=4 3 2\n3.163 1.785 \n1.0 1.0 \n",
|
|
16
|
-
"Yb": "6\nn=5 0 1\n3.163 \n1.0 \nn=6 0 2\n7.796 5.761 \n1.0 1.0 \nn=5 1 1\n3.812 \n1.0 \nn=6 1 1\n7.796 \n1.0 \nn=5 2 2\n7.78 5.468 \n1.0 1.0 \nn=4 3 2\n3.119 1.753 \n1.0 1.0 \n"
|
|
15
|
+
"Yb": "6\nn=5 0 1\n3.163 \n1.0 \nn=6 0 2\n7.796 5.761 \n1.0 1.0 \nn=5 1 1\n3.812 \n1.0 \nn=6 1 1\n7.796 \n1.0 \nn=5 2 2\n7.78 5.468 \n1.0 1.0 \nn=4 3 2\n3.119 1.753 \n1.0 1.0 \n",
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
fincore_basis ={
|
|
20
|
-
#"La": "4\nn=5 0 1\n3.889 \n1.0 \nn=6 0 2\n9.113 6.906 \n1.0 1.0 \nn=5 1 1\n4.548 \n1.0 \nn=6 1 1\n9.113 \n1.0 \nn=5 2 2\n7.55 5.398 \n1.0 1.0 \n",
|
|
18
|
+
fincore_basis = {
|
|
19
|
+
# "La": "4\nn=5 0 1\n3.889 \n1.0 \nn=6 0 2\n9.113 6.906 \n1.0 1.0 \nn=5 1 1\n4.548 \n1.0 \nn=6 1 1\n9.113 \n1.0 \nn=5 2 2\n7.55 5.398 \n1.0 1.0 \n",
|
|
21
20
|
"Ce": "5\nn=5 0 1\n3.805 \n1.0 \nn=6 0 2\n8.968 6.776 \n1.0 1.0 \nn=5 1 1\n4.458 \n1.0 \nn=6 1 1\n8.968 \n1.0 \nn=5 2 2\n7.489 5.339 \n1.0 1.0 \n",
|
|
22
21
|
"Pr": "5\nn=5 0 1\n3.737 \n1.0 \nn=6 0 2\n8.791 6.621 \n1.0 1.0 \nn=5 1 1\n4.387 \n1.0 \nn=6 1 1\n8.791 \n1.0 \nn=5 2 2\n7.46 5.307 \n1.0 1.0 \n",
|
|
23
22
|
"Nd": "5\nn=5 0 1\n3.67 \n1.0 \nn=6 0 2\n8.634 6.49 \n1.0 1.0 \nn=5 1 1\n4.317 \n1.0 \nn=6 1 1\n8.634 \n1.0 \nn=5 2 2\n7.445 5.285 \n1.0 1.0 \n",
|
|
@@ -30,21 +29,19 @@ fincore_basis ={
|
|
|
30
29
|
"Ho": "5\nn=5 0 1\n3.293 \n1.0 \nn=6 0 2\n7.827 5.79 \n1.0 1.0 \nn=5 1 1\n3.944 \n1.0 \nn=6 1 1\n7.827 \n1.0 \nn=5 2 2\n7.61 5.349 \n1.0 1.0 \n",
|
|
31
30
|
"Er": "5\nn=5 0 1\n3.247 \n1.0 \nn=6 0 2\n7.626 5.624 \n1.0 1.0 \nn=5 1 1\n3.897 \n1.0 \nn=6 1 1\n7.626 \n1.0 \nn=5 2 2\n7.656 5.387 \n1.0 1.0 \n",
|
|
32
31
|
"Tm": "5\nn=5 0 1\n3.202 \n1.0 \nn=6 0 2\n7.474 5.49 \n1.0 1.0 \nn=5 1 1\n3.858 \n1.0 \nn=6 1 1\n7.474 \n1.0 \nn=5 2 2\n7.718 5.419 \n1.0 1.0 \n",
|
|
33
|
-
"Yb": "5\nn=5 0 1\n3.163 \n1.0 \nn=6 0 2\n7.312 5.36 \n1.0 1.0 \nn=5 1 1\n3.812 \n1.0 \nn=6 1 1\n7.312 \n1.0 \nn=5 2 2\n7.78 5.468 \n1.0 1.0 \n"
|
|
32
|
+
"Yb": "5\nn=5 0 1\n3.163 \n1.0 \nn=6 0 2\n7.312 5.36 \n1.0 1.0 \nn=5 1 1\n3.812 \n1.0 \nn=6 1 1\n7.312 \n1.0 \nn=5 2 2\n7.78 5.468 \n1.0 1.0 \n",
|
|
34
33
|
}
|
|
35
34
|
|
|
35
|
+
|
|
36
36
|
def get_basis(element, fincore=False):
|
|
37
37
|
if not fincore:
|
|
38
38
|
return withf_basis[element]
|
|
39
|
-
elif basis == "fincore":
|
|
40
|
-
return fincore_basis[element]
|
|
41
39
|
else:
|
|
42
|
-
|
|
40
|
+
return fincore_basis[element]
|
|
41
|
+
|
|
43
42
|
|
|
44
43
|
if __name__ == "__main__":
|
|
45
44
|
print(get_basis("La"))
|
|
46
45
|
print(get_basis("La", fincore=True))
|
|
47
46
|
print(get_basis("Ce"))
|
|
48
47
|
print(get_basis("Ce", fincore=True))
|
|
49
|
-
|
|
50
|
-
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Spin-phonon coupling calculation workflow using SIESTA and TB2J.
|
|
4
|
+
|
|
5
|
+
This module generates distorted structures from phonon modes and computes
|
|
6
|
+
exchange parameters for each structure.
|
|
7
|
+
|
|
8
|
+
Purpose:
|
|
9
|
+
Compute exchange parameters as a function of phonon mode amplitude
|
|
10
|
+
to study spin-phonon coupling.
|
|
11
|
+
|
|
12
|
+
How to run:
|
|
13
|
+
python -m TB2Jflows.siesta_spinphonon
|
|
14
|
+
|
|
15
|
+
Expected behavior:
|
|
16
|
+
For each amplitude, generates a distorted structure and runs SIESTA+TB2J
|
|
17
|
+
to compute exchange parameters.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Callable, List, Optional, Union
|
|
22
|
+
|
|
23
|
+
# Import from phononkit
|
|
24
|
+
from phononkit import generate_structure_for_mode, load_from_phonopy_yaml
|
|
25
|
+
|
|
26
|
+
from TB2Jflows import auto_siesta_TB2J
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def compute_spinphonon_coupling(
|
|
30
|
+
phonon_yaml: str,
|
|
31
|
+
qpoint: list,
|
|
32
|
+
mode_index: int,
|
|
33
|
+
amplitudes: list,
|
|
34
|
+
supercell: Union[List[int], List[List[int]], None] = None,
|
|
35
|
+
base_path: str = "./spinphonon_results",
|
|
36
|
+
spin: str = "collinear",
|
|
37
|
+
magnetic_elements: Optional[List[str]] = None,
|
|
38
|
+
Udict: Optional[dict] = None,
|
|
39
|
+
xc: str = "PBE",
|
|
40
|
+
kmesh: Optional[List[int]] = None,
|
|
41
|
+
relax: bool = False,
|
|
42
|
+
scf: bool = True,
|
|
43
|
+
TB2J: bool = True,
|
|
44
|
+
rotate_type: str = "structure",
|
|
45
|
+
fincore: bool = True,
|
|
46
|
+
siesta_kwargs: Optional[dict] = None,
|
|
47
|
+
TB2J_kwargs: Optional[dict] = None,
|
|
48
|
+
fdf_kwargs: Optional[dict] = None,
|
|
49
|
+
prepare_atoms_callback: Optional[Callable] = None,
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Compute exchange parameters for phonon-distorted structures.
|
|
53
|
+
|
|
54
|
+
This function generates distorted structures from a phonon mode at different
|
|
55
|
+
amplitudes and computes exchange parameters using SIESTA and TB2J for each.
|
|
56
|
+
|
|
57
|
+
Parameters:
|
|
58
|
+
phonon_yaml: Path to phonopy YAML file containing phonon data
|
|
59
|
+
qpoint: Q-point coordinates [qx, qy, qz] in reduced coordinates
|
|
60
|
+
(e.g., [0, 0, 0] for Gamma point)
|
|
61
|
+
mode_index: Index of the phonon mode at the specified q-point (0-based)
|
|
62
|
+
amplitudes: List of displacement amplitudes in Angstroms
|
|
63
|
+
(e.g., [0.1, 0.2, 0.3, 0.4, 0.5])
|
|
64
|
+
supercell: Supercell specification (optional):
|
|
65
|
+
- 3-vector [n1, n2, n3] for diagonal matrix (e.g., [2, 2, 2])
|
|
66
|
+
- Full 3x3 matrix [[n1, 0, 0], [0, n2, 0], [0, 0, n3]]
|
|
67
|
+
- If None, uses primitive cell
|
|
68
|
+
base_path: Base directory for output files
|
|
69
|
+
spin: Spin type ('collinear', 'noncollinear', 'spinorbit')
|
|
70
|
+
magnetic_elements: List of magnetic element symbols (if None, auto-detect)
|
|
71
|
+
Udict: Dictionary of Hubbard U parameters {element: U_value}
|
|
72
|
+
xc: Exchange-correlation functional
|
|
73
|
+
kmesh: K-point mesh [nk1, nk2, nk3] (if None, auto-generate)
|
|
74
|
+
relax: Whether to perform structural relaxation
|
|
75
|
+
scf: Whether to run SCF calculation
|
|
76
|
+
TB2J: Whether to run TB2J calculation
|
|
77
|
+
rotate_type: Type of rotation for non-collinear calculations
|
|
78
|
+
fincore: Whether to use frozen core approximation
|
|
79
|
+
siesta_kwargs: Additional SIESTA parameters
|
|
80
|
+
TB2J_kwargs: Additional TB2J parameters
|
|
81
|
+
fdf_kwargs: Additional FDF file parameters
|
|
82
|
+
prepare_atoms_callback: Optional callback function to modify atoms
|
|
83
|
+
before calculation (e.g., set magnetic moments)
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
dict: Dictionary mapping amplitudes to their calculation directories
|
|
87
|
+
|
|
88
|
+
Examples:
|
|
89
|
+
>>> # Compute exchange for Gamma-point mode 3 at various amplitudes
|
|
90
|
+
>>> results = compute_spinphonon_coupling(
|
|
91
|
+
... phonon_yaml='phonopy.yaml',
|
|
92
|
+
... qpoint=[0, 0, 0],
|
|
93
|
+
... mode_index=3,
|
|
94
|
+
... amplitudes=[0.1, 0.2, 0.3, 0.4, 0.5],
|
|
95
|
+
... supercell=[2, 2, 2],
|
|
96
|
+
... magnetic_elements=['Fe', 'Co'],
|
|
97
|
+
... Udict={'Fe': 4.0, 'Co': 4.5}
|
|
98
|
+
... )
|
|
99
|
+
|
|
100
|
+
>>> # Use results
|
|
101
|
+
>>> for amp, calc_dir in results.items():
|
|
102
|
+
... print(f"Amplitude {amp} Å: {calc_dir}")
|
|
103
|
+
"""
|
|
104
|
+
# Set defaults
|
|
105
|
+
if Udict is None:
|
|
106
|
+
Udict = {}
|
|
107
|
+
if siesta_kwargs is None:
|
|
108
|
+
siesta_kwargs = {}
|
|
109
|
+
if TB2J_kwargs is None:
|
|
110
|
+
TB2J_kwargs = {}
|
|
111
|
+
if fdf_kwargs is None:
|
|
112
|
+
fdf_kwargs = {}
|
|
113
|
+
|
|
114
|
+
# Create base directory
|
|
115
|
+
path_obj = Path(base_path)
|
|
116
|
+
path_obj.mkdir(parents=True, exist_ok=True)
|
|
117
|
+
|
|
118
|
+
# Load phonon data to get the structure
|
|
119
|
+
print(f"Loading phonon data from: {phonon_yaml}")
|
|
120
|
+
structure, modes = load_from_phonopy_yaml(phonon_yaml)
|
|
121
|
+
|
|
122
|
+
# Auto-detect magnetic elements if not provided
|
|
123
|
+
if magnetic_elements is None:
|
|
124
|
+
# For now, assume all unique elements with initial magnetic moments
|
|
125
|
+
# User should provide this explicitly
|
|
126
|
+
magnetic_elements = list(set(structure.get_chemical_symbols()))
|
|
127
|
+
print(f"Warning: Auto-detected magnetic elements: {magnetic_elements}")
|
|
128
|
+
print("Consider providing magnetic_elements explicitly")
|
|
129
|
+
|
|
130
|
+
# Dictionary to store results
|
|
131
|
+
results = {}
|
|
132
|
+
|
|
133
|
+
# Loop over amplitudes
|
|
134
|
+
for amplitude in amplitudes:
|
|
135
|
+
print("=" * 70)
|
|
136
|
+
print(f"Processing amplitude: {amplitude} Å")
|
|
137
|
+
print("=" * 70)
|
|
138
|
+
|
|
139
|
+
# Generate distorted structure
|
|
140
|
+
print("Generating distorted structure...")
|
|
141
|
+
print(f" Q-point: {qpoint}")
|
|
142
|
+
print(f" Mode index: {mode_index}")
|
|
143
|
+
print(f" Amplitude: {amplitude} Å")
|
|
144
|
+
if supercell is not None:
|
|
145
|
+
print(f" Supercell: {supercell}")
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
distorted_atoms = generate_structure_for_mode(
|
|
149
|
+
yaml_path=phonon_yaml,
|
|
150
|
+
qpoint=qpoint,
|
|
151
|
+
mode_index=mode_index,
|
|
152
|
+
amplitude=amplitude,
|
|
153
|
+
supercell=supercell,
|
|
154
|
+
)
|
|
155
|
+
print(f" Generated structure with {len(distorted_atoms)} atoms")
|
|
156
|
+
|
|
157
|
+
# Apply callback if provided
|
|
158
|
+
if prepare_atoms_callback is not None:
|
|
159
|
+
distorted_atoms = prepare_atoms_callback(distorted_atoms)
|
|
160
|
+
|
|
161
|
+
except Exception as e:
|
|
162
|
+
print(f"Error generating structure for amplitude {amplitude}: {e}")
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
# Create calculation directory
|
|
166
|
+
calc_dir = path_obj / f"amplitude_{amplitude:.4f}"
|
|
167
|
+
calc_dir.mkdir(parents=True, exist_ok=True)
|
|
168
|
+
|
|
169
|
+
print(f"Running SIESTA+TB2J calculation in: {calc_dir}")
|
|
170
|
+
|
|
171
|
+
# Run auto_siesta_TB2J
|
|
172
|
+
try:
|
|
173
|
+
auto_siesta_TB2J(
|
|
174
|
+
path=str(calc_dir),
|
|
175
|
+
atoms=distorted_atoms,
|
|
176
|
+
spin=spin,
|
|
177
|
+
elems=magnetic_elements,
|
|
178
|
+
Udict=Udict,
|
|
179
|
+
xc=xc,
|
|
180
|
+
kmesh=kmesh,
|
|
181
|
+
split_soc=False,
|
|
182
|
+
relax=relax,
|
|
183
|
+
scf=scf,
|
|
184
|
+
TB2J=TB2J,
|
|
185
|
+
rotate_type=rotate_type,
|
|
186
|
+
fincore=fincore,
|
|
187
|
+
siesta_kwargs=siesta_kwargs,
|
|
188
|
+
TB2J_kwargs=TB2J_kwargs,
|
|
189
|
+
fdf_kwargs=fdf_kwargs,
|
|
190
|
+
)
|
|
191
|
+
results[amplitude] = str(calc_dir)
|
|
192
|
+
print(f"✓ Completed calculation for amplitude {amplitude} Å")
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print(f"✗ Error running calculation for amplitude {amplitude}: {e}")
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
# Summary
|
|
198
|
+
print("\n" + "=" * 70)
|
|
199
|
+
print("Spin-Phonon Coupling Calculation Summary")
|
|
200
|
+
print("=" * 70)
|
|
201
|
+
print(f"Total amplitudes processed: {len(results)}/{len(amplitudes)}")
|
|
202
|
+
print(f"Results stored in: {path_obj}")
|
|
203
|
+
print("\nCompleted calculations:")
|
|
204
|
+
for amp, calc_dir in sorted(results.items()):
|
|
205
|
+
print(f" {amp:.4f} Å → {calc_dir}")
|
|
206
|
+
|
|
207
|
+
return results
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def main():
|
|
211
|
+
"""
|
|
212
|
+
Example usage of compute_spinphonon_coupling.
|
|
213
|
+
|
|
214
|
+
This example shows how to compute exchange parameters for a phonon mode
|
|
215
|
+
at multiple amplitudes. Users should modify the parameters below for
|
|
216
|
+
their specific calculation.
|
|
217
|
+
"""
|
|
218
|
+
# Example parameters - MODIFY THESE FOR YOUR CALCULATION
|
|
219
|
+
phonon_yaml = "phonopy.yaml"
|
|
220
|
+
qpoint = [0, 0, 0] # Gamma point
|
|
221
|
+
mode_index = 3 # Fourth mode (0-indexed)
|
|
222
|
+
amplitudes = [0.1, 0.2, 0.3, 0.4, 0.5] # Amplitudes in Angstroms
|
|
223
|
+
supercell = [2, 2, 2] # 2x2x2 supercell
|
|
224
|
+
magnetic_elements = ["Fe"] # Magnetic elements
|
|
225
|
+
Udict = {"Fe": 4.0} # Hubbard U parameters
|
|
226
|
+
|
|
227
|
+
compute_spinphonon_coupling(
|
|
228
|
+
phonon_yaml=phonon_yaml,
|
|
229
|
+
qpoint=qpoint,
|
|
230
|
+
mode_index=mode_index,
|
|
231
|
+
amplitudes=amplitudes,
|
|
232
|
+
supercell=supercell,
|
|
233
|
+
base_path="./spinphonon_results",
|
|
234
|
+
spin="collinear",
|
|
235
|
+
magnetic_elements=magnetic_elements,
|
|
236
|
+
Udict=Udict,
|
|
237
|
+
xc="PBE",
|
|
238
|
+
kmesh=None, # Auto-generate
|
|
239
|
+
relax=False,
|
|
240
|
+
scf=True,
|
|
241
|
+
TB2J=True,
|
|
242
|
+
rotate_type="structure",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
print("\n" + "=" * 70)
|
|
246
|
+
print("Calculation complete!")
|
|
247
|
+
print("=" * 70)
|
|
248
|
+
print("Next steps:")
|
|
249
|
+
print("1. Check the results in the output directories")
|
|
250
|
+
print("2. Extract exchange parameters from TB2J output files")
|
|
251
|
+
print("3. Plot exchange parameters vs. amplitude to study spin-phonon coupling")
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
if __name__ == "__main__":
|
|
255
|
+
main()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
TB2Jflows/__init__.py,sha256=hRwR95ChyiFkUTX2djY2hQI9ph7x2YIJxiOehvxe79I,242
|
|
2
|
+
TB2Jflows/ase_siesta.py,sha256=d8Li-zwUGlulT0haEIKLSSgsveMU6YzV0thrFnDhV2U,14359
|
|
3
|
+
TB2Jflows/auto_siesta_TB2J.py,sha256=c5dviznEbXyO3BJbh-LNBlC9efT7YbdC7ydWxyCJZ5E,3113
|
|
4
|
+
TB2Jflows/find_pp.py,sha256=Od8Zb-Rd0kDC6ALBa39knYFNVKfNxhZx9OLocOS8YUA,2435
|
|
5
|
+
TB2Jflows/mysiesta.py,sha256=IUgfNu_X9rmX_CkqNsHrwFk3Hp8o5Wlp7Fd19PaCZPw,19518
|
|
6
|
+
TB2Jflows/run_abacus.py,sha256=96tZ2n02FFLl8IZf4W_4TvkCcsd6oS1FVEIqhZpY76o,2692
|
|
7
|
+
TB2Jflows/siesta_basis.py,sha256=ysGZUfjwuoOGImj0k-pDicKKG_o6Me0hzJqf5p5x9MQ,5319
|
|
8
|
+
TB2Jflows/siesta_spinphonon.py,sha256=jgTTtyWoJMnhyEJdWMnJ7pjxc6EM_aKEeu_-EgbtUFs,9037
|
|
9
|
+
tb2jflows-0.2.0.dist-info/METADATA,sha256=3Sy89-TX_nkmlZOxbCekV4sVrreG5q-cl9iNm1iX6Ho,2170
|
|
10
|
+
tb2jflows-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
11
|
+
tb2jflows-0.2.0.dist-info/licenses/LICENSE,sha256=KNu68sa-XR_2jZJKhDcSnxoNve8jtHgkw_w9PjP1YOk,1315
|
|
12
|
+
tb2jflows-0.2.0.dist-info/RECORD,,
|
tb2jflows-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
TB2Jflows/__init__.py,sha256=0aRVj9v-u64LxJl7Cyxcr4YUjpeMkBGPuv-nJbxbHOg,100
|
|
2
|
-
TB2Jflows/ase_siesta.py,sha256=1b6j0dFNnXCeLnCTWoLAJ158JQul8UcORIttGOhNydw,14366
|
|
3
|
-
TB2Jflows/auto_siesta_TB2J.py,sha256=c5dviznEbXyO3BJbh-LNBlC9efT7YbdC7ydWxyCJZ5E,3113
|
|
4
|
-
TB2Jflows/find_pp.py,sha256=iTqxLPuR88Jsuh4lc7hPEzFczhoOB0x6K5MWqYTEeiQ,2134
|
|
5
|
-
TB2Jflows/mysiesta.py,sha256=DV9dnYibrpPzegKy_MvoMOOrmoom2NPKblhVNBUZkeg,18943
|
|
6
|
-
TB2Jflows/run_abacus.py,sha256=96tZ2n02FFLl8IZf4W_4TvkCcsd6oS1FVEIqhZpY76o,2692
|
|
7
|
-
TB2Jflows/siesta_basis.py,sha256=UIO0VF_558jIK6kGctqEwHyyV_t7KEWGOtv4Obw4fyg,5408
|
|
8
|
-
tb2jflows-0.1.0.dist-info/METADATA,sha256=mnUu8VXO3Dndka3rbgqWJ0hiWxOXmWyF9AjauzpVQZk,2170
|
|
9
|
-
tb2jflows-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
10
|
-
tb2jflows-0.1.0.dist-info/licenses/LICENSE,sha256=KNu68sa-XR_2jZJKhDcSnxoNve8jtHgkw_w9PjP1YOk,1315
|
|
11
|
-
tb2jflows-0.1.0.dist-info/RECORD,,
|
|
File without changes
|