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