def test_convert_text_to_xml(text_value, definitions, results):
    """
    Test of the convert_xml_attribute function
    """
    from masci_tools.util.xml.converters import convert_text_to_xml

    expected_val, expected_suc = results
    if not expected_suc:
        with pytest.raises(ValueError):
            ret_val, suc = convert_text_to_xml(text_value, definitions)
    else:
        ret_val, suc = convert_text_to_xml(text_value, definitions)
        assert ret_val == expected_val
        assert suc == expected_suc
def xml_set_text(xmltree,
                 schema_dict,
                 xpath,
                 base_xpath,
                 text,
                 occurrences=None,
                 create=False):
    """
    Sets the text on tags in a xmltree to a given value. By default the text will be set
    on all nodes returned for the specified xpath.
    If there are no nodes under the specified xpath a tag can be created with `create=True`.
    The text values are converted automatically according to the types
    with :py:func:`~masci_tools.util.xml.converters.convert_text_to_xml()` if they
    are not `str` already.

    :param xmltree: an xmltree that represents inp.xml
    :param schema_dict: InputSchemaDict containing all information about the structure of the input
    :param xpath: a path where to set the text
    :param base_xpath: path where to place a new tag without complex syntax ([] conditions and so on)
    :param text: value or list of values to set
    :param occurrences: int or list of int. Which occurence of the node to set. By default all are set.
    :param create: bool optional (default False), if True the tag is created if is missing

    :raises ValueError: If the conversion to string failed
    :raises ValueError: If the tag is missing and `create=False`

    :returns: xmltree with set text
    """
    from masci_tools.util.xml.xml_setters_basic import xml_set_text_no_create
    from masci_tools.util.xml.converters import convert_text_to_xml
    from masci_tools.util.xml.common_functions import check_complex_xpath, split_off_tag

    check_complex_xpath(xmltree, base_xpath, xpath)

    if create:
        nodes = eval_xpath_create(xmltree,
                                  schema_dict,
                                  xpath,
                                  base_xpath,
                                  create_parents=True,
                                  occurrences=occurrences,
                                  list_return=True)
    else:
        nodes = eval_xpath(xmltree, xpath, list_return=True)

    if len(nodes) == 0:
        raise ValueError(
            f"Could not set text on path '{xpath}' because atleast one subtag is missing. "
            'Use create=True to create the subtags')

    _, tag_name = split_off_tag(base_xpath)

    possible_definitions = schema_dict['simple_elements'][tag_name]

    converted_text, suc = convert_text_to_xml(text, possible_definitions)

    return xml_set_text_no_create(xmltree,
                                  xpath,
                                  converted_text,
                                  occurrences=occurrences)
def test_convert_text_to_xml_warnings(caplog, text_value, definitions, results, warnings):
    """
    Test of the convert_xml_attribute function
    """
    from masci_tools.util.xml.converters import convert_text_to_xml

    expected_val, expected_suc = results

    with caplog.at_level(logging.WARNING):
        ret_val, suc = convert_text_to_xml(text_value, definitions, logger=LOGGER)
    assert ret_val == expected_val
    assert suc == expected_suc
    if len(warnings) == 0:
        assert caplog.text == ''
    else:
        for expected_warning in warnings:
            assert expected_warning in caplog.text
예제 #4
0
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
예제 #5
0
def set_kpath_max4(xmltree, schema_dict, kpath, count, gamma=False):
    """
    Sets a k-path directly into inp.xml as a alternative kpoint set with purpose 'bands'

    :param xmltree: xml tree that represents inp.xml
    :param schema_dict: InputSchemaDict containing all information about the structure of the input
    :param kpath: a dictionary with kpoint name as key and k point coordinate as value
    :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 tag_exists
    from masci_tools.util.xml.converters import convert_to_fortran_bool, convert_text_to_xml
    from masci_tools.util.xml.xml_setters_basic import xml_replace_tag
    from lxml import etree

    alt_kpt_set_xpath = get_tag_xpath(schema_dict, 'altKPointSet')

    if not tag_exists(xmltree, schema_dict, 'kPointCount', contains='altKPoint'):
        xmltree = create_tag(xmltree, schema_dict, 'kPointCount', contains='altKPoint', create_parents=True)
        xmltree = set_first_attrib_value(xmltree, schema_dict, 'purpose', 'bands')

    new_kpo = etree.Element('kPointCount', count=f'{count}', gamma=f'{convert_to_fortran_bool(gamma)}')
    for label, coord in kpath.items():
        new_k = etree.Element('specialPoint', name=f'{label}')
        text, _ = convert_text_to_xml(coord, [{'type': ['float', 'float_expression'], 'length': 3}])
        new_k.text = text
        new_kpo.append(new_k)

    kpath_xpath = f"{alt_kpt_set_xpath}[@purpose='bands']/kPointCount"

    xmltree = xml_replace_tag(xmltree, kpath_xpath, new_kpo)

    return xmltree
예제 #6
0
def set_kpointlist(xmltree,
                   schema_dict,
                   kpoints,
                   weights,
                   name=None,
                   kpoint_type='path',
                   special_labels=None,
                   switch=False,
                   overwrite=False):
    """
    Explicitely create a kPointList from the given kpoints and weights. This routine will add the
    specified kPointList with the given name.

    .. warning::
        For input versions Max4 and older **all** keyword arguments are not valid (`name`, `kpoint_type`,
        `special_labels`, `switch` and `overwrite`)

    :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
    :param name: str for the name of the list, if not given a default name is generated
    :param kpoint_type: str specifying the type of the kPointList ('path', 'mesh', 'spex', 'tria', ...)
    :param special_labels: dict mapping indices to labels. The labels will be inserted for the kpoints
                           corresponding to the given index
    :param switch: bool, if True the kPointlist will be used by Fleur when starting the next calculation
    :param overwrite: bool, if True and a kPointlist with the given name already exists it will be overwritten

    :returns: an xmltree of the inp.xml file with changes.
    """
    from masci_tools.util.schema_dict_util import evaluate_attribute
    from masci_tools.util.xml.converters import convert_text_to_xml, convert_attribute_to_xml
    from masci_tools.util.xml.xml_setters_basic import xml_delete_tag
    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')

    kpointlist_xpath = get_tag_xpath(schema_dict, 'kPointList')
    nkpts = len(kpoints)

    if special_labels is None:
        special_labels = {}

    existing_labels = evaluate_attribute(xmltree, schema_dict, 'name', contains='kPointList', list_return=True)

    if name is None:
        name = f'default-{len(existing_labels)+1}'

    if name in existing_labels:
        if not overwrite:
            raise ValueError(f'kPointList named {name} already exists. Use overwrite=True to ignore')

        xmltree = xml_delete_tag(xmltree, f"{kpointlist_xpath}[@name='{name}']")

    new_kpo = etree.Element('kPointList', name=name, count=f'{nkpts:d}', type=kpoint_type)
    for indx, (kpoint, weight) in enumerate(zip(kpoints, weights)):
        weight, _ = convert_attribute_to_xml(weight, ['float', 'float_expression'])
        if indx in special_labels:
            new_k = etree.Element('kPoint', weight=weight, label=special_labels[indx])
        else:
            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)

    if switch:
        xmltree = switch_kpointset(xmltree, schema_dict, name)

    return xmltree