Skip to content

Commit f5ce135

Browse files
committed
Add parser for seedname_tb.dat
1 parent 9febeb9 commit f5ce135

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

examples/silicon/read_tb.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env python
2+
3+
# (c) 2015-2018, ETH Zurich, Institut fuer Theoretische Physik
4+
# Author: Dominik Gresch <[email protected]>
5+
6+
import os
7+
import shutil
8+
import subprocess
9+
import numpy as np
10+
import tbmodels as tb
11+
import matplotlib.pyplot as plt
12+
13+
if __name__ == "__main__":
14+
WANNIER90_COMMAND = os.path.expanduser("~/git/wannier90/wannier90.x")
15+
BUILD_DIR = "./build"
16+
17+
# shutil.rmtree(BUILD_DIR, ignore_errors=True)
18+
# shutil.copytree("./input", BUILD_DIR)
19+
# subprocess.call([WANNIER90_COMMAND, "silicon"], cwd=BUILD_DIR)
20+
21+
# model = tb.Model.from_wannier_folder(BUILD_DIR, prefix="silicon")
22+
# print(model)
23+
# print(model.hop[(2, 1, -3)])
24+
25+
BUILD_DIR = "./build2"
26+
model = tb.Model.from_wannier_tb_file(
27+
tb_file=f'{BUILD_DIR}/silicon_tb.dat',
28+
wsvec_file=f'{BUILD_DIR}/silicon_wsvec.dat'
29+
)
30+
print(model)
31+
# print(model.hop[(2, 1, -3)])
32+
# exit()
33+
34+
theta = 37 / 180 * np.pi
35+
phi = 43 / 180 * np.pi
36+
rlist = np.linspace(0, 2, 20)
37+
klist = [[r*np.sin(theta)*np.cos(phi), r*np.sin(theta)*np.sin(phi), r*np.cos(theta)] for r in rlist]
38+
39+
eigvals = model.eigenval(klist)
40+
41+
plt.plot(eigvals)
42+
plt.show()

tbmodels/_tb_model.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,57 @@ def _mat_to_hr(R, mat):
531531
)
532532
return lines
533533

534+
@staticmethod
535+
def _read_tb(iterator, ignore_orbital_order=False):
536+
"""Reads the content of a seedname_tb.dat file"""
537+
next(iterator) # skip comment line
538+
539+
lattice = np.zeros((3, 3))
540+
for i in range(3):
541+
lattice[i, :] = np.fromstring(next(iterator), sep=' ')
542+
543+
num_wann = int(next(iterator))
544+
545+
nrpts = int(next(iterator))
546+
547+
# degeneracy of Wigner-Seitz grid points, 15 entries per line
548+
deg_pts = []
549+
# order in zip important because else the next data element is consumed
550+
for _, line in zip(range(int(np.ceil(nrpts / 15))), iterator):
551+
deg_pts.extend(int(x) for x in line.split())
552+
assert len(deg_pts) == nrpts
553+
554+
# <0n|H|Rm>
555+
hop_list = []
556+
for ir in range(nrpts):
557+
next(iterator) # skip empty
558+
r_vec = [int(_) for _ in next(iterator).strip().split()]
559+
for j in range(num_wann):
560+
for i in range(num_wann):
561+
line = next(iterator).strip().split()
562+
iw, jw = [int(_) for _ in line[:2]]
563+
if not ignore_orbital_order and (iw != i + 1 or jw != j + 1):
564+
raise ValueError(f"Inconsistent orbital numbers in line '{line}'")
565+
ham = (float(line[2]) + 1j * float(line[3])) / deg_pts[ir]
566+
hop_list.append([ham, i, j, r_vec])
567+
568+
# <0n|r|Rm>
569+
r_list = []
570+
for ir in range(nrpts):
571+
next(iterator) # skip empty
572+
r_vec = [int(_) for _ in next(iterator).strip().split()]
573+
for j in range(num_wann):
574+
for i in range(num_wann):
575+
line = next(iterator).strip().split()
576+
iw, jw = [int(_) for _ in line[:2]]
577+
if not ignore_orbital_order and (iw != i + 1 or jw != j + 1):
578+
raise ValueError(f"Inconsistent orbital numbers in line '{line}'")
579+
r = np.array([float(_) for _ in line[2:]])
580+
r = r[::2] + 1j * r[1::2]
581+
r_list.append([r, i, j, r_vec])
582+
583+
return lattice, num_wann, nrpts, deg_pts, hop_list, r_list
584+
534585
@classmethod
535586
def from_wannier_folder(
536587
cls, folder: str = ".", prefix: str = "wannier", **kwargs
@@ -717,6 +768,98 @@ def remap_hoppings(hop_entries):
717768

718769
return cls.from_hop_list(size=num_wann, hop_list=hop_entries, **kwargs)
719770

771+
@classmethod # noqa: MC0001
772+
def from_wannier_tb_file( # pylint: disable=too-many-locals
773+
cls,
774+
*,
775+
tb_file: str,
776+
wsvec_file: str,
777+
**kwargs,
778+
) -> Model:
779+
"""
780+
Create a :class:`.Model` instance from Wannier90 output files.
781+
782+
Parameters
783+
----------
784+
tb_file :
785+
Path of the ``*_tb.dat`` file. Together with the
786+
``*_wsvec.dat`` file, this determines the hopping terms.
787+
wsvec_file :
788+
Path of the ``*_wsvec.dat`` file. This file determines the
789+
remapping of hopping terms when ``use_ws_distance`` is used
790+
in the Wannier90 calculation.
791+
kwargs :
792+
:class:`.Model` keyword arguments.
793+
"""
794+
795+
if "uc" in kwargs:
796+
raise ValueError(
797+
"Ambiguous unit cell: It can be given either via 'uc' or the 'tb_file' keywords, but not both."
798+
)
799+
if "pos" in kwargs:
800+
raise ValueError(
801+
"Ambiguous orbital positions: The positions can be given either via the 'pos' or the 'xyz_file' keywords, but not both."
802+
)
803+
804+
with open(tb_file) as f:
805+
lattice, num_wann, nrpts, deg_pts, hop_list, r_list = cls._read_tb(f)
806+
807+
kwargs["uc"] = lattice
808+
809+
def get_centers(r_list: list) -> list:
810+
centers = [None for _ in range(num_wann)]
811+
for r, i, j, r_vec in r_list:
812+
if r_vec != [0, 0, 0]:
813+
continue
814+
if i != j:
815+
continue
816+
r = np.array(r)
817+
if not np.allclose(np.abs(r.imag), 0):
818+
raise ValueError(f'Center should be real: WF {i+1}, center = {r}')
819+
centers[i] = r.real
820+
return centers
821+
822+
pos_cartesian: ty.Union[
823+
ty.List[npt.NDArray[np.float_]], npt.NDArray[np.float_]
824+
] = get_centers(r_list)
825+
826+
kwargs["pos"] = la.solve(kwargs["uc"].T, np.array(pos_cartesian).T).T
827+
828+
# hop_entries = (hop for hop in hop_entries if abs(hop[0]) > h_cutoff)
829+
hop_entries = hop_list
830+
831+
with open(wsvec_file) as f:
832+
wsvec_generator = cls._async_parse(
833+
cls._read_wsvec(f), chunksize=num_wann
834+
)
835+
836+
def remap_hoppings(hop_entries):
837+
for t, orbital_1, orbital_2, R in hop_entries:
838+
# Step _async_parse to where it accepts
839+
# a new key.
840+
# The _async_parse does not raise StopIteration
841+
next( # pylint: disable=stop-iteration-return
842+
wsvec_generator
843+
)
844+
T_list = wsvec_generator.send(
845+
(orbital_1, orbital_2, tuple(R))
846+
)
847+
N = len(T_list)
848+
for T in T_list:
849+
# not using numpy here increases performance
850+
yield (
851+
t / N,
852+
orbital_1,
853+
orbital_2,
854+
tuple(r + t for r, t in zip(R, T)),
855+
)
856+
857+
hop_entries = remap_hoppings(hop_entries)
858+
859+
return cls.from_hop_list(
860+
size=num_wann, hop_list=hop_entries, **kwargs
861+
)
862+
720863
@staticmethod
721864
def _async_parse(iterator, chunksize=1):
722865
"""

0 commit comments

Comments
 (0)