def test_set_inpchanges(self, inpxml_etree, name, path): from aiida_fleur.tools.xml_util import set_inpchanges, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) skip_paths = ['atomSpecies', 'atomGroups', 'bzIntegration', 'kPointCount', 'bulkLattice', 'bravaisMatrix', 'a1'] if any(x in path for x in skip_paths): pytest.skip('This attribute is not tested for FePt/inp.xml') elif name in self.xml_structure[11].keys(): set_inpchanges(etree, change_dict={name: 'test'}) if name not in ['relPos', 'absPos']: result = eval_xpath2(etree, path)[0] assert result.text == 'test' elif name in self.xml_structure[0]: set_inpchanges(etree, change_dict={name: 'T'}) result = eval_xpath2(etree, path + '/@{}'.format(name)) assert result[0] == 'T' elif name in self.xml_structure[4] or name in self.xml_structure[3]: set_inpchanges(etree, change_dict={name: 33}) result = eval_xpath2(etree, path + '/@{}'.format(name)) assert float(result[0]) == 33 elif name in self.xml_structure[5]: set_inpchanges(etree, change_dict={name: 'test'}) if name == 'xcFunctional': result = eval_xpath2(etree, path + '/@{}'.format('name')) else: result = eval_xpath2(etree, path + '/@{}'.format(name)) assert result[0] == 'test' else: raise BaseException('A switch that you want to set is not one of the supported types.' 'Or you made a mistake during new switch registration')
def test_delete_tag(inpxml_etree): from aiida_fleur.tools.xml_util import delete_tag, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos') != [] delete_tag(etree, '/fleurInput/atomGroups/atomGroup/filmPos') assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos') == []
def test_delete_att(inpxml_etree): from aiida_fleur.tools.xml_util import delete_att, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos/@label')[0] == ' 222' delete_att(etree, '/fleurInput/atomGroups/atomGroup/filmPos', 'label') assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos/@label') == []
def test_xml_set_text(inpxml_etree): from aiida_fleur.tools.xml_util import xml_set_text, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) second_text = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text xml_set_text(etree, '/fleurInput/atomGroups/atomGroup/filmPos', 'test_text') assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[0].text == 'test_text' assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text == second_text
def get_res(self): """ Check how the last Fleur calculation went Parse some results. """ # TODO maybe do this different # or if complexer output node exists take from there. xpath_energy = '/fleurOutput/scfLoop/iteration/totalEnergy/@value' xpath_distance = '/fleurOutput/scfLoop/iteration/densityConvergence/chargeDensity/@distance' # be aware of magnetism #densityconvergence_xpath = 'densityConvergence' #chargedensity_xpath = 'densityConvergence/chargeDensity' #overallchargedensity_xpath = 'densityConvergence/overallChargeDensity' #spindensity_xpath = 'densityConvergence/spinDensity' if self.ctx.successful: #self.report('last calc successful = {}'.format(self.ctx.successful)) last_calc = self.ctx.last_calc ''' spin = get_xml_attribute(eval_xpath(root, magnetism_xpath), jspin_name) charge_densitys = eval_xpath(iteration_node, chargedensity_xpath) charge_density1 = get_xml_attribute(charge_densitys[0], distance_name) write_simple_outnode( charge_density1, 'float', 'charge_density1', simple_data) charge_density2 = get_xml_attribute(charge_densitys[1], distance_name) write_simple_outnode( charge_density2, 'float', 'charge_density2', simple_data) spin_density = get_xml_attribute( eval_xpath(iteration_node, spindensity_xpath), distance_name) write_simple_outnode( spin_density, 'float', 'spin_density', simple_data) overall_charge_density = get_xml_attribute( eval_xpath(iteration_node, overallchargedensity_xpath), distance_name) write_simple_outnode( overall_charge_density, 'float', 'overall_charge_density', simple_data) ''' #TODO: dangerous, can fail, error catching outxmlfile = last_calc.out.output_parameters.dict.outputfile_path #outpara = last_calc.get('output_parameters', None) #outxmlfile = outpara.dict.outputfile_path tree = etree.parse(outxmlfile) root = tree.getroot() energies = eval_xpath2(root, xpath_energy) for energy in energies: self.ctx.total_energy.append(float(energy)) distances = eval_xpath2(root, xpath_distance) for distance in distances: self.ctx.distance.append(float(distance)) else: errormsg = 'ERROR: scf wc was not successful, check log for details' self.control_end_wc(errormsg) return # otherwise this will lead to erros further down
def test_shift_species_label(self, inpxml_etree, att_name, tag): from aiida_fleur.tools.xml_util import shift_value_species_label, eval_xpath2 import math etree = inpxml_etree(TEST_INP_XML_PATH) path = '/fleurInput/atomSpecies/species[@name = "Fe-1"]/' + tag + '/@' + att_name old_result = eval_xpath2(etree, path)[0] shift_value_species_label(etree, ' 222', att_name, 3, mode='abs') result = eval_xpath2(etree, path)[0] assert math.isclose(float(result) - float(old_result), 3)
def test_shift_species_label_all_rel(self, inpxml_etree, att_name, tag): from aiida_fleur.tools.xml_util import shift_value_species_label, eval_xpath2 import numpy as np etree = inpxml_etree(TEST_INP_XML_PATH) path = '/fleurInput/atomSpecies/species/' + tag + '/@' + att_name old_result = np.array(eval_xpath2(etree, path)).astype('float') shift_value_species_label(etree, 'all', att_name, 2, mode='rel') result = np.array(eval_xpath2(etree, path)).astype('float') assert np.all(np.isclose(old_result * 2, result))
def test_replace_tag(inpxml_etree): from aiida_fleur.tools.xml_util import replace_tag, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) to_insert = eval_xpath2(etree, '/fleurInput/calculationSetup/cutoffs')[0] print(to_insert) print(eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')) replace_tag(etree, '/fleurInput/atomGroups/atomGroup/filmPos', to_insert) assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos') == [] assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/cutoffs')[0] == to_insert
def test_add_num_to_att(self, inpxml_etree, attr_name): from aiida_fleur.tools.xml_util import add_num_to_att, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) path = self.xml_structure[12][attr_name] result_before = eval_xpath2(etree, path + '/@{}'.format(attr_name)) if not result_before: if attr_name in ['nx', 'ny', 'nz', 'scale']: pytest.skip('This attribute is not tested for FePt/inp.xml') raise BaseException('Can not find attribute that should exist in FePt/inp.xml') else: result_before = result_before[0] add_num_to_att(etree, path, attr_name, 333) result = eval_xpath2(etree, path + '/@{}'.format(attr_name))[0] assert float(result) - float(result_before) == 333
def test_change_atomgr_att_position(inpxml_etree, attr_dict, correct_result, path): from aiida_fleur.tools.xml_util import change_atomgr_att, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) change_atomgr_att(etree, attributedict=attr_dict, position=1) result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/' + path) assert result[0] == correct_result
def test_change_atomgr_att_label_all(inpxml_etree, attr_dict, correct_result, path): from aiida_fleur.tools.xml_util import change_atomgr_att_label, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) change_atomgr_att_label(etree, attributedict=attr_dict, at_label='all') result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/' + path) assert result == correct_result
def test_change_atomgr_att_fail(self, inpxml_etree): from aiida_fleur.tools.xml_util import change_atomgr_att, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) change_atomgr_att(etree, attributedict=self.attdicts[0]) result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup[@species="Fe-1"]/' + self.paths[0]) assert result[0] == 'TTT'
def test_shift_value_rel(self, inpxml_etree, attr_name): import math from aiida_fleur.tools.xml_util import shift_value, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) path = self.xml_structure[12][attr_name] result_before = eval_xpath2(etree, path + '/@{}'.format(attr_name)) if not result_before: if attr_name in ['scale']: pytest.skip('This attribute is not tested for FePt/inp.xml') raise BaseException('Can not find attribute that should exist in FePt/inp.xml') else: result_before = result_before[0] shift_value(etree, {attr_name: 1.2442}, mode='rel') result = eval_xpath2(etree, path + '/@{}'.format(attr_name))[0] if float(result_before) != 0: assert math.isclose(float(result) / float(result_before), 1.2442, rel_tol=1e-6) else: assert float(result) == 0
def test_set_species_label_all(inpxml_etree, attr_dict, correct_result, path): from aiida_fleur.tools.xml_util import set_species_label, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) set_species_label(etree, 'all', attributedict=attr_dict) result = eval_xpath2(etree, '/fleurInput/atomSpecies/species/' + path) if 'coreConfig' in path: assert [x.text for x in result] == correct_result else: assert result == correct_result
def test_set_species_all_string(inpxml_etree, attr_dict, correct_result, path): from aiida_fleur.tools.xml_util import set_species, eval_xpath2 etree = inpxml_etree(TEST_INP_XML_PATH) set_species(etree, 'all-Fe', attributedict=attr_dict) result = eval_xpath2(etree, '/fleurInput/atomSpecies/species[@name="Fe-1"]/' + path) if isinstance(correct_result, str): if 'coreConfig' in path: assert result[0].text == correct_result else: assert result[0] == correct_result elif isinstance(correct_result, (float, int)): assert result[0] == correct_result else: assert correct_result == result
def compress_fleuroutxml(outxmlfilepath, dest_file_path=None, delete_eig=True, iterations_to_keep=None): """ Compresses a fleur out.xml file by deleting certain things like eigenvalues tags and/or iterations from it :param outxmlfilepath: (absolut) file path :type outxmlfilepath: str :param dest_file_path: (absolut) for the compressed file to be saved, if no desitination file path is given the file is overriden in place (default)! :type dest_file_path: str, optional :param delete_eig: if eigenvalues are deleted from file default is True :type delete_eig: boolean, optional :param iterations_to_keep: index of 'till' whihc iterations to be keep, i.e '-2' means only last two, '15' default (None) is keep all :type iterations_to_keep: int ### usage example: outxmldes = '/Users/broeder/test/FePt_out_test.xml' outxmlsrc = '/Users/broeder/test/FePt_out.xml' compress_fleuroutxml(outxmlsrc, dest_file_path=outxmldes, iterations_to_keep=14) compress_fleuroutxml(outxmlsrc, dest_file_path=outxmldes, iterations_to_keep=-1) """ from aiida_fleur.tools.xml_util import delete_tag, eval_xpath2 from lxml import etree xpath_eig = '/fleurOutput/scfLoop/iteration/eigenvalues' xpath_iter = '/fleurOutput/scfLoop/iteration' tree = None parser = etree.XMLParser(recover=False) outfile_broken = False try: tree = etree.parse(outxmlfilepath, parser) except etree.XMLSyntaxError: outfile_broken = True print('broken') if outfile_broken: # repair xmlfile and try to parse what is possible. parser = etree.XMLParser(recover=True) try: tree = etree.parse(outxmlfilepath, parser) except etree.XMLSyntaxError: parse_xml = False successful = False print('failed to parse broken file, I abort.') return if tree is None: print('xml tree is None, should not happen, ...') return # delete eigenvalues (all) if delete_eig: new_etree = delete_tag(tree, xpath_eig) # delete certain iterations if iterations_to_keep is not None: root = new_etree.getroot() iteration_nodes = eval_xpath2(root, xpath_iter) n_iters = len(iteration_nodes) print(n_iters) if iterations_to_keep < 0: # the first element has 1 (not 0) in xpath expresions position_keep = n_iters + iterations_to_keep + 1 delete_xpath = xpath_iter + '[position()<{}]'.format(int(position_keep)) else: delete_xpath = xpath_iter + '[position()>{}]'.format(int(iterations_to_keep)) if abs(iterations_to_keep) > n_iters: print('Warning: iterations_to_keep is larger then the number of iterations' ' in the given out.xml file, I keep all.') else: print(delete_xpath) new_etree = delete_tag(new_etree, delete_xpath) if dest_file_path is None: dest_file_path = outxmlfilepath # overwrite file if new_etree.getroot() is not None: #otherwise write fails new_etree.write(dest_file_path) else: print('new_etree has no root..., I cannot write to proper xml, skipping this now') return
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 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 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 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 extract_corelevels(outxmlfile, options=None): """ Extracts corelevels out of out.xml files :params outxmlfile: path to out.xml file :param options: A dict: 'iteration' : X/'all' :returns corelevels: A list of the form: .. code-block:: python [atomtypes][spin][dict={atomtype : '', corestates : list_of_corestates}] [atomtypeNumber][spin]['corestates'][corestate number][attribute] get corelevel energy of first atomtype, spin1, corelevels[0][0]['corestates'][i]['energy'] :example of output: .. code-block:: python [[{'atomtype': ' 1', 'corestates': [{'energy': -3.6489930627, 'j': ' 0.5', 'l': ' 0', 'n': ' 1', 'weight': 2.0}], 'eigenvalue_sum': ' -7.2979861254', 'kin_energy': ' 13.4757066163', 'spin': '1'}], [{'atomtype': ' 2', 'corestates': [{'energy': -3.6489930627, 'j': ' 0.5', 'l': ' 0', 'n': ' 1', 'weight': 2.0}], 'eigenvalue_sum': ' -7.2979861254', 'kin_energy': ' 13.4757066163', 'spin': '1'}]] """ ########################################## #1. read out.xml in etree #2. get all species #3. get number of atom types and their species #4 get corelevel dimension from atoms types. #5 init saving arrays: #list length number of atom types, which contains dictionaries: # in the form { 'species' : species, 'coresetup' : '', 'atom' : W , 'corelevels' : []} lists of corelevels from last iteration (Do i want all iterations, optional?) Or do I even want a dictionaries of corelevels? (but coresetup is in atom type info #6 parse corelevels: # get last iteration # fill corelevel list ####################################### ######################## #XPATHS to maintain species_xpath = '/fleurOutput/inputData/atomSpecies' iteration_xpath = '/fleurOutput/scfLoop/iteration' atomgroup_xpath = '/fleurOutput/inputData/atomGroups' coreconfig_xpath = 'electronConfig/coreConfig/text()' valenceconfig_xpath = 'electronConfig/valenceConfig/text()' state_occ_xpath = 'electronConfig/stateOccupation' relcoreStates_xpath = 'coreStates' relpos_xpath = 'relPos' abspos_xpath = 'absPos' filmpos_xpath = 'filmPos' #TODO all the attribute names... ###################### #1. read out.xml in etree # TODO this should be common, moved somewhere else and importet parsed_data = {} outfile_broken = False parse_xml = True parser = etree.XMLParser(recover=False) #, remove_blank_text=True) parser_info = {'parser_warnings': [], 'unparsed': []} try: tree = etree.parse(outxmlfile, parser) except etree.XMLSyntaxError: outfile_broken = True #print 'broken xml' parser_info['parser_warnings'].append( 'The out.xml file is broken I try to repair it.') if outfile_broken: #repair xmlfile and try to parse what is possible. parser = etree.XMLParser(recover=True) #, remove_blank_text=True) try: tree = etree.parse(outxmlfile, parser) except etree.XMLSyntaxError: parser_info['parser_warnings'].append( 'Skipping the parsing of the xml file. Repairing was not possible.' ) parse_xml = False #if parse_xml: root = tree.getroot() # 2. get all species from input # get element, name, coreStates # TODO why can this not be eval_xpath2? species_nodes = eval_xpath(root, species_xpath, parser_info) species_atts = {} species_names = [] for species in species_nodes: species_name = species.get('name') species_corestates = species.get('coreStates') species_element = species.get('element') species_atomicnumber = species.get('atomicNumber') species_magMom = species.get('magMom') #TODO sometimes not in inp.xml... what if it is not there coreconfig = eval_xpath(species, coreconfig_xpath, parser_info) valenceconfig = eval_xpath(species, valenceconfig_xpath, parser_info) state_occ = eval_xpath2(species, state_occ_xpath, parser_info) #parse state occ state_results = [] for tag in state_occ: #always a list? state = tag.get('state') spinUp = tag.get('spinUp') spinDown = tag.get('spinDown') state_results.append({state: [spinUp, spinDown]}) species_atts[species_name] = { 'name': species_name, 'corestates': species_corestates, 'element': species_element, 'atomgroups': [], 'mag_mom': species_magMom, 'atomic_number': species_atomicnumber, 'coreconfig': coreconfig, 'valenceconfig': valenceconfig, 'stateOccupation': state_results } species_names.append(species_name) #3. get number of atom types and their species from input atomtypes = [] atomgroup_nodes = eval_xpath(root, atomgroup_xpath, parser_info) #/fleurinp/ # always a list? for atomgroup in atomgroup_nodes: types_dict = {} group_species = atomgroup.get('species') if group_species in species_names: species_atts[group_species]['atomgroups'].append(atomgroup) element = species_atts[group_species]['element'] atomicnumber = int(species_atts[group_species]['atomic_number']) coreconf = species_atts[group_species]['coreconfig'] valenceconf = species_atts[group_species]['valenceconfig'] stateocc = species_atts[group_species]['stateOccupation'] a = eval_xpath2( atomgroup, relpos_xpath, parser_info) + eval_xpath2( atomgroup, abspos_xpath, parser_info) + eval_xpath2( atomgroup, filmpos_xpath, parser_info) # always list natoms = len(a) types_dict = { 'species': group_species, 'element': element, 'atomic_number': atomicnumber, 'coreconfig': coreconf, 'valenceconfig': valenceconf, 'stateOccupation': stateocc, 'natoms': natoms } atomtypes.append(types_dict) #natomgroup = len(atomgroup_nodes) #print(natomgroup) corelevels = [] #4 get corelevel dimension from atoms types. #5 init saving arrays: #6 parse corelevels: iteration_nodes = eval_xpath2(root, iteration_xpath, parser_info) nIteration = len(iteration_nodes) if nIteration >= 1: iteration_to_parse = iteration_nodes[-1] #TODO:Optional all or other #print iteration_to_parse corestatescards = eval_xpath2(iteration_to_parse, relcoreStates_xpath, parser_info) # maybe does not return a list... for atype in atomtypes: # spin=2 is already in there corelevels.append([]) for corestatescard in corestatescards: corelv = parse_state_card(corestatescard, iteration_to_parse, parser_info) corelevels[int(corelv['atomtype']) - 1].append( corelv) # is corelv['atomtype'] always an integer? #print parser_info #pprint(corelevels[0][1]['corestates'][2]['energy']) #corelevels[atomtypeNumber][spin]['corestates'][corestate number][attribute] return corelevels, atomtypes
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