def test_get_xml_attribute(caplog):
    """
    Test of the clear_xml function
    """
    from lxml import etree
    from masci_tools.util.xml.common_functions import get_xml_attribute, eval_xpath
    parser = etree.XMLParser(attribute_defaults=True, encoding='utf-8')
    xmltree = etree.parse(CLEAR_XML_TEST_FILE, parser)
    root = xmltree.getroot()

    scfLoop = eval_xpath(root, '//scfLoop')
    assert get_xml_attribute(scfLoop, 'alpha') == '.05000000'
    assert get_xml_attribute(scfLoop, 'itmax') == '1'
    assert get_xml_attribute(scfLoop, 'maxIterBroyd') == '99'

    with pytest.raises(
            ValueError,
            match='Tried to get attribute: "TEST" from element scfLoop.'):
        get_xml_attribute(scfLoop, 'TEST')

    with caplog.at_level(logging.WARNING):
        assert get_xml_attribute(scfLoop, 'TEST', logger=LOGGER) is None
    assert 'Tried to get attribute: "TEST" from element scfLoop.' in caplog.text

    with pytest.raises(
            TypeError,
            match=
            'Can not get attributename: "TEST" from node of type <class \'lxml.etree._ElementTree\'>'
    ):
        get_xml_attribute(xmltree, 'TEST')

    with caplog.at_level(logging.WARNING):
        assert get_xml_attribute(xmltree, 'TEST', logger=LOGGER) is None
    assert 'Can not get attributename: "TEST" from node of type <class \'lxml.etree._ElementTree\'>' in caplog.text
Пример #2
0
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
Пример #3
0
def convert_fleur_lo(loelements):
    """
    Converts lo xml elements from the inp.xml file into a lo string for the inpgen
    """
    # Developer hint: Be careful with using '' and "", basestring and str are not the same...
    # therefore other conversion methods might fail, or the wrong format could be written.
    from masci_tools.util.xml.common_functions import get_xml_attribute

    shell_map = {0: 's', 1: 'p', 2: 'd', 3: 'f'}

    lo_string = ''
    for element in loelements:
        lo_type = get_xml_attribute(element, 'type')
        if lo_type != 'SCLO':  # non standard los not supported for now
            continue
        l_num = get_xml_attribute(element, 'l')
        n_num = get_xml_attribute(element, 'n')
        lostr = f'{n_num}{shell_map[int(l_num)]}'
        lo_string = lo_string + ' ' + lostr
    return lo_string.strip()
Пример #4
0
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)

    kinEnergy = get_xml_attribute(corestateNode, kinEnergy_name)
    vE2, suc = convert_to_float(kinEnergy)
    eigenvalueSum = get_xml_attribute(corestateNode, eigenvalueSum_name)
    vE2, suc = convert_to_float(eigenvalueSum)

    spin = get_xml_attribute(corestateNode, spin_name)
    #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_xpath(corestateNode, state_xpath,
                            list_return=True)  #, parser_info)

    for corestate in corestates:  # be careful that corestates is a list
        state_dict = {}
        n_state = get_xml_attribute(corestate, n_name)
        l_state = get_xml_attribute(corestate, l_name)
        j_state = get_xml_attribute(corestate, j_name)
        energy, suc = convert_to_float(
            get_xml_attribute(corestate, energy_name))
        weight, suc = convert_to_float(
            get_xml_attribute(corestate, weight_name))
        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
Пример #5
0
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
Пример #6
0
def evaluate_parent_tag(node, schema_dict, name, constants=None, logger=None, **kwargs):
    """
    Evaluates all attributes of the parent tag based on the given name
    and additional further specifications with the available type information

    :param node: etree Element, on which to execute the xpath evaluations
    :param schema_dict: dict, containing all the path information and more
    :param name: str, name of the tag
    :param constants: dict, contains the defined constants
    :param logger: logger object for logging warnings, errors, if not provided all errors will be raised

    Kwargs:
        :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
        :param only_required: bool (optional, default False), if True only required attributes are parsed
        :param ignore: list of str (optional), attributes not to parse
        :param list_return: if True, the returned quantity is always a list even if only one element is in it
        :param strict_missing_error: if True, and no logger is given an error is raised if any attribute is not found

    :returns: dict, with attribute values converted via convert_xml_attribute
    """
    from masci_tools.util.xml.common_functions import eval_xpath, get_xml_attribute
    from masci_tools.util.xml.converters import convert_xml_attribute

    strict_missing_error = kwargs.pop('strict_missing_error', False)
    list_return = kwargs.pop('list_return', False)
    only_required = kwargs.pop('only_required', False)
    ignore = kwargs.pop('ignore', None)

    tag_xpath = None
    if isinstance(node, etree._Element):
        if node.tag != schema_dict['root_tag'] and node.tag != 'iteration':
            kwargs['contains'] = set(kwargs.get('contains', []))
            kwargs['contains'].add(node.tag)
            tag_xpath = get_relative_tag_xpath(schema_dict, name, node.tag, **kwargs)

    if tag_xpath is None:
        tag_xpath = get_tag_xpath(schema_dict, name, **kwargs)

    #Which attributes are expected
    try:
        tag_info = get_tag_info(schema_dict, name, path_return=False, multiple_paths=True, parent=True, **kwargs)
        attribs = tag_info['attribs']
        optional = tag_info['optional_attribs']
    except ValueError as err:
        if logger is None:
            raise ValueError(f'Failed to evaluate attributes from parent tag of {name}: '
                             'No attributes to parse either the tag does not '
                             'exist or it has no attributes') from err
        else:
            logger.exception(
                'Failed to evaluate attributes from parent tag of %s: '
                'No attributes to parse either the tag does not '
                'exist or it has no attributes', name)
        attribs = set()
        optional = set()

    if only_required:
        attribs = attribs.difference(optional)

    if ignore is not None:
        attribs = attribs.difference(ignore)

    if not attribs:
        if logger is None:
            raise ValueError(f'Failed to evaluate attributes from parent tag of {name}: '
                             'No attributes to parse either the tag does not '
                             'exist or it has no attributes')
        else:
            logger.error(
                'Failed to evaluate attributes from parent tag of %s: '
                'No attributes to parse either the tag does not '
                'exist or it has no attributes', name)
    else:
        attribs = sorted(list(attribs.original_case.values()))

    elems = eval_xpath(node, tag_xpath, logger=logger, list_return=True)

    out_dict = {}
    for attrib in attribs:
        out_dict[attrib] = []

    for elem in elems:
        parent = elem.getparent()
        for attrib in attribs:

            stringattribute = get_xml_attribute(parent, attrib, logger=logger)

            if stringattribute is None:
                if logger is None:
                    if strict_missing_error and attrib not in optional:
                        raise ValueError(f'No values found for attribute {attrib} for parent tag of {name}')
                else:
                    logger.warning('No values found for attribute %s for parent tag of %s', attrib, name)
                out_dict[attrib].append(None)
                continue

            possible_types = schema_dict['attrib_types'][attrib]

            value, suc = convert_xml_attribute(stringattribute, possible_types, constants=constants, logger=logger)

            out_dict[attrib].append(value)

            if not suc:
                if logger is None:
                    raise ValueError(f'Failed to evaluate attribute {attrib}, Got value: {stringattribute}')
                else:
                    logger.warning('Failed to evaluate attribute %s, Got value: %s', attrib, stringattribute)

    if all(len(x) == 1 for x in out_dict.values()) and not list_return:
        out_dict = {key: val[0] for key, val in out_dict.items()}

    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 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 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