Source code for aiida_gulp.parsers.parse_opt

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2019 Chris Sewell
#
# This file is part of aiida-gulp.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms and conditions
# of version 3 of the GNU Lesser General Public License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
"""
A parser to read output from a standard CRYSTAL17 run
"""
import traceback
import warnings
from ase.io import read as ase_read
from aiida.common import exceptions
from aiida.engine import ExitCode
from aiida.orm import Dict
from aiida.parsers.parser import Parser
from aiida.plugins import DataFactory

from aiida_gulp.parsers.raw.parse_output_std import parse_file
from aiida_gulp.parsers.raw.write_geometry import validate_1d_geometry


[docs]class GulpOptParser(Parser): """ Parser class for parsing output of a GULP single point energy calculation """
[docs] 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"]) ) ) # 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()) self.out("results", Dict(dict=result_dict)) out_structure, exit_code_structure = self.create_structure( result_dict, output_folder ) if out_structure is not None: self.out("structure", out_structure) if exit_code is not None: return self.exit_codes[exit_code] if exit_code_structure is not None: return self.exit_codes[exit_code_structure] return ExitCode()
[docs] def create_structure(self, results_dict, output_folder): """ create the output structure """ opt_type = results_dict.get("opt_type", None) if opt_type == "polymer": if "final_coords" not in results_dict: return None, "ERROR_STRUCTURE_PARSING" final_coords = results_dict.pop("final_coords") if "structure" not in self.node.inputs: self.logger.error("the input structure node is not set") return None, "ERROR_MISSING_INPUT_STRUCTURE" if not set(final_coords.keys()).issuperset(["id", "x", "y", "z", "label"]): self.logger.error( 'expected final_coords to contain ["id", "x", "y", "z", "label"]' ) return None, "ERROR_STRUCTURE_PARSING" if not final_coords["id"] == list(range(1, len(final_coords["id"]) + 1)): self.logger.error("the final_coords ids were not ordered 1,2,3,...") return None, "ERROR_STRUCTURE_PARSING" if ( not final_coords["label"] == self.node.inputs.structure.get_ase().get_chemical_symbols() ): self.logger.error( "the final_coords labels are != to the input structure symbols" ) return None, "ERROR_STRUCTURE_PARSING" try: validate_1d_geometry(self.node.inputs.structure) except Exception as err: self.logger.error(str(err)) return None, "ERROR_STRUCTURE_PARSING" out_structure = self.node.inputs.structure.clone() positions = [] for x, y, z in zip(final_coords["x"], final_coords["y"], final_coords["z"]): # x are fractional and y,z are cartesian positions.append([x * out_structure.cell[0][0], y, z]) out_structure.reset_sites_positions(positions) elif opt_type == "surface": self.logger.error( "creating output structures for surface optimisations has not yet been implemented" ) return None, "ERROR_STRUCTURE_PARSING" else: cif_file = self.node.get_option("out_cif_file_name") if cif_file not in output_folder.list_object_names(): self.logger.error("the output cif file is missing") return None, "ERROR_CIF_FILE_MISSING" # We do not use this method, since currently different kinds are set for each atom # see aiidateam/aiida_core#2942 # NOTE cif files are read as binary, by default, since aiida-core v1.0.0b3 # with output_folder.open(cif_file, mode="rb") as handle: # cif = DataFactory('cif')(file=handle) # structure = cif.get_structure(converter="ase") with warnings.catch_warnings(): # ase.io.read returns a warnings that can be ignored # UserWarning: crystal system 'triclinic' is not interpreted for space group 1. # This may result in wrong setting! warnings.simplefilter("ignore", UserWarning) with output_folder.open(cif_file, mode="r") as handle: atoms = ase_read(handle, index=":", format="cif")[-1] atoms.set_tags(0) if self.node.get_option("use_input_kinds"): if "structure" not in self.node.inputs: self.logger.error("the input structure node is not set") return None, "ERROR_MISSING_INPUT_STRUCTURE" in_structure = self.node.inputs.structure in_atoms = in_structure.get_ase() if in_atoms.get_chemical_symbols() != atoms.get_chemical_symbols(): self.logger.error( "the input and cif structures have different atomic configurations" ) return None, "ERROR_CIF_INCONSISTENT" out_structure = in_structure.clone() out_structure.set_cell(atoms.cell) out_structure.reset_sites_positions(atoms.positions) else: out_structure = DataFactory("structure")(ase=atoms) return out_structure, None