Source code for aiida_quantumespresso.parsers.dos
# -*- coding: utf-8 -*-
from aiida.orm import Dict, XyData
import numpy as np
from aiida_quantumespresso.parsers import QEOutputParsingError
from aiida_quantumespresso.utils.mapping import get_logging_container
from .base import BaseParser
[docs]class DosParser(BaseParser):
"""``Parser`` implementation for the ``DosCalculation`` calculation job class."""
[docs] def parse(self, **kwargs):
"""Parse the retrieved files of a ``DosCalculation`` into output nodes."""
logs = get_logging_container()
_, parsed_stdout, logs = self.parse_stdout_from_retrieved(logs)
base_exit_code = self.check_base_errors(logs)
if base_exit_code:
return self.exit(base_exit_code, logs)
self.out('output_parameters', Dict(parsed_stdout))
if 'ERROR_OUTPUT_STDOUT_INCOMPLETE'in logs.error:
return self.exit(self.exit_codes.ERROR_OUTPUT_STDOUT_INCOMPLETE, logs)
# Parse the DOS
try:
with self.retrieved.base.repository.open(self.node.process_class._DOS_FILENAME, 'r') as handle:
dos_file = handle.readlines()
except OSError:
return self.exit(self.exit_codes.ERROR_READING_DOS_FILE, logs)
array_names = [[], []]
array_units = [[], []]
array_names[0] = ['dos_energy', 'dos', 'integrated_dos'] # When spin is not displayed
array_names[1] = ['dos_energy', 'dos_spin_up', 'dos_spin_down', 'integrated_dos'] # When spin is displayed
array_units[0] = ['eV', 'states/eV', 'states'] # When spin is not displayed
array_units[1] = ['eV', 'states/eV', 'states/eV', 'states'] # When spin is displayed
# grabs parsed data from aiida.dos
# TODO: should I catch any QEOutputParsingError from parse_raw_dos,
# log an error and return an exit code?
array_data, spin = parse_raw_dos(dos_file, array_names, array_units)
energy_units = 'eV'
dos_units = 'states/eV'
int_dos_units = 'states'
xy_data = XyData()
xy_data.set_x(array_data['dos_energy'], 'dos_energy', energy_units)
y_arrays = []
y_names = []
y_units = []
y_arrays += [array_data['integrated_dos']]
y_names += ['integrated_dos']
y_units += ['states']
if spin:
y_arrays += [array_data['dos_spin_up']]
y_arrays += [array_data['dos_spin_down']]
y_names += ['dos_spin_up']
y_names += ['dos_spin_down']
y_units += ['states/eV'] * 2
else:
y_arrays += [array_data['dos']]
y_names += ['dos']
y_units += ['states/eV']
xy_data.set_y(y_arrays, y_names, y_units)
self.out('output_dos', xy_data)
return self.exit(logs=logs)
[docs]def parse_raw_dos(dos_file, array_names, array_units):
"""This function takes as input the dos_file as a list of filelines along with information on how to give labels and
units to the parsed data.
:param dos_file: dos file lines in the form of a list
:type dos_file: list
:param array_names: list of all array names, note that array_names[0]
is for the case with non spin-polarized calculations
and array_names[1] is for the case with spin-polarized
calculation
:type array_names: list
:param array_units: list of all array units, note that array_units[0] is
for the case with non spin-polarized calculations and
array_units[1] is for the case with spin-polarized
calculation
:type array_units: list
:return array_data: narray, a dictionary for ArrayData type, which contains
all parsed dos output along with labels and units
:return spin: boolean, indicates whether the parsed results are spin
polarized
"""
dos_header = dos_file[0]
try:
dos_data = np.genfromtxt(dos_file)
except ValueError:
raise QEOutputParsingError('dosfile could not be loaded using genfromtxt')
if len(dos_data) == 0:
raise QEOutputParsingError('Dos file is empty.')
if np.isnan(dos_data).any():
raise QEOutputParsingError('Dos file contains non-numeric elements.')
# Checks the number of columns, essentially to see whether spin was used
if len(dos_data[0]) == 3:
# spin is not used
array_names = array_names[0]
array_units = array_units[0]
spin = False
elif len(dos_data[0]) == 4:
# spin is used
array_names = array_names[1]
array_units = array_units[1]
spin = True
else:
raise QEOutputParsingError('Dos file in format that the parser is not designed to handle.')
i = 0
array_data = {}
array_data['header'] = np.array(dos_header)
while i < len(array_names):
array_data[array_names[i]] = dos_data[:, i]
array_data[array_names[i] + '_units'] = np.array(array_units[i])
i += 1
return array_data, spin