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 _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
def band_parser_legacy(band_dat, band_kpt, special_points, structure): # pylint: disable=too-many-locals """ Parsers the bands output data, along with the special points retrieved from the input kpoints to construct a BandsData object which is then returned. Cannot handle discontinuities in the kpath, if two points are assigned to same spot only one will be passed. Used for wannier90 < 3.0 :param band_dat: list of str with each str stores one line of aiida_band.dat file :param band_kpt: list of str with each str stores one line of aiida_band.kpt file :param special_points: special points to add labels to the bands a dictionary in the form expected in the input as described in the wannier90 documentation :return: BandsData object constructed from the input params, and a list contains warnings. """ import numpy as np from aiida.orm import BandsData from aiida.orm import KpointsData warnings = [] warnings.append(( "Note: no file named SEEDNAME_band.labelinfo.dat found. " "You are probably using a version of Wannier90 before 3.0. " "There, the labels associated with each k-points were not printed in output " "and there were also cases in which points were not calculated " "(see issue #195 on the Wannier90 GitHub page). " "I will anyway try to do my best to assign labels, " "but the assignment might be wrong " "(especially if there are path discontinuities).")) # imports the data out_kpt = np.genfromtxt(band_kpt, skip_header=1, usecols=(0, 1, 2)) out_dat = np.genfromtxt(band_dat, usecols=1) # reshaps the output bands out_dat = out_dat.reshape(len(out_kpt), (len(out_dat) // len(out_kpt)), order="F") # finds expected points of discontinuity kpath = special_points['path'] cont_break = [(i, (kpath[i - 1][1], kpath[i][0])) for i in range(1, len(kpath)) if kpath[i - 1][1] != kpath[i][0]] # finds the special points special_points_dict = special_points['point_coords'] # We set atol to 1e-5 because in the kpt file the coords are printed with fixed precision labels = [ (i, k) for k in special_points_dict for i in range(len(out_kpt)) if all( np.isclose(special_points_dict[k], out_kpt[i], rtol=0, atol=1.e-5)) ] labels.sort() # Checks and appends labels if discontinuity appends = [] for x in cont_break: # two cases the break is before or the break is after # if the break is before if labels[x[0]][1] != x[1][0]: # checks to see if the discontinuity was already there if labels[x[0] - 1] == x[1][0]: continue insert_point = x[0] new_label = x[1][0] kpoint = labels[x[0]][0] - 1 appends += [[insert_point, new_label, kpoint]] # if the break is after if labels[x[0]][1] != x[1][1]: # checks to see if the discontinuity was already there if labels[x[0] + 1] == x[1][1]: continue insert_point = x[0] + 1 new_label = x[1][1] kpoint = labels[x[0]][0] + 1 appends += [[insert_point, new_label, kpoint]] appends.sort() for i, append in enumerate(appends): labels.insert(append[0] + i, (append[2], six.text_type(append[1]))) bands = BandsData() k = KpointsData() k.set_cell_from_structure(structure) k.set_kpoints(out_kpt, cartesian=False) bands.set_kpointsdata(k) bands.set_bands(out_dat, units='eV') bands.labels = labels return bands, warnings
def band_parser(band_dat, band_kpt, band_labelinfo, structure): # pylint: disable=too-many-locals """ Parsers the bands output data to construct a BandsData object which is then returned. Used for wannier90 >= 3.0 :param band_dat: list of str with each str stores one line of aiida_band.dat file :param band_kpt: list of str with each str stores one line of aiida_band.kpt file :param band_labelinfo: list of str with each str stores one line in aiida_band.labelinfo.dat file :return: BandsData object constructed from the input params """ import numpy as np from aiida.orm import BandsData from aiida.orm import KpointsData warnings = [] # imports the data out_kpt = np.genfromtxt(band_kpt, skip_header=1, usecols=(0, 1, 2)) out_dat = np.genfromtxt(band_dat, usecols=1) # reshaps the output bands out_dat = out_dat.reshape(len(out_kpt), (len(out_dat) // len(out_kpt)), order="F") labels_dict = {} for line_idx, line in enumerate(band_labelinfo, start=1): if not line.strip(): continue try: # label, idx, xval, kx, ky, kz = line.split() label, idx, _, _, _, _ = line.split() except ValueError: warnings.append( ('Wrong number of items in line {} of the labelinfo file - ' 'I will not assign that label')).format(line_idx) continue try: idx = int(idx) except ValueError: warnings.append(( "Invalid value for the index in line {} of the labelinfo file, " "it's not an integer - I will not assign that label" )).format(line_idx) continue # I use a dictionary because there are cases in which there are # two lines for the same point (e.g. when I do a zero-length path, # from a point to the same point, just to have that value) # Note the -1 because in fortran indices are 1-based, in Python are # 0-based labels_dict[idx - 1] = label labels = [(key, labels_dict[key]) for key in sorted(labels_dict)] bands = BandsData() k = KpointsData() k.set_cell_from_structure(structure) k.set_kpoints(out_kpt, cartesian=False) bands.set_kpointsdata(k) bands.set_bands(out_dat, units='eV') bands.labels = labels return bands, warnings