HydPy-Exch-Branch-IO (branch model using external water transfer requests)

exch_branch_io modifies the flow through a stream by transferring water based on externally defined requests. These are usually positive (withdrawals) but can also be negative (supplies). If external requests are missing, it estimates the water transfer using season-dependent interpolation rules.

exch_branch_io is compatible with the LARSIM routines for modelling simple (VERZ) and extended (ERWV) branching of river discharge, while offering some additional functionalities:

  • Handling of negative inflow (reverse flow) is possible.

  • Water transfers can be positive (withdrawals) and negative (supplies).

  • Configuration of minimum and maximum flows that must not be violated by externally requested water transfers (including different modes of transfer adjustment).

  • Inflow-dependent piecewise interpolation (for the fallback calculations) can be linear or non-linear (spline interpolation).

  • The transition between season-dependent interpolation rules is usually smoothed by linear interpolation over time.

Before reading the following integration tests, it is advisable to review the documentation for method Pass_ActualTransfer_StreamOutflow_V1, which is the core of exch_branch_io.

Integration tests

Note

When new to HydPy, consider reading section Integration Tests first.

We perform the following examples over a simulation period of 11 days:

>>> from hydpy import pub, Nodes, Element
>>> pub.timegrids = "2000-01-01", "2000-01-12", "1d"

exch_branch_io has no parameter whose values depend on the simulation time step, so we do not need to define the parameter time step size here:

>>> from hydpy.models.exch_branch_io import *
>>> parameterstep()

According to the following setting, exch_branch_io queries inflow from two inlet nodes and passes the branched outflow to two outlet nodes:

>>> nodes = Nodes("inflow1", "inflow2", "river", "diversion")
>>> branch = Element(
...     "branch", inlets=["inflow1", "inflow2"], outlets=["river", "diversion"]
... )

There may be fewer or more inlet nodes, but the number of outlet nodes must be exactly two.

The variable types of the respective Node instances do not matter. Instead, exch_branch_io lumps all inlet nodes together as inflow providers and builds different connections to the two outlet nodes, for which it uses the node names passed to parameter Targets:

>>> targets(stream="river", transfer="diversion")
>>> branch.model = model

By assigning “river” to the keyword stream and “diversion” to the keyword transfer, the roles of the two outlet nodes become clear. The “transfer” node will receive the calculated water transfer, while the “river” node will receive the remaining (or enhanced) streamflow.

We do not have to define any initial values in the test settings because exch_branch_io has no memory:

>>> from hydpy.core.testtools import IntegrationTest
>>> test = IntegrationTest(branch)
>>> test.dateformat = "%d Jan."

Instead of defining a realistic inflow series, the sum of the inlet nodes’ values increases linearly:

>>> import numpy
>>> nodes.inflow1.sequences.sim.series = numpy.arange(0.0, 11.0)
>>> nodes.inflow2.sequences.sim.series = -1.0

requested transfer

exch_branch_io’s priority is to withdraw water from or supply water to the main stream based on externally requested transfers, given as an input time series. In this example, these requests are positive (withdrawals) in the first half and negative (supplies) in the second half of the simulation period:

>>> inputs.requestedtransfer.series = 6 * [2.0] + 5 * [-2.0]

Withdrawals could cause too-low (and even negative) flows. To prevent this, we set parameter MinStream to 1 m³/s:

>>> minstream(1.0)

Likewise, supplies could cause too-high flows, which we prevent by setting parameter MaxStream to 8 m³/s:

>>> maxstream(8.0)

Parameter KeepWaterBalance configures the exact behaviour during possible violations of MinStream and MaxStream, which is explained in detail in the documentation on method Pass_ActualTransfer_StreamOutflow_V1. Here, we set the more intuitive behaviour of reducing transfers as strongly as necessary to maintain the water balance:

>>> keepwaterbalance(True)

As the simulation results show, the transfer is reduced to zero if necessary. Still, withdrawals are never turned into supplies for already too-low inflows and supplies are never turned into withdrawals for already too-high inflows:

>>> test("exch_branch_io_requested_transfer")
Click to see the table

internal estimates

If external requests are unavailable (preferably indicated by inf or alternatively by nan), exch_branch_io must fall back to its internal routine for calculating requested transfers:

>>> inputs.requestedtransfer.series = numpy.inf

Configuring this internal routine means defining piecewise linear or polynomial interpolation functions and passing them to parameter FlowTransferRules. The documentation for parameter FlowTransferRules explains the available degrees of freedom for doing so. However, here we restrict ourselves to simple PPoly instances, each interpolating the water transfer for a specific time of year:

>>> flowtransferrules(
...     toy_01_01_12=PPoly(xs=[0.0, 2.0, 4.0, 6.0], ys=[0.0, 0.0, 1.0, 1.0]),
...     toy_01_09_12=PPoly(xs=[0.0, 2.0, 4.0, 6.0], ys=[0.0, 0.0, 1.0, 1.0]),
...     toy_01_11_12=PPoly(xs=[0.0, 2.0, 4.0, 6.0], ys=[0.0, 0.0, 2.0, 2.0]),
... )

From 1 to 9 Jan, the first rule (defined twice) applies, which diverts up to 1 m³/s. After that, there is a continuous transition from the first to the second rule, which diverts up to 2 m³/s:

>>> test("exch_branch_io_internal_estimates")
Click to see the table
class hydpy.models.exch_branch_io.Model[source]

Bases: AdHocModel

HydPy-Exch-Branch-IO (branch model using external water transfer requests).

The following “inlet update methods” are called in the given sequence at the beginning of each simulation step:
The following “outlet update methods” are called in the given sequence at the end of each simulation step:
  • Pass_ActualTransfer_StreamOutflow_V1 Calculate the actual water transfer and remaining stream outflow based on external transfer requests (if available) or based on internal transfer calculations (as a fallback).

DOCNAME = ('Exch-Branch-IO', 'branch model using external water transfer requests')
property targetnames: tuple[str, str]

Names of the target nodes.

The first and the second name belong to the mainstream node and the transfer node, respectively. Note that this order can differ from the alphabetical order of the outputs of parameter FlowTransferRules (see StreamIndex).

connect() → None[source]

Connect all available inlet nodes to the inlet sequence Inflow, and connect correctly named target outlet nodes to the outlet sequences ActualTransfer and StreamOutflow.

The following test configuration involves two inlet nodes (inflow1 and inflow2) and two outlet nodes (diversion and river):

>>> from hydpy import Element, Nodes, pub
>>> pub.timegrids = "2000-01-01", "2000-01-12", "1d"
>>> diversion, inflow1, inflow2, river = Nodes(
...     "diversion", "inflow1", "inflow2", "river"
... )
>>> branch = Element(
...     "branch", inlets=[inflow1, inflow2], outlets=[diversion, river]
... )

Without giving exch_branch_io more information about the outlet nodes’ roles, you cannot build connections to them:

>>> from hydpy.models.exch_branch_io import *
>>> parameterstep()
>>> branch.model = model
Traceback (most recent call last):
...
RuntimeError: While trying to connect model `exch_branch_io` with element `branch`, the following error occurred: The names of the target nodes are still unknown.  Please define them via parameter `targets` first.

Providing this information works via parameter Targets, which requires the (correct) target node names:

>>> targets(stream="channel", transfer="diversion")
>>> model.connect()
Traceback (most recent call last):
...
RuntimeError: While trying to connect model `exch_branch_io` with element `branch`, the following error occurred: The target node names `channel and diversion` do not agree with the available outlet nodes `diversion and river`.

With consistent definitions, the transfer and the mainstream outflow reach the nodes diversion and river as intended:

>>> targets(stream="river", transfer="diversion")
>>> flowtransferrules(PPoly(xs=[0.0, 3.0], ys=[0.0, 2.0]))
>>> derived.toy.update()
>>> derived.streamindex.update()
>>> model.connect()
>>> fluxes.inflow = 3.0
>>> model.update_outlets()
>>> river.sequences.sim
sim(1.0)
>>> diversion.sequences.sim
sim(2.0)
REUSABLE_METHODS = ()
class hydpy.models.exch_branch_io.ControlParameters(master: Parameters[TM_co], cls_fastaccess: type[FastAccessParameter] | None = None, cymodel: CyModelProtocol | None = None)

Bases: ControlParameters

Control parameters of model exch_branch_io.

The following classes are selected:
  • Targets() Target nodes.

  • MinStream() Minimum stream outflow that externally requested withdrawals must not undercut [m³/s].

  • MaxStream() Maximum stream outflow that externally requested supplies must not exceed [m³/s].

  • KeepWaterBalance() Flag to indicate if too-high requested transfers should be reduced [-].

  • FlowTransferRules() Seasonally varying interpolation rules for branching the (adjusted) inflow [-].

class hydpy.models.exch_branch_io.DerivedParameters(master: Parameters[TM_co], cls_fastaccess: type[FastAccessParameter] | None = None, cymodel: CyModelProtocol | None = None)

Bases: DerivedParameters

Derived parameters of model exch_branch_io.

The following classes are selected:
class hydpy.models.exch_branch_io.FluxSequences(master: Sequences[TM_co], cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: FluxSequences

Flux sequences of model exch_branch_io.

The following classes are selected:
class hydpy.models.exch_branch_io.InletSequences(master: Sequences[TM_co], cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: InletSequences

Inlet sequences of model exch_branch_io.

The following classes are selected:
class hydpy.models.exch_branch_io.InputSequences(master: Sequences[TM_co], cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: InputSequences

Input sequences of model exch_branch_io.

The following classes are selected:
class hydpy.models.exch_branch_io.OutletSequences(master: Sequences[TM_co], cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: OutletSequences

Outlet sequences of model exch_branch_io.

The following classes are selected: