def test_xml_set_first_attribv(inpxml_etree): from aiida_fleur.tools.xml_util import xml_set_first_attribv, eval_xpath etree = inpxml_etree(TEST_INP_XML_PATH) xml_set_first_attribv(etree, '/fleurInput/calculationSetup/cutoffs', 'Gmax', 11.00) assert float(eval_xpath(etree, '/fleurInput/calculationSetup/cutoffs/@Gmax')) == 11 xml_set_first_attribv(etree, '/fleurInput/atomGroups/atomGroup', 'species', 'TEST-1') assert eval_xpath(etree, '/fleurInput/atomGroups/atomGroup/@species') == ['TEST-1', 'Pt-1']
def test_fleurinp_modifier2(create_fleurinp, inpxml_etree): """Tests if fleurinp_modifier with various other modifations methods, the detailed tests for method functionality is tested elsewhere.""" from aiida_fleur.tools.xml_util import eval_xpath fleurinp_tmp = create_fleurinp(inpxmlfilefolder) etree = inpxml_etree(inpxmlfilefolder) fm = FleurinpModifier(fleurinp_tmp) actions = fm.get_avail_actions() assert isinstance(actions, dict) new_tag = eval_xpath(etree, '/fleurInput/calculationSetup/scfLoop') fm.delete_tag('/fleurInput/calculationSetup/scfLoop') fm.replace_tag('/fleurInput/calculationSetup/cutoffs', new_tag) fm.delete_att('/fleurInput/calculationSetup/soc', 'theta') fm.create_tag('/fleurInput/calculationSetup/soc', 'theta') fm.xml_set_all_text('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') fm.xml_set_text_occ('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') fm.xml_set_text('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') fm.xml_set_all_attribv('/fleurInput/calculationSetup/soc', 'theta', 12) fm.xml_set_first_attribv('/fleurInput/calculationSetup/soc', 'theta', 12) fm.xml_set_attribv_occ('/fleurInput/calculationSetup/soc', 'theta', 12) fm.set_species_label(' 222', {'mtSphere': {'radius': 3.333}}) fm.set_atomgr_att_label(attributedict={'force': [('relaxXYZ', 'FFF')]}, atom_label=' 222') fm.set_atomgr_att(attributedict={'force': [('relaxXYZ', 'TFF')]}, species='Fe-1') fm.set_nkpts(500, gamma='T') fm.set_kpath({'gamma': (0, 0, 0), 'L': (0.1, 0.1, 0.1)}, 300) fm.add_num_to_att('/fleurInput/calculationSetup/soc', 'theta', 4) #fm.set_species1 fm.show()
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 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 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}')