def set_nkpts_max4(xmltree, schema_dict, count, gamma=False): """ Sets a k-point mesh directly into inp.xml specific for inputs of version Max4 :param xmltree: xml tree that represents inp.xml :param schema_dict: InputSchemaDict containing all information about the structure of the input :param count: number of k-points :param gamma: bool that controls if the gamma-point should be included in the k-point mesh :returns: an xmltree of the inp.xml file with changes. """ from masci_tools.util.schema_dict_util import eval_simple_xpath, tag_exists if not tag_exists(xmltree, schema_dict, 'kPointCount', not_contains='altKPoint'): bzintegration_tag = eval_simple_xpath(xmltree, schema_dict, 'bzIntegration') for child in bzintegration_tag.iterchildren(): if 'kPoint' in child.tag: bzintegration_tag.remove(child) xmltree = create_tag(xmltree, schema_dict, 'kPointCount', not_contains='altKPoint') xmltree = set_attrib_value(xmltree, schema_dict, 'count', count, contains='kPointCount', not_contains='altKPoint') xmltree = set_attrib_value(xmltree, schema_dict, 'gamma', gamma, contains='kPointCount', not_contains='altKPoint') return xmltree
def set_atomgroup_label(xmltree, schema_dict, atom_label, attributedict, create=False): """ This method calls :func:`~masci_tools.util.xml.xml_setters_names.set_atomgroup()` method for a certain atom species that corresponds to an atom with a given label. :param xmltree: xml etree of the inp.xml :param schema_dict: InputSchemaDict containing all information about the structure of the input :param atom_label: string, a label of the atom which specie will be changed. 'all' to change all the species :param attributedict: a python dict specifying what you want to change. :param create: bool, if species does not exist create it and all subtags? :returns: xml etree of the new inp.xml **attributedict** is a python dictionary containing dictionaries that specify attributes to be set inside the certain specie. For example, if one wants to set a beta noco parameter it can be done via:: 'attributedict': {'nocoParams': {'beta': val}} """ from masci_tools.util.schema_dict_util import tag_exists, eval_simple_xpath from masci_tools.util.xml.common_functions import get_xml_attribute if atom_label == 'all': xmltree = set_atomgroup(xmltree, schema_dict, attributedict, position=None, species='all') return xmltree atom_label = f'{atom_label: >20}' all_groups = eval_simple_xpath(xmltree, schema_dict, 'atomGroup', list_return=True) species_to_set = set() # set all species, where given label is present for group in all_groups: if tag_exists(group, schema_dict, 'filmPos'): atoms = eval_simple_xpath(group, schema_dict, 'filmPos', list_return=True) else: atoms = eval_simple_xpath(group, schema_dict, 'relPos', list_return=True) for atom in atoms: label = get_xml_attribute(atom, 'label') if label == atom_label: species_to_set.add(get_xml_attribute(group, 'species')) for species_name in species_to_set: xmltree = set_atomgroup(xmltree, schema_dict, attributedict, position=None, species=species_name) return xmltree
def get_nkpts(xmltree, schema_dict, logger=None): """ Get the number of kpoints that will be used in the calculation specified in the given fleur XMl file. .. warning:: For file versions before Max5 only kPointList or kPointCount tags will work. However, for kPointCount there is no real guarantee that for every occasion it will correspond to the number of kpoints. So a warning is written out :param xmltree: etree representing the fleur xml file :param schema_dict: schema dictionary corresponding to the file version of the xmltree :param logger: logger object for logging warnings, errors :returns: int with the number of kpoints """ from masci_tools.util.schema_dict_util import eval_simple_xpath from masci_tools.util.schema_dict_util import evaluate_attribute from masci_tools.util.xml.common_functions import clear_xml if isinstance(xmltree, etree._ElementTree): xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() else: root = xmltree #Get the name of the current selected kPointSet list_name = evaluate_attribute(root, schema_dict, 'listName', logger=logger) kpointlists = eval_simple_xpath(root, schema_dict, 'kPointList', list_return=True, logger=logger) if len(kpointlists) == 0: raise ValueError('No Kpoint lists found in the given inp.xml') labels = [kpoint_set.attrib.get('name') for kpoint_set in kpointlists] if list_name not in labels: raise ValueError( f'Selected Kpoint list with the name: {list_name} does not exist' f'Available list names: {labels}') kpoint_index = labels.index(list_name) kpoint_set = kpointlists[kpoint_index] nkpts = evaluate_attribute(kpoint_set, schema_dict, 'count', logger=logger) return nkpts
def test_schema_dict_util_abs_to_rel_path(): """ Test of the absolute to relative xpath conversion in schema_dict_util functions """ from lxml import etree from masci_tools.util.schema_dict_util import eval_simple_xpath, get_number_of_nodes, tag_exists, \ evaluate_attribute, evaluate_tag, evaluate_parent_tag, \ evaluate_text schema_dict = schema_dict_34 parser = etree.XMLParser(attribute_defaults=True, recover=False, encoding='utf-8') root = etree.parse(TEST_INPXML_PATH, parser).getroot() species = eval_simple_xpath(root, schema_dict, 'species') assert tag_exists(species[0], schema_dict, 'lo') assert tag_exists(species[1], schema_dict, 'lo') assert get_number_of_nodes(species[0], schema_dict, 'lo') == 2 assert get_number_of_nodes(species[1], schema_dict, 'lo') == 1 assert evaluate_attribute(species[0], schema_dict, 'name', constants=FLEUR_DEFINED_CONSTANTS) == 'Fe-1' assert evaluate_text(species[0], schema_dict, 'coreConfig', constants=FLEUR_DEFINED_CONSTANTS) == ['[Ne]'] assert evaluate_tag(species[1], schema_dict, 'lo', constants=FLEUR_DEFINED_CONSTANTS) == { 'eDeriv': 0, 'l': 1, 'n': 5, 'type': 'SCLO' } assert evaluate_parent_tag(species[1], schema_dict, 'lo', constants=FLEUR_DEFINED_CONSTANTS) == { 'atomicNumber': 78, 'element': 'Pt', 'name': 'Pt-1' }
def set_kpointlist_max4(xmltree, schema_dict, kpoints, weights): """ Explicitely create a kPointList from the given kpoints and weights. This routine is specific to input versions Max4 and before and will replace any existing kPointCount, kPointMesh, ... with the specified kPointList :param xmltree: xml tree that represents inp.xml :param schema_dict: InputSchemaDict containing all information about the structure of the input :param kpoints: list or array containing the **relative** coordinates of the kpoints :param weights: list or array containing the weights of the kpoints :returns: an xmltree of the inp.xml file with changes. """ from masci_tools.util.schema_dict_util import eval_simple_xpath from masci_tools.util.xml.converters import convert_text_to_xml, convert_attribute_to_xml from lxml import etree import numpy as np if not isinstance(kpoints, (list, np.ndarray)) or not isinstance(weights, (list, np.ndarray)): raise ValueError('kPoints and weights have to be given as a list or array') if len(kpoints) != len(weights): raise ValueError('kPoints and weights do not have the same length') nkpts = len(kpoints) bzintegration_tag = eval_simple_xpath(xmltree, schema_dict, 'bzIntegration') for child in bzintegration_tag.iterchildren(): if 'kPoint' in child.tag: bzintegration_tag.remove(child) new_kpo = etree.Element('kPointList', posScale='1.0', weightScale='1.0', count=f'{nkpts:d}') for kpoint, weight in zip(kpoints, weights): weight, _ = convert_attribute_to_xml(weight, ['float', 'float_expression']) new_k = etree.Element('kPoint', weight=weight) text, _ = convert_text_to_xml(kpoint, [{'type': ['float', 'float_expression'], 'length': 3}]) new_k.text = text new_kpo.append(new_k) xmltree = create_tag(xmltree, schema_dict, new_kpo, not_contains='altKPoint') return xmltree
def get_kpoints_data(xmltree, schema_dict, name=None, index=None, logger=None): """ Get the kpoint sets defined in the given fleur xml file. .. warning:: For file versions before Max5 the name argument is not valid :param xmltree: etree representing the fleur xml file :param schema_dict: schema dictionary corresponding to the file version of the xmltree :param name: str, optional, if given only the kpoint set with the given name is returned :param index: int, optional, if given only the kpoint set with the given index is returned :param logger: logger object for logging warnings, errors :returns: tuple containing the kpoint information The tuple contains the following entries: 1. :kpoints: dict or list (list if there is only one kpoint set), containing the coordinates of the kpoints 2. :weights: dict or list (list if there is only one kpoint set), containing the weights of the kpoints 3. :cell: numpy array, bravais matrix of the given system 4. :pbc: list of booleans, determines in which directions periodic boundary conditions are applicable """ from masci_tools.util.schema_dict_util import read_constants, eval_simple_xpath from masci_tools.util.schema_dict_util import evaluate_text, evaluate_attribute from masci_tools.util.xml.common_functions import clear_xml if name is not None and index is not None: raise ValueError( 'Only provide one of index or name to select kpoint lists') if isinstance(xmltree, etree._ElementTree): xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() else: root = xmltree constants = read_constants(root, schema_dict, logger=logger) cell, pbc = get_cell(root, schema_dict, logger=logger) kpointlists = eval_simple_xpath(root, schema_dict, 'kPointList', list_return=True, logger=logger) if len(kpointlists) == 0: raise ValueError('No Kpoint lists found in the given inp.xml') labels = [kpoint_set.attrib.get('name') for kpoint_set in kpointlists] if name is not None and name not in labels: raise ValueError(f'Found no Kpoint list with the name: {name}' f'Available list names: {labels}') if index is not None: try: kpointlists = [kpointlists[index]] except IndexError as exc: raise ValueError(f'No kPointList with index {index} found.' f' Only {len(kpointlists)} available') from exc kpoints_data = {} weights_data = {} for kpointlist in kpointlists: label = evaluate_attribute(kpointlist, schema_dict, 'name', logger=logger) if name is not None and name != label: continue kpoints = evaluate_text(kpointlist, schema_dict, 'kPoint', constants=constants, list_return=True, logger=logger) weights = evaluate_attribute(kpointlist, schema_dict, 'weight', constants=constants, list_return=True, logger=logger) if not isinstance(kpoints[0], list): kpoints = [kpoints] weights = [weights] kpoints_data[label] = kpoints weights_data[label] = weights if len(kpoints_data) == 1: _, kpoints_data = kpoints_data.popitem() _, weights_data = weights_data.popitem() return kpoints_data, weights_data, cell, pbc
def get_structure_data(xmltree, schema_dict, logger=None): """ Get the structure defined in the given fleur xml file. .. warning:: Only the explicit definition of the Bravais matrix is supported. Old inputs containing the `latnam` definitions are not supported :param xmltree: etree representing the fleur xml file :param schema_dict: schema dictionary corresponding to the file version of the xmltree :param logger: logger object for logging warnings, errors :returns: tuple containing the structure information The tuple contains the following entries: 1. :atom_data: list of tuples containing the absolute positions and symbols of the atoms 2. :cell: numpy array, bravais matrix of the given system 3. :pbc: list of booleans, determines in which directions periodic boundary conditions are applicable """ from masci_tools.util.schema_dict_util import read_constants, eval_simple_xpath from masci_tools.util.schema_dict_util import evaluate_text, evaluate_attribute from masci_tools.util.xml.common_functions import clear_xml from masci_tools.io.common_functions import rel_to_abs, rel_to_abs_f if isinstance(xmltree, etree._ElementTree): xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() else: root = xmltree constants = read_constants(root, schema_dict, logger=logger) cell, pbc = get_cell(root, schema_dict, logger=logger) species_names = evaluate_attribute(root, schema_dict, 'name', constants=constants, contains='species', logger=logger) species_elements = evaluate_attribute(root, schema_dict, 'element', constants=constants, contains='species', logger=logger) if not isinstance(species_names, list): species_names = [species_names] if not isinstance(species_elements, list): species_elements = [species_elements] if len(species_names) != len(species_elements): raise ValueError( f'Failed to read in species names and elements. Got {len(species_names)} names and {len(species_elements)} elements' ) species_dict = dict(zip(species_names, species_elements)) atom_data = [] atom_groups = eval_simple_xpath(root, schema_dict, 'atomGroup', list_return=True, logger=logger) for group in atom_groups: group_species = evaluate_attribute(group, schema_dict, 'species', constants=constants, logger=logger) atom_positions = [] absolute_positions = evaluate_text(group, schema_dict, 'absPos', constants=constants, list_return=True, logger=logger, optional=True) relative_positions = evaluate_text(group, schema_dict, 'relPos', constants=constants, list_return=True, logger=logger, optional=True) film_positions = evaluate_text(group, schema_dict, 'filmPos', constants=constants, list_return=True, logger=logger, optional=True) atom_positions = absolute_positions for rel_pos in relative_positions: atom_positions.append(rel_to_abs(rel_pos, cell)) for film_pos in film_positions: atom_positions.append(rel_to_abs_f(film_pos, cell)) if len(atom_positions) == 0: raise ValueError('Failed to read atom positions for group') atom_data.extend( (pos, species_dict[group_species]) for pos in atom_positions) return atom_data, cell, pbc
def get_parameter_data(xmltree, schema_dict, inpgen_ready=True, write_ids=True, logger=None): """ This routine returns an python dictionary produced from the inp.xml file, which contains all the parameters needed to setup a new inp.xml from a inpgen input file to produce the same input (for parameters that the inpgen can control) :param xmltree: etree representing the fleur xml file :param schema_dict: schema dictionary corresponding to the file version of the xmltree :param inpgen_ready: Bool, return a dict which can be inputed into inpgen while setting atoms :param write_ids: Bool, if True the atom ids are added to the atom namelists :param logger: logger object for logging warnings, errors :returns: dict, which will lead to the same inp.xml (in case if other defaults, which can not be controlled by input for inpgen, were changed) """ from masci_tools.util.schema_dict_util import read_constants, eval_simple_xpath from masci_tools.util.schema_dict_util import evaluate_attribute, evaluate_text from masci_tools.util.xml.common_functions import clear_xml from masci_tools.util.xml.converters import convert_fleur_lo from masci_tools.io.common_functions import filter_out_empty_dict_entries # TODO: convert econfig # TODO: parse kpoints, somehow count is bad (if symmetry changes), mesh is not known, path cannot be specified ######## parameters = {} if isinstance(xmltree, etree._ElementTree): xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() else: root = xmltree constants = read_constants(root, schema_dict, logger=logger) # Create the cards # &input # most things are not needed for AiiDA here. or we ignor them for now. # film is set by the plugin depended on the structure # symor per default = False? to avoid input which fleur can't take # &comp comp_dict = {} comp_dict['jspins'] = evaluate_attribute(root, schema_dict, 'jspins', constants=constants, logger=logger) comp_dict['frcor'] = evaluate_attribute(root, schema_dict, 'frcor', constants=constants, logger=logger, optional=True) comp_dict['ctail'] = evaluate_attribute(root, schema_dict, 'ctail', constants=constants, logger=logger) comp_dict['kcrel'] = evaluate_attribute(root, schema_dict, 'kcrel', constants=constants, logger=logger, optional=True) comp_dict['gmax'] = evaluate_attribute(root, schema_dict, 'gmax', constants=constants, logger=logger) comp_dict['gmaxxc'] = evaluate_attribute(root, schema_dict, 'gmaxxc', constants=constants, logger=logger) comp_dict['kmax'] = evaluate_attribute(root, schema_dict, 'kmax', constants=constants, logger=logger) parameters['comp'] = filter_out_empty_dict_entries(comp_dict) # &atoms species_list = eval_simple_xpath(root, schema_dict, 'species', list_return=True, logger=logger) species_several = {} # first we see if there are several species with the same atomic number for species in species_list: atom_z = evaluate_attribute(species, schema_dict, 'atomicNumber', constants, logger=logger) species_several[atom_z] = species_several.get(atom_z, 0) + 1 species_count = {} for indx, species in enumerate(species_list): atom_dict = {} atoms_name = f'atom{indx}' atom_z = evaluate_attribute(species, schema_dict, 'atomicNumber', constants=constants, logger=logger) if not inpgen_ready: atom_dict['z'] = atom_z species_count[atom_z] = species_count.get(atom_z, 0) + 1 atom_id = f'{atom_z}.{species_count[atom_z]}' if write_ids: if species_several[atom_z] > 1: atom_dict['id'] = atom_id if schema_dict.inp_version <= (0, 31): atom_dict['ncst'] = evaluate_attribute(species, schema_dict, 'coreStates', constants, logger=logger) atom_dict['rmt'] = evaluate_attribute(species, schema_dict, 'radius', constants=constants, logger=logger) atom_dict['dx'] = evaluate_attribute(species, schema_dict, 'logIncrement', constants=constants, logger=logger) atom_dict['jri'] = evaluate_attribute(species, schema_dict, 'gridPoints', constants=constants, logger=logger) atom_dict['lmax'] = evaluate_attribute(species, schema_dict, 'lmax', constants=constants, logger=logger) atom_dict['lnonsph'] = evaluate_attribute(species, schema_dict, 'lnonsphr', constants=constants, logger=logger) atom_dict['bmu'] = evaluate_attribute(species, schema_dict, 'magMom', constants, logger=logger, optional=True) atom_dict['element'] = evaluate_attribute(species, schema_dict, 'element', constants=constants, logger=logger) #atom_econfig = eval_simple_xpath(species, schema_dict, 'electronConfig') atom_lo = eval_simple_xpath(species, schema_dict, 'lo', list_return=True, logger=logger) #atom_econfig = eval_simple_xpath(species, schema_dict, 'electronConfig') if len(atom_lo) != 0: atom_dict['lo'] = convert_fleur_lo(atom_lo) parameters[atoms_name] = filter_out_empty_dict_entries(atom_dict) # &soc soc = evaluate_attribute(root, schema_dict, 'l_soc', constants=constants, logger=logger, optional=True) theta = evaluate_attribute(root, schema_dict, 'theta', constants=constants, contains='soc', logger=logger, optional=True) phi = evaluate_attribute(root, schema_dict, 'phi', constants=constants, contains='soc', logger=logger, optional=True) if soc is not None and soc: parameters['soc'] = {'theta': theta, 'phi': phi} # &kpt #attrib = convert_from_fortran_bool(eval_xpath(root, l_soc_xpath)) #theta = eval_xpath(root, theta_xpath) #phi = eval_xpath(root, phi_xpath) # if kpt: # new_parameters['kpt'] = {'theta' : theta, 'phi' : phi} # # ['nkpt', 'kpts', 'div1', 'div2', 'div3', 'tkb', 'tria'], # title title = evaluate_text(root, schema_dict, 'comment', constants=constants, logger=logger, optional=True) if title: parameters['title'] = title.replace('\n', '').strip() # &exco #TODO, easy exco_dict = {} exco_dict['xctyp'] = evaluate_attribute(root, schema_dict, 'name', constants, contains='xcFunctional', logger=logger) # 'exco' : ['xctyp', 'relxc'], parameters['exco'] = filter_out_empty_dict_entries(exco_dict) # &film # TODO # &qss # TODO # lattice, not supported? return parameters
def get_cell(xmltree, schema_dict, logger=None): """ Get the Bravais matrix from the given fleur xml file. In addition a list determining in, which directions there are periodic boundary conditions in the system. .. warning:: Only the explicit definition of the Bravais matrix is supported. Old inputs containing the `latnam` definitions are not supported :param xmltree: etree representing the fleur xml file :param schema_dict: schema dictionary corresponding to the file version of the xmltree :param logger: logger object for logging warnings, errors :returns: numpy array of the bravais matrix and list of boolean values for periodic boundary conditions """ from masci_tools.util.schema_dict_util import read_constants, eval_simple_xpath from masci_tools.util.schema_dict_util import evaluate_text, tag_exists from masci_tools.util.xml.common_functions import clear_xml from masci_tools.util.constants import BOHR_A import numpy as np if isinstance(xmltree, etree._ElementTree): xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() else: root = xmltree constants = read_constants(root, schema_dict, logger=logger) cell = None lattice_tag = None if tag_exists(root, schema_dict, 'bulkLattice', logger=logger): lattice_tag = eval_simple_xpath(root, schema_dict, 'bulkLattice', logger=logger) pbc = [True, True, True] elif tag_exists(root, schema_dict, 'filmLattice', logger=logger): lattice_tag = eval_simple_xpath(root, schema_dict, 'filmLattice', logger=logger) pbc = [True, True, False] if lattice_tag is not None: row1 = evaluate_text(lattice_tag, schema_dict, 'row-1', constants=constants, contains='bravaisMatrix', logger=logger, optional=True) row2 = evaluate_text(lattice_tag, schema_dict, 'row-2', constants=constants, contains='bravaisMatrix', logger=logger, optional=True) row3 = evaluate_text(lattice_tag, schema_dict, 'row-3', constants=constants, contains='bravaisMatrix', logger=logger, optional=True) if all(x is not None and x != [] for x in [row1, row2, row3]): cell = np.array([row1, row2, row3]) * BOHR_A if cell is None: raise ValueError( 'Could not extract Bravais matrix out of inp.xml. Is the ' 'Bravais matrix explicitly given? i.e Latnam definition ' 'not supported.') return cell, pbc
def get_nkpts_max4(xmltree, schema_dict, logger=None): """ Get the number of kpoints that will be used in the calculation specified in the given fleur XMl file. Version specific for Max4 versions or older .. warning:: For file versions before Max5 only kPointList or kPointCount tags will work. However, for kPointCount there is no real guarantee that for every occasion it will correspond to the number of kpoints. So a warning is written out :param xmltree: etree representing the fleur xml file :param schema_dict: schema dictionary corresponding to the file version of the xmltree :param logger: logger object for logging warnings, errors :returns: int with the number of kpoints """ from masci_tools.util.schema_dict_util import evaluate_attribute, eval_simple_xpath from masci_tools.util.xml.common_functions import clear_xml import warnings if isinstance(xmltree, etree._ElementTree): xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() else: root = xmltree modes = get_fleur_modes(root, schema_dict, logger=logger) alt_kpt_set = None if modes['band'] or modes['gw']: expected_mode = 'bands' if modes['band'] else 'gw' alt_kpts = eval_simple_xpath(root, schema_dict, 'altKPointSet', list_return=True, logger=logger) for kpt_set in alt_kpts: if evaluate_attribute(kpt_set, schema_dict, 'purpose', logger=logger) == expected_mode: alt_kpt_set = kpt_set break kpt_tag = None if alt_kpt_set is not None: kpt_tag = eval_simple_xpath(alt_kpt_set, schema_dict, 'kPointList', list_return=True, logger=logger) if len(kpt_tag) == 0: kpt_tag = eval_simple_xpath(alt_kpt_set, schema_dict, 'kPointCount', list_return=True, logger=logger) if len(kpt_tag) == 0: kpt_tag = None else: warnings.warn( 'kPointCount is not guaranteed to result in the given number of kpoints' ) if kpt_tag is None: kpt_tag = eval_simple_xpath(root, schema_dict, 'kPointList', not_contains='altKPointSet', list_return=True, logger=logger) if len(kpt_tag) == 0: kpt_tag = eval_simple_xpath(root, schema_dict, 'kPointCount', not_contains='altKPointSet', list_return=True, logger=logger) if len(kpt_tag) == 0: raise ValueError('No kPointList or kPointCount found') else: warnings.warn( 'kPointCount is not guaranteed to result in the given number of kpoints' ) kpt_tag = kpt_tag[0] nkpts = evaluate_attribute(kpt_tag, schema_dict, 'count', logger=logger) return nkpts
def set_nmmpmat(xmltree, nmmplines, schema_dict, species_name, orbital, spin, state_occupations=None, orbital_occupations=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 xmltree: an xmltree that represents inp.xml :param nmmplines: list of lines in the n_mmp_mat file :param schema_dict: InputSchemaDict containing all information about the structure of the input :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 state_occupations: 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 :returns: list with modified nmmplines """ from masci_tools.util.xml.common_functions import eval_xpath, get_xml_attribute from masci_tools.util.schema_dict_util import evaluate_attribute, eval_simple_xpath, attrib_exists from masci_tools.io.io_nmmpmat import write_nmmpmat, write_nmmpmat_from_states, write_nmmpmat_from_orbitals #All lda+U procedures have to be considered since we need to keep the order species_base_path = get_tag_xpath(schema_dict, 'species') if species_name == 'all': species_xpath = species_base_path elif species_name[:4] == 'all-': #format all-<string> species_xpath = f'{species_base_path}[contains(@name,"{species_name[4:]}")]' else: species_xpath = f'{species_base_path}[@name = "{species_name}"]' all_species = eval_xpath(xmltree, species_xpath, list_return=True) nspins = evaluate_attribute(xmltree, schema_dict, 'jspins') if 'l_mtnocoPot' in schema_dict['attrib_types']: if attrib_exists(xmltree, schema_dict, 'l_mtnocoPot', contains='Setup'): if evaluate_attribute(xmltree, schema_dict, 'l_mtnocoPot', contains='Setup'): nspins = 3 if spin > nspins: raise ValueError(f'Invalid input: spin {spin} requested, but input has only {nspins} spins') all_ldau = eval_simple_xpath(xmltree, schema_dict, 'ldaU', contains='species', list_return=True) numRows = nspins * 14 * len(all_ldau) if state_occupations is not None: new_nmmpmat_entry = write_nmmpmat_from_states(orbital, state_occupations, phi=phi, theta=theta) elif orbital_occupations is not None: new_nmmpmat_entry = write_nmmpmat_from_orbitals(orbital, orbital_occupations, phi=phi, theta=theta) elif denmat is not None: new_nmmpmat_entry = write_nmmpmat(orbital, denmat, phi=phi, theta=theta) else: raise ValueError('Invalid definition of density matrix. Provide either state_occupations, ' 'orbital_occupations or denmat') #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 nmmplines is not None: #Remove blank lines while '' in nmmplines: nmmplines.remove('') if numRows != len(nmmplines): 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') 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 = evaluate_attribute(ldau, schema_dict, 'l', contains='species') 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}') #check if fleurinp has a specified n_mmp_mat file if not initialize it with 0 if nmmplines is None: nmmplines = [] for index in range(numRows): nmmplines.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 nmmplines[startRow:startRow + 14] = new_nmmpmat_entry return nmmplines
def shift_value_species_label(xmltree, schema_dict, atom_label, attributename, value_given, mode='abs', **kwargs): """ Shifts the value of an attribute on a species by label if atom_label contains 'all' then applies to all species :param xmltree: xml etree of the inp.xml :param schema_dict: InputSchemaDict containing all information about the structure of the input :param atom_label: string, a label of the atom which specie will be changed. 'all' if set up all species :param attributename: name of the attribute to change :param value_given: value to add or to multiply by :param mode: 'rel' for multiplication or 'abs' for addition Kwargs if the attributename does not correspond to a unique path: :param contains: str, this string has to be in the final path :param not_contains: str, this string has to NOT be in the final path :returns: xml etree of the new inp.xml """ from masci_tools.util.schema_dict_util import tag_exists, eval_simple_xpath from masci_tools.util.xml.common_functions import get_xml_attribute from masci_tools.util.xml.xml_setters_xpaths import xml_add_number_to_first_attrib from masci_tools.util.xml.common_functions import split_off_attrib if 'contains' in kwargs: contains = kwargs.get('contains') if not isinstance(contains, list): contains = [contains] contains.append('species') kwargs['contains'] = contains else: kwargs['contains'] = 'species' species_base_path = get_tag_xpath(schema_dict, 'species') attr_base_path = get_attrib_xpath(schema_dict, attributename, **kwargs) tag_base_xpath, attributename = split_off_attrib(attr_base_path) if atom_label != 'all': atom_label = f'{atom_label: >20}' all_groups = eval_simple_xpath(xmltree, schema_dict, 'atomGroup', list_return=True) species_to_set = set() for group in all_groups: if tag_exists(group, schema_dict, 'filmPos'): atoms = eval_simple_xpath(group, schema_dict, 'filmPos', list_return=True) else: atoms = eval_simple_xpath(group, schema_dict, 'relPos', list_return=True) for atom in atoms: label = get_xml_attribute(atom, 'label') if atom_label in ('all', label): species_to_set.add(get_xml_attribute(group, 'species')) for species_name in species_to_set: xpath_species = f'{species_base_path}[@name="{species_name}"]' tag_xpath = tag_base_xpath.replace(species_base_path, xpath_species) xmltree = xml_add_number_to_first_attrib(xmltree, schema_dict, tag_xpath, tag_base_xpath, attributename, value_given, mode=mode) return xmltree
def outxml_parser(outxmlfile, version=None, parser_info_out=None, iteration_to_parse=None, strict=False, debug=False, **kwargs): """ Parses the out.xml file to a dictionary based on the version and the given tasks :param outxmlfile: either path to the out.xml file, opened file handle or a xml etree to be parsed :param version: version string to enforce that a given schema is used :param parser_info_out: dict, with warnings, info, errors, ... :param iteration_to_parse: either str or int, (optional, default 'last') determines which iteration should be parsed. Accepted are 'all', 'first', 'last' or an index for the iteration :param strict: bool if True and no parser_info_out is provided any encountered error will immediately be raised :param debug: bool if True additional information is printed out in the logs Kwargs: :param ignore_validation: bool, if True schema validation errors are only logged :param minimal_mode: bool, if True only total Energy, iteration number and distances are parsed :param list_return: bool, if True one-item lists in the output dict are not converted to simple values :param additional_tasks: dict to define custom parsing tasks. For detailed explanation See :py:mod:`~masci_tools.io.parsers.fleur.default_parse_tasks`. :param overwrite: bool, if True and keys in additional_tasks collide with defaults The defaults will be overwritten :param append: bool, if True and keys in additional_tasks collide with defaults The inner tasks will be written into the dict. If inner keys collide they are overwritten :return: python dictionary with the information parsed from the out.xml :raises ValueError: If the validation against the schema failed, or an irrecoverable error occured during parsing :raises FileNotFoundError: If no Schema file for the given version was found :raises KeyError: If an unknown task is encountered """ __parser_version__ = '0.5.0' logger = logging.getLogger(__name__) parser_log_handler = None if parser_info_out is not None or not strict: if parser_info_out is None: parser_info_out = {} logging_level = logging.INFO if debug: logging_level = logging.DEBUG logger.setLevel(logging_level) parser_log_handler = DictHandler(parser_info_out, WARNING='parser_warnings', ERROR='parser_errors', INFO='parser_info', DEBUG='parser_debug', CRITICAL='parser_critical', ignore_unknown_levels=True, level=logging_level) logger.addHandler(parser_log_handler) if strict: logger = None if logger is not None: logger.info('Masci-Tools Fleur out.xml Parser v%s', __parser_version__) outfile_broken = False if isinstance(outxmlfile, etree._ElementTree): xmltree = outxmlfile else: parser = etree.XMLParser(attribute_defaults=True, recover=False, encoding='utf-8') try: xmltree = etree.parse(outxmlfile, parser) except etree.XMLSyntaxError: outfile_broken = True if logger is None: warnings.warn('The out.xml file is broken I try to repair it.') else: logger.warning( '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(attribute_defaults=True, recover=True, encoding='utf-8') try: xmltree = etree.parse(outxmlfile, parser) except etree.XMLSyntaxError: if logger is None: raise else: logger.exception('Skipping the parsing of the xml file. ' 'Repairing was not possible.') return {} if version is None: out_version = eval_xpath(xmltree, '//@fleurOutputVersion', logger=logger) out_version = str(out_version) if out_version is None: logger.error('Failed to extract outputVersion') raise ValueError('Failed to extract outputVersion') else: out_version = version if out_version == '0.27': program_version = eval_xpath(xmltree, '//programVersion/@version', logger=logger) if program_version == 'fleur 32': #Max5 release (before bugfix) out_version = '0.33' inp_version = '0.33' ignore_validation = True if logger is not None: logger.warning( "Ignoring '0.27' outputVersion for MaX5.0 release") else: warnings.warn( "Ignoring '0.27' outputVersion for MaX5.0 release") elif program_version == 'fleur 31': #Max4 release out_version = '0.31' inp_version = '0.31' ignore_validation = True if logger is not None: logger.warning( "Ignoring '0.27' outputVersion for MaX4.0 release") else: warnings.warn( "Ignoring '0.27' outputVersion for MaX4.0 release") elif program_version == 'fleur 30': #Max3.1 release out_version = '0.30' inp_version = '0.30' ignore_validation = True if logger is not None: logger.warning( "Ignoring '0.27' outputVersion for MaX3.1 release") else: warnings.warn( "Ignoring '0.27' outputVersion for MaX3.1 release") elif program_version == 'fleur 27': #Max3.1 release out_version = '0.29' inp_version = '0.29' ignore_validation = True if logger is not None: logger.warning( "Found version before MaX3.1 release falling back to file version '0.29'" ) warnings.warn( 'out.xml files before the MaX3.1 release are not explicitely supported.' ' No guarantee is given that the parser will work without error', UserWarning) else: if logger is not None: logger.error( "Unknown fleur version: File-version '%s' Program-version '%s'", out_version, program_version) raise ValueError( f"Unknown fleur version: File-version '{out_version}' Program-version '{program_version}'" ) else: ignore_validation = False inp_version = eval_xpath(xmltree, '//@fleurInputVersion', logger=logger) inp_version = str(inp_version) if inp_version is None: if logger is not None: logger.error('Failed to extract inputVersion') raise ValueError('Failed to extract inputVersion') ignore_validation = kwargs.get('ignore_validation', ignore_validation) #Load schema_dict (inp and out) outschema_dict = OutputSchemaDict.fromVersion(out_version, inp_version=inp_version, logger=logger) if outschema_dict['out_version'] != out_version or \ outschema_dict['inp_version'] != inp_version: ignore_validation = True out_version = outschema_dict['out_version'] inp_version = outschema_dict['inp_version'] if logger is not None: logger.info('Found fleur out file with the versions out: %s; inp: %s', out_version, inp_version) xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() errmsg = '' try: validate_xml( xmltree, outschema_dict.xmlschema, error_header='Output file does not validate against the schema') except etree.DocumentInvalid as err: errmsg = str(err) if logger is not None: logger.warning(errmsg) if not ignore_validation: if logger is not None: logger.exception(errmsg) raise ValueError(errmsg) from err if not outschema_dict.xmlschema.validate(xmltree) and errmsg == '': msg = 'Output file does not validate against the schema: Reason is unknown' if logger is not None: logger.warning(msg) if not ignore_validation: if logger is not None: logger.exception(msg) raise ValueError(msg) parser = ParseTasks(out_version) additional_tasks = kwargs.pop('additional_tasks', {}) for task_name, task_definition in additional_tasks.items(): parser.add_task(task_name, task_definition, **kwargs) out_dict, constants = parse_general_information( root, parser, outschema_dict, logger=logger, iteration_to_parse=iteration_to_parse, **kwargs) out_dict['input_file_version'] = outschema_dict['inp_version'] # get all iterations in out.xml file iteration_nodes = eval_simple_xpath(root, outschema_dict, 'iteration', logger=logger, list_return=True) n_iters = len(iteration_nodes) # parse only last stable interation # (if modes (dos and co) maybe parse anyway if broken?) if outfile_broken and (n_iters >= 2): iteration_nodes = iteration_nodes[:-2] if logger is not None: logger.info('The last parsed iteration is %s', n_iters - 2) elif outfile_broken and (n_iters == 1): iteration_nodes = [iteration_nodes[0]] if logger is not None: logger.info('The last parsed iteration is %s', n_iters) elif not outfile_broken and (n_iters >= 1): pass else: # there was no iteration found. # only the starting charge density could be generated msg = 'There was no iteration found in the outfile, either just a ' \ 'starting density was generated or something went wrong.' if logger is None: raise ValueError(msg) else: logger.error(msg) if iteration_to_parse is None: iteration_to_parse = 'last' #This is the default from the aiida_fleur parser if iteration_to_parse == 'last': iteration_nodes = iteration_nodes[-1] elif iteration_to_parse == 'first': iteration_nodes = iteration_nodes[0] elif iteration_to_parse == 'all': pass elif isinstance(iteration_to_parse, int): try: iteration_nodes = iteration_nodes[iteration_to_parse] except IndexError as exc: if logger is not None: logger.exception(exc) raise ValueError( f"Invalid value for iteration_to_parse: Got '{iteration_to_parse}'" f"; but only '{len(iteration_nodes)}' iterations are available" ) from exc else: if logger is not None: logger.error( "Invalid value for iteration_to_parse: Got '%s' " "Valid values are: 'first', 'last', 'all', or int", iteration_to_parse) raise ValueError( f"Invalid value for iteration_to_parse: Got '{iteration_to_parse}' " "Valid values are: 'first', 'last', 'all', or int") if not isinstance(iteration_nodes, list): iteration_nodes = [iteration_nodes] logger_info = {'iteration': 'unknown'} iteration_logger = OutParserLogAdapter(logger, logger_info) for node in iteration_nodes: iteration_number = evaluate_attribute(node, outschema_dict, 'numberForCurrentRun', optional=True) if iteration_number is not None: logger_info['iteration'] = iteration_number out_dict = parse_iteration(node, parser, outschema_dict, out_dict, constants, logger=iteration_logger, **kwargs) logger_info['iteration'] = 'unknown' if not kwargs.get('list_return', False): #Convert one item lists to simple values for key, value in out_dict.items(): if isinstance(value, list): if len(value) == 1: out_dict[key] = value[0] elif isinstance(value, dict): for subkey, subvalue in value.items(): if isinstance(subvalue, list): if len(subvalue) == 1: out_dict[key][subkey] = subvalue[0] if parser_log_handler is not None: if logger is not None: logger.removeHandler(parser_log_handler) return out_dict
def validate_nmmpmat(xmltree, nmmplines, schema_dict): """ Checks that the given nmmp_lines is valid with the given xmltree 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 xmltree: an xmltree that represents inp.xml :param nmmplines: list of lines in the n_mmp_mat file :raises ValueError: if any of the above checks are violated. """ from masci_tools.util.xml.common_functions import get_xml_attribute from masci_tools.util.schema_dict_util import evaluate_attribute, eval_simple_xpath, attrib_exists nspins = evaluate_attribute(xmltree, schema_dict, 'jspins') if 'l_mtnocoPot' in schema_dict['attrib_types']: if attrib_exists(xmltree, schema_dict, 'l_mtnocoPot', contains='Setup'): if evaluate_attribute(xmltree, schema_dict, 'l_mtnocoPot', contains='Setup'): nspins = 3 all_ldau = eval_simple_xpath(xmltree, schema_dict, 'ldaU', contains='species', list_return=True) 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 nmmplines is not None: #Remove blank lines while '' in nmmplines: nmmplines.remove('') if numRows != len(nmmplines): 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 = evaluate_attribute(ldau, schema_dict, 'l', contains='species') 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 = nmmplines[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 get_kpoints_data_max4(xmltree, schema_dict, logger=None): """ Get the kpoint sets defined in the given fleur xml file. .. note:: This function is specific to file version before and including the Max4 release of fleur :param xmltree: etree representing the fleur xml file :param schema_dict: schema dictionary corresponding to the file version of the xmltree :param logger: logger object for logging warnings, errors :returns: tuple containing the kpoint information The tuple contains the following entries: 1. :kpoints: list containing the coordinates of the kpoints 2. :weights: list containing the weights of the kpoints 3. :cell: numpy array, bravais matrix of the given system 4. :pbc: list of booleans, determines in which directions periodic boundary conditions are applicable """ from masci_tools.util.schema_dict_util import read_constants, eval_simple_xpath from masci_tools.util.schema_dict_util import evaluate_text, evaluate_attribute from masci_tools.util.xml.common_functions import clear_xml if isinstance(xmltree, etree._ElementTree): xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() else: root = xmltree constants = read_constants(root, schema_dict, logger=logger) cell, pbc = get_cell(root, schema_dict, logger=logger) kpointlist = eval_simple_xpath(root, schema_dict, 'kPointList', list_return=True, not_contains='altKPoint', logger=logger) if len(kpointlist) == 0: raise ValueError('No Kpoint lists found in the given inp.xml') kpointlist = kpointlist[0] kpoints = evaluate_text(kpointlist, schema_dict, 'kPoint', constants=constants, not_contains='altKPoint', list_return=True, logger=logger) weights = evaluate_attribute(kpointlist, schema_dict, 'weight', constants=constants, not_contains='altKPoint', list_return=True, logger=logger) return kpoints, weights, cell, pbc
def get_relaxation_information(xmltree, schema_dict, logger=None): """ Get the relaxation information from the given fleur XML file. This includes the current displacements, energy and posforce evolution :param xmltree: etree representing the fleur xml file :param schema_dict: schema dictionary corresponding to the file version of the xmltree :param logger: logger object for logging warnings, errors :returns: dict with the relaxation information :raises ValueError: If no relaxation section is included in the xml tree """ from masci_tools.util.schema_dict_util import tag_exists, read_constants, evaluate_text, eval_simple_xpath from masci_tools.util.schema_dict_util import evaluate_attribute from masci_tools.util.xml.common_functions import clear_xml if isinstance(xmltree, etree._ElementTree): xmltree, _ = clear_xml(xmltree) root = xmltree.getroot() else: root = xmltree constants = read_constants(root, schema_dict, logger=logger) if not tag_exists(root, schema_dict, 'relaxation', logger=logger): raise ValueError( 'No relaxation information included in the given XML file') relax_tag = eval_simple_xpath(root, schema_dict, 'relaxation', logger=logger) out_dict = {} out_dict['displacements'] = evaluate_text(relax_tag, schema_dict, 'displace', list_return=True, constants=constants, logger=logger) out_dict['energies'] = evaluate_attribute(relax_tag, schema_dict, 'energy', list_return=True, constants=constants, logger=logger) out_dict['posforces'] = [] relax_iters = eval_simple_xpath(relax_tag, schema_dict, 'step', list_return=True, logger=logger) for step in relax_iters: posforces = evaluate_text(step, schema_dict, 'posforce', list_return=True, constants=constants, logger=logger) out_dict['posforces'].append(posforces) return out_dict
def rotate_nmmpmat(xmltree, nmmplines, schema_dict, species_name, orbital, phi, theta): """ Rotate the density matrix with the given angles phi and theta :param xmltree: an xmltree that represents inp.xml :param nmmplines: list of lines in the n_mmp_mat file :param schema_dict: InputSchemaDict containing all information about the structure of the input :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 phi: float, angle (radian), by which to rotate the density matrix :param theta: float, angle (radian), by which to rotate the density matrix :raises ValueError: If something in the input is wrong :raises KeyError: If no LDA+U procedure is found on a species :returns: list with modified nmmplines """ from masci_tools.util.xml.common_functions import eval_xpath, get_xml_attribute from masci_tools.util.schema_dict_util import evaluate_attribute, eval_simple_xpath, attrib_exists from masci_tools.io.io_nmmpmat import read_nmmpmat_block, rotate_nmmpmat_block, format_nmmpmat species_base_path = get_tag_xpath(schema_dict, 'species') if species_name == 'all': species_xpath = species_base_path elif species_name[:4] == 'all-': #format all-<string> species_xpath = f'{species_base_path}[contains(@name,"{species_name[4:]}")]' else: species_xpath = f'{species_base_path}[@name = "{species_name}"]' all_species = eval_xpath(xmltree, species_xpath, list_return=True) nspins = evaluate_attribute(xmltree, schema_dict, 'jspins') if 'l_mtnocoPot' in schema_dict['attrib_types']: if attrib_exists(xmltree, schema_dict, 'l_mtnocoPot', contains='Setup'): if evaluate_attribute(xmltree, schema_dict, 'l_mtnocoPot', contains='Setup'): nspins = 3 all_ldau = eval_simple_xpath(xmltree, schema_dict, 'ldaU', contains='species', list_return=True) 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 nmmplines is not None: #Remove blank lines while '' in nmmplines: nmmplines.remove('') if numRows != len(nmmplines): 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') else: raise ValueError('rotate_nmmpmat has to be called with a initialized density matrix') 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 = evaluate_attribute(ldau, schema_dict, 'l', contains='species') 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}') denmat = [] for spin in range(nspins): startRow = (spin * len(all_ldau) + ldau_index) * 14 denmat = read_nmmpmat_block(nmmplines, spin * len(all_ldau) + ldau_index) denmat = rotate_nmmpmat_block(denmat, orbital, phi=phi, theta=theta) nmmplines[startRow:startRow + 14] = format_nmmpmat(denmat) return nmmplines