def hsd_node_factory(classtype, classes, node, query): '''Creates an object depending on the node and a class dictionary. Parameters ---------- classtype : str Textual name of the class to create for error messages (e.g. 'twocenter calculator') classes : dict Contains classes (not instances!) with their corresponding hsd-name. Each must have a `fromhsd(node, query)` class method. node : Element HSD representation of the node. query : query object Query object to use. Returns ------- node : Element or None Returns the element created using the hsd input in the node or None if the node passed was None. ''' if node is None: return None myclass = classes.get(node.tag) if myclass is None: raise hsd.HSDInvalidTagException( "Unknown {} '{}'".format(classtype, node.tag)) return myclass.fromhsd(node, query)
def get_shellvalues(node, query): '''Returns dictionary with the values assigned to individual shells. Args: node (Element): parent node query (HSDQuery): queries an HSD-tree Returns: values (dict): dictionary with the values assigned to individual shells ''' values = {} for child in node: try: shell = shell_name_to_ind(child.tag) except ValueError: raise hsd.HSDInvalidTagException( msg="Invalid shell name '{}'".format(child.tag), node=child) value = query.getvalue(child, '.', conv.float0) values[shell] = value return values
def getshellvalues(node, query): """Returns dictionary with the values assigned to individual shells.""" values = {} for child in node: try: shell = shell_name_to_ind(child.tag) except ValueError: raise hsd.HSDInvalidTagException( msg="Invalid shell name '{}'".format(child.tag), node=child) value = query.getvalue(child, ".", conv.float0) values[shell] = value return values
def fromhsd(cls, root, query): myself = cls() for shellnode in root: try: nn, ll = sc.shell_name_to_ind(shellnode.tag) except ValueError: raise hsd.HSDInvalidTagException( "Invalid shell name '{}'".format(shellnode.tag), shellnode) wavecompr = sc.hsd_node_factory( 'wavefunction compression', COMPRESSIONS, query.getvaluenode(shellnode, '.'), query) myself[(nn, ll)] = wavecompr return myself
def findchild(self, node, name, optional=False): """Finds a child of a node with a given name. Parameters ---------- node : Element Parent node. name : string Name of the child to look for. optional : bool, optional Whether the child is optional only. Returns ------- child : Element or None A hsd node if child has been found or None. Raises ------- HSDMissingTagException If child was not found and the optional flag was False. HSDInvalidTagException Iff there are duplicates of the child and the query object was initialized with `check_uniqueness=True`. """ if self.chkunique: children = node.findall("./" + name) if len(children) > 1: raise hsd.HSDInvalidTagException( node=children[1], msg="Double occurance of unique tag '{}'.".format(name)) child = children[0] if children else None else: child = node.find("./" + name) if child is None and not optional: raise hsd.HSDMissingTagException( msg="Required tag '{}' not found.".format(name), node=node) self.markprocessed(child) return child
def _test_module(): """Testing module capabilities.""" from io import StringIO from sktools.hsd.treebuilder import HSDTreeBuilder from sktools.hsd.parser import HSDParser from sktools.hsd.tree import HSDTree import sktools.hsd.converter as conv unit_attr = "unit" unit_only = frozenset([unit_attr]) parser = HSDParser(defattrib=unit_attr) builder = HSDTreeBuilder(parser=parser) # Defining force type (scalar, list) force_units = {"ev/aa": 0.0194469050555} # Trivial unit conversion routine. def multiply_unit(child, value, unitattr, units): unit = child.get(unitattr) convfact = units.get(unit.lower(), None) if convfact is None: hsd.HSDInvalidAttributeValueException( node=child, msg="Invalid unit '{}'".format(unit)) return value * convfact stream = StringIO(""" # Various driver possibilities # # No driver specification #Driver {} # Use the default driver (whatever it is) #Driver = None {} # Use the driver None {} #Driver = None Driver = ConjugateGradient { MaxForceComponent [eV/AA] = 1e-2 } Hamiltonian = DFTB { # SCC = True # SCCTolerance = 1e-4 # MaxSCCIterations = 100 MaxAngularMomentum { O = "p" H = "s" } Mixer = Broyden #Mixer = Broyden { # MixingParameter = 0.3 #} #ReadInitialCharges = No KPointsAndWeights { 0.0 0.0 0.0 0.25 0.25 0.25 0.25 0.75 } } Options { WriteAutotestTag = Yes UnknownOption = No } #ParserOptions { # ParserVersion = 4 #} """) root = builder.build(stream) qy = HSDQuery(markprocessed=True) # A complex case: If driver was not specified, it defaults to None {} # If it was specified but nothing was assinged to it (no child) # it defaults to ConjugateGradient {}. dtype, driver = qy.getvaluenode(root, "Driver", "None", allowtextvalue=True, returnchild=True) # Since the in the previous getvaluenode() call a default had been specified # dtype can only be None, if "Driver" was in the input, but had no # child (e.g. 'Driver {}' or 'Driver = ;'). In this case we set # it to ConjugateGradient if dtype is None: dtype = qy.getchild(driver, "ConjugateGradient", optional=True) if dtype.tag == "None": pass elif dtype.tag == "ConjugateGradient": forcetol, child = qy.getvalue(dtype, "MaxForceComponent", conv.float0, 1e-4, returnchild=True, attribs=unit_only) multiply_unit(child, forcetol, unit_attr, force_units) elif dtype.tag == "SteepestDescent": forcetol, child = qy.getvalue(dtype, "MaxForceComponent", conv.float0, 1e-4, returnchild=True, attribs=unit_only) multiply_unit(child, forcetol, unit_attr, force_units) stepsize = qy.getvalue(dtype, "StepSize", conv.float0, 40.0) pass else: raise hsd.HSDInvalidTagException(node=dtype, msg="Unknown driver type '{}'".format( dtype.tag)) ham = qy.getchild(root, "Hamiltonian") dftb = qy.getchild(ham, "DFTB") scc = qy.getvalue(dftb, "SCC", conv.bool0, True) scctol = qy.getvalue(dftb, "SCCTolerance", conv.float0, 1e-4) scciter = qy.getvalue(dftb, "MaxSCCIterations", conv.int0, 100) mangmom = qy.getchild(dftb, "MaxAngularMomentum") maxangs = [ qy.getvalue(mangmom, species, conv.str0) for species in ["O", "H"] ] mixer = qy.getvaluenode(dftb, "Mixer", "Broyden", allowtextvalue=True) if mixer.tag == "Broyden": mixparam = qy.getvalue(mixer, "MixingParameter", conv.float0, 0.2) else: raise hsd.HSDInvalidTagException(node=mixer, msg="Unknown mixer type '{}'.".format( mixer.tag)) readcharges = qy.getvalue(dftb, "ReadInitalCharges", conv.bool0, False) kpoints = qy.getvalue(dftb, "KPointsAndWeights", conv.float1) if len(kpoints) % 4: raise hsd.HSDInvalidTagValueException(node=kpoints, msg="Incorrect number of floats") options = qy.getchild(root, "Options", optional=True) autotest = qy.getvalue(options, "WriteAutotestTag", conv.bool0, False) parseroptions = qy.getchild(root, "ParserOptions", optional=True) parserversion = qy.getvalue(parseroptions, "ParserVersion", conv.int0, 4) tree = HSDTree(root) tree.writehsd() print("\nUnprocessed: ", qy.findunprocessednodes(root))
def getvaluenode(self, node, name, defvalue=None, defvaluename=None, defattribs=None, hsdblock=False, allowtextvalue=False, returnchild=False): """Returns the value node stored in a child with a given name. The child should contain either no nodes at all or only this one. Parameters ---------- node : Element Parent node. name : string Name of the child to look for. defvalue : Element, optional If child is not found, it will be created and contain the specified node as subnode. defvaluename : string, optional If child is not found, it will be created and contain a subnode with the given name. If name is "", the child node will not contain a subnode. It is ignored, if defvalue had been specified. defattribs : dict, optional Default attribute dictionary used if child has not been found. hsdblock : bool, optional Whether the given value should be added in hsd block notation (enclosed in curly braces) instead of an assignment. allowtextvalue : bool, optional If set to yes, the child (if it exists) is allowed to have no subnode, but a text value instead. In that case the text value will be deleted and converted into an empty node. returnchild : bool, optional Returns ------- node : Element or None The child node's first child or a node with the specified default name if child had not been found. In latter case, an appropriate child node with this as subnode is inserted into the tree. child : Element, optional The child not itself. Only returned if `returnchild=Yes` was set. Raises ------ HSDMissingTagException If child was not found and no default value had been specified. HSDInvalidTagException If child has more than one child. Notes ----- This routine should be used, if the child is not a leaf but contains a further child. """ optional = defvalue is not None or defvaluename is not None child = self.findchild(node, name, optional) if child is not None: if len(child) > 1: raise hsd.HSDInvalidTagException( "Tag '{}' is not allowed to have" " more than one child".format(child.tag), node=child) self.markprocessed(child) if len(child): self.markprocessed(child[0]) return (child[0], child) if returnchild else child[0] elif allowtextvalue and child.text: valuenode = Element(child.text) child.text = "" child.append(valuenode) self.markprocessed(valuenode) return (valuenode, child) if returnchild else valuenode else: return (None, child) if returnchild else None else: if defvalue is None: defvalue = Element(defvaluename) child, valuenode = self.setvaluenode(node, name, defvalue, defattribs, hsdblock) return (valuenode, child) if returnchild else valuenode
def getvalue(self, node, name, converter=None, defvalue=None, attribs=None, defattribs=None, hsdblock=False, returnchild=False): """Returns the converted value of the data stored in a child with a given name. Parameters ---------- node : Element Parent node. name : string Name of the child to look for. converter : converter object Object with methods fromhsd() and tohsd() which can convert between the hsd element and the desired type. See converters in hsd.converter for examples. defvalue : arbitrary, optional Default value used if child has not been found. It will be converted to text by the tohsd() method of the converter. attribs : frozen set Set of attributes the node is allowed to have. defattribs: dict, optional Default attribute dictionary used if child has not been found. hsdblock : bool, optional Whether the given value should be added in hsd block notation (enclosed in curly braces) instead of an assignment. returnchild : bool, optional Whether not only the value but also the child node should be returned. Returns ------- value : arbitrary The converted value of the child node's text or the default value if the child had not been found. In latter case, an appropriate node with the appropriate text representation of the default value is inserted into the tree. child : Element, optional Child node. Only returned, if `returnchild=True` was set. Raises ------ HSDMissingTagException If child was not found and no default value had been specified. HSDInvalidTagValueException If conversion from tag values was unsuccessful. HSDInvalidAttributeException If node posses an attribute which is not allowed. Notes ----- This method may store a reference to the converter object. Make sure you pass something which does not change afterwards. """ optional = defvalue is not None child = self.findchild(node, name, optional) if child is not None: if len(child): raise hsd.HSDInvalidTagException("Unexpected children") self._checkattribs(child, attribs) if converter: try: value = converter.fromhsd(child.text) except ValueError as ex: raise hsd.HSDInvalidTagValueException( msg="Conversion error: " + str(ex), node=child) else: value = child.text return (value, child) if returnchild else value else: child = self.setvalue(node, name, converter, defvalue, defattribs, hsdblock) return (defvalue, child) if returnchild else defvalue