Skip to content

Commit

Permalink
Merge pull request #679 from aiida-vasp/fix/acwf-handlers
Browse files Browse the repository at this point in the history
Porting fixes for ACWF v1.0 to the `develop` branch
  • Loading branch information
atztogo committed Dec 20, 2023
2 parents ace503f + db6dcff commit e0da77f
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 14 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- Remaining parsers moved to ``parsevasp``.

[v2.2.0]
--------

**Added**
- Error handlers for the ACWF study, disabled by default to retain backwards compatibility. These
offer bare minimum, basically changing `ALGO` and `NBANDS` in case that is needed due to lack of
electronic convergence.

**Changed**
- Deviates from the `develop` branch and releases `>2` from commit `837870c4f0080434955a491b269858caa9243005` and forward.
- Removed pylint requirement in CI.

[v2.1.1]
--------

Expand Down
1 change: 1 addition & 0 deletions aiida_vasp/calcs/vasp.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ def define(cls, spec):
'ERROR_NOT_ABLE_TO_CREATE_NODE',
message='the parser is not able to compose one or more output nodes: {nodes}',
)
spec.exit_code(1005, 'ERROR_OVERFLOW_IN_XML', message='Overflow detected in XML while parsing.')

def prepare_for_submission(self, folder):
"""
Expand Down
2 changes: 1 addition & 1 deletion aiida_vasp/parsers/content_parsers/outcar.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def magnetization(self):
"""
magnetization = self.site_magnetization
if magnetization is not None:
magnetization = magnetization['full_cell'] or None
magnetization = magnetization['full_cell']
return magnetization


Expand Down
10 changes: 8 additions & 2 deletions aiida_vasp/parsers/content_parsers/vasprun.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,16 @@ class VasprunParser(BaseFileParser):
def _init_from_handler(self, handler):
"""Initialize using a file like handler."""

self.overflow = False
try:
self._content_parser = Xml(file_handler=handler, k_before_band=True, logger=self._logger)
except SystemExit:
self._logger.warning('Parsevasp exited abnormally.')
except SystemExit as exception:
if exception.code == 509:
# Xml might be fine but overflow is detected
self.overflow = True
self._logger.warning('Parsevasp exited abnormally due to overflow in XML file.')
else:
self._logger.warning('Parsevasp exited abnormally.')

@property
def version(self):
Expand Down
25 changes: 21 additions & 4 deletions aiida_vasp/parsers/neb.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,17 +326,18 @@ def _parse_quantities(self):
per_image_quantities = {}
#per_image_failed_quantities = {}
failed_quantities = []
parser_notifications = {'xml_overflow': False}

for image_idx in range(1, nimages + 1):
quantities, failed = self._parse_quantities_for_image(image_idx)
quantities, failed = self._parse_quantities_for_image(image_idx, parser_notifications)
per_image_quantities[f'{image_idx:02d}'] = quantities
#per_image_failed_quantities[f'{image_idx:02d}'] = failed
failed_quantities.extend([f'image_{image_idx:02d}_{name}' for name in failed])

return per_image_quantities, failed_quantities
return per_image_quantities, failed_quantities, parser_notifications

# Override super class methods
def _parse_quantities_for_image(self, image_idx):
def _parse_quantities_for_image(self, image_idx, parser_notifications):
"""
This method dispatch the parsing to file parsers
Expand Down Expand Up @@ -381,6 +382,14 @@ def _parse_quantities_for_image(self, image_idx):

file_parser_instances[content_parser_cls] = parser

try:
if parser.overflow:
# We check for overflow and set the appropriate exit status
parser_notifications['xml_overflow'] = True
except AttributeError:
# Not the XML parser
pass

# if the parser cannot be instantiated, add the quantity to a list of unavalaible ones
if parser is None:
failed_to_parse_quantities.append(quantity_key)
Expand All @@ -407,7 +416,7 @@ def _parse_quantities_for_image(self, image_idx):

return parsed_quantities, failed_to_parse_quantities

def _check_vasp_errors(self, quantities):
def _check_vasp_errors(self, quantities, parser_notifications): # pylint: disable=too-many-return-statements
"""
Detect simple vasp execution problems and returns the exit_codes to be set
"""
Expand All @@ -419,6 +428,14 @@ def _check_vasp_errors(self, quantities):
if any(data is None for data in neb_data_list) or any(data is None for data in run_status_list):
return self.exit_codes.ERROR_DIAGNOSIS_OUTPUTS_MISSING

try:
# We have an overflow in the XML file which is critical, but not reported by VASP in
# the standard output, so checking this here.
if parser_notifications['xml_overflow']:
return self.exit_codes.ERROR_OVERFLOW_IN_XML
except AttributeError:
pass

# Return errors related to execution and convergence problems.
# Note that the order is important here - if a calculation is not finished, we cannot
# comment on wether properties are converged are not.
Expand Down
2 changes: 1 addition & 1 deletion aiida_vasp/parsers/tests/test_node_composer.py
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ def test_create_node_misc_all(fresh_aiida_env, vasprun_parser, outcar_parser, st
assert misc['run_stats']['elapsed_time'] == pytest.approx(22.518)
assert 'symmetries' in misc
# No magnetization
assert misc['magnetization'] is None
assert not misc['magnetization']
assert misc['site_magnetization'] == {
'sphere': {
'x': {
Expand Down
49 changes: 45 additions & 4 deletions aiida_vasp/parsers/vasp.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
'add_sgrcon': True,
'add_no_potimm': True,
'add_magmom': True,
'add_bandocc': True
}
}

Expand Down Expand Up @@ -178,7 +179,7 @@ def parse(self, **kwargs): # pylint: disable=too-many-return-statements
self._settings.update_quantities_to_parse(self._parsable_quantities.quantity_keys_to_parse)

# Parse the quantities from retrived objects
parsed_quantities, failed_to_parse_quantities = self._parse_quantities()
parsed_quantities, failed_to_parse_quantities, parser_notifications = self._parse_quantities()
# Compose the output nodes using the parsed quantities
requested_nodes = self._settings.output_nodes_dict
equivalent_quantity_keys = dict(self._parsable_quantities.equivalent_quantity_keys)
Expand All @@ -191,7 +192,7 @@ def parse(self, **kwargs): # pylint: disable=too-many-return-statements
nodes_failed_to_create = composed_nodes.failed

# Check for execution related errors
exit_code = self._check_vasp_errors(parsed_quantities)
exit_code = self._check_vasp_errors(parsed_quantities, parser_notifications)
if exit_code is not None:
return exit_code

Expand All @@ -217,6 +218,7 @@ def _parse_quantities(self):
# A dictionary for catching instantiated object parser objects
content_parser_instances = {}
failed_to_parse_quantities = []
parser_notifications = {'xml_overflow': False}
for quantity_key in self._parsable_quantities.quantity_keys_to_parse:
name = self._parsable_quantities.quantity_keys_to_content[quantity_key]
content_parser_cls = self._definitions.parser_definitions[name]['parser_class']
Expand All @@ -235,11 +237,20 @@ def _parse_quantities(self):

content_parser_instances[content_parser_cls] = parser

try:
if parser.overflow:
# We check for overflow and set the appropriate exit status
parser_notifications['xml_overflow'] = True
except AttributeError:
# Not the XML parser
pass

if parser is None:
# If the parser cannot be instantiated, add the quantity to a list of unavailable ones
failed_to_parse_quantities.append(quantity_key)
continue
exception = None

try:
# The next line may still except for ill-formated object - some parser load all data at
# instantiation time, the others may not.
Expand All @@ -254,7 +265,7 @@ def _parse_quantities(self):
self.logger.warning(f'Parsing {quantity_key} from {parser} failed, exception: {exception}')
failed_to_parse_quantities.append(quantity_key)

return parsed_quantities, failed_to_parse_quantities
return parsed_quantities, failed_to_parse_quantities, parser_notifications

@property
def parser_settings(self):
Expand Down Expand Up @@ -284,7 +295,7 @@ def _check_ionic_convergence(self):
settings = {}
return settings.get('CHECK_IONIC_CONVERGENCE', True)

def _check_vasp_errors(self, quantities):
def _check_vasp_errors(self, quantities, parser_notifications): # pylint: disable=too-many-return-statements
"""
Detect simple vasp execution problems and returns the exit_codes to be set
"""
Expand All @@ -293,6 +304,14 @@ def _check_vasp_errors(self, quantities):
return self.exit_codes.ERROR_DIAGNOSIS_OUTPUTS_MISSING
run_status = quantities['run_status']

try:
# We have an overflow in the XML file which is critical, but not reported by VASP in
# the standard output, so checking this here.
if parser_notifications['xml_overflow']:
return self.exit_codes.ERROR_OVERFLOW_IN_XML
except AttributeError:
pass

# Return errors related to execution and convergence problems.
# Note that the order is important here - if a calculation is not finished, we cannot
# comment on wether properties are converged are not.
Expand Down Expand Up @@ -381,3 +400,25 @@ def brmix(self):
return None

return self.exit_codes.ERROR_VASP_CRITICAL_ERROR.format(error_message=self.notifications_dict['brmix'])

@property
def edddav_zhegv(self):
"""Check if EDDDAV call to ZHEGV should be emitted. Sometimes it has converged."""
if not 'edddav_zhegv' in self.notifications_dict:
return None

if self.parsed_quantities['run_status']['electronic_converged']:
return None

return self.exit_codes.ERROR_VASP_CRITICAL_ERROR.format(error_message=self.notifications_dict['edddav_zhegv'])

@property
def eddrmm_zhegv(self):
"""Check if EDDRMM call to ZHEGV should be emitted. Sometimes it has converged."""
if not 'eddrmm_zhegv' in self.notifications_dict:
return None

if self.parsed_quantities['run_status']['electronic_converged']:
return None

return self.exit_codes.ERROR_VASP_CRITICAL_ERROR.format(error_message=self.notifications_dict['eddrmm_zhegv'])
2 changes: 1 addition & 1 deletion aiida_vasp/utils/default_symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def get_all(version_nr='latest', use_gw=False):
row[0] = symbol
row[1] = int(row[1])
row[2] = float(row[2])
syms[element][suffix] = (PawInfo(*row))
syms[element][suffix] = PawInfo(*row)
return syms


Expand Down
2 changes: 1 addition & 1 deletion aiida_vasp/utils/fixtures/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ def _ref_kp_mesh():
@pytest.fixture
def wannier_params():
from aiida.orm import Dict
return Dict(dict=dict(
return Dict(dict=dict( # pylint: disable=use-dict-literal
dis_num_iter=1000,
num_bands=24,
num_iter=0,
Expand Down

0 comments on commit e0da77f

Please sign in to comment.