Source code for inout.espresso
import numpy as np
from typing import List
from InterPhon.util import get_atomic_weight
[docs]def read_input_lines(structure_file: str) -> tuple:
"""
Parser function to read Quantum ESPRESSO input file.
:param structure_file: Path of Quantum ESPRESSO input file
:type structure_file: str
:return: A standardized set of data that defines a crystal structure such as lattice_matrix and atom_type
:rtype: tuple
"""
try:
with open(structure_file, 'r') as infile:
_lines = infile.readlines()
except IOError:
print("\nFail to open '{0}' file".format(structure_file))
print("Please check the path of Quantum ESPRESSO input file\n")
raise
num_total = None
_lattice_index = None
_pos_atom_index = None
for _ind_line, _line in enumerate(_lines):
if 'nat' in _line:
num_total = int(_line.split()[2])
elif 'CELL_PARAMETERS' in _line:
_lattice_index = _ind_line
elif 'ATOMIC_POSITIONS' in _line:
_pos_atom_index = _ind_line
if num_total is None:
print("'The number of atom (tag: nat)' is not written in '{0}'".format(structure_file))
assert False
elif _lattice_index is None:
print("'CELL_PARAMETERS' is not written in '{0}'".format(structure_file))
assert False
elif _pos_atom_index is None:
print("'ATOMIC_POSITIONS' is not written in '{0}'".format(structure_file))
assert False
for _ind_line, _line in enumerate(_lines[_lattice_index + 1:]):
if _line == '\n':
_lattice_index += 1
else:
break
for _ind_line, _line in enumerate(_lines[_pos_atom_index + 1:]):
if _line == '\n':
_pos_atom_index += 1
else:
break
lattice_matrix = np.asfarray([_line.split()[0:3] for _line in _lines[_lattice_index + 1:_lattice_index + 4]])
__atom_type = []
selective = False
__atom_cart = []
__atom_true = []
for _ind_line, _line in enumerate(_lines[_pos_atom_index + 1:_pos_atom_index + 1 + num_total]):
__atom_type.append(_line.split()[0])
__atom_cart.append(_line.split()[1:4])
if '0' in _line.split():
selective = True
else:
__atom_true.append(_ind_line)
set_atom_type = []
num_atom = []
pre_atom = None
num = 0
for atom in __atom_type:
if atom != pre_atom:
pre_atom = atom
if num != 0:
num_atom.append(num)
set_atom_type.append(atom)
num = 1
else:
num += 1
num_atom.append(num)
coordinate = 'cartesian'
__atom_cart = np.asfarray(__atom_cart)
# # Rearrangement of the atomic position sequence according to the atomic type
# set_atom_type = []
# for atom in __atom_type:
# if atom not in set_atom_type:
# set_atom_type.append(atom)
#
# tmp_atom = []
# for atom in __atom_type:
# for ind_set_atom, set_atom in enumerate(set_atom_type):
# if atom == set_atom:
# tmp_atom.append(ind_set_atom)
# set_atom_arg = np.argsort(np.array(tmp_atom))
#
# _atom_cart = __atom_cart[set_atom_arg]
# atom_type = []
# _atom_true = []
# for ind_atom_arg, atom_arg in enumerate(set_atom_arg):
# atom_type.append(__atom_type[atom_arg])
# if atom_arg in __atom_true:
# _atom_true.append(ind_atom_arg)
#
# # Rearrangement of the atomic position sequence according to the z-position in ascending order
# num_atom = [atom_type.count(atom) for atom in set_atom_type]
# num1 = 0
# set_atom_arg = np.array([], dtype=int)
# for num in num_atom:
# set_atom_arg = np.concatenate((set_atom_arg, np.argsort(_atom_cart[num1:num1 + num, 2]) + num1), axis=0)
# num1 += num
#
# atom_cart = _atom_cart[set_atom_arg]
# atom_true = []
# for ind_atom_arg, atom_arg in enumerate(set_atom_arg):
# if atom_arg in _atom_true:
# atom_true.append(ind_atom_arg)
# Define the index of xyz selective dynamics True
xyz_true = []
for ind_T in __atom_true:
xyz_true.extend([ind_T for i in range(3)])
return lattice_matrix, __atom_type, num_atom, selective, coordinate, __atom_cart, __atom_true, xyz_true
[docs]def write_input_lines(unit_cell,
comment: str) -> List[str]:
"""
Parser function to write Quantum ESPRESSO input file.
:param unit_cell: Instance of UnitCell class
:type unit_cell: :class:`core.UnitCell`
:param comment: Comment to display in Quantum ESPRESSO input file
:type comment: str
:return: List of each line of Quantum ESPRESSO input file
:rtype: List[str]
"""
lines = ["&CONTROL" + '\n' + "/\n"]
_line = "&SYSTEM" + '\n'
_line += " {0:<20}".format('ntyp') + " = {0:<}".format(len(set(unit_cell.atom_type))) + '\n'
_line += " {0:<20}".format('nat') + " = {0:<}".format(len(unit_cell.atom_type)) + '\n'
_line += " {0:<20}".format('ibrav') + " = {0:<}".format(0) + '\n'
_line += "/\n"
lines.append(_line)
lines.append("&ELECTRONS" + '\n' + "/\n")
lines.append("&IONS" + '\n' + "/\n")
lines.append("&CELL" + '\n' + "/\n")
lines.append('\n')
# set_atom_type = []
# for atom in unit_cell.atom_type:
# if atom not in set_atom_type:
# set_atom_type.append(atom)
_line = "ATOMIC_SPECIES" + '\n'
for atom in set(unit_cell.atom_type):
_line += atom + " {0:<}".format(get_atomic_weight(atom)) + " {0:<}_dummy.URF".format(atom) + '\n'
lines.append(_line + '\n')
lines.append("K_POINTS gamma" + '\n' + '\n')
_line = "CELL_PARAMETERS angstrom" + '\n'
for v in unit_cell.lattice_matrix:
_line += " {0:>20.16f} {1:>20.16f} {2:20.16f}".format(v[0], v[1], v[2]) + '\n'
lines.append(_line + '\n')
_line = "ATOMIC_POSITIONS angstrom" + '\n'
if unit_cell.selective:
for i, (atom, pos_atom) in enumerate(zip(unit_cell.atom_type, unit_cell.atom_cart)):
if i in unit_cell.atom_true:
_line += "{0} {1:>20.16f} {2:>20.16f} {3:20.16f}".format(atom, pos_atom[0], pos_atom[1], pos_atom[2])
_line += '\n'
else:
_line += "{0} {1:>20.16f} {2:>20.16f} {3:20.16f} 0 0 0".format(atom, pos_atom[0], pos_atom[1], pos_atom[2])
_line += '\n'
else:
for atom, pos_atom in zip(unit_cell.atom_type, unit_cell.atom_cart):
_line += "{0} {1:>20.16f} {2:>20.16f} {3:20.16f}".format(atom, pos_atom[0], pos_atom[1], pos_atom[2])
_line += '\n'
lines.append(_line)
return lines
[docs]def read_output_lines(force_file: str,
num_super_atom: int) -> np.ndarray:
"""
Parser function to read Quantum ESPRESSO output file in which the atomic forces are written.
:param force_file: Path of Quantum ESPRESSO output file
:type force_file: str
:param num_super_atom: The number of atoms in super cell
:type num_super_atom: int
:return: A standardized atomic forces, _force_matrix
:rtype: np.ndarray[float]
"""
try:
with open(force_file, 'r') as infile:
_lines = infile.readlines()
except IOError:
print("\nFail to open '{0}' file".format(force_file))
print("Please check the path of Quantum ESPRESSO output file\n")
raise
_unit_convert = (13.605662 * 1.602 * 10 ** (-19)) / (0.529177 * 10 ** (-10)) # (Ry/au) to (J/m)
_force_index = None
_tag_atomic_force = 'Forces acting on atoms'
for _ind_line, _line in enumerate(_lines):
if _tag_atomic_force in _line:
_force_index = _ind_line
break
if _force_index is None:
print("'Forces acting on atoms' is not written in '{0}'".format(force_file))
print("Corresponding DFT calculation may be incompletely stopped")
assert False
_atomic_forces = _lines[_force_index + 2: _force_index + 2 + num_super_atom]
_force_matrix = np.asfarray([atomic_force.split()[6:9] for atomic_force in _atomic_forces]) * _unit_convert
return _force_matrix