Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add raw parser of Lowdin charges from projwfc output file #425

Closed
12 changes: 8 additions & 4 deletions aiida_quantumespresso/calculations/namelists.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from aiida.engine import CalcJob
from aiida.orm import Dict
from aiida.orm import RemoteData, FolderData, SinglefileData
from aiida.plugins import ParserFactory

from aiida_quantumespresso.calculations import _lowercase_dict, _uppercase_dict, _pop_parser_options
from aiida_quantumespresso.utils.convert import convert_input_to_namelist_entry
Expand All @@ -39,7 +38,8 @@ class NamelistsCalculation(CalcJob):
_default_namelists = ['INPUTPP']
_blocked_keywords = [] # a list of tuples with key and value fixed

_retrieve_singlefile_list = []
_retrieve_temporary_list = ()
_retrieve_output_as_temp = False

_DEFAULT_INPUT_FILE = 'aiida.in'
_DEFAULT_OUTPUT_FILE = 'aiida.out'
Expand Down Expand Up @@ -174,12 +174,16 @@ def prepare_for_submission(self, folder):

# Retrieve by default the output file and the xml file
calcinfo.retrieve_list = []
calcinfo.retrieve_list.append(self.inputs.metadata.options.output_filename)
if not self._retrieve_output_as_temp:
calcinfo.retrieve_list.append(self.inputs.metadata.options.output_filename)
settings_retrieve_list = settings.pop('ADDITIONAL_RETRIEVE_LIST', [])
calcinfo.retrieve_list += settings_retrieve_list
calcinfo.retrieve_list += self._internal_retrieve_list

calcinfo.retrieve_singlefile_list = self._retrieve_singlefile_list
calcinfo.retrieve_temporary_list = []
if self._retrieve_output_as_temp:
calcinfo.retrieve_temporary_list.append(self.inputs.metadata.options.output_filename)
calcinfo.retrieve_temporary_list += list(self._retrieve_temporary_list)

# We might still have parser options in the settings dictionary: pop them.
_pop_parser_options(self, settings)
Expand Down
26 changes: 19 additions & 7 deletions aiida_quantumespresso/calculations/projwfc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from aiida.orm import RemoteData, FolderData, Dict, XyData
from aiida_quantumespresso.calculations.namelists import NamelistsCalculation


class ProjwfcCalculation(NamelistsCalculation):
"""
Projwfc.x code of the Quantum ESPRESSO distribution, handles the the
Expand All @@ -24,30 +25,41 @@ class ProjwfcCalculation(NamelistsCalculation):
]
_default_parser = 'quantumespresso.projwfc'
_internal_retrieve_list = [NamelistsCalculation._PREFIX + '.pdos*']
_retrieve_output_as_temp = True

@classmethod
def define(cls, spec):
from aiida.orm import ProjectionData, BandsData
super(ProjwfcCalculation, cls).define(spec)
spec.input('parent_folder', valid_type=(RemoteData, FolderData), help='The output folder of a pw.x calculation')
spec.output('output_parameters', valid_type=Dict)
spec.output('lowdin', valid_type=Dict)
spec.output('Dos', valid_type=XyData)
# if spin
spec.output('projections_up', valid_type=ProjectionData, required=False)
spec.output('projections_up', valid_type=ProjectionData, required=False)
spec.output('projections_down', valid_type=ProjectionData, required=False)
spec.output('bands_up', valid_type=BandsData, required=False)
spec.output('bands_up', valid_type=BandsData, required=False)
spec.output('bands_down', valid_type=BandsData, required=False)
# if non-spin
spec.output('projections', valid_type=ProjectionData, required=False)
spec.output('bands', valid_type=BandsData, required=False)
spec.default_output_node = 'output_parameters'
spec.exit_code(
100, 'ERROR_NO_RETRIEVED_FOLDER', message='The retrieved folder data node could not be accessed.')
100, 'ERROR_NO_RETRIEVED_FOLDER', message='The retrieved folder data node could not be accessed.'
)
spec.exit_code(
101, 'ERROR_NO_RETRIEVED_TEMPORARY_FOLDER', message='The retrieved temporary folder could not be accessed.'
)
spec.exit_code(
110, 'ERROR_READING_OUTPUT_FILE', message='The output file could not be read from the retrieved folder.')
110, 'ERROR_READING_OUTPUT_FILE', message='The output file could not be read from the retrieved folder.'
)
spec.exit_code(
111, 'ERROR_READING_PDOSTOT_FILE', message='The pdos_tot file could not be read from the retrieved folder.')
111, 'ERROR_READING_PDOSTOT_FILE', message='The pdos_tot file could not be read from the retrieved folder.'
)
spec.exit_code(
112, 'ERROR_PARSING_PROJECTIONS', message='An exception was raised parsing bands and projections.')
112, 'ERROR_PARSING_PROJECTIONS', message='An exception was raised parsing bands and projections.'
)
spec.exit_code(113, 'ERROR_PARSING_LOWDIN', message='An exception was raised parsing Lowdin charges.')
spec.exit_code(
130, 'ERROR_JOB_NOT_DONE', message='The computation did not finish properly (\'JOB DONE\' not found).')
130, 'ERROR_JOB_NOT_DONE', message='The computation did not finish properly (\'JOB DONE\' not found).'
)
58 changes: 58 additions & 0 deletions aiida_quantumespresso/parsers/parse_raw/projwfc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from __future__ import absolute_import
import re


def parse_lowdin_charges(out_file_lines, lowdin_lines=None):
"""Parse the Lowdin Charge section of the ``projwfc.x`` output.

:param out_file_lines: The projwfc.x stdout file content
:type out_file_lines: list[str]
:param lowdin_lines: indices of lines where 'Lowdin Charges:' has been found
:return: A tuple of lowdin data dict and spill parameter float
"""
# find start of Lowdin charge output
if lowdin_lines is None:
lowdin_lines = []
for i, line in enumerate(out_file_lines):
if line.strip() == 'Lowdin Charges:':
lowdin_lines.append(i)

if len(lowdin_lines) > 1:
raise IOError("'Lowdin Charges:' found on multiple lines: {}".format(lowdin_lines))

if not lowdin_lines:
return None, None

spill_parameter = None
started_section = False
atom_index = None
output = {}
for i, line in enumerate(out_file_lines[lowdin_lines[0] + 1:]):
lineno = lowdin_lines[0] + 2 + i
# break on empty line or spill parameter
if not line.strip():
if started_section:
break
started_section = True
continue
if line.strip().startswith('Spilling Parameter'):
spill_parameter = float(line.strip().split()[-1])
break
atom_line_match = re.match(r'^\s*Atom\s\#\s*(\d+)\:(.*)$', line)
if atom_line_match:
atom_index = int(atom_line_match.group(1))
line = atom_line_match.group(2)
if atom_index is None:
raise IOError('No atom index specified on or before line {}'.format(lineno))
charge_type_match = re.match(r'^\s*(total charge|spin up|spin down|polarization)\s*\=\s*(\-?[\d\.]+)', line)
if charge_type_match:
charge_type = charge_type_match.group(1).replace(' ', '_')
output.setdefault(atom_index, {}).setdefault('sum', {})[charge_type] = float(charge_type_match.group(2))
else:
raise IOError('No charge type specified on line {}'.format(lineno))
for orbital_type, value in re.findall(
r'(s|p|d|f|px|py|pz|dxy|dxz|dyz|dz2|dx2-y2|f[xyz23\-\+]+)\s*\=\s*(\-?[\d\.]+)', line
):
output.setdefault(atom_index, {}).setdefault(charge_type, {})[orbital_type] = float(value)

return output, spill_parameter
Loading