Skip to content

Commit 808ff4e

Browse files
committed
Adding parsers for wavefunctions and U matrices
A parser for the _u.mat and _u_dis.mat files has been added. Moreover, also a parser for wavefunctions (UNKNNNNN.N files) has been added, but also in the (non-default) case where they are printed in formatted format, for now. Tests are added (skipping U matrices for Wannier 2.x where this was not implemented) and CLI is expanded as well with two new commands.
1 parent fec57cb commit 808ff4e

File tree

9 files changed

+185
-3
lines changed

9 files changed

+185
-3
lines changed

docs/api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
::: wannier90io.read_amn
77
::: wannier90io.write_amn
88
::: wannier90io.read_chk
9+
::: wannier90io.read_u
10+
::: wannier90io.read_unk_formatted
911
::: wannier90io.read_eig
1012
::: wannier90io.write_eig
1113
::: wannier90io.read_mmn

src/wannier90io/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@
44
from ._eig import *
55
from ._mmn import *
66
from ._nnkp import *
7+
from ._u import *
8+
from ._unk import *
79
from ._win import *
810
from ._wout import *

src/wannier90io/__main__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,26 @@ def info_chk(args):
119119
print(chk['have_disentangled'])
120120

121121

122+
def info_u(args):
123+
with args.file:
124+
kpoints, u_matrices = w90io.read_u(args.file)
125+
126+
print(f'Nk = {u_matrices.shape[0]}')
127+
print(f'Nb = {u_matrices.shape[1]}')
128+
print(f'Nw = {u_matrices.shape[2]}')
129+
130+
131+
def info_unk_formatted(args):
132+
with args.file:
133+
ik, wvfn = w90io.read_unk_formatted(args.file)
134+
135+
print(f'ik = {ik}')
136+
print(f'ngx = {wvfn.shape[0]}')
137+
print(f'ngy = {wvfn.shape[1]}')
138+
print(f'ngz = {wvfn.shape[2]}')
139+
print(f'Nb = {wvfn.shape[3]}')
140+
141+
122142
def main():
123143
parser = argparse.ArgumentParser()
124144
subparsers = parser.add_subparsers(dest='subparser', required=True)
@@ -157,6 +177,12 @@ def main():
157177
#
158178
parser_info_chk = subparsers.add_parser('info-chk', parents=[parser_common])
159179
parser_info_chk.set_defaults(func=info_chk)
180+
#
181+
parser_info_u = subparsers.add_parser('info-u', parents=[parser_common])
182+
parser_info_u.set_defaults(func=info_u)
183+
#
184+
parser_info_unk_formatted = subparsers.add_parser('info-unk-formatted', parents=[parser_common])
185+
parser_info_unk_formatted.set_defaults(func=info_unk_formatted)
160186

161187
args = parser.parse_args()
162188
args.func(args)

src/wannier90io/_u.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from __future__ import annotations
2+
import typing
3+
4+
import numpy as np
5+
6+
# TODO: implement also read_u_dis
7+
__all__ = ['read_u']
8+
9+
10+
def read_u(stream: typing.TextIO) -> tuple[np.ndarray, np.ndarray]:
11+
"""
12+
Read unitary matrix file (seedname_u.mat) or the rectangular U_dis matrix
13+
file (seedname_u_dis.mat).
14+
15+
Note: for the _u.mat file, num_bands == num_wann.
16+
17+
Arguments:
18+
stream: a file-like stream
19+
20+
Returns:
21+
kpoint coordinates in fractional coordinates (num_kpts, 3)
22+
U matrix U(k) or U_dis(k) (num_kpts, num_bands, num_wann)
23+
24+
25+
"""
26+
stream.readline() # header
27+
28+
[nkpt, num_wann, num_bands] = np.fromstring(stream.readline(), sep=' ', dtype=int)
29+
u_matrices = np.zeros((nkpt, num_bands, num_wann), dtype=complex)
30+
kpoints = []
31+
32+
for ikpt in range(nkpt):
33+
empty = stream.readline() # header
34+
assert not empty.strip(), f"Expected empty line but found instead: '{empty}'"
35+
36+
kpoint = np.fromstring(stream.readline(), sep=' ', dtype=float)
37+
assert len(kpoint) == 3
38+
kpoints.append(kpoint)
39+
u_matrices[ikpt, :, :] = np.loadtxt(stream, max_rows=(num_wann * num_bands)).view(complex).reshape((num_bands, num_wann), order='F')
40+
41+
return np.array(kpoints), u_matrices

src/wannier90io/_unk.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from __future__ import annotations
2+
import typing
3+
4+
import numpy as np
5+
6+
# TODO: implement also read_unk_unformatted (that is the default)
7+
__all__ = ['read_unk_formatted']
8+
9+
10+
def read_unk_formatted(stream: typing.TextIO) -> tuple[int, np.ndarray]:
11+
"""
12+
Read wavefunction files (UNKnnnnn.n files) in formatted format.
13+
14+
Note that the UNK files must have been created using the `wvfn_formatted`
15+
option set to True in the interface code (e.g. pw2wannier90.x for the
16+
Quantum ESPRESSO interface). Note that this is *not* the default, however
17+
for reading into an external code, this is recommended for portability.
18+
19+
Note:
20+
for now only works in the non-spinor case.
21+
Spinor case still to be implemented.
22+
23+
Arguments:
24+
stream: a file-like stream
25+
26+
Returns:
27+
k-point index ik (integer)
28+
complex wavefunction (ngx, ngy, ngz, Nb)
29+
30+
31+
"""
32+
[ngx, ngy, ngz, ik, nbnd] = np.fromstring(stream.readline(), sep=' ', dtype=int)
33+
34+
wvfn = np.zeros((ngx, ngy, ngz, nbnd), dtype=complex)
35+
36+
for ibnd in range(nbnd):
37+
wvfn[:, :, :, ibnd] = np.loadtxt(stream, max_rows=(ngx * ngy * ngz)).view(complex).reshape((ngx, ngy, ngz), order='F')
38+
39+
return (ik, wvfn)

tests/fixtures/fixtures.mk

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ else
2020
WRITE_HR = "write_hr=true"
2121
endif
2222

23+
# Only implemented in version 3.x
24+
ifeq ($(WANNIER90_VERSION), 2.0.1)
25+
WRITE_U_MATRICES = ""
26+
else ifeq ($(WANNIER90_VERSION), 2.1)
27+
WRITE_U_MATRICES = ""
28+
else
29+
WRITE_U_MATRICES = "write_u_matrices=true"
30+
endif
31+
2332
define modify_win
2433
echo $(WRITE_HR) >> wannier.win
2534
echo "write_xyz=true" >> wannier.win
@@ -78,6 +87,7 @@ run-example04:
7887
$(W90) -pp wannier
7988
$(call modify_win)
8089
echo "geninterp_alsofirstder=true" >> wannier.win
90+
echo $(WRITE_U_MATRICES) >> wannier.win
8191
$(W90) wannier
8292
$(W90CHK2CHK) -export wannier
8393
echo "" >> wannier_geninterp.kpt

tests/test_cli.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pytest
55

66

7-
@pytest.mark.parametrize('example', ['example02'])
7+
@pytest.mark.parametrize('example', ['example01', 'example02', 'example04'])
88
def test_cli(wannier90, example):
99
if not pathlib.Path(wannier90).name == 'wannier90-3.1.0':
1010
pytest.skip()
@@ -16,11 +16,24 @@ def test_cli(wannier90, example):
1616
chk_file = pathlib.Path(wannier90)/f'examples/{example}/wannier.chk.fmt'
1717
eig_file = pathlib.Path(wannier90)/f'examples/{example}/wannier.eig'
1818
mmn_file = pathlib.Path(wannier90)/f'examples/{example}/wannier.mmn'
19+
u_file = pathlib.Path(wannier90)/f'examples/{example}/wannier_u.mat'
20+
u_dis_file = pathlib.Path(wannier90)/f'examples/{example}/wannier_u_dis.mat'
21+
UNK_file = pathlib.Path(wannier90)/f'examples/{example}/UNK00001.1'
1922

2023
assert subprocess.run(['w90io', 'parse-win', win_file]).returncode == 0
2124
assert subprocess.run(['w90io', 'parse-nnkp', nnkp_file]).returncode == 0
2225
assert subprocess.run(['w90io', 'parse-wout-iteration-info', wout_file]).returncode == 0
2326
assert subprocess.run(['w90io', 'info-amn', amn_file]).returncode == 0
24-
assert subprocess.run(['w90io', 'info-chk', chk_file]).returncode == 0
25-
assert subprocess.run(['w90io', 'info-eig', eig_file]).returncode == 0
2627
assert subprocess.run(['w90io', 'info-mmn', mmn_file]).returncode == 0
28+
assert subprocess.run(['w90io', 'info-chk', chk_file]).returncode == 0
29+
30+
# This test does not have the .eig file
31+
if example != 'example01':
32+
assert subprocess.run(['w90io', 'info-eig', eig_file]).returncode == 0
33+
34+
# These tests require these specific example folders
35+
if example == 'example01':
36+
assert subprocess.run(['w90io', 'info-unk-formatted', UNK_file]).returncode == 0
37+
if example == 'example04':
38+
assert subprocess.run(['w90io', 'info-u', u_file]).returncode == 0
39+
assert subprocess.run(['w90io', 'info-u', u_dis_file]).returncode == 0

tests/test_u.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import pathlib
2+
3+
import numpy as np
4+
import pytest
5+
6+
import wannier90io as w90io
7+
8+
9+
@pytest.mark.parametrize('example', ['example04'])
10+
def test_read_u(wannier90, example):
11+
with open(pathlib.Path(wannier90)/f'examples/{example}/wannier.win', 'r') as fh:
12+
win = w90io.parse_win_raw(fh.read())
13+
nkpt = len(win['kpoints']['kpoints'])
14+
num_bands = win['parameters']['num_bands']
15+
num_wann = win['parameters']['num_wann']
16+
17+
if 'write_u_matrices' not in win['parameters']:
18+
# an old 2.x version, didn't have write_u_matrices, so skip this test
19+
# The fixture-making script will already avoid to add the flag in W90 2.x
20+
pytest.skip("U matrix printing not implemented in Wannier90 2.x")
21+
return
22+
23+
with open(pathlib.Path(wannier90)/f'examples/{example}/wannier_u.mat', 'r') as fh:
24+
(kpts, u_matrices) = w90io.read_u(fh)
25+
assert np.allclose(kpts, np.asarray(win['kpoints']['kpoints'], dtype=float))
26+
# Note that the U matrix is num_wann x num_wann, not num_bands x num_wann!
27+
assert np.allclose(u_matrices.shape, [nkpt, num_wann, num_wann])
28+
29+
with open(pathlib.Path(wannier90)/f'examples/{example}/wannier_u_dis.mat', 'r') as fh:
30+
(kpts, u_dis_matrices) = w90io.read_u(fh)
31+
assert np.allclose(kpts, np.asarray(win['kpoints']['kpoints'], dtype=float))
32+
assert np.allclose(u_dis_matrices.shape, [nkpt, num_bands, num_wann])

tests/test_unk_formatted.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pathlib
2+
3+
import numpy as np
4+
import pytest
5+
6+
import wannier90io as w90io
7+
8+
9+
@pytest.mark.parametrize('example', ['example01'])
10+
def test_read_unk_formatted(wannier90, example):
11+
for ikpt in range(1, 8+1):
12+
with open(pathlib.Path(wannier90)/f'examples/{example}/UNK0000{ikpt}.1', 'r') as fh:
13+
(ikpt_parsed, wvfn) = w90io.read_unk_formatted(fh)
14+
assert ikpt == ikpt_parsed
15+
16+
ngx, ngy, ngz, nbnd = 20, 20, 20, 4
17+
assert np.allclose(wvfn.shape, [ngx, ngy, ngz, nbnd])

0 commit comments

Comments
 (0)