Source code for aiida_hubbard.parsers.parse_raw.hp
# -*- coding: utf-8 -*-
"""A collection of function that are used to parse the output of Quantum Espresso HP.
The function that needs to be called from outside is parse_raw_output().
The functions mostly work without aiida specific functionalities.
"""
import re
from aiida_quantumespresso.utils.mapping import get_logging_container
[docs]def parse_raw_output(stdout):
"""Parse the output parameters from the output of a Hp calculation written to standard out.
:param filepath: path to file containing output written to stdout
:returns: boolean representing success status of parsing, True equals parsing was successful
:returns: dictionary with the parsed parameters
"""
parsed_data = {}
logs = get_logging_container()
is_prematurely_terminated = True
# Parse the output line by line by creating an iterator of the lines
iterator = iter(stdout.split('\n'))
for line in iterator:
# If the output does not contain the line with 'JOB DONE' the program was prematurely terminated
if 'JOB DONE' in line:
is_prematurely_terminated = False
detect_important_message(logs, line)
# A calculation that will only perturb a single atom will only print one line
match = re.search(r'.*The grid of q-points.*?(\d+)+\s+q-points.*', line)
if match:
parsed_data['number_of_qpoints'] = int(match.group(1))
# Determine the atomic sites that will be perturbed, or that the calculation expects
# to have been calculated when post-processing the final matrices
match = re.search(r'.*List of.*?(\d+)\s+atoms which will be perturbed.*', line)
if match:
hubbard_sites = {}
number_of_perturbed_atoms = int(match.group(1))
_ = next(iterator) # skip blank line
for _ in range(number_of_perturbed_atoms):
values = next(iterator).split()
index = values[0]
kind = values[1]
hubbard_sites[index] = kind
parsed_data['hubbard_sites'] = hubbard_sites
# A calculation that will only perturb a single atom will only print one line
match = re.search(r'.*Atom which will be perturbed.*', line)
if match:
hubbard_sites = {}
number_of_perturbed_atoms = 1
_ = next(iterator) # skip blank line
for _ in range(number_of_perturbed_atoms):
values = next(iterator).split()
index = values[0]
kind = values[1]
hubbard_sites[index] = kind
parsed_data['hubbard_sites'] = hubbard_sites
if is_prematurely_terminated:
logs.error.append('ERROR_OUTPUT_STDOUT_INCOMPLETE')
# Remove duplicate log messages by turning it into a set. Then convert back to list as that is what is expected
logs.error = list(set(logs.error))
logs.warning = list(set(logs.warning))
return parsed_data, logs
[docs]def detect_important_message(logs, line):
"""Detect error or warning messages, and append to the log if a match is found."""
REG_ERROR_CONVERGENCE_NOT_REACHED = re.compile(
r'.*Convergence has not been reached after\s+([0-9]+)\s+iterations!.*'
)
ERROR_POSITIONS = 'WARNING! All Hubbard atoms must be listed first in the ATOMIC_POSITIONS card of PWscf'
message_map = {
'error': {
'Error in routine hub_read_chi (1)': 'ERROR_MISSING_PERTURBATION_FILE',
'Maximum CPU time exceeded': 'ERROR_OUT_OF_WALLTIME',
'reading inputhp namelist': 'ERROR_INVALID_NAMELIST',
'problems computing cholesky': 'ERROR_COMPUTING_CHOLESKY',
'S matrix not positive definite': 'ERROR_S_MATRIX_NOT_POSITIVE_DEFINITE',
'Reconstruction problem: some chi were not found': 'ERROR_MISSING_CHI_MATRICES',
'incompatible FFT grid': 'ERROR_INCOMPATIBLE_FFT_GRID',
REG_ERROR_CONVERGENCE_NOT_REACHED: 'ERROR_CONVERGENCE_NOT_REACHED',
ERROR_POSITIONS: 'ERROR_INCORRECT_ORDER_ATOMIC_POSITIONS',
'WARNING: The Fermi energy shift is zero or too big!': 'ERROR_FERMI_SHIFT',
},
'warning': {
'Warning:': None,
'DEPRECATED:': None,
}
}
# Match any known error and warning messages
for marker, message in message_map['error'].items():
# Replace with isinstance(marker, re.Pattern) once Python 3.6 is dropped
if hasattr(marker, 'search'):
if marker.match(line):
if message is None:
message = line
logs.error.append(message)
else:
if marker in line:
if message is None:
message = line
logs.error.append(message)
for marker, message in message_map['warning'].items():
if marker in line:
if message is None:
message = line
logs.warning.append(message)