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 example_bands_v2(fresh_aiida_env): """Example eigen values and occupations""" from aiida.orm import BandsData bdata = BandsData() bdata.set_kpoints([[0, 0, 0], [0.25, 0.25, 0.25]]) occ = np.array([[1, 1, 1, 0, 0, 0], [1, 1, 1, 0, 0, 0]], dtype=np.float64) eigen = np.array([[-1, -1, -1, 0, 0, 0], [-0.5, -0.5, -0.5, 0, 0, 0]]) bdata.set_bands(bands=eigen, occupations=occ) return bdata
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 bands_to_bandsdata(bands_info, kpoints, bands): """ Convert the result of parser_dot_bands into a BandsData object :param bands_info: A dictionary of the informations of the bands file. contains field such as eferemi, units, cell :param kpoints: An array of the kpoints of the bands, rows are (kindex, kx, ky, kz, weight) :param bands: The actual bands array :return: A BandsData object :rtype: ``aiida.orm.bands.data.array.bands.BandsData`` """ bands_node = BandsData() # Extract the index of the kpoints kpn_array = np.asarray(kpoints) k_index = kpn_array[:, 0] # We need to restore the order of the kpoints k_sort = np.argsort(k_index) # Sort the kpn_array kpn_array = kpn_array[k_sort] _weights = kpn_array[:, -1] kpts = kpn_array[:, 1:-1] bands_node.set_kpoints(kpts, weights=_weights) # Sort the bands to match the order of the kpoints bands_array = np.asarray(bands)[k_sort] # We need to swap the axes from kpt,spin,engs to spin,kpt,engs bands_array = bands_array.swapaxes(0, 1) # Squeeze the first dimension e.g when there is a single spin if bands_array.shape[0] == 1: bands_array = bands_array[0] bands_array = bands_array * units['Eh'] bands_info = dict(bands_info) # Create a copy # Convert the units for the fermi energies if isinstance(bands_info['efermi'], list): bands_info['efermi'] = [x * units['Eh'] for x in bands_info['efermi']] else: bands_info['efermi'] = bands_info['efermi'] * units['Eh'] bands_node.set_bands(bands_array, units="eV") # PBC is always true as this is PW DFT.... bands_node.set_cell(bands_info['cell'], pbc=(True, True, True)) # Store information from *.bands in the attributes # This is needs as we need to know the number of electrons # and the fermi energy for key, value in bands_info.items(): bands_node.set_attribute(key, value) return bands_node
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 bands_from_castepbin(seedname, fmanager): """ Acquire and prepare bands data from the castep_bin file instead """ with fmanager.open(seedname + '.castep_bin', 'rb') as handle: binfile = CastepbinFile(fileobj=handle) bands_node = BandsData() kidx = binfile.kpoints_indices sort_idx = np.argsort(kidx) # Generated sorted arrays kpoints = binfile.kpoints[sort_idx, :] weights = binfile.kpoint_weights[sort_idx].astype(float) eigenvalues = binfile.eigenvalues[:, sort_idx, :] occupancies = binfile.occupancies[:, sort_idx, :] efermi = binfile.fermi_energy bands_node.set_kpoints(kpoints, weights=weights) bands_node.set_bands(eigenvalues, occupations=occupancies, units="eV") bands_node.set_cell(binfile.cell, pbc=(True, True, True)) bands_node.set_attribute('efermi', efermi) return bands_node
def spin_dependent_subparser(out_info_dict): """This find the projection and bands arrays from the out_file and out_info_dict. Used to handle the different possible spin-cases in a convenient manner. :param out_info_dict: contains various technical internals useful in parsing :return: ProjectionData, BandsData parsed from out_file """ out_file = out_info_dict['out_file'] spin_down = out_info_dict['spin_down'] od = out_info_dict #using a shorter name for convenience # regular expressions needed for later parsing WaveFraction1_re = re.compile(r'\=(.*?)\*') # state composition 1 WaveFractionremain_re = re.compile(r'\+(.*?)\*') # state comp 2 FunctionId_re = re.compile(r'\#(.*?)\]') # state identity # primes arrays for the later parsing num_wfc = len(od['wfc_lines']) bands = np.zeros([od['k_states'], od['num_bands']]) projection_arrays = np.zeros([od['k_states'], od['num_bands'], num_wfc]) try: for i in range(od['k_states']): if spin_down: i += od['k_states'] # grabs band energy for j in range(i * od['num_bands'], (i + 1) * od['num_bands'], 1): out_ind = od['e_lines'][j] try: # post ~6.3 output format "e =" val = out_file[out_ind].split()[2] float(val) except ValueError: # pre ~6.3 output format? "==== e(" val = out_file[out_ind].split()[4] bands[i % od['k_states']][j % od['num_bands']] = val #subloop grabs pdos wave_fraction = [] wave_id = [] for k in range(od['e_lines'][j] + 1, od['psi_lines'][j], 1): out_line = out_file[k] wave_fraction += WaveFraction1_re.findall(out_line) wave_fraction += WaveFractionremain_re.findall(out_line) wave_id += FunctionId_re.findall(out_line) if len(wave_id) != len(wave_fraction): raise IndexError for l in range(len(wave_id)): wave_id[l] = int(wave_id[l]) wave_fraction[l] = float(wave_fraction[l]) #sets relevant values in pdos_array projection_arrays[i % od['k_states']][j % od['num_bands']][ wave_id[l] - 1] = wave_fraction[l] except IndexError: raise QEOutputParsingError( 'the standard out file does not comply with the official documentation.' ) bands_data = BandsData() # Attempts to retrieve the kpoints from the parent calc parent_calc = out_info_dict['parent_calc'] try: parent_kpoints = parent_calc.get_incoming( link_label_filter='kpoints').one().node except ValueError: raise QEOutputParsingError( 'The parent had no input kpoints! Cannot parse from this!') try: if len(od['k_vect']) != len(parent_kpoints.get_kpoints()): raise AttributeError bands_data.set_kpointsdata(parent_kpoints) except AttributeError: bands_data.set_kpoints(od['k_vect'].astype(float)) bands_data.set_bands(bands, units='eV') orbitals = out_info_dict['orbitals'] if len(orbitals) != np.shape(projection_arrays[0, 0, :])[0]: raise QEOutputParsingError('There was an internal parsing error, ' ' the projection array shape does not agree' ' with the number of orbitals') projection_data = ProjectionData() projection_data.set_reference_bandsdata(bands_data) projections = [projection_arrays[:, :, i] for i in range(len(orbitals))] # Do the bands_check manually here for projection in projections: if np.shape(projection) != np.shape(bands): raise AttributeError('Projections not the same shape as the bands') #insert here some logic to assign pdos to the orbitals pdos_arrays = spin_dependent_pdos_subparser(out_info_dict) energy_arrays = [out_info_dict['energy']] * len(orbitals) projection_data.set_projectiondata(orbitals, list_of_projections=projections, list_of_energy=energy_arrays, list_of_pdos=pdos_arrays, bands_check=False) # pdos=pdos_arrays return bands_data, projection_data
def _aiida_bands_data(self, data, cell, kpoints_dict): if not data: return False kpt_idx = sorted(data.keys()) # list of kpoint indices try: k_list = [kpoints_dict[i] for i in kpt_idx] # list of k-point triplet except KeyError: # kpoint triplets are not present (true for .qp and so on, can not use BandsData) # We use the internal Yambo Format [ [Eo_1, Eo_2,... ], ...[So_1,So_2,] ] # QP_TABLE [[ib_1,ik_1,isp_1] ,[ib_n,ik_n,isp_n]] # Each entry in DATA has corresponding legend in QP_TABLE that defines its details # like ib= Band index, ik= kpoint index, isp= spin polarization index. # Eo_1 => at ib_1, ik_1 isp_1. pdata = ArrayData() QP_TABLE = [] ORD = [] Eo = [] E_minus_Eo = [] So = [] Z = [] for ky in data.keys(): # kp == kpoint index as a string 1,2,.. for ind in range(len(data[ky]['Band'])): try: Eo.append(data[ky]['Eo'][ind]) except KeyError: pass try: E_minus_Eo.append(data[ky]['E-Eo'][ind]) except KeyError: pass try: So.append(data[ky]['Sc|Eo'][ind]) except KeyError: pass try: Z.append(data[ky]['Z'][ind]) except KeyError: pass ik = int(ky) ib = data[ky]['Band'][ind] isp = 0 if 'Spin_Pol' in list(data[ky].keys()): isp = data[ky]['Spin_Pol'][ind] QP_TABLE.append([ik, ib, isp]) pdata.set_array('Eo', numpy.array(Eo)) pdata.set_array('E_minus_Eo', numpy.array(E_minus_Eo)) pdata.set_array('So', numpy.array(So)) pdata.set_array('Z', numpy.array(Z)) pdata.set_array('qp_table', numpy.array(QP_TABLE)) return pdata quasiparticle_bands = BandsData() quasiparticle_bands.set_cell(cell) quasiparticle_bands.set_kpoints(k_list, cartesian=True) # labels will come from any of the keys in the nested kp_point data, # there is a uniform set of observables for each k-point, ie Band, Eo, ... # ***FIXME BUG does not seem to handle spin polarizes at all when constructing bandsdata*** bands_labels = [ legend for legend in sorted(data[list(data.keys())[0]].keys()) ] append_list = [[] for i in bands_labels] for kp in kpt_idx: for i in range(len(bands_labels)): append_list[i].append(data[kp][bands_labels[i]]) generalised_bands = [numpy.array(it) for it in append_list] quasiparticle_bands.set_bands(bands=generalised_bands, units='eV', labels=bands_labels) return quasiparticle_bands
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 _parse_gsr(self): """Abinit GSR parser.""" # Output GSR Abinit NetCDF file - Default name is aiidao_GSR.nc fname = f'{self.node.get_attribute("prefix")}o_GSR.nc' # Absolute path of the folder in which aiidao_GSR.nc is stored path = self.node.get_remote_workdir() if fname not in self.retrieved.list_object_names(): return self.exit_codes.ERROR_MISSING_OUTPUT_FILES with abilab.abiopen(path + '/' + fname) as gsr: gsr_data = { 'abinit_version': gsr.abinit_version, 'cart_stress_tensor': gsr.cart_stress_tensor.tolist(), 'cart_stress_tensor' + UNITS_SUFFIX: DEFAULT_STRESS_UNITS, 'is_scf_run': bool(gsr.is_scf_run), # 'cart_forces': gsr.cart_forces.tolist(), # 'cart_forces' + units_suffix: DEFAULT_FORCE_UNITS, 'forces': gsr.cart_forces.tolist(), # backwards compatibility 'forces' + UNITS_SUFFIX: DEFAULT_FORCE_UNITS, 'energy': float(gsr.energy), 'energy' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_localpsp': float(gsr.energy_terms.e_localpsp), 'e_localpsp' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_eigenvalues': float(gsr.energy_terms.e_eigenvalues), 'e_eigenvalues' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_ewald': float(gsr.energy_terms.e_ewald), 'e_ewald' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_hartree': float(gsr.energy_terms.e_hartree), 'e_hartree' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_corepsp': float(gsr.energy_terms.e_corepsp), 'e_corepsp' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_corepspdc': float(gsr.energy_terms.e_corepspdc), 'e_corepspdc' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_kinetic': float(gsr.energy_terms.e_kinetic), 'e_kinetic' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_nonlocalpsp': float(gsr.energy_terms.e_nonlocalpsp), 'e_nonlocalpsp' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_entropy': float(gsr.energy_terms.e_entropy), 'e_entropy' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'entropy': float(gsr.energy_terms.entropy), 'entropy' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_xc': float(gsr.energy_terms.e_xc), 'e_xc' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_xcdc': float(gsr.energy_terms.e_xcdc), 'e_xcdc' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_paw': float(gsr.energy_terms.e_paw), 'e_paw' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_pawdc': float(gsr.energy_terms.e_pawdc), 'e_pawdc' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_elecfield': float(gsr.energy_terms.e_elecfield), 'e_elecfield' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_magfield': float(gsr.energy_terms.e_magfield), 'e_magfield' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_fermie': float(gsr.energy_terms.e_fermie), 'e_fermie' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_sicdc': float(gsr.energy_terms.e_sicdc), 'e_sicdc' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_exactX': float(gsr.energy_terms.e_exactX), 'e_exactX' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'h0': float(gsr.energy_terms.h0), 'h0' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_electronpositron': float(gsr.energy_terms.e_electronpositron), 'e_electronpositron' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'edc_electronpositron': float(gsr.energy_terms.edc_electronpositron), 'edc_electronpositron' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e0_electronpositron': float(gsr.energy_terms.e0_electronpositron), 'e0_electronpositron' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'e_monopole': float(gsr.energy_terms.e_monopole), 'e_monopole' + UNITS_SUFFIX: DEFAULT_ENERGY_UNITS, 'pressure': float(gsr.pressure), 'pressure' + UNITS_SUFFIX: DEFAULT_STRESS_UNITS } try: # will return an integer 0 if non-magnetic calculation is run; convert it to a float total_magnetization = float(gsr.ebands.get_collinear_mag()) gsr_data['total_magnetization'] = total_magnetization gsr_data['total_magnetization' + UNITS_SUFFIX] = DEFAULT_MAGNETIZATION_UNITS except ValueError as valerr: # get_collinear_mag will raise ValueError if it doesn't know what to do if 'Cannot calculate collinear magnetization' in valerr.args[ 0]: pass else: raise valerr try: bands_data = BandsData() bands_data.set_kpoints(gsr.ebands.kpoints.get_cart_coords()) bands_data.set_bands(np.array(gsr.ebands.eigens), units=str(gsr.ebands.eigens.unit)) self.out('output_bands', bands_data) except: # pylint: disable=bare-except pass self.out('output_parameters', Dict(dict=gsr_data))