Skip to content

Commit ecba0c8

Browse files
committed
Merge remote-tracking branch 'origin/develop'
2 parents a5a0817 + a94b9e0 commit ecba0c8

File tree

9 files changed

+259
-175
lines changed

9 files changed

+259
-175
lines changed

qha/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010
__credits__ = {'Renata M. M. Wentzcovitch': '[email protected]'}
1111
__date__ = 'Feb 17, 2018'
1212
__maintainer__ = 'Tian Qin, Qi Zhang'
13+
__version__ = '1.0.4'
1314

1415
try:
15-
from .unit_conversion import *
1616
from .tools import *
1717
from .eos import *
18-
from .bmf import *
1918
except ImportError:
2019
raise

qha/calculator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def desired_pressure_status(self) -> None:
202202
203203
DESIRED PRESSURE is too high (NTV is too large)!
204204
QHA results might not be right!
205-
Please reduce the NTV accordingly, for example, try to set NTV < %4d.
205+
Please reduce the NTV accordingly, for example, try to set NTV < {:4d}.
206206
""".format(ntv_max)))
207207

208208
raise ValueError("DESIRED PRESSURE is too high (NTV is too large), qha results might not be right!")

qha/readers/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python3
22

3-
from .matdyn2input import *
3+
from .make_input import *
44
from .read_input import *
55
from .read_matdyn import *

qha/readers/make_input.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
#!/usr/bin/env python3
2+
"""
3+
:mod: make_input
4+
================================
5+
6+
.. module make_input
7+
:platform: Unix, Windows, Mac, Linux
8+
:synopsis: Use data files from Quantum ESPRESSO and other software to generate a standard input.
9+
.. moduleauthor:: Qi Zhang <[email protected]>
10+
.. moduleauthor:: Tian Qin <[email protected]>
11+
"""
12+
13+
import pathlib
14+
import re
15+
import warnings
16+
from typing import Iterator, List, Tuple, Optional
17+
18+
import numpy as np
19+
import yaml
20+
from scientific_string import strings_to_integers
21+
from text_stream import TextStream
22+
23+
from qha.type_aliases import Vector, Matrix
24+
25+
# ===================== What can be exported? =====================
26+
__all__ = ['QEInputMaker']
27+
28+
29+
class QEInputMaker:
30+
def __init__(self, inp_file_list: str, inp_static: str, inp_q_points: str):
31+
self._inp_file_list = inp_file_list
32+
self._inp_static = inp_static
33+
self._inp_q_points = inp_q_points
34+
self._frequency_files: Optional[List[str]] = None
35+
36+
self.formula_unit_number: Optional[int] = None
37+
self.comment: Optional[str] = None
38+
self.pressures = None
39+
self.volumes = None
40+
self.static_energies = None
41+
self.q_coordinates = None
42+
self.q_weights = None
43+
self.frequencies = None
44+
45+
def read_file_list(self):
46+
with open(self._inp_file_list, 'r') as f:
47+
d = yaml.load(f)
48+
49+
self.formula_unit_number = int(d['formula_unit_number'])
50+
self.comment: str = d['comment']
51+
self._frequency_files: List[str] = d['frequency_files']
52+
53+
def read_static(self) -> None:
54+
"""
55+
Read static information, including pressures (will not be used in calculation),
56+
optimized volumes, and minimized static energies. These information is from electronic
57+
calculation.
58+
"""
59+
pressures = []
60+
volumes = []
61+
energies = []
62+
63+
with open(self._inp_static, 'r') as f:
64+
print("Reading static data: emtpy lines or lines starting with '#' will be ignored!")
65+
66+
for line in f:
67+
if not line.strip() or line.startswith('#'): # Ignore empty line or comment line
68+
continue
69+
70+
match = re.match(r"p\s*=\s*(-?\d*\.?\d*)\s*v\s*=\s*(-?\d*\.?\d*)\s*e\s*=\s*(-?\d*\.?\d*)", line,
71+
flags=re.IGNORECASE)
72+
if match is None:
73+
continue
74+
75+
p, v, e = match.groups()
76+
pressures.append(p), volumes.append(v), energies.append(e)
77+
78+
self.pressures = np.array(pressures, dtype=float)
79+
self.volumes = np.array(volumes, dtype=float)
80+
self.static_energies = np.array(energies, dtype=float)
81+
82+
def read_q_points(self) -> None:
83+
"""
84+
Read q-points' coordinates and their weights in Brillouin zone. The q-points are assumed to be 3-dimensional.
85+
No other information should be given. If you want, write lines that start with '#', they will be ignored when
86+
reading.
87+
"""
88+
q_coordinates = []
89+
q_weights = []
90+
91+
with open(self._inp_q_points, 'r') as f:
92+
print("Reading q-points file: emtpy lines or lines starting with '#' will be ignored!")
93+
94+
regex = re.compile(r"(-?\d*\.?\d*)\s*(-?\d*\.?\d*)\s*(-?\d*\.?\d*)\s*(-?\d*\.?\d*)")
95+
96+
for line in f:
97+
if not line.strip() or line.startswith('#'): # Ignore empty line or comment line
98+
continue
99+
100+
match = regex.match(line)
101+
if not regex.match(line):
102+
raise RuntimeError(
103+
"Unknown line! Should be 3 coordinates and 1 weight, other lines should be commented with '#'.")
104+
else:
105+
g = match.groups()
106+
q_coordinates.append(g[0:3])
107+
q_weights.append(g[3])
108+
109+
self.q_coordinates = np.array(q_coordinates) # TODO: Possible bug, ``np.array([])`` is regarded as ``False``
110+
self.q_weights = np.array(q_weights)
111+
112+
@staticmethod
113+
def read_frequency_file(inp: str) -> Tuple[Vector, Matrix]:
114+
"""
115+
Read Quantum ESPRESSO's output for phonon frequencies. This method is provided for reading only
116+
one file.
117+
118+
:param inp: The name or path of the file.
119+
:return: The q-space coordinates of each q-point in the file, and corresponding frequencies for each
120+
q-point on each band.
121+
"""
122+
text_stream = TextStream(pathlib.Path(inp))
123+
124+
gen: Iterator[str] = text_stream.generator_telling_position()
125+
126+
q_coordinates = []
127+
frequencies = []
128+
129+
regex = re.compile(r"nbnd\s*=\s*(\d*),\s*nks=\s*(\d*)")
130+
131+
offset = None # Initialization
132+
bands_amount = None
133+
q_points_amount = None
134+
135+
for line, offset in gen:
136+
if not line.strip():
137+
continue
138+
139+
if 'nbnd' or 'nks' in line:
140+
match = regex.search(line)
141+
142+
if not match:
143+
raise RuntimeError(
144+
"The head line '{0}' is not complete! Here 'nbnd' and 'nks' are not found!".format(line))
145+
else:
146+
bands_amount, q_points_amount = strings_to_integers(match.groups())
147+
break
148+
149+
gen: Iterator[str] = text_stream.generator_starts_from(offset)
150+
151+
for line in gen:
152+
if not line.strip():
153+
continue
154+
155+
q_coordinates.append(line.split())
156+
line = next(gen) # Start a new line
157+
frequencies.append(line.split())
158+
159+
q_coordinates = np.array(q_coordinates, dtype=float)
160+
frequencies = np.array(frequencies, dtype=float)
161+
162+
if q_coordinates.shape[0] != q_points_amount:
163+
raise RuntimeError(
164+
"The number of q-points detected, {0}, is not the same as what specified in head line!".format(
165+
q_coordinates.shape[0]))
166+
167+
if frequencies.shape != (q_points_amount, bands_amount):
168+
raise RuntimeError(
169+
"The frequencies array shape '{0}' is not the same as what specified in head line!".format(
170+
frequencies.shape))
171+
172+
return q_coordinates, frequencies
173+
174+
def read_frequency_files(self) -> None:
175+
"""
176+
Read the phonon frequencies for all files, using ``read_frequency_file`` method for each file.
177+
"""
178+
frequencies_for_all_files = []
179+
180+
if any(_ is None for _ in (self.q_coordinates, self.q_weights)): # If any of them is ``None``
181+
self.read_q_points() # Fill these 2 properties
182+
183+
for i in range(len(self._frequency_files)):
184+
q_coordinates, frequencies = self.read_frequency_file(self._frequency_files[i])
185+
186+
if not np.array_equal(q_coordinates, self.q_coordinates):
187+
warnings.warn("The q-points' coordinates are different from what specified in the q-point file!",
188+
stacklevel=2)
189+
190+
frequencies_for_all_files.append(frequencies)
191+
192+
self.frequencies = np.array(frequencies_for_all_files) # Shape: (# volumes, # q-points, # bands on each point)
193+
194+
def write_to_file(self, outfile='input'):
195+
path = pathlib.Path(outfile)
196+
if path.is_file():
197+
path.rename(outfile + '_backup')
198+
199+
with open(outfile, 'w') as f:
200+
f.write("# {0}\n".format(self.comment))
201+
f.write('# The file contains frequencies and weights at the END!\n')
202+
f.write('Number of volumes (nv), q-vectors (nq), normal mode (np), formula units(nm)\n\n')
203+
# TODO: Possible bug introduced in formatting
204+
f.write("{0} {1} {2} {3}\n\n".format(len(self.volumes), len(self.q_weights),
205+
self.frequencies.shape[-1], self.formula_unit_number))
206+
207+
for i in range(len(self.volumes)):
208+
f.write("P= {0} V= {1} E= {2}\n".format(self.pressures[i], self.volumes[i], self.static_energies[i]))
209+
210+
for j in range(len(self.q_weights)):
211+
f.write("{0} {1} {2}\n".format(*self.q_coordinates[j]))
212+
213+
for k in range(self.frequencies.shape[-1]):
214+
f.write("{0}\n".format(self.frequencies[i, j, k]))
215+
216+
f.write('\nweight\n')
217+
for j in range(len(self.q_weights)):
218+
f.write("{0} {1} {2} {3}\n".format(*self.q_coordinates[j], self.q_weights[j]))

qha/readers/matdyn2input.py

Lines changed: 0 additions & 149 deletions
This file was deleted.

0 commit comments

Comments
 (0)