Exemplo n.º 1
0
 def check_debug_output(self, calculation):
     repo = calculation.outputs.retrieved._repository._get_base_folder()
     try:
         repo.get_abs_path('debug', check_existence=True)
     except OSError:
         return
     debug_folder = repo.get_subfolder('debug')
     # debug folder exists, error probably happened.
     if "jobname" in self.ctx.inputs.metadata.options:
         jobname = self.ctx.inputs.metadata.options.jobname
     else:
         jobname = 'BigDFT job'
     posout_list = debug_folder.get_content_list(pattern="bigdft-err*")
     for filename in posout_list:
         log = YamlIO.load(debug_folder.get_abs_path(filename))
         err = log[0].get('BIGDFT_INPUT_VARIABLES_ERROR')
         info = log[0].get('Additional Info')
         if err is not None:
             self.report('{}<{}> input error : {} Id: {}.\n\
                         Additional Information : {}'.format(
                 jobname, calculation.pk, err['Message'], err['Id']))
             if info is not None:
                 self.report('Additional Info :', info)
             return ProcessHandlerReport(True, ExitCode(100))
         err = log[0].get('BIGDFT_RUNTIME_ERROR')
         if err is not None:
             self.report('{}<{}> runtime error : {} Id: {}'.format(
                 jobname, calculation.pk, err['Message'], err['Id']))
             if info is not None:
                 self.report('Additional Info :', info)
             return ProcessHandlerReport(True, ExitCode(200))
Exemplo n.º 2
0
def test_exit_code_equality():
    """Test that the equality operator works properly."""
    exit_code_origin = ExitCode(1, 'message', True)
    exit_code_clone = ExitCode(1, 'message', True)
    exit_code_different = ExitCode(2, 'message', True)

    assert exit_code_origin == exit_code_clone
    assert exit_code_clone != exit_code_different

    # Check default `ExitCode` also matches normal tuples
    exit_code = ExitCode()
    assert exit_code == (0, None, False)

    assert exit_code != {}
    assert exit_code != []
    assert exit_code != ()
    assert exit_code != (0)
    assert exit_code != (None, )
    assert exit_code != (0, None)
    assert exit_code != [0, None, False]
    assert exit_code != [0, None, False, 'test']

    # `ExitCode` instances should match bare tuples as long as the content is the same
    exit_code = ExitCode(1, 'message', True)
    assert exit_code == (1, 'message', True)
    assert exit_code != ()
Exemplo n.º 3
0
def parse_results(process, retrieved_temporary_folder=None):
    """
    Parse the results for a given CalcJobNode (job)

    :returns: integer exit code, where 0 indicates success and non-zero failure
    """
    from aiida.engine import ExitCode

    assert process.node.get_state() == CalcJobState.PARSING, \
        'job should be in the PARSING state when calling this function yet it is {}'.format(process.node.get_state())

    parser_class = process.node.get_parser_class()
    exit_code = ExitCode()
    logger_extra = get_dblogger_extra(process.node)

    if retrieved_temporary_folder:
        files = []
        for root, directories, filenames in os.walk(retrieved_temporary_folder):
            for directory in directories:
                files.append('- [D] {}'.format(os.path.join(root, directory)))
            for filename in filenames:
                files.append('- [F] {}'.format(os.path.join(root, filename)))

        execlogger.debug('[parsing of calc {}] '
                         'Content of the retrieved_temporary_folder: \n'
                         '{}'.format(process.node.pk, '\n'.join(files)), extra=logger_extra)
    else:
        execlogger.debug('[parsing of calc {}] '
                         'No retrieved_temporary_folder.'.format(process.node.pk), extra=logger_extra)

    if parser_class is not None:

        parser = parser_class(process.node)
        parse_kwargs = parser.get_outputs_for_parsing()

        if retrieved_temporary_folder:
            parse_kwargs['retrieved_temporary_folder'] = retrieved_temporary_folder

        exit_code = parser.parse(**parse_kwargs)

        if exit_code is None:
            exit_code = ExitCode(0)

        if not isinstance(exit_code, ExitCode):
            raise ValueError('parse should return an `ExitCode` or None, and not {}'.format(type(exit_code)))

        if exit_code.status:
            parser.logger.error('parser returned exit code<{}>: {}'.format(exit_code.status, exit_code.message))

        for link_label, node in parser.outputs.items():
            try:
                process.out(link_label, node)
            except ValueError as exception:
                parser.logger.error('invalid value {} specified with label {}: {}'.format(node, link_label, exception))
                exit_code = process.exit_codes.ERROR_INVALID_OUTPUT
                break

    return exit_code
Exemplo n.º 4
0
def create_aiida_bands_data(fleurinp, retrieved):
    """
    Creates :py:class:`aiida.orm.BandsData` object containing the kpoints and eigenvalues
    from the `banddos.hdf` file of the calculation

    :param fleurinp: :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` for the calculation
    :param retrieved: :py:class:`aiida.orm.FolderData` for the bandstructure calculation

    :returns: :py:class:`aiida.orm.BandsData` for the bandstructure calculation

    :raises: ExitCode 300, banddos.hdf file is missing
    :raises: ExitCode 310, banddos.hdf reading failed
    :raises: ExitCode 320, reading kpointsdata from Fleurinp failed
    """
    from masci_tools.io.parsers.hdf5 import HDF5Reader, HDF5TransformationError
    from masci_tools.io.parsers.hdf5.recipes import FleurSimpleBands  #no projections only eigenvalues for now
    from aiida.engine import ExitCode

    try:
        kpoints = fleurinp.get_kpointsdata_ncf(only_used=True)
    except ValueError as exc:
        return ExitCode(
            320,
            message=f'Retrieving kpoints data from fleurinp failed with: {exc}'
        )

    if 'banddos.hdf' in retrieved.list_object_names():
        try:
            with retrieved.open('banddos.hdf', 'rb') as f:
                with HDF5Reader(f) as reader:
                    data, attributes = reader.read(recipe=FleurSimpleBands)
        except (HDF5TransformationError, ValueError) as exc:
            return ExitCode(310,
                            message=f'banddos.hdf reading failed with: {exc}')
    else:
        return ExitCode(300,
                        message='banddos.hdf file not in the retrieved files')

    bands = BandsData()
    bands.set_kpointsdata(kpoints)

    nkpts, nbands = attributes['nkpts'], attributes['nbands']
    eigenvalues = data['eigenvalues_up'].reshape((nkpts, nbands))
    if 'eigenvalues_down' in data:
        eigenvalues_dn = data['eigenvalues_down'].reshape((nkpts, nbands))
        eigenvalues = [eigenvalues, eigenvalues_dn]

    bands.set_bands(eigenvalues, units='eV')

    bands.label = 'output_banddos_wc_bands'
    bands.description = (
        'Contains BandsData for the bandstructure calculation')

    return bands
Exemplo n.º 5
0
    def test_exit_codes_keyword_only(self):
        """The `exit_codes` should be keyword only."""
        with self.assertRaises(TypeError):

            @register_process_handler(BaseRestartWorkChain, ExitCode())  # pylint: disable=too-many-function-args
            def _(self, node):
                pass

        @register_process_handler(BaseRestartWorkChain, exit_codes=ExitCode())
        def _(self, node):
            pass
Exemplo n.º 6
0
    def run_results(self):
        """
        Attach the relevant output nodes from the band calculation to the workchain outputs
        for convenience
        """

        from aiida.engine import ExitCode

        base_bands = self.ctx.workchain_base_bands

        #self.out('scf_plus_bands_summary', base_bands_results.output_parameters)
        #self.out('bands', base_bands_results.bands)
        for name, port in six.iteritems(self.spec().outputs):

            try:
                node = base_bands.get_outgoing(
                    link_label_filter=name).one().node
            except ValueError:
                if port.required:
                    self.report(
                        "the process spec specifies the output '{}' as required but was not an output of {}<{}>"
                        .format(name, base_bands, base_bands.pk))
            else:
                self.out(name, node)
                #self.report("attaching the node {}<{}> as '{}'"
                #            .format(node.__class__.__name__, node.pk, name))

        self.report('Bands workchain succesfully completed')
        return ExitCode(0)
Exemplo n.º 7
0
    def parse(self, **kwargs):
        """
        Parse outputs, store results in database.

        :returns: an exit code, if parsing fails (or nothing if parsing succeeds)
        """

        # Check if retrieved folder is present
        try:
            output_folder = self.retrieved
        except exceptions.NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDE

        # Check that folder content is as expected
        files_retrieved = output_folder.list_object_names()
        files_expected = ['PreModRun', 'PreModRun.log']
        # Note: set(A) <= set(B) checks whether A is a subset of B
        if not set(files_expected) <= set(files_retrieved):
            self.logger.error("Found files '{}', expected to find '{}'".format(
                files_retrieved, files_expected))
            return self.exit_codes.ERROR_MISSING_OUTPUT_FILES
        summary_parser = SummaryParser(self)
        summary = summary_parser.parse()
        self.out('summary', summary['summary'])
        log_parser = LogParser(self)
        log = log_parser.parse()
        self.out('log', log['log'])
        micro_parser = MicroParser(self)
        micro = micro_parser.parse()
        self.out('micro', micro['micro'])

        return ExitCode(0)
Exemplo n.º 8
0
    def parse(self, **kwargs):
        """
        Receives in input a dictionary of retrieved nodes.
        Does all the logic here.
        """
        from aiida.engine import ExitCode

        try:
            output_folder = self.retrieved
        except exceptions.NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        output_path, messages_path, xml_path, json_path, bands_path = \
            self._fetch_output_files(output_folder)

        out_results = self._get_output_nodes(output_path, messages_path,
                                             xml_path, json_path, bands_path)

        for line in out_results["warnings"]:
            if u'GEOM_NOT_CONV' in line:
                return (self.exit_codes.GEOM_NOT_CONV)
            if u'SCF_NOT_CONV' in line:
                return (self.exit_codes.SCF_NOT_CONV)

        return ExitCode(0)
Exemplo n.º 9
0
    def parse(self, **kwargs):
        """
        Parse outputs, store results in database.
         
        Receives in input a dictionary of retrieved nodes. 
        """

        try:
            _ = self.retrieved
        except exceptions.NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        exit_code = self._parse_GSR()
        if exit_code is not None:
            return exit_code

        #try:
        #    returned = self._parse_trajectory()
        #    if isinstance(returned, StructureData):
        #        self.out('output_structure', returned)
        #    else:  # in case this is an error code
        #        return returned
        #except exceptions.NotExistent:
        #    pass

        return ExitCode(0)
Exemplo n.º 10
0
def test_sanity_check_no_bands(generate_workchain_pw):
    """Test that `sanity_check_insufficient_bands` does not except if there is no `output_band`, which is optional."""
    process = generate_workchain_pw(exit_code=ExitCode(0))
    process.setup()

    calculation = process.ctx.children[-1]
    assert process.sanity_check_insufficient_bands(calculation) is None
Exemplo n.º 11
0
    def parse(self, **kwargs):
        """Parse output structur, store it in database in CifData format."""

        # Check that the retrieved folder is there
        try:
            out_folder = self.retrieved
        except NotExistent:
            self.logger.error('No retrieved folder found')
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        # Check what is inside the folder
        list_of_files = out_folder._repository.list_object_names()  # pylint: disable=protected-access

        output_file = self.node.process_class._DEFAULT_OUTPUT_FILE

        # We need at least the output file name as defined in calcs.py
        if output_file not in list_of_files:
            raise self.exit_codes.ERROR_NO_OUTPUT_FILE

        finished = False
        with out_folder.open(output_file) as file:
            for line in file.readlines():
                if 'Finished chargemol in' in line:
                    finished = True

        if not finished:
            raise OutputParsingError('Calculation did not finish correctly')

        # Create CifData object from the following the file path returned by xyz2cif
        with out_folder.open(
                'DDEC6_even_tempered_net_atomic_charges.xyz') as handle:
            output_cif = xyz2cif(handle.readlines())
        self.out('structure_ddec', output_cif)

        return ExitCode(0)
Exemplo n.º 12
0
    def resubmit_random_gpu_error(self, calc):
        content_string = calc.outputs.retrieved.get_object_content(
            calc.get_attribute('scheduler_stdout'))

        gpu_error = "Invalid argument: No OpKernel was registered to support " \
                    "Op 'DescrptSeA' with these attrs."
        time_exceeded = "Total wall time:"
        if gpu_error in content_string:
            self.report("Inspect GPU Error")
            return ProcessHandlerReport(False, ExitCode(500))

        if (gpu_error not in content_string
                and time_exceeded not in content_string):
            self.report("Something wrong during moodel deviation")
            return ProcessHandlerReport(True, ExitCode(1))
        return None
Exemplo n.º 13
0
    def parse(self, **kwargs):
        """
        Parse outputs, store results in database.

        :returns: an exit code, if parsing fails (or nothing if parsing succeeds)
        """
        output_filename = self.node.get_option("output_filename")

        # Check that folder content is as expected
        files_retrieved = self.retrieved.list_object_names()
        files_expected = [output_filename]
        # Note: set(A) <= set(B) checks whether A is a subset of B
        if not set(files_expected) <= set(files_retrieved):
            self.logger.error(
                f"Found files '{files_retrieved}', expected to find '{files_expected}'"
            )
            return self.exit_codes.ERROR_MISSING_OUTPUT_FILES

        # add output file
        self.logger.info(f"Parsing '{output_filename}'")
        with self.retrieved.open(output_filename, "rb") as handle:
            output_node = SinglefileData(file=handle)
        self.out("{{cookiecutter.entry_point_prefix}}", output_node)

        return ExitCode(0)
Exemplo n.º 14
0
    def parse(self, **kwargs):
        """Receives in input a dictionary of retrieved nodes. Does all the logic here."""

        retrieved_folders = []

        try:
            retrieved_folder = self.retrieved
            retrieved_folders.append(retrieved_folder)
        except NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        retrieve_temp_list_input = self.node.get_attribute(
            'retrieve_temporary_list', None)
        # If temporary files were specified, check that we have them
        if retrieve_temp_list_input:
            try:
                retrieved_temp_folder_path = kwargs[
                    'retrieved_temporary_folder']
                # create a folderdata object to treat this the same way
                temp_fd = FolderData(tree=retrieved_temp_folder_path)
                retrieved_folders.append(temp_fd)
            except KeyError:
                return self.exit_codes.ERROR_NO_RETRIEVED_TEMPORARY_FOLDER

        if "parser_params" in self.node.inputs:
            parser_params = dict(self.node.inputs.parser_params)
        else:
            parser_params = {}

        self._parse_folders(retrieved_folders, parser_params)

        return ExitCode(0)
Exemplo n.º 15
0
    def run_results(self):
        """
        Attach the relevant output nodes
        """

        from aiida.engine import ExitCode

        if self.ctx.spinstm == "non-collinear":
            cumarray = {}
            for spinmod in ("q", "x", "y", "z"):
                stmnode = self.ctx[spinmod]
                if not stmnode.is_finished_ok:
                    return self.exit_codes.ERROR_STM_PLUGIN
                cumarray[spinmod] = stmnode.outputs.stm_array
            stm_array = create_non_coll_array(**cumarray)
            self.out('stm_array', stm_array)
        else:
            if not self.ctx.stm_calc.is_finished_ok:
                return self.exit_codes.ERROR_STM_PLUGIN
            stm_plot_calc = self.ctx.stm_calc
            stm_array = stm_plot_calc.outputs.stm_array
            self.out('stm_array', stm_array)

        if self.ctx.workchain_base.outputs.output_parameters.attributes[
                "variable_geometry"]:
            output_structure = self.ctx.siesta_ldos.outputs.output_structure
            ##CAREFULL, if strip relax options ,this must be modified
            self.out('output_structure', output_structure)

        self.report('STM workchain succesfully completed')
        return ExitCode(0)
Exemplo n.º 16
0
    def parse(self, **kwargs):
        """
        Receives in input a dictionary of retrieved nodes.
        Does all the logic here.
        """
        from aiida.engine import ExitCode

        try:
            output_folder = self.retrieved
        except exceptions.NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        filename_plot = None
        for element in output_folder.list_object_names():
            if ".STM" in element:
                filename_plot = element

        if filename_plot is None:
            return self.exit_codes.ERROR_OUTPUT_PLOT_MISSING

        old_version = True
        if "LDOS" in filename_plot:
            old_version = False
        if old_version and self.node.inputs.spin_option.value != "q":
            self.node.logger.warning(
                "Spin option different from 'q' was requested in "
                "input, but the plstm version used for the calculation do not implement spin support. "
                "The parsed STM data refers to the total charge option.")

        try:
            plot_contents = output_folder.get_object_content(filename_plot)
        except (IOError, OSError):
            return self.exit_codes.ERROR_OUTPUT_PLOT_READ

        # Save grid_X, grid_Y, and STM arrays in an ArrayData object
        try:
            stm_data = get_stm_data(plot_contents)
        except (IOError, OSError):
            return self.exit_codes.ERROR_CREATION_STM_ARRAY

        self.out('stm_array', stm_data)

        parser_info = {}
        parser_info['parser_info'] = 'AiiDA STM(Siesta) Parser V. {}'.format(
            self._version)
        parser_info['parser_warnings'] = []
        parser_info['output_data_filename'] = filename_plot

        # Possibly put here some parsed data from the stm.out file
        # (but it is not very interesting)
        result_dict = {}

        # Add parser info dictionary
        parsed_dict = dict(
            list(result_dict.items()) + list(parser_info.items()))

        output_data = Dict(dict=parsed_dict)
        self.out('output_parameters', output_data)

        return ExitCode(0)
Exemplo n.º 17
0
    def test_exit_codes_filter(self):
        """Test that the `exit_codes` argument properly filters, returning `None` if the `node` has different status."""

        exit_code_filter = ExitCode(400)

        # This process node should match the exit code filter of the error handler
        node_match = ProcessNode()
        node_match.set_exit_status(exit_code_filter.status)

        # This one should not because it has a different exit status
        node_skip = ProcessNode()
        node_skip.set_exit_status(200)  # Some other exit status

        class ArithmeticAddBaseWorkChain(BaseRestartWorkChain):
            """Minimal base restart workchain for the ``ArithmeticAddCalculation``."""

            _process_class = ArithmeticAddCalculation

            @process_handler(exit_codes=exit_code_filter)
            def _(self, node):
                return ProcessHandlerReport()

        # Create dummy process instance
        process = ArithmeticAddBaseWorkChain()

        # Loop over all handlers, which should be just the one, and call it with the two different nodes
        for handler in process.get_process_handlers():
            # The `node_match` should match the `exit_codes` filter and so return a report instance
            assert isinstance(handler(process, node_match),
                              ProcessHandlerReport)

            # The `node_skip` has a wrong exit status and so should get skipped, returning `None`
            assert handler(process, node_skip) is None
Exemplo n.º 18
0
def test_analyze_process(aiida_profile, generate_workchain_converge,
                         fixture_localhost, generate_wc_job_node):
    """ 
    Test method `_analyze_process` of `SiestaConvereger`. Moreover it
    calls return_results but we no correct setup. Therefore it does
    only test the case when the simulation is not converged and as output
    only the Bool `converged` is returned.
    """

    process = generate_workchain_converge()
    process.initialize()

    basewc = generate_wc_job_node("siesta.base", fixture_localhost)
    basewc.set_process_state(ProcessState.FINISHED)
    basewc.set_exit_status(ExitCode(0).status)
    out_par = orm.Dict(dict={"E_KS": 1111, "E_KS_units": "eV"})
    out_par.store()
    out_par.add_incoming(basewc,
                         link_type=LinkType.RETURN,
                         link_label='output_parameters')

    assert len(process.ctx.target_values) == 0

    process._analyze_process(basewc)

    assert len(process.ctx.target_values) == 1

    process.ctx.used_values = []

    process.return_results()

    assert "converged" in process.outputs
    assert process.outputs["converged"].value == False
Exemplo n.º 19
0
    def parse(self, **kwargs):
        """
        Parse outputs, store results in database.

        :returns: an exit code, if parsing fails (or nothing if parsing succeeds)
        """
        from aiida.orm import SinglefileData

        output_filename = self.node.get_option('output_filename')
        print('output_filename (PARSER):', output_filename)

        # Check that folder content is as expected
        files_retrieved = self.retrieved.list_object_names()
        files_expected = [output_filename, 'output.json']
        # Note: set(A) <= set(B) checks whether A is a subset of B
        # this will fail if calcuation did not converge
        if not set(files_expected) <= set(files_retrieved):
            self.logger.error("Found files '{}', expected to find '{}'".format(
                files_retrieved, files_expected))
            return self.exit_codes.ERROR_MISSING_OUTPUT_FILES

        # add output file
        self.logger.info("Parsing '{}'".format(output_filename))
        with self.retrieved.open(output_filename, 'rb') as handle:
            output_node = SinglefileData(file=handle)
        with self.retrieved.open('output.json', 'r') as handle:
            result_json = json.load(handle)
        self.out('sirius', output_node)
        self.out('output', Dict(dict=result_json))

        if not result_json['ground_state']['converged']:
            return self.exit_codes.ERROR_NOT_CONVERGED

        return ExitCode(0)
Exemplo n.º 20
0
    def return_results(self):

        from aiida.engine import ExitCode

        collectwcinfo = {
            f"s{scale.value}".replace(".", "_"): info
            for (scale,
                 ), info in zip(self.ctx.used_values, self.ctx.collectwcinfo)
        }

        res_dict = fit_and_final_dicts(**collectwcinfo)

        self.out('results_dict', res_dict)

        if "fit_res" in res_dict.attributes:
            self.report(
                'Birch-Murnaghan fit was succesfull, creating the equilibrium structure output node'
            )
            eq_structure = scale_to_vol(
                self.ctx.inputs.structure,
                Float(res_dict["fit_res"]["Vo(ang^3/atom)"]))
            self.out('equilibrium_structure', eq_structure)
        else:
            self.report(
                "WARNING: Birch-Murnaghan fit failed, check your results_dict['eos_data']"
            )

        return ExitCode(0)
Exemplo n.º 21
0
    def parse(self, **kwargs):
        """Parse outputs, store results in database."""
        ionmov = self.node.inputs['parameters'].get_dict().get('ionmov', 0)
        optcell = self.node.inputs['parameters'].get_dict().get('optcell', 0)

        if ionmov == 0 and optcell == 0:
            is_relaxation = False
        else:
            is_relaxation = True

        try:
            _ = self.retrieved
        except exceptions.NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_TEMPORARY_FOLDER

        exit_code = self._parse_stdout()
        if exit_code is not None:
            return exit_code

        exit_code = self._parse_gsr()
        if exit_code is not None:
            return exit_code

        if is_relaxation:
            exit_code = self._parse_trajectory()  # pylint: disable=assignment-from-none
            if exit_code is not None:
                return exit_code

        return ExitCode(0)
Exemplo n.º 22
0
    def parse(self, **kwargs):
        """
        Parse outputs, store results in database.

        :returns: an exit code, if parsing fails (or nothing if parsing succeeds)
        """
        from aiida.orm import SinglefileData

        output_filename = self.node.get_option('output_filename')

        # Check that folder content is as expected
        files_retrieved = self.retrieved.list_object_names()
        files_expected = [output_filename]
        # Note: set(A) <= set(B) checks whether A is a subset of B
        if not set(files_expected) <= set(files_retrieved):
            self.logger.error("Found files '{}', expected to find '{}'".format(
                files_retrieved, files_expected))
            return self.exit_codes.ERROR_MISSING_OUTPUT_FILES

        # add output file
        self.logger.info("Parsing '{}'".format(output_filename))
        with self.retrieved.open(output_filename, 'rb') as handle:
            output_node = SinglefileData(file=handle)
        self.out('hightc', output_node)

        return ExitCode(0)
Exemplo n.º 23
0
    def parse(self, **kwargs):
        """
        Parse outputs, store results in database.

        :returns: an exit code, if parsing fails (or nothing if parsing succeeds)
        """
        from aiida.orm import SinglefileData

        # Check that the retrieved folder is there
        try:
            output_folder = self.retrieved
        except exceptions.NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        # Check the folder content is as expected
        list_of_files = output_folder.list_object_names()
        output_files = [self.node.get_option('output_filename')]
        output_links = ['alloy']
        # Note: set(A) <= set(B) checks whether A is a subset
        if set(output_files) <= set(list_of_files):
            pass
        else:
            self.logger.error(
                "Not all expected output files {} were found".format(
                    output_files))

        # Use something like this to loop over multiple output files
        for fname, link in zip(output_files, output_links):

            with output_folder.open(fname) as handle:
                node = SinglefileData(file=handle)
            self.out(link, node)

        return ExitCode(0)
Exemplo n.º 24
0
def test_sequential_not_conv(aiida_profile, generate_workchain_seq_converger,
                             generate_wc_job_node, fixture_localhost):
    """
    We test here the SiestaSequentialConverger, in the case a Converger fails
    to converge.
    """

    from aiida.common.extendeddicts import AttributeDict

    process = generate_workchain_seq_converger()
    process.initialize()

    assert process.ctx.iteration_keys == ('iterate_over', )

    inputs = {"iterate_over": orm.Dict(dict={"s": [2, 2]})}
    convergerwc = generate_wc_job_node("siesta.converger", fixture_localhost,
                                       inputs)
    convergerwc.set_process_state(ProcessState.FINISHED)
    convergerwc.set_exit_status(ExitCode(0).status)
    out_conv = orm.Bool(False)
    out_conv.store()
    out_conv.add_incoming(convergerwc,
                          link_type=LinkType.RETURN,
                          link_label='converged')

    process.ctx.last_inputs = AttributeDict({"parameters": {"yo": "yo"}})

    process._analyze_process(convergerwc)

    assert process.ctx.already_converged == {}
    assert "parameters" in process.ctx.last_inputs
Exemplo n.º 25
0
def create_substrate_bulk(wf_dict_node):
    """
    Calcfunction to create a bulk structure of a substrate.

    :params wf_dict: AiiDA dict node with at least keys lattice, host_symbol and latticeconstant
    (If they are not there, raises KeyError)
    Lattice key supports only fcc and bcc

    raises ExitCode 380, ERROR_NOT_SUPPORTED_LATTICE
    """

    from aiida.engine import ExitCode
    from ase.lattice.cubic import FaceCenteredCubic
    from ase.lattice.cubic import BodyCenteredCubic

    wf_dict = wf_dict_node.get_dict()
    lattice = wf_dict['lattice']
    if lattice == 'fcc':
        structure_factory = FaceCenteredCubic
    elif lattice == 'bcc':
        structure_factory = BodyCenteredCubic
    else:
        return ExitCode(380, 'ERROR_NOT_SUPPORTED_LATTICE', message='Specified substrate has to be bcc or fcc.')

    miller = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
    host_symbol = str(wf_dict['host_symbol'])
    latticeconstant = float(wf_dict['latticeconstant'])
    size = (1, 1, 1)
    structure = structure_factory(miller=miller,
                                  symbol=host_symbol,
                                  pbc=(1, 1, 1),
                                  latticeconstant=latticeconstant,
                                  size=size)

    return StructureData(ase=structure)
Exemplo n.º 26
0
class ArithmeticAddBaseWorkChain(BaseRestartWorkChain):
    """Ridiculous work chain around `AritmethicAddCalculation` with automated sanity checks and error handling."""

    _process_class = ArithmeticAddCalculation

    @classmethod
    def define(cls, spec):
        """Define the process specification."""
        super().define(spec)
        spec.expose_inputs(ArithmeticAddCalculation, namespace='add')
        spec.expose_outputs(ArithmeticAddCalculation)
        spec.outline(
            cls.setup,
            while_(cls.should_run_process)(
                cls.run_process,
                cls.inspect_process,
            ),
            cls.results,
        )
        spec.exit_code(100, 'ERROR_TOO_BIG', message='The sum was too big.')
        spec.exit_code(110,
                       'ERROR_ENABLED_DOOM',
                       message='You should not have done that.')

    def setup(self):
        """Call the `setup` of the `BaseRestartWorkChain` and then create the inputs dictionary in `self.ctx.inputs`.

        This `self.ctx.inputs` dictionary will be used by the `BaseRestartWorkChain` to submit the process in the
        internal loop.
        """
        super().setup()
        self.ctx.inputs = AttributeDict(
            self.exposed_inputs(ArithmeticAddCalculation, 'add'))

    @process_handler(priority=500)
    def sanity_check_not_too_big(self, node):
        """My puny brain cannot deal with numbers that I cannot count on my hand."""
        if node.is_finished_ok and node.outputs.sum > 10:
            return ProcessHandlerReport(True, self.exit_codes.ERROR_TOO_BIG)  # pylint: disable=no-member

    @process_handler(priority=460, enabled=False)
    def disabled_handler(self, node):  # pylint: disable=unused-argument
        """By default this is not enabled and so should never be called, irrespective of exit codes of sub process."""
        return ProcessHandlerReport(True, self.exit_codes.ERROR_ENABLED_DOOM)  # pylint: disable=no-member

    @process_handler(priority=450,
                     exit_codes=ExitCode(1000, 'Unicorn encountered'))
    def a_magic_unicorn_appeared(self, node):  # pylint: disable=no-self-argument,no-self-use
        """As we all know unicorns do not exist so we should never have to deal with it."""
        raise RuntimeError('this handler should never even have been called')

    @process_handler(
        priority=400,
        exit_codes=ArithmeticAddCalculation.exit_codes.ERROR_NEGATIVE_NUMBER)
    def error_negative_sum(self, node):
        """What even is a negative number, how can I have minus three melons?!."""
        self.ctx.inputs.x = Int(abs(node.inputs.x.value))
        self.ctx.inputs.y = Int(abs(node.inputs.y.value))
        return ProcessHandlerReport(True)
def cif_to_structure(cif, converter=None):
    if converter is None:
        converter_type = "ase"
    elif isinstance(converter, Str):
        converter_type = converter.value
    else:
        return ExitCode(300, "ERROR_INVALID_CONVERTER_TYPE")
    return cif.get_structure(converter=converter_type)
Exemplo n.º 28
0
    def parse(self, **kwargs):
        """Parse outputs, store results in database."""
        try:
            output_folder = self.retrieved
        except exceptions.NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        # parse stderr
        pbs_error = None
        sterr_file = self.node.get_option("scheduler_stderr")
        if sterr_file in output_folder.list_object_names():
            with output_folder.open(sterr_file) as fileobj:
                pbs_exit_code = parse_pbs_stderr(fileobj)
            if pbs_exit_code:
                pbs_error = self.exit_codes[pbs_exit_code]

        # parse stdout file
        stdout_error = None
        stdout_data = {}
        stdout_fname = self.node.get_option("stdout_file_name")
        if stdout_fname not in self.retrieved.list_object_names():
            stdout_error = self.exit_codes.ERROR_OUTPUT_FILE_MISSING
        else:
            with output_folder.open(stdout_fname) as handle:
                stdout_data = read_properties_stdout(handle.read())
            stdout_exit_code = stdout_data.pop("exit_code", None)
            if stdout_exit_code:
                stdout_error = self.exit_codes[stdout_exit_code]

        final_data = stdout_data
        final_data.update({
            "parser_version": str(__version__),
            "parser_class": str(self.__class__.__name__),
        })

        # log errors
        errors = final_data.get("errors", [])
        parser_errors = final_data.get("parser_errors", [])
        if parser_errors:
            self.logger.warning(
                "the parser raised the following errors:\n{}".format(
                    "\n\t".join(parser_errors)))
        if errors:
            self.logger.warning(
                "the calculation raised the following errors:\n{}".format(
                    "\n\t".join(errors)))

        # make output nodes
        self.out("results", Dict(dict=final_data))

        if pbs_error is not None:
            return pbs_error

        if stdout_error is not None:
            return stdout_error

        return ExitCode()
Exemplo n.º 29
0
    def parse(self, **kwargs):
        """
        Parse outputs, store results in database.
        """
        try:
            output_folder = self.retrieved
        except exceptions.NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        mainout_file = self.node.get_option("output_main_file_name")
        if mainout_file not in output_folder.list_object_names():
            return self.exit_codes.ERROR_OUTPUT_FILE_MISSING

        # parse the main output file and add nodes
        self.logger.info("parsing main out file")
        with output_folder.open(mainout_file) as handle:
            try:
                result_dict, exit_code = parse_file(
                    handle, parser_class=self.__class__.__name__)
            except Exception:
                traceback.print_exc()
                return self.exit_codes.ERROR_PARSING_STDOUT

        if result_dict["parser_errors"]:
            self.logger.warning(
                "the parser raised the following errors:\n{}".format(
                    "\n\t".join(result_dict["parser_errors"])))
        if result_dict["errors"]:
            self.logger.warning(
                "the calculation raised the following errors:\n{}".format(
                    "\n\t".join(result_dict["errors"])))

        if "structure_names.json" in self.node.list_object_names():
            result_dict["config_names"] = json.loads(
                self.node.get_object_content("structure_names.json"))

        # look a stderr for fortran warnings, etc, e.g. IEEE_INVALID_FLAG IEEE_OVERFLOW_FLAG IEEE_UNDERFLOW_FLAG
        stderr_file = self.node.get_option("output_stderr_file_name")
        if stderr_file in output_folder.list_object_names():
            with output_folder.open(stderr_file) as handle:
                stderr_content = handle.read()
                if stderr_content:
                    self.logger.warning(
                        "the calculation stderr file was not empty:")
                    self.logger.warning(stderr_content)
                    result_dict["warnings"].append(stderr_content.strip())

        exit_code_dump = self.extract_from_dump(output_folder)

        self.out("results", Dict(dict=result_dict))

        if exit_code is not None:
            return self.exit_codes[exit_code]
        elif exit_code_dump is not None and not self.node.get_option(
                "allow_create_potential_fail"):
            return self.exit_codes[exit_code_dump]
        return ExitCode()
Exemplo n.º 30
0
    def test_priority(self):
        """Test that the handlers are called in order of their `priority`."""
        attribute_key = 'handlers_called'

        class ArithmeticAddBaseWorkChain(BaseRestartWorkChain):
            """Implementation of a possible BaseRestartWorkChain for the ``ArithmeticAddCalculation``."""

            _process_class = ArithmeticAddCalculation

            # Register some handlers that should be called in order of 4 -> 3 -> 2 -> 1 but are on purpose registered in
            # a different order. When called, they should add their name to `handlers_called` attribute of the node.
            # This can then be checked after invoking `inspect_process` to ensure they were called in the right order
            @process_handler(priority=100)
            def handler_01(self, node):
                """Example handler returing ExitCode 100."""
                handlers_called = node.get_attribute(attribute_key, default=[])
                handlers_called.append('handler_01')
                node.set_attribute(attribute_key, handlers_called)
                return ProcessHandlerReport(False, ExitCode(100))

            @process_handler(priority=300)
            def handler_03(self, node):
                """Example handler returing ExitCode 300."""
                handlers_called = node.get_attribute(attribute_key, default=[])
                handlers_called.append('handler_03')
                node.set_attribute(attribute_key, handlers_called)
                return ProcessHandlerReport(False, ExitCode(300))

            @process_handler(priority=200)
            def handler_02(self, node):
                """Example handler returing ExitCode 200."""
                handlers_called = node.get_attribute(attribute_key, default=[])
                handlers_called.append('handler_02')
                node.set_attribute(attribute_key, handlers_called)
                return ProcessHandlerReport(False, ExitCode(200))

            @process_handler(priority=400)
            def handler_04(self, node):
                """Example handler returing ExitCode 400."""
                handlers_called = node.get_attribute(attribute_key, default=[])
                handlers_called.append('handler_04')
                node.set_attribute(attribute_key, handlers_called)
                return ProcessHandlerReport(False, ExitCode(400))

        child = ProcessNode()
        child.set_process_state(ProcessState.FINISHED)
        child.set_exit_status(400)
        process = ArithmeticAddBaseWorkChain()
        process.setup()
        process.ctx.iteration = 1
        process.ctx.children = [child]

        # Last called handler should be `handler_01` which returned `ExitCode(100)`
        assert process.inspect_process() == ExitCode(100)
        assert child.get_attribute(attribute_key, []) == [
            'handler_04', 'handler_03', 'handler_02', 'handler_01'
        ]