def __init__(self, name: str, nodes: devicetools.NodesConstrArg = None, elements: devicetools.ElementsConstrArg = None): self.name = str(name) self.nodes = devicetools.Nodes(nodes).copy() self.elements = devicetools.Elements(elements).copy()
def nodes(self): """A |Nodes| collection of all required nodes. >>> from hydpy import RiverBasinNumbers2Selection >>> rbns2s = RiverBasinNumbers2Selection( ... (111, 113, 1129, 11269, 1125, 11261, ... 11262, 1123, 1124, 1122, 1121)) Note that the required outlet node is added: >>> rbns2s.nodes Nodes("node_1123", "node_1125", "node_11269", "node_1129", "node_113", "node_outlet") It is both possible to change the prefix names of the nodes and the name of the outlet node separately: >>> rbns2s.node_prefix = 'b_' >>> rbns2s.last_node = 'l_node' >>> rbns2s.nodes Nodes("b_1123", "b_1125", "b_11269", "b_1129", "b_113", "l_node") """ return (devicetools.Nodes(self.node_prefix + routers for routers in self._router_numbers) + devicetools.Node(self.last_node))
def __init__(self, element, seqs=None, inits=None): nodes = devicetools.Nodes() for connection in (element.inlets, element.outlets, element.receivers, element.senders): nodes += connection.slaves for (name, node) in nodes: if (node in element.inlets) or (node in element.receivers): node.routingmode = 'oldsim' sim = node.sequences.sim sim.ramflag = True sim._setarray(numpy.zeros(len(pub.timegrids.init), dtype=float)) for (name, seq) in getattr(element.model.sequences, 'inputs', ()): seq.ramflag = True seq._setarray(numpy.zeros(len(pub.timegrids.init), dtype=float)) if seqs is None: seqs = [] for subseqs in ('inputs', 'fluxes', 'states'): for (name, seq) in getattr(element.model.sequences, subseqs, ()): seqs.append(seq) for (name, node) in nodes: seqs.append(node.sequences.sim) element.prepare_fluxseries() element.prepare_stateseries() self.element = element self.nodes = nodes self.seqs = seqs self.inits = {} if inits is None else inits self.model = element.model hydpytools.HydPy.nmb_instances = 0 self.hp = hydpytools.HydPy() self.hp.updatedevices(selectiontools.Selection('test', nodes, element))
def extract_nodes(self): """Return all nodes connected to the actual element.""" nodes = devicetools.Nodes() for connection in (self.element.inlets, self.element.outlets, self.element.receivers, self.element.senders): nodes += connection.slaves return nodes
def endnodes(self): endnodes = devicetools.Nodes() for (name, node) in self.nodes: for (name, element) in node.exits: if ((element in self.elements) and (node not in element.receivers)): break else: endnodes += node return endnodes
def deselect_modeltypes(self, *models: ModelTypesArg) -> 'Selection': """Restrict the current selection to all elements not containing the given model types (removes all nodes). See the documentation on method |Selection.search_modeltypes| for additional information. """ self.nodes = devicetools.Nodes() self.elements -= self.search_modeltypes(*models).elements return self
def deselect_modelclasses(self, *modelclasses): """Limits the current selection to all elements not containing the given modelclass(es). (All nodes are removed.) Argument: * modelclass (subclass of :class:`~hydpy.core.models.Model`): Model type(s) as the selection criterion/criteria. """ self.nodes = devicetools.Nodes() self.elements -= self.getby_modelclasses(*modelclasses) return self
def select_modelclasses(self, *modelclass): """Limits the current selection to all elements containing the given modelclass(es). (All nodes are removed.) Argument: * modelclass (subclass of |Model|): Model type(s) as the selection criterion/criteria. """ self.nodes = devicetools.Nodes() self.elements = self.getby_modelclasses(modelclass) return self
def endnodes(self): """|Nodes| object containing all |Node| objects currently handled by the |HydPy| object which define a downstream end point of a network.""" endnodes = devicetools.Nodes() for node in self.nodes: for element in node.exits: if ((element in self.elements) and (node not in element.receivers)): break else: endnodes += node return endnodes
def getby_nodenames(self, *substrings): """Returns all nodes of the current selection with a name containing the given substrings(s). Argument: * substrings (:class:`str`): (Possible) Part(s) of the nodes name as the selection criterion/criteria. """ nodes = devicetools.Nodes() for (name, node) in self.nodes: for substring in substrings: if substring in name: nodes += node break return nodes
def nodes(self) -> devicetools.Nodes: """A |set| containing the |Node| objects of all handled |Selection| objects. >>> from hydpy import Selection, Selections >>> selections = Selections( ... Selection('sel1', ['node1', 'node2'], ['element1']), ... Selection('sel2', ['node1', 'node3'], ['element2'])) >>> selections.nodes Nodes("node1", "node2", "node3") """ nodes = devicetools.Nodes() for selection in self: nodes += selection.nodes return nodes
def getby_upstream(self, device): """Returns the network upstream of the given starting point, including the starting point itself. Argument: * device (|Node| or |Element|): Lowest point to be selected. """ nodes = devicetools.Nodes() elements = devicetools.Elements() if isinstance(device, devicetools.Node): nodes, elements = self._nextnode(device, nodes, elements) elif isinstance(device, devicetools.Element): nodes, elements = self._nextelement(device, nodes, elements) else: raise AttributeError('Pass either a `Node` or an `Element` ' 'instance to the function. The given ' '`device` value `%s` is of type `%s`.' % (device, type(device))) return nodes, elements
def nodes(self) -> devicetools.Nodes: """A |Nodes| object containing all required nodes, including the outlet node. >>> from hydpy import RiverBasinNumbers2Selection >>> rbns2s = RiverBasinNumbers2Selection( ... (111, 113, 1129, 11269, 1125, 11261, ... 11262, 1123, 1124, 1122, 1121)) >>> rbns2s.nodes Nodes("node_1123", "node_1125", "node_11269", "node_1129", "node_113", "node_outlet") It is both possible to change the prefix names of the nodes and the name of the outlet node separately: >>> rbns2s.node_prefix = "b_" >>> rbns2s.last_node = "l_node" >>> rbns2s.nodes Nodes("b_1123", "b_1125", "b_11269", "b_1129", "b_113", "l_node") """ return devicetools.Nodes( self.node_prefix + routers for routers in self._router_numbers) + devicetools.Node( self.last_node)
def connect(self): """Connect the |InletSequence| and |OutletSequence| objects of the actual model to the |NodeSequence| objects handled by an arbitrary number of inlet and outlet nodes. To application models derived from |conv_model.Model|, you first need to define an |Element| connected with an arbitrary number of inlet and outlet nodes: >>> from hydpy import Element >>> conv = Element("conv", ... inlets=["in1", "in2"], ... outlets=["out1", "out2", "out3"]) Second, you must define the inlet and outlet nodes' coordinates via parameter |InputCoordinates| and |OutputCoordinates|, respectively. In both cases, use the names of the |Node| objects as keyword arguments to pass the corresponding coordinates: >>> from hydpy.models.conv_v001 import * >>> parameterstep() >>> inputcoordinates( ... in1=(0.0, 3.0), ... in2=(2.0, -1.0)) >>> outputcoordinates( ... out1=(0.0, 3.0), ... out2=(3.0, -2.0), ... out3=(1.0, 2.0)) >>> maxnmbinputs() >>> parameters.update() |conv| passes the current values of the inlet nodes correctly to the outlet nodes (note that node `in1` works with simulated values while node `in2` works with observed values, as we set its |Node.deploymode| to `obs`): >>> conv.model = model >>> conv.inlets.in1.sequences.sim = 1.0 >>> conv.inlets.in2.deploymode = "obs" >>> conv.inlets.in2.sequences.obs = 2.0 >>> model.simulate(0) >>> conv.outlets.out1.sequences.sim sim(1.0) >>> conv.outlets.out2.sequences.sim sim(2.0) >>> conv.outlets.out3.sequences.sim sim(1.0) When you forget a node (or misspell its name), you get the following error message: >>> outputcoordinates( ... out1=(0.0, 3.0), ... out2=(3.0, -2.0)) >>> maxnmbinputs() >>> parameters.update() >>> conv.model = model Traceback (most recent call last): ... RuntimeError: While trying to connect model `conv_v001` of element \ `conv`, the following error occurred: The node handled by control parameter \ outputcoordinates (out1 and out2) are not the same as the outlet nodes \ handled by element conv (out1, out2, and out3). """ try: for coordinates, sequence, nodes in ( ( self.parameters.control.inputcoordinates, self.sequences.inlets.inputs, self.element.inlets, ), ( self.parameters.control.outputcoordinates, self.sequences.outlets.outputs, self.element.outlets, ), ): if nodes == devicetools.Nodes(coordinates.nodes): sequence.shape = len(coordinates) for idx, node in enumerate(coordinates.nodes): sequence.set_pointer( node.get_double(sequence.subseqs.name), idx) else: parameternodes = objecttools.enumeration(coordinates.nodes) elementnodes = objecttools.enumeration(nodes) raise RuntimeError( f"The node handled by control parameter " f"{coordinates.name} ({parameternodes}) are not the " f"same as the {sequence.subseqs.name[:-1]} nodes " f"handled by element {self.element.name} " f"({elementnodes}).") except BaseException: objecttools.augment_excmessage( f"While trying to connect model " f"{objecttools.elementphrase(self)}")
def prepare_io_example_1() -> Tuple[devicetools.Nodes, devicetools.Elements]: """Prepare an IO example configuration for testing purposes. Function |prepare_io_example_1| is thought for testing the functioning of *HydPy* and thus should be of interest for framework developers only. It uses the application models |lland_v1| and |lland_v2|. Here, we apply |prepare_io_example_1| and shortly discuss different aspects of the data it generates. >>> from hydpy.examples import prepare_io_example_1 >>> nodes, elements = prepare_io_example_1() (1) It defines a short initialisation period of five days: >>> from hydpy import pub >>> pub.timegrids Timegrids("2000-01-01 00:00:00", "2000-01-05 00:00:00", "1d") (2) It creates a flat IO testing directory structure: >>> pub.sequencemanager.inputdirpath 'inputpath' >>> pub.sequencemanager.fluxdirpath 'outputpath' >>> pub.sequencemanager.statedirpath 'outputpath' >>> pub.sequencemanager.nodedirpath 'nodepath' >>> import os >>> from hydpy import TestIO >>> with TestIO(): ... print(sorted(filename for filename in os.listdir(".") ... if not filename.startswith("_"))) ['inputpath', 'nodepath', 'outputpath'] (3) It returns three |Element| objects handling either application model |lland_v1| or |lland_v2|, and two |Node| objects handling variables `Q` and `T`: >>> for element in elements: ... print(element.name, element.model) element1 lland_v1 element2 lland_v1 element3 lland_v2 >>> for node in nodes: ... print(node.name, node.variable) node1 Q node2 T (4) It generates artificial time series data of the input sequence |lland_inputs.Nied|, the flux sequence |lland_fluxes.NKor|, and the state sequence |lland_states.BoWa| of each model instance, and the |Sim| sequence of each node instance. For unambiguous test results, all generated values are unique: >>> nied1 = elements.element1.model.sequences.inputs.nied >>> nied1.series InfoArray([ 0., 1., 2., 3.]) >>> nkor1 = elements.element1.model.sequences.fluxes.nkor >>> nkor1.series InfoArray([[ 12.], [ 13.], [ 14.], [ 15.]]) >>> bowa3 = elements.element3.model.sequences.states.bowa >>> bowa3.series InfoArray([[ 48., 49., 50.], [ 51., 52., 53.], [ 54., 55., 56.], [ 57., 58., 59.]]) >>> sim2 = nodes.node2.sequences.sim >>> sim2.series InfoArray([ 64., 65., 66., 67.]) (5) All sequences carry |numpy.ndarray| objects with (deep) copies of the time series data for testing: >>> import numpy >>> (numpy.all(nied1.series == nied1.testarray) and ... numpy.all(nkor1.series == nkor1.testarray) and ... numpy.all(bowa3.series == bowa3.testarray) and ... numpy.all(sim2.series == sim2.testarray)) InfoArray(True, dtype=bool) >>> bowa3.series[1, 2] = -999.0 >>> numpy.all(bowa3.series == bowa3.testarray) InfoArray(False, dtype=bool) """ testtools.TestIO.clear() hydpy.pub.sequencemanager = filetools.SequenceManager() with testtools.TestIO(): hydpy.pub.sequencemanager.inputdirpath = "inputpath" hydpy.pub.sequencemanager.fluxdirpath = "outputpath" hydpy.pub.sequencemanager.statedirpath = "outputpath" hydpy.pub.sequencemanager.nodedirpath = "nodepath" hydpy.pub.timegrids = "2000-01-01", "2000-01-05", "1d" node1 = devicetools.Node("node1") node2 = devicetools.Node("node2", variable="T") nodes = devicetools.Nodes(node1, node2) element1 = devicetools.Element("element1", outlets=node1) element2 = devicetools.Element("element2", outlets=node1) element3 = devicetools.Element("element3", outlets=node1) elements = devicetools.Elements(element1, element2, element3) element1.model = importtools.prepare_model("lland_v1") element2.model = importtools.prepare_model("lland_v1") element3.model = importtools.prepare_model("lland_v2") for idx, element in enumerate(elements): parameters = element.model.parameters parameters.control.nhru(idx + 1) parameters.control.lnk(lland.ACKER) parameters.derived.absfhru(10.0) # pylint: disable=not-callable # pylint usually understands that all options are callable # but, for unknown reasons, not in the following line: with hydpy.pub.options.printprogress(False): nodes.prepare_simseries() elements.prepare_inputseries() elements.prepare_fluxseries() elements.prepare_stateseries() # pylint: enable=not-callable def init_values(seq, value1_): value2_ = value1_ + len(seq.series.flatten()) values_ = numpy.arange(value1_, value2_, dtype=float) seq.testarray = values_.reshape(seq.seriesshape) seq.series = seq.testarray.copy() return value2_ value1 = 0 for subname, seqname in zip(["inputs", "fluxes", "states"], ["nied", "nkor", "bowa"]): for element in elements: subseqs = getattr(element.model.sequences, subname) value1 = init_values(getattr(subseqs, seqname), value1) for node in nodes: value1 = init_values(node.sequences.sim, value1) return nodes, elements
def __init__(self, name, nodes=None, elements=None): self.name = name self.nodes = devicetools.Nodes(nodes) self.elements = devicetools.Elements(elements)
def prepare_io_example_1() -> Tuple[devicetools.Nodes, devicetools.Elements]: """Prepare an IO example configuration for testing purposes. Function |prepare_io_example_1| is thought for testing the functioning of *HydPy* and thus should be of interest for framework developers only. It uses the application models |lland_v1|, |lland_v2|, and |hland_v1|. Here, we apply |prepare_io_example_1| and shortly discuss different aspects of its generated data. >>> from hydpy.examples import prepare_io_example_1 >>> nodes, elements = prepare_io_example_1() (1) It defines a short initialisation period of five days: >>> from hydpy import pub >>> pub.timegrids Timegrids("2000-01-01 00:00:00", "2000-01-05 00:00:00", "1d") (2) It prepares an empty directory for IO testing: >>> import os >>> from hydpy import repr_, TestIO >>> with TestIO(): # doctest: +ELLIPSIS ... repr_(pub.sequencemanager.currentpath) ... os.listdir("project/series/default") '...iotesting/project/series/default' [] (3) It returns four |Element| objects handling either application model |lland_v1| |lland_v2|, or |hland_v1|, and two |Node| objects handling variables `Q` and `T`: >>> for element in elements: ... print(element.name, element.model) element1 lland_v1 element2 lland_v1 element3 lland_v2 element4 hland_v1 >>> for node in nodes: ... print(node.name, node.variable) node1 Q node2 T (4) It generates artificial time series data for the input sequence |lland_inputs.Nied|, the flux sequence |lland_fluxes.NKor|, and the state sequence |lland_states.BoWa| of each |lland| model instance, the state sequence |hland_states.SP| of the |hland_v1| model instance, and the |Sim| sequence of each node instance. For precise test results, all generated values are unique: >>> nied1 = elements.element1.model.sequences.inputs.nied >>> nied1.series InfoArray([0., 1., 2., 3.]) >>> nkor1 = elements.element1.model.sequences.fluxes.nkor >>> nkor1.series InfoArray([[12.], [13.], [14.], [15.]]) >>> bowa3 = elements.element3.model.sequences.states.bowa >>> bowa3.series InfoArray([[48., 49., 50.], [51., 52., 53.], [54., 55., 56.], [57., 58., 59.]]) >>> sim2 = nodes.node2.sequences.sim >>> sim2.series InfoArray([64., 65., 66., 67.]) >>> sp4 = elements.element4.model.sequences.states.sp >>> sp4.series InfoArray([[[68., 69., 70.], [71., 72., 73.]], <BLANKLINE> [[74., 75., 76.], [77., 78., 79.]], <BLANKLINE> [[80., 81., 82.], [83., 84., 85.]], <BLANKLINE> [[86., 87., 88.], [89., 90., 91.]]]) (5) All sequences carry |numpy.ndarray| objects with (deep) copies of the time-series data for testing: >>> import numpy >>> (numpy.all(nied1.series == nied1.testarray) and ... numpy.all(nkor1.series == nkor1.testarray) and ... numpy.all(bowa3.series == bowa3.testarray) and ... numpy.all(sim2.series == sim2.testarray) and ... numpy.all(sp4.series == sp4.testarray)) InfoArray(True) >>> bowa3.series[1, 2] = -999.0 >>> numpy.all(bowa3.series == bowa3.testarray) InfoArray(False) """ testtools.TestIO.clear() hydpy.pub.projectname = "project" hydpy.pub.sequencemanager = filetools.SequenceManager() with testtools.TestIO(): os.makedirs("project/series/default") hydpy.pub.timegrids = "2000-01-01", "2000-01-05", "1d" node1 = devicetools.Node("node1") node2 = devicetools.Node("node2", variable="T") nodes = devicetools.Nodes(node1, node2) element1 = devicetools.Element("element1", outlets=node1) element2 = devicetools.Element("element2", outlets=node1) element3 = devicetools.Element("element3", outlets=node1) element4 = devicetools.Element("element4", outlets=node1) elements_lland = devicetools.Elements(element1, element2, element3) elements = elements_lland + element4 element1.model = importtools.prepare_model("lland_v1") element2.model = importtools.prepare_model("lland_v1") element3.model = importtools.prepare_model("lland_v2") element4.model = importtools.prepare_model("hland_v1") for idx, element in enumerate(elements_lland): parameters = element.model.parameters parameters.control.nhru(idx + 1) parameters.control.lnk(lland.ACKER) parameters.derived.absfhru(10.0) control = element4.model.parameters.control control.nmbzones(3) control.sclass(2) control.zonetype(hland.FIELD) control.zonearea.values = 10.0 # pylint: disable=not-callable # pylint usually understands that all options are callable # but, for unknown reasons, not in the following line: with hydpy.pub.options.printprogress(False): nodes.prepare_simseries(allocate_ram=False) # ToDo: add option "reset" nodes.prepare_simseries(allocate_ram=True) elements.prepare_inputseries(allocate_ram=False) elements.prepare_inputseries(allocate_ram=True) elements.prepare_factorseries(allocate_ram=False) elements.prepare_factorseries(allocate_ram=True) elements.prepare_fluxseries(allocate_ram=False) elements.prepare_fluxseries(allocate_ram=True) elements.prepare_stateseries(allocate_ram=False) elements.prepare_stateseries(allocate_ram=True) # pylint: enable=not-callable def init_values(seq: TestIOSequence, value1_: float) -> float: value2_ = value1_ + len(seq.series.flatten()) values_ = numpy.arange(value1_, value2_, dtype=float) seq.testarray = values_.reshape(seq.seriesshape) seq.series = seq.testarray.copy() return value2_ value1 = 0.0 for subname, seqname in zip(["inputs", "fluxes", "states"], ["nied", "nkor", "bowa"]): for element in elements_lland: subseqs = getattr(element.model.sequences, subname) value1 = init_values(getattr(subseqs, seqname), value1) for node in nodes: value1 = init_values(node.sequences.sim, value1) # type: ignore[arg-type] init_values(element4.model.sequences.states.sp, value1) # type: ignore[arg-type] return nodes, elements