def verify_next_workchain(self): """ Correct for unexpected physics/chemistry/material science behavior. Here we should correct all things that voids what we expect from physics/chemistry/material science. I.e. things that cannot be corrected for at the calculation level (simple restarts etc.). """ # Currently only set to finished on first go. self.ctx.is_finished = True try: workchain = self.ctx.workchains[-1] except IndexError: self.report('There is no {} in the called workchain list.'.format( self._next_workchain.__name__)) return self.exit_codes.ERROR_NO_CALLED_WORKCHAIN # pylint: disable=no-member # Inherit exit status from last workchain (supposed to be # successfull) next_workchain_exit_status = workchain.exit_status next_workchain_exit_message = workchain.exit_message if not next_workchain_exit_status: self.ctx.exit_code = self.exit_codes.NO_ERROR # pylint: disable=no-member else: self.ctx.exit_code = compose_exit_code( next_workchain_exit_status, next_workchain_exit_message) self.report('The called {}<{}> returned a non-zero exit status. ' 'The exit status {} is inherited'.format( workchain.__class__.__name__, workchain.pk, self.ctx.exit_code)) return self.ctx.exit_code
def verify_next_workchain(self): """Verify and inherit exit status from child workchains.""" try: workchain = self.ctx.workchains[-1] except IndexError: self.report('There is no {} in the called workchain list.'.format( self._next_workchain.__name__)) return self.exit_codes.ERROR_NO_CALLED_WORKCHAIN # pylint: disable=no-member # Inherit exit status from last workchain (supposed to be # successfull) next_workchain_exit_status = workchain.exit_status next_workchain_exit_message = workchain.exit_message if not next_workchain_exit_status: self.ctx.exit_code = self.exit_codes.NO_ERROR # pylint: disable=no-member else: self.ctx.exit_code = compose_exit_code( next_workchain_exit_status, next_workchain_exit_message) self.report('The called {}<{}> returned a non-zero exit status. ' 'The exit status {} is inherited'.format( workchain.__class__.__name__, workchain.pk, self.ctx.exit_code)) return self.ctx.exit_code
def verify_next_workchain(self): """Correct for unexpected behavior.""" try: workchain = self.ctx.workchains[-1] except IndexError: self.report('There is no {} in the called workchain list.'.format( self._next_workchain.__name__)) return self.exit_codes.ERROR_NO_CALLED_WORKCHAIN # pylint: disable=no-member # Inherit exit status from last workchain (supposed to be # successfull) next_workchain_exit_status = workchain.exit_status next_workchain_exit_message = workchain.exit_message if not next_workchain_exit_status: self.ctx.exit_code = self.exit_codes.NO_ERROR # pylint: disable=no-member else: self.ctx.exit_code = compose_exit_code( next_workchain_exit_status, next_workchain_exit_message) self.report('The called {}<{}> returned a non-zero exit status. ' 'The exit status {} is inherited'.format( workchain.__class__.__name__, workchain.pk, self.ctx.exit_code)) # Stop further executions of workchains if there are no more structure # entries in the structures dictionary if not self.ctx.structures: self.ctx.is_finished = True return self.ctx.exit_code
def verify_next_workchain(self): """Verify and inherit exit status from child workchains.""" try: workchain = self.ctx.workchains[-1] except IndexError: self.report('There is no {} in the called workchain list.'.format( self._next_workchain.__name__)) return self.exit_codes.ERROR_NO_CALLED_WORKCHAIN # pylint: disable=no-member # Inherit exit status from last workchain (supposed to be # successfull) next_workchain_exit_status = workchain.exit_status next_workchain_exit_message = workchain.exit_message if not next_workchain_exit_status: self.ctx.exit_code = self.exit_codes.NO_ERROR # pylint: disable=no-member else: self.ctx.exit_code = compose_exit_code( next_workchain_exit_status, next_workchain_exit_message) self.report('The called {}<{}> returned a non-zero exit status. ' 'The exit status {} is inherited'.format( workchain.__class__.__name__, workchain.pk, self.ctx.exit_code)) # Make sure at the very minimum we attach the misc node (if it exists) that contains notifications and other # quantities that can be salvaged try: self.out('misc', workchain.outputs['misc']) except NotExistent: pass return self.ctx.exit_code
def verify_calculation(self): """ Analyse the results of the last calculation. In particular, check whether it finished successfully or if not troubleshoot the cause and handle the errors, or abort if unrecoverable error was found. Note that this workchain only check system level issues and does not handle errors or warnings from the calculation itself. That is handled in its parent. """ try: calculation = self.ctx.calculations[-1] except IndexError: self.report = 'The first iteration finished without returning a {}'.format( self._calculation.__name__) # pylint: disable=not-callable return self.exit_codes.ERROR_ITERATION_RETURNED_NO_CALCULATION # pylint: disable=no-member # Check if the calculation already has an exit status, if so, inherit that if calculation.exit_status: exit_code = compose_exit_code(calculation.exit_status, calculation.exit_message) self.report('The called {}<{}> returned a non-zero exit status. ' # pylint: disable=not-callable 'The exit status {} is inherited'.format( calculation.__class__.__name__, calculation.pk, exit_code)) # Make sure at the very minimum we attach the misc node that contains notifications and other # quantities that can be salvaged self.out('misc', calculation.outputs['misc']) return exit_code # Set default exit status to an unknown failure self.ctx.exit_code = self.exit_codes.ERROR_UNKNOWN # pylint: disable=no-member # Done: successful completion of last calculation if calculation.is_finished_ok: self._handle_succesfull(calculation) # Abort: exceeded maximum number of retries elif self.ctx.iteration > self.inputs.max_iterations.value: self._handle_max_iterations(calculation) # Abort: unexpected state of last calculation elif calculation.process_state not in self._expected_calculation_states: self._handle_unexpected(calculation) # Abort: killed elif calculation.is_killed: self._handle_killed(calculation) # Abort: excepted elif calculation.is_excepted: self._handle_excepted(calculation) # Retry or abort: calculation finished or failed (but is not ok) elif calculation.is_finished: self._handle_other(calculation) return self.ctx.exit_code
def _handle_calculation_failure(self, calculation): """ Analyze calculation failure and prepare to restart with corrected inputs. The calculation has failed so we try to analyze the reason and change the inputs accordingly for the next calculation. If the calculation failed, but did so cleanly, we set it as the restart_calc, in all other cases we do not replace the restart_calc """ try: outputs = calculation.outputs.misc.get_dict() _ = outputs['warnings'] _ = outputs['parser_warnings'] except (AttributeError, KeyError) as exception: self.ctx.exit_code = compose_exit_code( self.exit_codes.ERROR_UNEXPECTED_CALCULATION_FAILURE.status, str(exception)) # pylint: disable=no-member is_handled = False handler_report = None # Sort the handlers based on their priority in reverse order handlers = sorted(self._error_handlers, key=lambda x: x.priority, reverse=True) if not handlers: self.ctx.exit_code = self.exit_codes.ERROR_NO_ERROR_HANDLERS # pylint: disable=no-member for handler in handlers: handler_report = handler.method(self, calculation) # If at least one error is handled, we consider the calculation # failure handled if handler_report and handler_report.is_handled: self.ctx.exit_code = self.exit_codes.NO_ERROR # pylint: disable=no-member self.ctx.restart_calc = calculation is_handled = True # After certain error handlers, we may want to skip all other error # handling if handler_report and handler_report.do_break: break # If none of the executed error handlers reported that they handled an # error, the failure reason is unknown if not is_handled: self.ctx.exit_code = self.exit_codes.ERROR_NO_SOLUTION_FROM_ERROR_HANDLERS # pylint: disable=no-member
def init_inputs(self): """Make sure all the required inputs are there and valid, create input dictionary for calculation.""" self.ctx.inputs = AttributeDict() # Set the code self.ctx.inputs.code = self.inputs.code # Set the structure (poscar) self.ctx.inputs.structure = self.inputs.structure # Set the kpoints (kpoints) self.ctx.inputs.kpoints = self.inputs.kpoints # Perform inputs massage to accommodate generalization in higher lying workchains # and set parameters parameters_massager = ParametersMassage(self, self.inputs.parameters) # Check exit codes from the parameter massager and set it if it exists if parameters_massager.exit_code is not None: return parameters_massager.exit_code self.ctx.inputs.parameters = parameters_massager.parameters # Set settings if 'settings' in self.inputs: self.ctx.inputs.settings = self.inputs.settings # Set options # Options is very special, not storable and should be # wrapped in the metadata dictionary, which is also not storable # and should contain an entry for options if 'options' in self.inputs: options = {} options.update(self.inputs.options) self.ctx.inputs.metadata = {} self.ctx.inputs.metadata['options'] = options # Override the parser name if it is supplied by the user. parser_name = self.ctx.inputs.metadata['options'].get( 'parser_name') if parser_name: self.ctx.inputs.metadata['options'][ 'parser_name'] = parser_name # Set MPI to True, unless the user specifies otherwise withmpi = self.ctx.inputs.metadata['options'].get('withmpi', True) self.ctx.inputs.metadata['options']['withmpi'] = withmpi # Verify and set potentials (potcar) if not self.inputs.potential_family.value: self.report( 'An empty string for the potential family name was detected.') # pylint: disable=not-callable return self.exit_codes.ERROR_NO_POTENTIAL_FAMILY_NAME # pylint: disable=no-member try: self.ctx.inputs.potential = get_data_class( 'vasp.potcar').get_potcars_from_structure( structure=self.inputs.structure, family_name=self.inputs.potential_family.value, mapping=self.inputs.potential_mapping.get_dict()) except ValueError as err: return compose_exit_code( self.exit_codes.ERROR_POTENTIAL_VALUE_ERROR.status, str(err)) # pylint: disable=no-member except NotExistent as err: return compose_exit_code( self.exit_codes.ERROR_POTENTIAL_DO_NOT_EXIST.status, str(err)) # pylint: disable=no-member try: self._verbose = self.inputs.verbose.value except AttributeError: pass # Set the charge density (chgcar) if 'chgcar' in self.inputs: self.ctx.inputs.charge_density = self.inputs.chgcar # Set the wave functions (wavecar) if 'wavecar' in self.inputs: self.ctx.inputs.wavefunctions = self.inputs.wavecar return self.exit_codes.NO_ERROR # pylint: disable=no-member
def init_inputs(self): # pylint: disable=too-many-branches, too-many-statements """Make sure all the required inputs are there and valid, create input dictionary for calculation.""" self.ctx.inputs = AttributeDict() self.ctx.inputs.parameters = self._init_parameters() # Set the code self.ctx.inputs.code = self.inputs.code # Set the structure (poscar) self.ctx.inputs.structure = self.inputs.structure # Set the kpoints (kpoints) self.ctx.inputs.kpoints = self.inputs.kpoints # Set settings unsupported_parameters = None skip_parameters_validation = False if self.inputs.get('settings'): self.ctx.inputs.settings = self.inputs.settings # Also check if the user supplied additional tags that is not in the supported file. settings_dict = self.ctx.inputs.settings.get_dict() unsupported_parameters = settings_dict.get('unsupported_parameters', unsupported_parameters) skip_parameters_validation = settings_dict.get('skip_parameters_validation', skip_parameters_validation) # Perform inputs massage to accommodate generalization in higher lying workchains # and set parameters. try: parameters_massager = ParametersMassage(self.ctx.inputs.parameters, unsupported_parameters, skip_parameters_validation=skip_parameters_validation) except Exception as exception: # pylint: disable=broad-except return self.exit_codes.ERROR_IN_PARAMETER_MASSAGER.format(exception=exception) # pylint: disable=no-member try: # Only set if they exists # Set any INCAR tags self.ctx.inputs.parameters = parameters_massager.parameters.incar # Set any dynamics input (currently only for selective dynamics, e.g. custom write to POSCAR) self.ctx.inputs.dynamics = parameters_massager.parameters.dynamics # Here we could set additional override flags, but those are not relevant for this VASP plugin except AttributeError: pass # Set options # Options is very special, not storable and should be # wrapped in the metadata dictionary, which is also not storable # and should contain an entry for options if 'options' in self.inputs: options = {} options.update(self.inputs.options) self.ctx.inputs.metadata = {'options': options} # Override the parser name if it is supplied by the user. parser_name = self.ctx.inputs.metadata['options'].get('parser_name') if parser_name: self.ctx.inputs.metadata['options']['parser_name'] = parser_name # Set MPI to True, unless the user specifies otherwise withmpi = self.ctx.inputs.metadata['options'].get('withmpi', True) self.ctx.inputs.metadata['options']['withmpi'] = withmpi # Make sure we also bring along any label and description set on the WorkChain to the CalcJob, it if does # not exists, set to empty string. if 'metadata' in self.inputs: label = self.inputs.metadata.get('label', '') description = self.inputs.metadata.get('description', '') if 'metadata' not in self.ctx.inputs: self.ctx.inputs.metadata = {} self.ctx.inputs.metadata['label'] = label self.ctx.inputs.metadata['description'] = description # Verify and set potentials (potcar) if not self.inputs.potential_family.value: self.report('An empty string for the potential family name was detected.') # pylint: disable=not-callable return self.exit_codes.ERROR_NO_POTENTIAL_FAMILY_NAME # pylint: disable=no-member try: self.ctx.inputs.potential = get_data_class('vasp.potcar').get_potcars_from_structure( structure=self.inputs.structure, family_name=self.inputs.potential_family.value, mapping=self.inputs.potential_mapping.get_dict()) except ValueError as err: return compose_exit_code(self.exit_codes.ERROR_POTENTIAL_VALUE_ERROR.status, str(err)) # pylint: disable=no-member except NotExistent as err: return compose_exit_code(self.exit_codes.ERROR_POTENTIAL_DO_NOT_EXIST.status, str(err)) # pylint: disable=no-member try: self._verbose = self.inputs.verbose.value except AttributeError: pass # Set the charge density (chgcar) if 'chgcar' in self.inputs: self.ctx.inputs.charge_density = self.inputs.chgcar # Set the wave functions (wavecar) if 'wavecar' in self.inputs: self.ctx.inputs.wavefunctions = self.inputs.wavecar return self.exit_codes.NO_ERROR # pylint: disable=no-member