HydPy-Dam-V5 (dam model, version 5)

Application model dam_v005 extends dam_v001 with two features enabling collaboration with other dam models for better drought and flood prevention.

Like dam_v001, dam_v005 tries to increase the discharge at a remote location in the channel downstream during low flow conditions and sometimes fails due to its limited storage content. The first additional feature of dam_v005 is that it passes the information on its anticipated failure to another remote location. This information enables other models to jump in when necessary.

The second additional feature of dam_v005 is that it receives input from two additional inlet nodes, one passing “supply discharge” and the other one “relief discharge”. We understand “supply discharge” as water delivered from a remote location to increase the storage content of dam_v005 when necessary (during droughts). In contrast, relief discharge serves to relieve the other location (possibly another dam) during floods. dam_v005 calculates both the desirable supply discharge and the acceptable relief discharge and passes that information to a single or two separate remote locations.

The following explanations focus on these differences. For further information on using dam_v005, please read the documentation on model dam_v001. Besides that, see the documentation on dam_v002 and dam_v004, which are possible counterparts for dam_v005.

Integration tests

Note

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

The following integration tests build on some of the examples demonstrating the functionality of model dam_v001. To achieve comparability, we define identical parameter values, initial conditions, and input time series. The following explanations focus on the differences between applications models dam_v005 and dam_v001.

The following time-related setup is identical to the one of dam_v001:

>>> from hydpy import pub
>>> pub.timegrids = "01.01.2000", "21.01.2000",  "1d"

Due to the high complexity of dam_v005 and our test setting, which includes two instances of another model type (arma_rimorido), we need to define lots of Node objects:

>>> from hydpy import Node
>>> inflow = Node("inflow", variable="Q")
>>> outflow = Node("outflow", variable="Q")
>>> natural = Node("natural", variable="Q")
>>> remote = Node("remote", variable="Q")
>>> actual_supply = Node("actual_supply", variable="S")
>>> required_supply = Node("required_supply", variable="S")
>>> allowed_relief = Node("allowed_relief", variable="R")
>>> actual_relief = Node("actual_relief", variable="R")
>>> demand = Node("demand", variable="D")
>>> from hydpy import Element
>>> dam = Element("dam",
...               inlets=(inflow, actual_supply, actual_relief),
...               outlets=outflow,
...               receivers=remote,
...               senders=(demand, required_supply, allowed_relief))
>>> stream1 = Element("stream1", inlets=outflow, outlets=remote)
>>> stream2 = Element("stream2", inlets=natural, outlets=remote)

The nodes inflow and outflow handle the “normal” discharge into and out of the dam. Node outflow passes its values to the routing element stream1. The second routing element, stream2, represents a natural tributary and receives the “natural” discharge of the subcatchment downstream of the dam provided by the natural node. Both routing elements give their outflow to node remote, representing the cross-section downstream where discharge should not undercut a certain threshold. So far, the setting is identical to the one of the documentation on dam_v001.

dam_v005 needs to connect to two additional inlet nodes and three sender nodes. The input nodes actual_supply and actual_relief handle the actual supply and relief discharge from remote locations. The sender nodes required_supply and allowed_relief inform other models of the currently required supply and the allowed relief. The sender node demand tells other models about the estimated demand for water at the cross-section downstream that it cannot currently release itself.

To enable dam_v005 to connect the different involved sequences and nodes correctly, we need to give suitable variable values to the Node instances. We use the string literal “Q” for the inflow and the outflow node, “S” for both supply nodes, “R” for both relief nodes, and “D” for node demand. See the documentation on method connect() for alternatives and more detailed explanations.

As mentioned above, dam_v002 and dam_v004 are good counterparts for dam_v005. Including them in the following examples would lead to a better agreement with typical use cases of dam_v005. However, we do not want to further bloat up the already scenario setting. Hence, we prefer to apply a predefined discharge time series instead of dynamically calculating the remote location discharges.

We configure both arma_rimorido models, the IntegrationTest object, and the initial conditions precisely as in the dam_v001 examples:

>>> from hydpy import prepare_model
>>> stream2.model = prepare_model("arma_rimorido")
>>> stream2.model.parameters.control.responses(((), (1.0,)))
>>> stream2.model.parameters.update()
>>> stream1.model = prepare_model("arma_rimorido")
>>> stream1.model.parameters.control.responses(((), (0.2, 0.4, 0.3, 0.1)))
>>> stream1.model.parameters.update()
>>> from hydpy.models.dam_v005 import *
>>> parameterstep("1d")
>>> dam.model = model
>>> from hydpy import IntegrationTest
>>> test = IntegrationTest(dam)
>>> test.dateformat = "%d.%m."
>>> test.plotting_options.axis1 = fluxes.inflow, fluxes.outflow
>>> test.plotting_options.axis2 = states.watervolume
>>> test.inits=((states.watervolume, 0.0),
...             (logs.loggedadjustedevaporation, 0.0),
...             (logs.loggedtotalremotedischarge, 1.9),
...             (logs.loggedoutflow, 0.0),
...             (stream1.model.sequences.logs.login, 0.0))

The following control parameters are common to both models. We apply the same values:

>>> watervolume2waterlevel(PPoly.from_data(xs=[0.0, 1.0], ys=[0.0, 0.25]))
>>> waterlevel2flooddischarge(PPoly.from_data(xs=[0.0], ys=[0.0]))
>>> catchmentarea(86.4)
>>> nmblogentries(1)
>>> remotedischargeminimum(1.4)
>>> remotedischargesafety(0.5)
>>> neardischargeminimumthreshold(0.2)
>>> neardischargeminimumtolerance(0.2)
>>> waterlevelminimumthreshold(0.0)
>>> waterlevelminimumtolerance(0.0)
>>> restricttargetedrelease(True)
>>> surfacearea(1.44)
>>> correctionprecipitation(1.2)
>>> correctionevaporation(1.2)
>>> weightevaporation(0.8)
>>> thresholdevaporation(0.0)
>>> toleranceevaporation(0.001)

dam_v005 implements six additional control parameters, three for calculating the required supply (HighestRemoteSupply, WaterLevelSupplyThreshold, and WaterLevelSupplyTolerance) and three for calculating the allowed relief (HighestRemoteRelief, WaterLevelReliefThreshold, and WaterLevelReliefTolerance):

>>> highestremotesupply(1.0)
>>> waterlevelsupplythreshold(0.2)
>>> waterlevelsupplytolerance(0.05)
>>> highestremoterelief(5.0)
>>> waterlevelreliefthreshold(0.5)
>>> waterlevelrelieftolerance(0.05)

We define identical time series for the subcatchment’s discharge time series:

>>> natural.sequences.sim.series = [1.8, 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1, 1.0, 1.0,
...                                 1.0, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8]

In the initial examples on dam_v001, the model receives a constant inflow of 1.0 m³/s from its single inlet node. To work with the same input sum and prove that all node connections work correctly, we set the “normal” inflow, the actual supply, and the actual relief discharge to a constant value of 1/3 m³/s:

>>> inflow.sequences.sim.series = 1.0/3.0
>>> actual_supply.sequences.sim.series = 1.0/3.0
>>> actual_relief.sequences.sim.series = 1.0/3.0

smooth near minimum

This example extends the smooth near minimum example of application model dam_v001.

All results achieved for the water level and the outflow agree exactly, confirming that dam_v005 captures the relevant functionalities of dam_v001 correctly. From the results specific to dam_v005, inspecting those of sequence RequiredRemoteSupply is most insightful. At the beginning of the simulation period, they reflect the value of parameter HighestRemoteSupply (1.0 m³/s). When the water level reaches the value of parameter WaterLevelSupplyThreshold (0.2 m, which corresponds to a water volume of 0.8 million m³), the required remote release decreases and finally reaches 0.0 m³/s. This transition happens over a relatively long period due to the large value of the smoothing parameter WaterLevelSupplyTolerance (0.05 m):

>>> test("dam_v005_smooth_near_minimum")
Click to see the table
Click to see the graph

restriction enabled

This example extends the restriction enabled example of application model dam_v001. It confirms that the restriction on releasing water when there is little inflow works as explained for model dam_v001. In addition, it shows that dam_v005 uses sequence MissingRemoteRelease to indicate when the ActualRelease is smaller than the estimated RequiredRemoteRelease and passes this information to the demand node:

>>> inflow.sequences.sim.series[:10] = 1.0
>>> inflow.sequences.sim.series[10:] = 0.1
>>> actual_supply.sequences.sim.series = 0.0
>>> actual_relief.sequences.sim.series = 0.0
>>> neardischargeminimumtolerance(0.0)
>>> test("dam_v005_restriction_enabled")
Click to see the table
Click to see the graph

smooth stage minimum

This example extends the smooth stage minimum example of application model dam_v001. We update parameters WaterLevelMinimumThreshold and WaterLevelMinimumTolerance, as well as the time series of the “normal” inflow, accordingly:

>>> neardischargeminimumthreshold(0.0)
>>> waterlevelminimumtolerance(0.01)
>>> waterlevelminimumthreshold(0.005)
>>> inflow.sequences.sim.series = numpy.linspace(0.2, 0.0, 20)

There is also a perfect agreement between dam_v001 and dam_v005 for the given case where the available water storage is too limited to reease enough discharge:

>>> test("dam_v005_smooth_stage_minimum")
Click to see the table
Click to see the graph

evaporation

This example repeats the evaporation example of application model dam_v001. We add an evap_ret_io submodel and update the time series of potential evaporation accordingly:

>>> with model.add_pemodel_v1("evap_ret_io") as pemodel:
...     evapotranspirationfactor(1.0)
>>> pemodel.prepare_inputseries()
>>> pemodel.sequences.inputs.referenceevapotranspiration.series = 10 * [1.0] + 10 * [5.0]

All internal evaporation-related results agree with the ones of dam_v001 exactly:

>>> test("dam_v005_evaporation")
Click to see the table
Click to see the graph
>>> del model.pemodel

flood retention

This example repeats the flood retention example of application model dam_v001. We use the same parameter and input time series configuration:

>>> remotedischargeminimum(0.0)
>>> remotedischargesafety(0.0)
>>> waterlevelminimumthreshold(0.0)
>>> waterlevelminimumtolerance(0.0)
>>> waterlevel2flooddischarge(PPoly.from_data(xs=[0.0, 1.0], ys= [0.0, 2.5]))
>>> neardischargeminimumthreshold(0.0)
>>> with model.add_precipmodel_v2("meteo_precip_io") as precipmodel:
...     precipitationfactor(1.0)
>>> precipmodel.prepare_inputseries()
>>> precipmodel.sequences.inputs.precipitation.series = [
...     0.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
...     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
>>> inflow.sequences.sim.series = [0.0, 0.0, 5.0, 9.0, 8.0, 5.0, 3.0, 2.0, 1.0, 0.0,
...                                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
>>> test.inits.loggedtotalremotedischarge = 1.0
>>> natural.sequences.sim.series = 1.0

The following test results show that dam_v005 reproduces the water levels and outflow values calculated by dam_v001 precisely. Furthermore, they illustrate the estimation of AllowedRemoteRelief based on the current water level, which is functionally similar to the one of RequiredRemoteSupply discussed in the smooth near minimum example:

>>> test("dam_v005_flood_retention")
Click to see the table
Click to see the graph
class hydpy.models.dam_v005.Model[source]

Bases: Main_PrecipModel_V2, Main_PEModel_V1

HydPy-Dam-V5 (dam model, version 5).

The following “receiver update methods” are called in the given sequence before performing a simulation step:
The following “inlet update methods” are called in the given sequence at the beginning of each simulation step:
The following methods define the relevant components of a system of ODE equations (e.g. direct runoff):
The following methods define the complete equations of an ODE system (e.g. change in storage of fast water due to effective precipitation and direct runoff):
The following “outlet update methods” are called in the given sequence at the end of each simulation step:
  • Calc_WaterLevel_V1 Determine the water level based on an interpolation approach approximating the relationship between water volume and water level.

  • Pass_Outflow_V1 Update the outlet link sequence Q.

  • Update_LoggedOutflow_V1 Log a new entry of discharge at a cross section far downstream.

The following “sender update methods” are called in the given sequence after performing a simulation step:
Users can hook submodels into the defined main model if they satisfy one of the following interfaces:
  • PrecipModel_V2 Simple interface for determining precipitation in one step.

  • PETModel_V1 Simple interface for calculating all potential evapotranspiration values in one step.

DOCNAME: DocName = ('Dam-V5', 'dam model, version 5')
precipmodel: modeltools.SubmodelProperty

Optional submodel that complies with the following interface: PrecipModel_V2.

pemodel: modeltools.SubmodelProperty

Optional submodel that complies with the following interface: PETModel_V1.

REUSABLE_METHODS: ClassVar[tuple[type[ReusableMethod], ...]] = ()
class hydpy.models.dam_v005.ControlParameters(master: Parameters, cls_fastaccess: type[FastAccessParameter] | None = None, cymodel: CyModelProtocol | None = None)

Bases: SubParameters

Control parameters of model dam_v005.

The following classes are selected:
class hydpy.models.dam_v005.DerivedParameters(master: Parameters, cls_fastaccess: type[FastAccessParameter] | None = None, cymodel: CyModelProtocol | None = None)

Bases: SubParameters

Derived parameters of model dam_v005.

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

Bases: FactorSequences

Factor sequences of model dam_v005.

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

Bases: FluxSequences

Flux sequences of model dam_v005.

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

Bases: InletSequences

Inlet sequences of model dam_v005.

The following classes are selected:
  • Q() Inflow [m³/s].

  • S() Actual water supply [m³/s].

  • R() Actual water relief [m³/s].

class hydpy.models.dam_v005.LogSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: LogSequences

Log sequences of model dam_v005.

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

Bases: OutletSequences

Outlet sequences of model dam_v005.

The following classes are selected:
  • Q() Outflow [m³/s].

class hydpy.models.dam_v005.ReceiverSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: ReceiverSequences

Receiver sequences of model dam_v005.

The following classes are selected:
  • Q() Remote discharge [m³/s].

class hydpy.models.dam_v005.SenderSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: SenderSequences

Sender sequences of model dam_v005.

The following classes are selected:
  • D() Water demand [m³/s].

  • S() Required water supply [m³/s].

  • R() Required water relief [m³/s].

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

Bases: SubParameters

Solver parameters of model dam_v005.

The following classes are selected:
  • AbsErrorMax() Absolute numerical error tolerance [m³/s].

  • RelErrorMax() Relative numerical error tolerance [1/T].

  • RelDTMin() Smallest relative integration time step size allowed [-].

  • RelDTMax() Largest relative integration time step size allowed [-].

class hydpy.models.dam_v005.StateSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: StateSequences

State sequences of model dam_v005.

The following classes are selected: