def parse_state_card(corestateNode, iteration_node, parser_info=None): """ Parses the ONE core state card :params corestateNode: an etree element (node), of a fleur output corestate card :params iteration_node: an etree element, iteration node :params jspin: integer 1 or 2 :returns: a pythondict of type: .. code-block:: python {'eigenvalue_sum' : eigenvalueSum, 'corestates': states, 'spin' : spin, 'kin_energy' : kinEnergy, 'atomtype' : atomtype} """ ##### all xpath of density convergence card (maintain) ######## coreStates_xpath = 'coreStates' state_xpath = 'state' units_name = 'units' value_name = 'value' distance_name = 'distance' n_name = 'n' j_name = 'j' l_name = 'l' energy_name = 'energy' weight_name = 'weight' spin_name = 'spin' kinEnergy_name = 'kinEnergy' eigenvalueSum_name = 'eigValSum' lostElectrons_name = 'lostElectrons' atomtype_name = 'atomType' ####### if parser_info is None: parser_info = {'parser_warnings': []} atomtype = get_xml_attribute(corestateNode, atomtype_name, parser_info) kinEnergy = get_xml_attribute(corestateNode, kinEnergy_name, parser_info) vE2, suc = convert_to_float(kinEnergy, parser_info) eigenvalueSum = get_xml_attribute(corestateNode, eigenvalueSum_name, parser_info) vE2, suc = convert_to_float(eigenvalueSum, parser_info) spin = get_xml_attribute(corestateNode, spin_name, parser_info) #print('spin {}'.format(spin)) #states = corestateNode.xpath( #for state in states: # get all corestate tags, (atomtypes * spin) #corestateNodes = eval_xpath(iteration_node, coreStates_xpath, parser_info) # for every corestate tag parse the attributes # some only the first interation, then get all state tags of the corestate tag (atom depended) # parse each core state #Attention to spin states = [] corestates = eval_xpath2(corestateNode, state_xpath) #, parser_info) for corestate in corestates: # be careful that corestates is a list state_dict = {} n_state = get_xml_attribute(corestate, n_name, parser_info) l_state = get_xml_attribute(corestate, l_name, parser_info) j_state = get_xml_attribute(corestate, j_name, parser_info) energy, suc = convert_to_float( get_xml_attribute(corestate, energy_name, parser_info), parser_info) weight, suc = convert_to_float( get_xml_attribute(corestate, weight_name, parser_info), parser_info) state_dict = { 'n': n_state, 'l': l_state, 'j': j_state, 'energy': energy, 'weight': weight } states.append(state_dict) core_states = { 'eigenvalue_sum': eigenvalueSum, 'corestates': states, 'spin': spin, 'kin_energy': kinEnergy, 'atomtype': atomtype } return core_states
def get_res(self): """ Check how the last Fleur calculation went Parse some results. """ self.report('INFO: get results FLEUR') xpath_energy = '/fleurOutput/scfLoop/iteration/totalEnergy/@value' xpath_iter = '/fleurOutput/scfLoop/iteration' xpath_force = 'totalForcesOnRepresentativeAtoms/forceTotal' # be aware of magnetism xpath_distance = '/fleurOutput/scfLoop/iteration/densityConvergence/chargeDensity/@distance' overallchargedensity_xpath = ('/fleurOutput/scfLoop/iteration/densityConvergence/' 'overallChargeDensity/@distance') mode = self.ctx.wf_dict.get('mode') if self.ctx.parse_last: last_base_wc = self.ctx.last_base_wc fleur_calcjob = load_node(find_last_submitted_calcjob(last_base_wc)) walltime = last_base_wc.outputs.output_parameters.dict.walltime if isinstance(walltime, int): self.ctx.total_wall_time = self.ctx.total_wall_time + walltime with fleur_calcjob.outputs.retrieved.open(fleur_calcjob.process_class._OUTXML_FILE_NAME, 'r') as outxmlfile_opened: tree = etree.parse(outxmlfile_opened) root = tree.getroot() energies = eval_xpath2(root, xpath_energy) for energy in energies: self.ctx.total_energy.append(float(energy)) overall_distances = eval_xpath2(root, overallchargedensity_xpath) if not overall_distances: distances = eval_xpath2(root, xpath_distance) for distance in distances: self.ctx.distance.append(float(distance)) else: for distance in overall_distances: self.ctx.distance.append(float(distance)) if mode == 'force': iter_all = eval_xpath2(root, xpath_iter) for iteration in iter_all: forces = eval_xpath2(iteration, xpath_force) forces_in_iter = [] for force in forces: force_x = float(get_xml_attribute(force, 'F_x')) force_y = float(get_xml_attribute(force, 'F_y')) force_z = float(get_xml_attribute(force, 'F_z')) forces_in_iter.append(force_x) forces_in_iter.append(force_y) forces_in_iter.append(force_z) self.ctx.all_forces.append(forces_in_iter) else: errormsg = 'ERROR: scf wc was not successful, check log for details' self.control_end_wc(errormsg) return self.exit_codes.ERROR_FLEUR_CALCULATION_FAILED if not self.ctx.distance: # if fleur relaxes an already converged crystal it stops directly if mode == 'force': self.report('INFO: System already force converged, could not extract distance.') self.ctx.last_charge_density = None else: errormsg = 'ERROR: did not manage to extract charge density from the calculation' self.control_end_wc(errormsg) return self.exit_codes.ERROR_FLEUR_CALCULATION_FAILED else: self.ctx.last_charge_density = self.ctx.distance[-1]
def validate_nmmpmat(fleurinp_tree, nmmp_lines): """ Checks that the given nmmp_lines is valid with the given fleurinp_tree Checks that the number of blocks is as expected from the inp.xml and each block does not contain non-zero elements outside their size given by the orbital quantum number in the inp.xml. Additionally the occupations, i.e. diagonal elements are checked that they are in between 0 and the maximum possible occupation :param fleurinp_tree_copy: an xmltree that represents inp.xml :param nmmp_lines_copy: list of lines in the n_mmp_mat file :raises ValueError: if any of the above checks are violated. """ #First check the number of ldau procedures ldau_xpath = '/fleurInput/atomSpecies/species/ldaU' magnetism_xpath = '/fleurInput/calculationSetup/magnetism' #Get number of spins (TODO for develop version also read l_mtnocoPot) mag_elem = eval_xpath(fleurinp_tree, magnetism_xpath) nspins = convert_to_int(get_xml_attribute(mag_elem, 'jspins'), suc_return=False) all_ldau = eval_xpath2(fleurinp_tree, ldau_xpath) numRows = nspins * 14 * len(all_ldau) tol = 0.01 if nspins > 1: maxOcc = 1.0 else: maxOcc = 2.0 #Check that numRows matches the number of lines in nmmp_lines if nmmp_lines is not None: #Remove blank lines while '' in nmmp_lines: nmmp_lines.remove('') if numRows != len(nmmp_lines): raise ValueError('The number of lines in n_mmp_mat does not match the number expected from '+\ 'the inp.xml file.') else: return #Now check for each block if the numbers make sense #(no numbers outside the valid area and no nonsensical occupations) for ldau_index, ldau in enumerate(all_ldau): orbital = convert_to_int(get_xml_attribute(ldau, 'l'), suc_return=False) species_name = get_xml_attribute(ldau.getparent(), 'name') for spin in range(nspins): startRow = (spin * len(all_ldau) + ldau_index) * 14 for index in range(startRow, startRow + 14): currentLine = index - startRow currentRow = currentLine // 2 line = nmmp_lines[index].split(' ') while '' in line: line.remove('') nmmp = np.array([float(x) for x in line]) outside_val = False if abs(currentRow - 3) > orbital: if any(np.abs(nmmp) > 1e-12): outside_val = True if currentLine % 2 == 0: #m=-3 to m=0 real part if any(np.abs(nmmp[:(3 - orbital) * 2]) > 1e-12): outside_val = True else: #m=0 imag part to m=3 if any(np.abs(nmmp[orbital * 2 + 1:]) > 1e-12): outside_val = True if outside_val: raise ValueError(f'Found value outside of valid range in for species {species_name}, spin {spin+1}' f' and l={orbital}') invalid_diag = False if spin < 2: if currentRow - 3 <= 0 and currentLine % 2 == 0: if nmmp[currentRow * 2] < -tol or nmmp[currentRow * 2] > maxOcc + tol: invalid_diag = True else: if nmmp[(currentRow - 3) * 2 - 1] < -tol or nmmp[(currentRow - 3) * 2 - 1] > maxOcc + tol: invalid_diag = True if invalid_diag: raise ValueError(f'Found invalid diagonal element for species {species_name}, spin {spin+1}' f' and l={orbital}')
def set_nmmpmat(fleurinp_tree_copy, nmmp_lines_copy, species_name, orbital, spin,\ occStates=None, denmat=None, phi=None, theta=None): """Routine sets the block in the n_mmp_mat file specified by species_name, orbital and spin to the desired density matrix :param fleurinp_tree_copy: an xmltree that represents inp.xml :param nmmp_lines_copy: list of lines in the n_mmp_mat file :param species_name: string, name of the species you want to change :param orbital: integer, orbital quantum number of the LDA+U procedure to be modified :param spin: integer, specifies which spin block should be modified :param occStates: list, sets the diagonal elements of the density matrix and everything else to zero :param denmat: matrix, specify the density matrix explicitely :param phi: float, optional angle (radian), by which to rotate the density matrix before writing it :param theta: float, optional angle (radian), by which to rotate the density matrix before writing it :raises ValueError: If something in the input is wrong :raises KeyError: If no LDA+U procedure is found on a species """ #All lda+U procedures have to be considered since we need to keep the order ldau_xpath = '/fleurInput/atomSpecies/species/ldaU' magnetism_xpath = '/fleurInput/calculationSetup/magnetism' if species_name == 'all': species_xpath = '/fleurInput/atomSpecies/species' elif species_name[:4] == 'all-': #format all-<string> species_xpath = '/fleurInput/atomSpecies/species[contains(@name,"{}")]'.format(species_name[4:]) else: species_xpath = '/fleurInput/atomSpecies/species[@name = "{}"]'.format(species_name) all_species = eval_xpath2(fleurinp_tree_copy, species_xpath) #Get number of spins (TODO for develop version also read l_mtnocoPot) mag_elem = eval_xpath(fleurinp_tree_copy, magnetism_xpath) nspins = convert_to_int(get_xml_attribute(mag_elem, 'jspins'), suc_return=False) if spin > nspins: raise ValueError(f'Invalid input: spin {spin} requested, but input has only {nspins} spins') all_ldau = eval_xpath2(fleurinp_tree_copy, ldau_xpath) numRows = nspins * 14 * len(all_ldau) #Check that numRows matches the number of lines in nmmp_lines_copy #If not either there was an n_mmp_mat file present in Fleurinp before and a lda+u calculation #was added or removed or the n_mmp_mat file was initialized and after the fact lda+u procedures were added #or removed. In both cases the resolution of this modification is very involved so we throw an error if nmmp_lines_copy is not None: #Remove blank lines while '' in nmmp_lines_copy: nmmp_lines_copy.remove('') if numRows != len(nmmp_lines_copy): raise ValueError('The number of lines in n_mmp_mat does not match the number expected from '+\ 'the inp.xml file. Either remove the existing file before making modifications '+\ 'and only use set_nmmpmat after all modifications to the inp.xml') if phi is not None or theta is not None: if phi is None: phi = 0.0 if theta is None: theta = 0.0 d_wigner = get_wigner_matrix(orbital, phi, theta) for species in all_species: current_name = get_xml_attribute(species, 'name') #Determine the place at which the given U procedure occurs ldau_index = None for index, ldau in enumerate(all_ldau): ldau_species = get_xml_attribute(ldau.getparent(), 'name') ldau_orbital = convert_to_int(get_xml_attribute(ldau, 'l'), suc_return=False) if current_name == ldau_species and ldau_orbital == orbital: ldau_index = index if ldau_index is None: raise KeyError(f'No LDA+U procedure found on species {current_name} with l={orbital}') if occStates is not None: #diagonal density matrix denmatpad = np.zeros((7, 7), dtype=complex) #Fill out the outer states with zero occStatespad = np.zeros(7, dtype=complex) occStatespad[3 - orbital:4 + orbital] = occStates[:] for i, occ in enumerate(occStatespad): denmatpad[i, i] = occ elif denmat is not None: #density matrix is completely specified denmatpad = np.zeros((7, 7), dtype=complex) denmatpad[3 - orbital:4 + orbital, 3 - orbital:4 + orbital] = denmat else: raise ValueError('Invalid definition of density matrix. Provide either occStates or denmat') if phi is not None and theta is not None: #Rotate the density matrix denmatpad = d_wigner.T.conj().dot(denmatpad.dot(d_wigner)) #check if fleurinp has a specified n_mmp_mat file if not initialize it with 0 if nmmp_lines_copy is None: nmmp_lines_copy = [] for index in range(numRows): nmmp_lines_copy.append(''.join(map(str, [f'{0.0:20.13f}' for x in range(7)]))) #Select the right block from n_mmp_mat and overwrite it with denmatpad startRow = ((spin - 1) * len(all_ldau) + ldau_index) * 14 for index in range(startRow, startRow + 14): currentLine = index - startRow currentRow = currentLine // 2 if currentLine % 2 == 0: #Line ends with a real part nmmp_lines_copy[index] = ''.join(map(str, [f'{x.real:20.13f}{x.imag:20.13f}'\ for x in denmatpad[currentRow, :3]])) +\ f'{denmatpad[currentRow, 3].real:20.13f}' else: #Line begins with a imaginary part nmmp_lines_copy[index] = f'{denmatpad[currentRow, 3].imag:20.13f}' +\ ''.join(map(str, [f'{x.real:20.13f}{x.imag:20.13f}'\ for x in denmatpad[currentRow, 4:]])) return nmmp_lines_copy
def create_corehole_fleurinp(fleurinp, species, stateocc, pos=[], coreconfig='same', valenceconfig='same'): """ Removes an electron from the core and adds it to the valence band of the kind given econfig as in inp.xml [Kr] (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2) if position(pos) is given the electronConfig for the specifed position will be set. (or todo? econfig, either [Kr] 5s2 4d10 4f13 | 5p6 5d4 6s2 or [Kr] 2(5s1/2) 4(4d3/2) 6(4d5/2) 6(4f5/2) 8(4f7/2) |2(5p1/2) 4(5p3/2) 2(6s1/2) 2(5d3/2) 2(5d5/2)) occ tags already there will be untouched, unless the state is the same as given :param fleurinp:, an unstored! changes are done on this fleurinp fleurinpdata object # TODO alternatively stored? :param species:, string with species name :param stateocc: dict state tuples (spinup, spindown), exp: {'(5d3/2)' : (2.5, 0.0), '(4f7/2)' : (3.5 , 4.0)} :param pos: list of tuples of 3, pos=[(0.0, 0.0, 0.0), ...] :param coreconfig: string, e.g: [Kr] (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2), default='same' (same as current in inp.xml) :param valenceconfig, string, e.g.: (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) :return: the changes fleurinpData object """ ''' <electronConfig> <coreConfig>[Kr] (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2)</coreConfig> <valenceConfig>(5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2)</valenceConfig> <stateOccupation state="(5d3/2)" spinUp="2.00000000" spinDown=".00000000"/> <stateOccupation state="(5d5/2)" spinUp="2.00000000" spinDown=".00000000"/> </electronConfig> ''' from aiida_fleur.tools.xml_util import eval_xpath2, get_xml_attribute #from aiida_fleur.data.fleurinpmodifier import FleurinpModifier # or from fleurinp? FleurinpData = DataFactory('fleur.fleurinp') ########### all xpath maintain ########### ? needed? electronConfig_xpath = '/fleurInput/atomSpecies/species/electronConfig' species_xpath = '/fleurInput/atomSpecies/species' ##################### # test input, if something wrong return/ throw error # get electronConfig tag from fleurinp # create new tag from old tag and method input # return new fleurinp data # Best use fleurinp functions # Do it with xml or rather new_core = False new_valence = False # test input. if not isinstance(fleurinp, FleurinpData): print 'No fleurinp Data given to "create_valence_corehole"' return None # TODO throw error? if coreconfig != 'same': new_core = True if valenceconfig != 'same': new_valence = True #print stateocc species_tags = fleurinp.get_tag(species_xpath) #print species_tags for speci in species_tags: if get_xml_attribute(speci, 'name') == species: # change econfig = eval_xpath2(speci, 'electronConfig')[0] coreconfig = eval_xpath2(econfig, 'coreConfig') valenceconfig = eval_xpath2(econfig, 'valenceConfig') occupations = eval_xpath2(econfig, 'stateOccupation') for key, val in stateocc.iteritems(): #print key added = False for occ in occupations: name = get_xml_attribute(occ, 'state') #print name if name == key: added = True # override and break (can occur only once) occ.set('spinUp', str(val[0])) occ.set('spinDown', str(val[0])) break if not added: pass #st_inpchanges(change_dict) #alternative change_dict = {'coreConfig' : '', 'valenceConfig': ''} change_dict2 = {'state' : '', 'spinUp' : '', 'spinDown' : ''} change_dict3 = {'valenceElectrons' : ''} return fleurinp