def test_get_tagattrib_xpath_case_insensitivity(): """ Test that the selection works with case insensitivity """ from masci_tools.util.schema_dict_util import get_tag_xpath, get_attrib_xpath schema_dict = schema_dict_34 assert get_tag_xpath(schema_dict, 'bzIntegration') == '/fleurInput/cell/bzIntegration' assert get_tag_xpath(schema_dict, 'BZINTEGRATION') == '/fleurInput/cell/bzIntegration' assert get_tag_xpath(schema_dict, 'bzintegration') == '/fleurInput/cell/bzIntegration' assert get_tag_xpath(schema_dict, 'bZInTegrAtIon') == '/fleurInput/cell/bzIntegration' assert get_attrib_xpath( schema_dict, 'jspins') == '/fleurInput/calculationSetup/magnetism/@jspins' assert get_attrib_xpath( schema_dict, 'JSPINS') == '/fleurInput/calculationSetup/magnetism/@jspins' assert get_attrib_xpath( schema_dict, 'jSpInS') == '/fleurInput/calculationSetup/magnetism/@jspins'
def delete_att(xmltree, schema_dict, attrib_name, complex_xpath=None, occurrences=None, **kwargs): """ This method deletes a attribute with a uniquely identified xpath. :param xmltree: an xmltree that represents inp.xml :param schema_dict: InputSchemaDict containing all information about the structure of the input :param tag: str of the attribute to delete :param complex_xpath: an optional xpath to use instead of the simple xpath for the evaluation :param occurrences: int or list of int. Which occurence of the parent nodes to delete a attribute. By default all nodes are used. Kwargs: :param tag_name: str, name of the tag where the attribute should be parsed :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 exclude: list of str, here specific types of attributes can be excluded valid values are: settable, settable_contains, other :returns: xmltree with deleted attributes """ from masci_tools.util.xml.xml_setters_basic import xml_delete_att from masci_tools.util.xml.common_functions import check_complex_xpath, split_off_attrib base_xpath = get_attrib_xpath(schema_dict, attrib_name, **kwargs) tag_xpath, attrib_name = split_off_attrib(base_xpath) if complex_xpath is None: complex_xpath = tag_xpath check_complex_xpath(xmltree, tag_xpath, complex_xpath) xmltree = xml_delete_att(xmltree, complex_xpath, attrib_name, occurrences=occurrences) return xmltree
def set_inpchanges(xmltree, schema_dict, change_dict, path_spec=None): """ This method sets all the attribute and texts provided in the change_dict. The first occurrence of the attribute/tag is set :param xmltree: xml tree that represents inp.xml :param schema_dict: InputSchemaDict containing all information about the structure of the input :params change_dict: dictionary {attrib_name : value} with all the wanted changes. :param path_spec: dict, with ggf. necessary further specifications for the path of the attribute An example of change_dict:: change_dict = {'itmax' : 1, 'l_noco': True, 'ctail': False, 'l_ss': True} :returns: an xmltree of the inp.xml file with changes. """ from masci_tools.util.xml.xml_setters_xpaths import xml_set_first_attrib_value, xml_set_first_text from masci_tools.util.xml.common_functions import split_off_attrib from masci_tools.util.case_insensitive_dict import CaseInsensitiveDict if path_spec is None: path_spec = {} path_spec = CaseInsensitiveDict(path_spec) for key, change_value in change_dict.items(): #Special alias for xcFunctional since name is not a very telling attribute name if key == 'xcFunctional': key = 'name' if key not in schema_dict['attrib_types'] and key not in schema_dict['simple_elements']: raise ValueError(f"You try to set the key:'{key}' to : '{change_value}', but the key is unknown" ' to the fleur plug-in') text_attrib = key not in schema_dict['attrib_types'] key_spec = path_spec.get(key, {}) #This method only support unique and unique_path attributes if 'exclude' not in key_spec: key_spec['exclude'] = ['other'] elif 'other' not in key_spec['exclude']: key_spec['exclude'].append('other') key_xpath = get_attrib_xpath(schema_dict, key, **key_spec) if not text_attrib: #Split up path into tag path and attribute name (original name of key could have different cases) key_xpath, key = split_off_attrib(key_xpath) if text_attrib: xml_set_first_text(xmltree, schema_dict, key_xpath, key_xpath, change_value) else: xml_set_first_attrib_value(xmltree, schema_dict, key_xpath, key_xpath, key, change_value) return xmltree
def test_get_attrib_xpath_output(): """ Test the path finding for tags for the input schema without additional options And verify with different version of the schema """ from masci_tools.util.schema_dict_util import get_attrib_xpath #absolute assert get_attrib_xpath( outschema_dict_31, 'nat') == '/fleurOutput/numericalParameters/atomsInCell/@nat' assert get_attrib_xpath( outschema_dict_34, 'nat') == '/fleurOutput/numericalParameters/atomsInCell/@nat' #relative assert get_attrib_xpath(outschema_dict_31, 'qvectors') == './Forcetheorem_SSDISP/@qvectors' assert get_attrib_xpath(outschema_dict_34, 'qvectors') == './Forcetheorem_SSDISP/@qvectors'
def test_get_attrib_xpath_exclude_output(): """ Test the selection of paths based on a contained keyword """ from masci_tools.util.schema_dict_util import get_attrib_xpath schema_dict = outschema_dict_34 with pytest.raises( ValueError, match= 'The attrib units has multiple possible paths with the current specification.' ): get_attrib_xpath(schema_dict, 'units') assert get_attrib_xpath(schema_dict, 'units', contains='DMI') == './Forcetheorem_DMI/@units' assert get_attrib_xpath(schema_dict, 'units', exclude=['other'], contains='DMI') == './Forcetheorem_DMI/@units' with pytest.raises( ValueError, match= 'The attrib units has no possible paths with the current specification.' ): get_attrib_xpath(schema_dict, 'units', exclude=['unique_path'], contains='DMI')
def test_get_attrib_xpath_exclude(): """ Test the selection of paths based on a contained keyword """ from masci_tools.util.schema_dict_util import get_attrib_xpath schema_dict = schema_dict_34 with pytest.raises( ValueError, match= 'The attrib alpha has multiple possible paths with the current specification.' ): get_attrib_xpath(schema_dict, 'alpha') assert get_attrib_xpath(schema_dict, 'alpha', exclude=[ 'unique_path', 'other' ]) == '/fleurInput/calculationSetup/scfLoop/@alpha' with pytest.raises( ValueError, match= 'The attrib alpha has multiple possible paths with the current specification.' ): get_attrib_xpath(schema_dict, 'alpha', exclude=['unique']) assert get_attrib_xpath( schema_dict, 'alpha', not_contains='atom', exclude=['unique'] ) == '/fleurInput/calculationSetup/greensFunction/contourSemicircle/@alpha'
def test_get_attrib_xpath_contains(): """ Test the selection of paths based on a contained keyword """ from masci_tools.util.schema_dict_util import get_attrib_xpath schema_dict = schema_dict_34 with pytest.raises( ValueError, match= 'The attrib l_mperp has multiple possible paths with the current specification.' ): get_attrib_xpath(schema_dict, 'l_mperp') assert get_attrib_xpath( schema_dict, 'l_mperp', contains='magnetism' ) == '/fleurInput/calculationSetup/magnetism/mtNocoParams/@l_mperp' assert get_attrib_xpath( schema_dict, 'l_mperp', contains='greensFunction' ) == '/fleurInput/calculationSetup/greensFunction/@l_mperp' with pytest.raises( ValueError, match= 'The attrib l_mperp has no possible paths with the current specification.' ): get_attrib_xpath(schema_dict, 'l_mperp', contains='atom')
def add_number_to_attrib(xmltree, schema_dict, attributename, add_number, complex_xpath=None, mode='abs', occurrences=None, **kwargs): """ Adds a given number to the attribute value in a xmltree specified by the name of the attribute and optional further specification If there are no nodes under the specified xpath an error is raised :param xmltree: an xmltree that represents inp.xml :param schema_dict: InputSchemaDict containing all information about the structure of the input :param attributename: the attribute name to change :param add_number: number to add/multiply with the old attribute value :param complex_xpath: an optional xpath to use instead of the simple xpath for the evaluation :param mode: str (either `rel` or `abs`). `rel` multiplies the old value with `add_number` `abs` adds the old value and `add_number` :param occurrences: int or list of int. Which occurence of the node to set. By default all are set. Kwargs: :param tag_name: str, name of the tag where the attribute should be parsed :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 exclude: list of str, here specific types of attributes can be excluded valid values are: settable, settable_contains, other :returns: xmltree with shifted attribute """ from masci_tools.util.xml.xml_setters_xpaths import xml_add_number_to_attrib from masci_tools.util.xml.common_functions import split_off_attrib attrib_xpath = get_attrib_xpath(schema_dict, attributename, **kwargs) base_xpath, attributename = split_off_attrib(attrib_xpath) if complex_xpath is None: complex_xpath = base_xpath xmltree = xml_add_number_to_attrib(xmltree, schema_dict, complex_xpath, base_xpath, attributename, add_number, mode=mode, occurrences=occurrences) return xmltree
def get_attrib_xpath(self, name, contains=None, not_contains=None, exclude=None, tag_name=None): """ Tries to find a unique path from the schema_dict based on the given name of the attribute and additional further specifications :param name: str, name of the attribute :param contains: str or list of str, this string has to be in the final path :param not_contains: str or list of str, this string has to NOT be in the final path :param exclude: list of str, here specific types of attributes can be excluded valid values are: settable, settable_contains, other :param tag_name: str, if given this name will be used to find a path to a tag with the same name in :py:func:`get_tag_xpath()` :returns: str, xpath to the tag with the given attribute :raises ValueError: If no unique path could be found """ return get_attrib_xpath(self, name, contains=contains, not_contains=not_contains, exclude=exclude, tag_name=tag_name)
def test_get_attrib_xpath_input(): """ Test the path finding for tags for the input schema without additional options And verify with different version of the schema """ from masci_tools.util.schema_dict_util import get_attrib_xpath #First example easy (magnetism tag is unique and should not differ between the versions) assert get_attrib_xpath( schema_dict_27, 'jspins') == '/fleurInput/calculationSetup/magnetism/@jspins' assert get_attrib_xpath( schema_dict_34, 'jspins') == '/fleurInput/calculationSetup/magnetism/@jspins' #Differing paths between the version assert get_attrib_xpath( schema_dict_27, 'mode') == '/fleurInput/calculationSetup/bzIntegration/@mode' assert get_attrib_xpath(schema_dict_34, 'mode') == '/fleurInput/cell/bzIntegration/@mode' #Non existent tag in old version assert get_attrib_xpath(schema_dict_34, 'l_mtNocoPot', exclude=[ 'other' ]) == '/fleurInput/calculationSetup/magnetism/mtNocoParams/@l_mtNocoPot' with pytest.raises( ValueError, match= 'The attrib l_mtNocoPot has no possible paths with the current specification.' ): get_attrib_xpath(schema_dict_27, 'l_mtNocoPot') #Multiple possible paths with pytest.raises( ValueError, match= 'The attrib l_amf has multiple possible paths with the current specification.' ): get_attrib_xpath(schema_dict_27, 'l_amf') with pytest.raises( ValueError, match= 'The attrib l_amf has multiple possible paths with the current specification.' ): get_attrib_xpath(schema_dict_34, 'l_amf')
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 set_attrib_value(xmltree, schema_dict, attributename, attribv, complex_xpath=None, occurrences=None, create=False, **kwargs): """ Sets an attribute in a xmltree to a given value, specified by its name and further specifications. If there are no nodes under the specified xpath a tag can be created with `create=True`. The attribute values are converted automatically according to the types of the attribute with :py:func:`~masci_tools.util.xml.converters.convert_attribute_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 attributename: the attribute name to set :param attribv: value or list of values to set :param complex_xpath: an optional xpath to use instead of the simple xpath for the evaluation :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 Kwargs: :param tag_name: str, name of the tag where the attribute should be parsed :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 exclude: list of str, here specific types of attributes can be excluded valid values are: settable, settable_contains, other :returns: xmltree with set attribute """ from masci_tools.util.xml.xml_setters_xpaths import xml_set_attrib_value from masci_tools.util.xml.common_functions import split_off_attrib #Special case for xcFunctional #(Also implemented here to not confuse users since it would only work in set_inpchanges otherwise) if attributename == 'xcFunctional': attributename = 'name' if 'exclude' not in kwargs: kwargs['exclude'] = ['other'] elif 'other' not in kwargs['exclude']: kwargs['exclude'].append('other') base_xpath = get_attrib_xpath(schema_dict, attributename, **kwargs) base_xpath, attributename = split_off_attrib(base_xpath) if complex_xpath is None: complex_xpath = base_xpath xmltree = xml_set_attrib_value(xmltree, schema_dict, complex_xpath, base_xpath, attributename, attribv, occurrences=occurrences, create=create) return xmltree