Esempio n. 1
0
    def _parse_stdout(self, out_folder):
        """CP2K output parser"""

        from aiida.orm import BandsData, Dict

        # pylint: disable=protected-access

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE
        if fname not in out_folder._repository.list_object_names():
            raise OutputParsingError("Cp2k output file not retrieved")

        abs_fn = os.path.join(
            out_folder._repository._get_base_folder().abspath, fname)

        with io.open(abs_fn, mode="r", encoding="utf-8") as fobj:
            result_dict = parse_cp2k_output(fobj)

        if "nwarnings" not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        if "kpoint_data" in result_dict:
            bnds = BandsData()
            bnds.set_kpoints(result_dict["kpoint_data"]["kpoints"])
            bnds.labels = result_dict["kpoint_data"]["labels"]
            bnds.set_bands(
                result_dict["kpoint_data"]["bands"],
                units=result_dict["kpoint_data"]["bands_unit"],
            )
            self.out("output_bands", bnds)
            del result_dict["kpoint_data"]

        self.out("output_parameters", Dict(dict=result_dict))
Esempio n. 2
0
    def _parse_stdout(self):
        """Advanced CP2K output file parser."""

        from aiida.orm import BandsData
        from aiida_cp2k.utils import parse_cp2k_output_advanced

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE  # pylint: disable=protected-access
        if fname not in self.retrieved.list_object_names():
            raise OutputParsingError("Cp2k output file not retrieved")

        try:
            output_string = self.retrieved.get_object_content(fname)
        except IOError:
            return self.exit_codes.ERROR_OUTPUT_STDOUT_READ

        result_dict = parse_cp2k_output_advanced(output_string)

        # nwarnings is the last thing to be printed in th eCP2K output file:
        # if it is not there, CP2K didn't finish properly
        if 'nwarnings' not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        if "aborted" in result_dict:
            return self.exit_codes.ERROR_OUTPUT_CONTAINS_ABORT

        # Compute the bandgap for Spin1 and Spin2 if eigen was parsed (works also with smearing!)
        if 'eigen_spin1_au' in result_dict:
            if result_dict['dft_type'] == "RKS":
                result_dict['eigen_spin2_au'] = result_dict['eigen_spin1_au']

            lumo_spin1_idx = result_dict['init_nel_spin1']
            lumo_spin2_idx = result_dict['init_nel_spin2']
            if (lumo_spin1_idx > len(result_dict['eigen_spin1_au'])-1) or \
               (lumo_spin2_idx > len(result_dict['eigen_spin2_au'])-1):
                #electrons jumped from spin1 to spin2 (or opposite): assume last eigen is lumo
                lumo_spin1_idx = len(result_dict['eigen_spin1_au']) - 1
                lumo_spin2_idx = len(result_dict['eigen_spin2_au']) - 1
            homo_spin1 = result_dict['eigen_spin1_au'][lumo_spin1_idx - 1]
            homo_spin2 = result_dict['eigen_spin2_au'][lumo_spin2_idx - 1]
            lumo_spin1 = result_dict['eigen_spin1_au'][lumo_spin1_idx]
            lumo_spin2 = result_dict['eigen_spin2_au'][lumo_spin2_idx]
            result_dict['bandgap_spin1_au'] = lumo_spin1 - homo_spin1
            result_dict['bandgap_spin2_au'] = lumo_spin2 - homo_spin2

        if "kpoint_data" in result_dict:
            bnds = BandsData()
            bnds.set_kpoints(result_dict["kpoint_data"]["kpoints"])
            bnds.labels = result_dict["kpoint_data"]["labels"]
            bnds.set_bands(
                result_dict["kpoint_data"]["bands"],
                units=result_dict["kpoint_data"]["bands_unit"],
            )
            self.out("output_bands", bnds)
            del result_dict["kpoint_data"]

        self.out("output_parameters", Dict(dict=result_dict))
        return None
Esempio n. 3
0
    def _parse_stdout(self, out_folder):
        """Advanced CP2K output file parser"""

        from aiida.orm import BandsData
        from .parser_functions import parse_cp2k_output_advanced

        # pylint: disable=protected-access

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE
        if fname not in out_folder._repository.list_object_names():
            raise OutputParsingError("Cp2k output file not retrieved")

        abs_fn = os.path.join(
            out_folder._repository._get_base_folder().abspath, fname)
        with io.open(abs_fn, mode="r", encoding="utf-8") as fobj:
            result_dict = parse_cp2k_output_advanced(fobj)

        # nwarnings is the last thing to be printed in th eCP2K output file:
        # if it is not there, CP2K didn't finish properly
        if 'nwarnings' not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        # Compute the bandgap for Spin1 and Spin2 if eigen was parsed (works also with smearing!)
        if 'eigen_spin1_au' in result_dict:
            if result_dict['dft_type'] == "RKS":
                result_dict['eigen_spin2_au'] = result_dict['eigen_spin1_au']

            lumo_spin1_idx = result_dict['init_nel_spin1']
            lumo_spin2_idx = result_dict['init_nel_spin2']
            if (lumo_spin1_idx > len(result_dict['eigen_spin1_au'])-1) or \
               (lumo_spin2_idx > len(result_dict['eigen_spin2_au'])-1):
                #electrons jumped from spin1 to spin2 (or opposite): assume last eigen is lumo
                lumo_spin1_idx = len(result_dict['eigen_spin1_au']) - 1
                lumo_spin2_idx = len(result_dict['eigen_spin2_au']) - 1
            homo_spin1 = result_dict['eigen_spin1_au'][lumo_spin1_idx - 1]
            homo_spin2 = result_dict['eigen_spin2_au'][lumo_spin2_idx - 1]
            lumo_spin1 = result_dict['eigen_spin1_au'][lumo_spin1_idx]
            lumo_spin2 = result_dict['eigen_spin2_au'][lumo_spin2_idx]
            result_dict['bandgap_spin1_au'] = lumo_spin1 - homo_spin1
            result_dict['bandgap_spin2_au'] = lumo_spin2 - homo_spin2

        if "kpoint_data" in result_dict:
            bnds = BandsData()
            bnds.set_kpoints(result_dict["kpoint_data"]["kpoints"])
            bnds.labels = result_dict["kpoint_data"]["labels"]
            bnds.set_bands(
                result_dict["kpoint_data"]["bands"],
                units=result_dict["kpoint_data"]["bands_unit"],
            )
            self.out("output_bands", bnds)
            del result_dict["kpoint_data"]

        self.out("output_parameters", Dict(dict=result_dict))
Esempio n. 4
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)
Esempio n. 5
0
    def parse_chi(self, filepath):
        """
        Parse the contents of the file {prefix}.chi.dat as written by a HpCalculation

        :param filepath: absolute filepath to the chi.dat output file
        :returns: dictionary with parsed contents
        """
        try:
            with open(filepath, 'r') as handle:
                data = handle.readlines()
        except IOError as exception:
            raise OutputParsingError(
                "could not read the '{}' output file".format(
                    os.path.basename(filepath)))

        result = {}
        blocks = {
            'chi0': [None, None],
            'chi1': [None, None],
        }

        for line_number, line in enumerate(data):
            if 'chi0' in line:
                blocks['chi0'][0] = line_number + 1

            if 'chi1' in line:
                blocks['chi0'][1] = line_number
                blocks['chi1'][0] = line_number + 1
                blocks['chi1'][1] = len(data)
                break

        if not all(sum(blocks.values(), [])):
            raise OutputParsingError(
                "could not determine beginning and end of all blocks in '{}'".
                format(os.path.basename(filepath)))

        for matrix_name in ('chi0', 'chi1'):
            matrix_block = blocks[matrix_name]
            matrix_data = data[matrix_block[0]:matrix_block[1]]
            matrix = numpy.matrix(self.parse_hubbard_matrix(matrix_data))
            result[matrix_name] = matrix

        return result
Esempio n. 6
0
def get_parsed_xml_doc(xml_path):

    from xml.dom import minidom

    try:
        xmldoc = minidom.parse(xml_path)
    except EOFError:
        raise OutputParsingError("Faulty Xml File")

    return xmldoc
Esempio n. 7
0
    def _parse_stdout(self, out_folder):
        """Basic CP2K output file parser"""

        from aiida_cp2k.utils import parse_cp2k_output

        # pylint: disable=protected-access

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE
        if fname not in out_folder._repository.list_object_names():
            raise OutputParsingError("Cp2k output file not retrieved")

        abs_fn = os.path.join(out_folder._repository._get_base_folder().abspath, fname)

        with io.open(abs_fn, mode="r", encoding="utf-8") as fobj:
            result_dict = parse_cp2k_output(fobj)

        if 'nwarnings' not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        self.out("output_parameters", Dict(dict=result_dict))
Esempio n. 8
0
    def _parse_stdout(self, out_folder):
        """BSSE CP2K output file parser"""

        from .parser_functions import parse_cp2k_output_bsse

        # pylint: disable=protected-access

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE
        if fname not in out_folder._repository.list_object_names():
            raise OutputParsingError("Cp2k output file not retrieved")

        abs_fn = os.path.join(
            out_folder._repository._get_base_folder().abspath, fname)
        with io.open(abs_fn, mode="r", encoding="utf-8") as fobj:
            result_dict = parse_cp2k_output_bsse(fobj)

        # nwarnings is the last thing to be printed in the CP2K output file:
        # if it is not there, CP2K didn't finish properly
        if 'nwarnings' not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        self.out("output_parameters", Dict(dict=result_dict))
Esempio n. 9
0
def get_parsed_xml_doc(xml_path):

    from xml.dom import minidom

    try:
        xmldoc = minidom.parse(xml_path)
    except:
        raise OutputParsingError("Faulty Xml File")
        #xmldoc = None

    # We might want to add some extra consistency checks

    return xmldoc
Esempio n. 10
0
    def _parse_stdout(self):
        """Very advanced CP2K output file parser."""

        from cp2k_output_tools import parse_iter

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE  # pylint: disable=protected-access
        if fname not in self.retrieved.list_object_names():
            raise OutputParsingError("CP2K output file not retrieved.")

        try:
            output_string = self.retrieved.get_object_content(fname)
        except IOError:
            return self.exit_codes.ERROR_OUTPUT_STDOUT_READ

        result_dict = {}

        # the CP2K output parser is a block-based parser return blocks of data, each under a block key
        # merge them into one dict
        for match in parse_iter(output_string, key_mangling=True):
            result_dict.update(match)

        # nwarnings is the last thing to be printed in the CP2K output file:
        # if it is not there, CP2K didn't finish properly most likely
        if 'nwarnings' not in result_dict:
            raise OutputParsingError("CP2K did not finish properly")

        if "aborted" in result_dict:
            return self.exit_codes.ERROR_OUTPUT_CONTAINS_ABORT

        try:
            # the cp2k-output-tools parser is more hierarchical, be compatible with
            # the basic parser here and provide the total force eval energy as energy
            result_dict["energy"] = result_dict["energies"]["total_force_eval"]
            result_dict["energy_units"] = "a.u."
        except KeyError:
            pass

        self.out("output_parameters", Dict(dict=result_dict))
        return None
Esempio n. 11
0
    def parse(self, **kwargs):
        """Receives in input a dictionary of retrieved nodes. Does all the logic here."""

        try:
            out_folder = self.retrieved
        except NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        fname = self.node.process_class.OUTPUT_FILE

        if fname not in out_folder._repository.list_object_names():
            raise OutputParsingError("Gaussian output file not retrieved")

        with open(
                os.path.join(out_folder._repository._get_base_folder().abspath,
                             fname), 'r') as log_file:
            full_output_log = log_file.read()

        # Split the output log according to link1 sections
        output_log_split = [""]
        for line_i, log_line in enumerate(full_output_log.splitlines()):
            output_log_split[-1] += log_line + "\n"
            if "Normal termination" in log_line:
                output_log_split.append("")
        output_log_split = output_log_split[:-1]

        for section_i, out_log in enumerate(output_log_split):
            # pymatgen can only parse files,
            # so create temporary ones for each link1 log section
            tempf = tempfile.NamedTemporaryFile(delete=False, mode='w')
            tempf.write(out_log)
            tempf.close()
            outobj = mgaus.GaussianOutput(tempf.name)
            parsed_dict = outobj.as_dict()

            # in case of main section, don't add prefix
            output_prefix = ""
            if section_i > 0:
                output_prefix = "link1_sec%d_" % section_i

            # in case of geometry optimization, return the geometry as a separated node
            if 'opt' in parsed_dict['input']['route']:
                structure = StructureData(
                    pymatgen_molecule=outobj.final_structure)
                self.out('%soutput_structure' % output_prefix, structure)

            self.out("%soutput_parameters" % output_prefix,
                     Dict(dict=parsed_dict))

        return ExitCode(0)
Esempio n. 12
0
    def __init__(self, calc_node):
        """
        Initialize Parser instance
        """
        calc_entry_points = ('crystal_dft.properties', )

        calc_cls = [
            CalculationFactory(entry_point).get_name()
            for entry_point in calc_entry_points
        ]
        if calc_node.process_label not in calc_cls:
            raise OutputParsingError(
                "{}: Unexpected calculation type to parse: {}".format(
                    self.__class__.__name__, calc_node.__class__.__name__))

        self._nodes = []
        super(PropertiesParser, self).__init__(calc_node)
Esempio n. 13
0
    def _parse_stdout(self):
        """BSSE CP2K output file parser"""

        fname = self.node.get_attribute('output_filename')

        if fname not in self.retrieved.list_object_names():
            return self.exit_codes.ERROR_OUTPUT_STDOUT_MISSING

        try:
            output_string = self.retrieved.get_object_content(fname)
        except IOError:
            return self.exit_codes.ERROR_OUTPUT_STDOUT_READ

        result_dict = parse_cp2k_output_bsse(output_string)

        # nwarnings is the last thing to be printed in the CP2K output file:
        # if it is not there, CP2K didn't finish properly
        if 'nwarnings' not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        self.out("output_parameters", Dict(dict=result_dict))
        return None
Esempio n. 14
0
    def __init__(self, calc_node):
        """
        Initialize Parser instance
        """

        # check for valid calculation node class
        calc_entry_points = ['crystal_dft.serial',
                             'crystal_dft.parallel'
                             ]
        calc_cls = {CalculationFactory(entry_point).get_name(): entry_point for entry_point in calc_entry_points}
        if calc_node.process_label not in calc_cls:
            raise OutputParsingError("{}: Unexpected calculation type to parse: {}".format(
                self.__class__.__name__,
                calc_node.__class__.__name__
            ))

        self.is_parallel = "parallel" in calc_cls[calc_node.process_label]
        self.stdout_parser = None
        self.converged_ionic = None
        self.converged_electronic = None

        super(CrystalParser, self).__init__(calc_node)
Esempio n. 15
0
    def parse(self, **kwargs):
        """
        It uses cclib to get the output dictionary.
        Herein, we report all parsed data by ccli in output_dict which
        can be parsed further at workchain level.
        If it would be an optimization run, the relaxed structure also will
        be stored under relaxed_structure key.
        """
        opt_run = False

        try:
            out_folder = self.retrieved
        except NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        fname_out = self.node.process_class._OUTPUT_FILE  #pylint: disable=protected-access
        fname_relaxed = self.node.process_class._RELAX_COORDS_FILE  #pylint: disable=protected-access
        # fname_traj = self.node.process_class._TRAJECTORY_FILE  #pylint: disable=protected-access
        # fname_hessian = self.node.process_class._HESSIAN_FILE  #pylint: disable=protected-access

        if fname_out not in out_folder._repository.list_object_names():  #pylint: disable=protected-access
            raise OutputParsingError('Orca output file not retrieved')

        parsed_obj = io.ccread(
            os.path.join(out_folder._repository._get_base_folder().abspath,
                         fname_out))  #pylint: disable=protected-access

        parsed_dict = parsed_obj.getattributes()

        def _remove_nan(parsed_dictionary: dict) -> dict:
            """cclib parsed object may contain nan values in ndarray.
            It will results in an exception in aiida-core which comes from
            json serialization and thereofore dictionary cannot be stored.
            This removes nan values to remedy this issue.
            See:
            https://github.com/aiidateam/aiida-core/issues/2412
            https://github.com/aiidateam/aiida-core/issues/3450

            Args:
                parsed_dictionary (dict): Parsed dictionary from `cclib`

            Returns:
                dict: Parsed dictionary without `NaN`
            """

            for key, value in parsed_dictionary.items():
                if isinstance(value, np.ndarray):
                    non_nan_value = np.nan_to_num(value,
                                                  nan=123456789,
                                                  posinf=2e308,
                                                  neginf=-2e308)
                    parsed_dictionary.update({key: non_nan_value})

            return parsed_dictionary

        output_dict = _remove_nan(parsed_dict)

        keywords = output_dict['metadata']['keywords']

        #opt_pattern = re.compile('(GDIIS-)?[CZ?OPT]', re.IGNORECASE)

        #if any(re.match(opt_pattern, keyword) for keyword in keywords):
        #opt_run = True
        opt_run = False
        for keyword in keywords:
            if 'opt' in keyword.lower():
                opt_run = True

        if opt_run:
            relaxed_structure = StructureData(
                pymatgen_molecule=Molecule.from_file(
                    os.path.join(
                        out_folder._repository._get_base_folder().abspath,
                        fname_relaxed))  #pylint: disable=protected-access
            )
            # relaxation_trajectory = SinglefileData(
            #     file=os.path.join(out_folder._repository._get_base_folder().abspath, fname_traj)  #pylint: disable=protected-access
            # )
            self.out('relaxed_structure', relaxed_structure)
            # self.out('relaxation_trajectory', relaxation_trajectory)

        pt = PeriodicTable()  #pylint: disable=invalid-name

        output_dict['elements'] = [
            pt.element[Z] for Z in output_dict['atomnos'].tolist()
        ]

        self.out('output_parameters', Dict(dict=output_dict))

        return ExitCode(0)
Esempio n. 16
0
    def parse_hubbard(self, filepath):
        """
        Parse the contents of the file {prefix}.Hubbard_U.dat as written by a HpCalculation

        :param filepath: absolute filepath to the Hubbard_U.dat output file
        :returns: dictionary with parsed contents
        """
        try:
            with open(filepath, 'r') as handle:
                data = handle.readlines()
        except IOError as exception:
            raise OutputParsingError(
                "could not read the '{}' output file".format(
                    os.path.basename(filepath)))

        result = {'hubbard_U': {'sites': []}}
        blocks = {
            'chi0': [None, None],
            'chi1': [None, None],
            'chi0_inv': [None, None],
            'chi1_inv': [None, None],
            'hubbard': [None, None],
        }

        for line_number, line in enumerate(data):

            if 'site n.' in line:
                parsed = False
                subline_number = line_number + 1
                while not parsed:
                    subline = data[subline_number].strip()
                    if subline:
                        subline_number += 1
                        subdata = subline.split()
                        result['hubbard_U']['sites'].append({
                            'index':
                            subdata[0],
                            'type':
                            subdata[1],
                            'kind':
                            subdata[2],
                            'spin':
                            subdata[3],
                            'new_type':
                            subdata[4],
                            'new_kind':
                            subdata[5],
                            'value':
                            subdata[6],
                        })
                    else:
                        parsed = True

            if 'chi0 matrix' in line:
                blocks['chi0'][0] = line_number + 1

            if 'chi1 matrix' in line:
                blocks['chi0'][1] = line_number
                blocks['chi1'][0] = line_number + 1

            if 'chi0^{-1} matrix' in line:
                blocks['chi1'][1] = line_number
                blocks['chi0_inv'][0] = line_number + 1

            if 'chi1^{-1} matrix' in line:
                blocks['chi0_inv'][1] = line_number
                blocks['chi1_inv'][0] = line_number + 1

            if 'Hubbard matrix' in line:
                blocks['chi1_inv'][1] = line_number
                blocks['hubbard'][0] = line_number + 1
                blocks['hubbard'][1] = len(data)
                break

        if not all(sum(blocks.values(), [])):
            raise OutputParsingError(
                "could not determine beginning and end of all matrix blocks in '{}'"
                .format(os.path.basename(filepath)))

        for matrix_name in ('chi0', 'chi1', 'chi0_inv', 'chi1_inv', 'hubbard'):
            matrix_block = blocks[matrix_name]
            matrix_data = data[matrix_block[0]:matrix_block[1]]
            matrix = self.parse_hubbard_matrix(matrix_data)

            if len(set(matrix.shape)) != 1:
                raise OutputParsingError(
                    "the matrix '{}' in '{}'' is not square but has shape {}".
                    format(matrix_name, os.path.basename(filepath),
                           matrix.shape))

            result[matrix_name] = matrix

        return result
Esempio n. 17
0
    def parse(self, **kwargs):  # noqa: MC0001  - is mccabe too complex funct -
        """
        Receives in input a dictionary of retrieved nodes. Does all the logic here.
        """
        from aiida.engine import ExitCode

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

        try:
            output_folder = self.retrieved
        except exceptions.NotExistent:
            raise OutputParsingError("Folder not retrieved")

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

        if xml_path is None:
            raise OutputParsingError("Xml file not retrieved")
        xmldoc = get_parsed_xml_doc(xml_path)
        result_dict = get_dict_from_xml_doc(xmldoc)

        if output_path is None:
            raise OutputParsingError("output file not retrieved")

        output_dict = dict(
            list(result_dict.items()) + list(parser_info.items()))

        warnings_list = []

        if json_path is not None:
            from .json_time import get_timing_info
            global_time, timing_decomp = get_timing_info(json_path)
            if global_time is None:
                warnings_list.append(["Cannot fully parse the time.json file"])
            else:
                output_dict["global_time"] = global_time
                output_dict["timing_decomposition"] = timing_decomp

        if basis_enthalpy_path is not None:
            the_file = open(basis_enthalpy_path)
            bas_enthalpy = float(the_file.read().split()[0])
            the_file.close()
            output_dict["basis_enthalpy"] = bas_enthalpy
            output_dict["basis_enthalpy_units"] = "eV"
        else:
            warnings_list.append(["BASIS_ENTHALPY file not retrieved"])

        have_errors_to_analyse = False
        if messages_path is None:
            # Perhaps using an old version of Siesta
            warnings_list.append([
                'WARNING: No MESSAGES file, could not check if calculation terminated correctly'
            ])
        else:
            have_errors_to_analyse = True
            #succesful when "INFO: Job completed" is present in message files
            succesful, from_message = self._get_warnings_from_file(
                messages_path)
            warnings_list.append(from_message)
        output_dict["warnings"] = warnings_list

        # An output_parametrs port is always return, even if only parser's info are present
        output_data = Dict(dict=output_dict)
        self.out('output_parameters', output_data)

        #
        # When using floating sites, Siesta associates 'atomic positions' to them, and
        # the structure and forces in the XML file include these fake atoms.
        # In order to return physical structures and forces, we need to remove them.
        # Recall that the input structure is the physical one, and the floating sites
        # are specified in the 'basis' input
        #
        physical_structure = self.node.inputs.structure
        number_of_real_atoms = len(physical_structure.sites)

        # If the structure has changed, save it
        #
        if output_dict['variable_geometry']:
            in_struc = self.node.inputs.structure
            # The next function never fails. If problems arise, the initial structure is
            # returned. The input structure is also necessary because the CML file
            # traditionally contains only the atomic symbols and not the site names.
            # The returned structure does not have any floating atoms, they are removed
            # in the `get_last_structure` call.
            success, out_struc = get_last_structure(xmldoc, in_struc)
            if not success:
                self.logger.warning(
                    "Problem in parsing final structure, returning inp structure in output_structure"
                )

            self.out('output_structure', out_struc)

        # Attempt to parse forces and stresses. In case of failure "None" is returned.
        # Therefore the function never crashes
        forces, stress = get_final_forces_and_stress(xmldoc)
        if forces is not None and stress is not None:
            from aiida.orm import ArrayData
            arraydata = ArrayData()
            arraydata.set_array('forces',
                                np.array(forces[0:number_of_real_atoms]))
            arraydata.set_array('stress', np.array(stress))
            self.out('forces_and_stress', arraydata)

        #Attempt to parse the ion files. Files ".ion.xml" are not produced by siesta if ions file are used
        #in input (`user-basis = T`). This explains the first "if" statement. The SiestaCal input is called
        #`ions__El` (El is the element label) therefore we look for the str "ions" in any of the inputs name.
        if not any(["ions" in inp for inp in self.node.inputs]):  #pylint: disable=too-many-nested-blocks
            from aiida_siesta.data.ion import IonData
            ions = {}
            #Ions from the structure
            in_struc = self.node.inputs.structure
            for kind in in_struc.get_kind_names():
                ion_file_name = kind + ".ion.xml"
                if ion_file_name in output_folder._repository.list_object_names(
                ):
                    ion_file_path = os.path.join(
                        output_folder._repository._get_base_folder().abspath,
                        ion_file_name)
                    ions[kind] = IonData(ion_file_path)
                else:
                    self.logger.warning(f"no ion file retrieved for {kind}")
            #Ions from floating_sites
            if "basis" in self.node.inputs:
                basis_dict = self.node.inputs.basis.get_dict()
                if "floating_sites" in basis_dict:
                    floating_kinds = []
                    for orb in basis_dict["floating_sites"]:
                        if orb["name"] not in floating_kinds:
                            floating_kinds.append(orb["name"])
                            ion_file_name = orb["name"] + ".ion.xml"
                            if ion_file_name in output_folder._repository.list_object_names(
                            ):
                                ion_file_path = os.path.join(
                                    output_folder._repository._get_base_folder(
                                    ).abspath, ion_file_name)
                                ions[orb["name"]] = IonData(ion_file_path)
                            else:
                                self.logger.warning(
                                    f"no ion file retrieved for {orb['name']}")
            #Return the outputs
            if ions:
                self.out('ion_files', ions)

        # Error analysis
        if have_errors_to_analyse:
            # No metter if "INFO: Job completed" is present (succesfull) or not, we check for known
            # errors. They might apprear as WARNING (therefore with succesful True) or FATAL
            # (succesful False)
            for line in from_message:
                if u'split options' in line:
                    min_split = get_min_split(output_path)
                    if min_split:
                        self.logger.error(
                            "Error in split_norm option. Minimum value is {}".
                            format(min_split))
                        return self.exit_codes.SPLIT_NORM
                if u'sys::die' in line:
                    #This is the situation when siesta dies with no specified error
                    #to be reported in "MESSAGES", unfortunately some interesting cases
                    #are treated in this way, we explore the .out file for more insights.
                    if is_polarization_problem(output_path):
                        return self.exit_codes.BASIS_POLARIZ
                if u'SCF_NOT_CONV' in line:
                    return self.exit_codes.SCF_NOT_CONV
                if u'GEOM_NOT_CONV' in line:
                    return self.exit_codes.GEOM_NOT_CONV

        #Because no known error has been found, attempt to parse bands if requested
        if bands_path is None:
            if "bandskpoints" in self.node.inputs:
                return self.exit_codes.BANDS_FILE_NOT_PRODUCED
        else:
            #bands, coords = self._get_bands(bands_path)
            try:
                bands = self._get_bands(bands_path)
            except (ValueError, IndexError):
                return self.exit_codes.BANDS_PARSE_FAIL
            from aiida.orm import BandsData
            arraybands = BandsData()
            #Reset the cell for KpointsData of bands, necessary
            #for bandskpoints without cell and if structure changed
            bkp = self.node.inputs.bandskpoints.clone()
            if output_dict['variable_geometry']:
                bkp.set_cell_from_structure(out_struc)
            else:
                bkp.set_cell_from_structure(self.node.inputs.structure)
            arraybands.set_kpointsdata(bkp)
            arraybands.set_bands(bands, units="eV")
            self.out('bands', arraybands)
            #bandsparameters = Dict(dict={"kp_coordinates": coords})
            #self.out('bands_parameters', bandsparameters)

        #At the very end, return a particular exit code if "INFO: Job completed"
        #was not present in the MESSAGES file, but no known error is detected.
        if have_errors_to_analyse:
            if not succesful:
                self.logger.error(
                    'The calculation finished without "INFO: Job completed", but no '
                    'error could be processed. Might be that the calculation was killed externally'
                )
                return self.exit_codes.UNEXPECTED_TERMINATION

        return ExitCode(0)
Esempio n. 18
0
    def _fetch_output_files(self, out_folder):
        """
        Checks the output folder for standard output and standard error
        files, returns their absolute paths on success.

        :param retrieved: A dictionary of retrieved nodes, as obtained from the
          parser.
        """
        # from aiida.common.datastructures import calc_states
        from aiida.common.exceptions import InvalidOperation
        import os

        list_of_files = out_folder._repository.list_object_names()

        output_path = None
        messages_path = None
        xml_path = None
        json_path = None
        bands_path = None

        if self.node.get_option('output_filename') in list_of_files:
            output_path = os.path.join(
                out_folder._repository._get_base_folder().abspath,
                self.node.get_option('output_filename'))
        else:
            raise OutputParsingError("Output file not retrieved")

        if self.node.process_class._DEFAULT_XML_FILE in list_of_files:
            xml_path = os.path.join(
                out_folder._repository._get_base_folder().abspath,
                self.node.process_class._DEFAULT_XML_FILE)
        else:
            raise OutputParsingError("Xml file not retrieved")

        if self.node.process_class._DEFAULT_JSON_FILE in list_of_files:
            json_path = os.path.join(
                out_folder._repository._get_base_folder().abspath,
                self.node.process_class._DEFAULT_JSON_FILE)
#        else:
#            raise OutputParsingError("json file not retrieved")

        if self.node.process_class._DEFAULT_MESSAGES_FILE in list_of_files:
            messages_path = os.path.join(
                out_folder._repository._get_base_folder().abspath,
                self.node.process_class._DEFAULT_MESSAGES_FILE)
#        else:
#            raise OutputParsingError("message file not retrieved")

        if self.node.process_class._DEFAULT_BANDS_FILE in list_of_files:
            bands_path = os.path.join(
                out_folder._repository._get_base_folder().abspath,
                self.node.process_class._DEFAULT_BANDS_FILE)

        if bands_path is None:
            supposed_to_have_bandsfile = True
            try:
                self.node.inputs.bandskpoints
            except:
                supposed_to_have_bandsfile = False
            if supposed_to_have_bandsfile:
                raise OutputParsingError("bands file not retrieved")

        return output_path, messages_path, xml_path, json_path, bands_path
Esempio n. 19
0
    def _get_output_nodes(self, output_path, messages_path, xml_path,
                          json_path, bands_path):
        """
        Extracts output nodes from the standard output and standard error
        files. (And XML and JSON files)
        """
        from aiida.orm import TrajectoryData
        import re

        parser_version = '1.0.1'
        parser_info = {}
        parser_info['parser_info'] = 'AiiDA Siesta Parser V. {}'.format(
            parser_version)
        parser_info['parser_warnings'] = []

        #No need for checks anymore

        xmldoc = get_parsed_xml_doc(xml_path)

        in_struc = self.node.inputs.structure

        try:
            in_settings = self.node.inputs.settings
        except exceptions.NotExistent:
            in_settings = None

        result_dict = get_dict_from_xml_doc(xmldoc)

        # Add timing information
        #if json_path is None:
        ###TODO###
        #Not sure how to implement what once was logger.info
        #Also not sure I understood the purpose of this file
        if json_path is not None:
            from .json_time import get_timing_info
            global_time, timing_decomp = get_timing_info(json_path)
            if global_time is None:
                raise OutputParsingError(
                    "Cannot fully parse the time.json file")
            else:
                result_dict["global_time"] = global_time
                result_dict["timing_decomposition"] = timing_decomp

        # Add warnings
        if messages_path is None:
            # Perhaps using an old version of Siesta
            warnings_list = ['WARNING: No MESSAGES file...']
        else:
            successful, warnings_list = self.get_warnings_from_file(
                messages_path)

        ###TODO### or maybe not
        #How do we process this successfull booelan?

        result_dict["warnings"] = warnings_list

        # 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)

        # If the structure has changed, save it
        if is_variable_geometry(xmldoc):
            # Get the input structure to copy its site names,
            # as the CML file traditionally contained only the
            # atomic symbols.
            #
            struc = get_last_structure(xmldoc, in_struc)
            # result_list.append((self.get_linkname_outstructure(),struc))
            self.out(self.get_linkname_outstructure(), struc)

        # Save forces and stress in an ArrayData object
        forces, stress = get_final_forces_and_stress(xmldoc)

        if forces is not None and stress is not None:
            # from aiida.orm.nodes.array import ArrayData
            from aiida.orm import ArrayData
            arraydata = ArrayData()
            arraydata.set_array('forces', np.array(forces))
            arraydata.set_array('stress', np.array(stress))
            # result_list.append((self.get_linkname_fs_and_stress(),arraydata))
            self.out(self.get_linkname_fs_and_stress(), arraydata)

        # Parse band-structure information if available
        if bands_path is not None:
            bands, coords = self.get_bands(bands_path)
            from aiida.orm import BandsData
            arraybands = BandsData()
            f = self.node.inputs.bandskpoints  #Temporary workaround due to a bug
            #f._set_reciprocal_cell()         #in KpointData (issue #2749)
            arraybands.set_kpoints(f.get_kpoints(cartesian=True))
            arraybands.labels = f.labels
            arraybands.set_bands(bands, units="eV")
            self.out(self.get_linkname_bands(), arraybands)
            bandsparameters = Dict(dict={"kp_coordinates": coords})
            self.out(self.get_linkname_bandsparameters(), bandsparameters)

        return result_dict