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