HydPy-ARMA-RIMO/RIDO (nonlinear routing by multiple ARMA processes)

arma_rimorido relies on the RIMO/RIDO method, which is based on the translation diffusion equation, which is a linear approximation of the Saint-Venant equations involving only two parameters - one for the celerity and one for the diffusivity of the flood wave. The linearity of the approach allows for constructing Unit Hydrograph ordinates for each specific combination of celerity, diffusivity, and the length of the considered river section. One can understand these ordinates as coefficients of a moving average (MA) process.

arma_rimorido adds two additional features to this conventional approach.

Firstly, RIMO/RIDO approximates the response function described by the MA coefficients by an ARMA process, which is helpful for response functions with long tails. Very often, autoregressive (AR) modelscan approximate long-tailed responses sufficiently with few parameters. Hence, ARMA models (which reflect the rising limb of a response function with their MA coefficients and its falling limb with their AR coefficients) are often more parameter efficient than pure MA models.

Secondly, RIMO/RIDO separates the flow into the river section into different “portions” based on discharge thresholds. Each portion is routed by a separate ARMA model, allowing RIMO/RIDO to reflect the nonlinearity of rating curves to a certain degree. For example, the bank-full discharge can serve as a threshold. Then, one can apply smaller celerity values and larger diffusivity values on the “upper” flow portion to simulate retention processes on floodplains.

If you want to apply arma_rimorido precisely like RIMO/RIDO, consider using TranslationDiffusionEquation for calculating its coefficients. But you can also define them differently, e.g. using LinearStorageCascade. Additionally, you are free to apply combined ARMA coefficients or pure MA coefficients only, as described in the following examples.

Integration tests

Note

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

We perform the following tests over 20 hours:

>>> from hydpy import pub, Nodes, Element
>>> pub.timegrids = "01.01.2000 00:00",  "01.01.2000 20:00", "1h"

Import the model and define the time settings:

>>> from hydpy.models.arma_rimorido import *
>>> parameterstep("1h")

For testing purposes, arma_rimorido shall retrieve its input from the nodes input1 and input2 and pass its output to the node output. Firstly, we define all nodes:

>>> nodes = Nodes("input1", "input2", "output")

Define the element stream and build the connections between the nodes defined above and the arma_rimorido model instance:

>>> stream = Element("stream",
...                  inlets=["input1", "input2"],
...                  outlets="output")
>>> stream.model = model

Prepare a test function object, which prints the respective values of the model sequences QIn, QPIn, QPOut, and QOut. The node sequence sim is added in to that the values calculated for QOut are actually passed to sim:

>>> from hydpy import IntegrationTest
>>> IntegrationTest.plotting_options.activated=(
...     fluxes.qin, fluxes.qout)
>>> test = IntegrationTest(
...     stream,
...     seqs=(fluxes.qin, fluxes.qpin, fluxes.qpout,
...           fluxes.qout, nodes.output.sequences.sim))

To start the respective example runs from stationary conditions, a base flow value of 2 m³/s is set for all values of the log sequences LogIn and LogOut:

>>> test.inits = ((logs.login, 2.0),
...               (logs.logout, 2.0))

We want to print just the time instead of the whole date:

>>> test.dateformat = "%H:%M"

We define two flood events, one for each inlet node:

>>> nodes.input1.sequences.sim.series = (
...     1.0, 1.0, 2.0, 4.0, 3.0, 2.0, 1.0, 1.0, 1.0, 1.0,
...     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
>>> nodes.input2.sequences.sim.series = (
...     1.0, 2.0, 6.0, 9.0, 8.0, 6.0, 4.0, 3.0, 2.0, 1.0,
...     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)

MA coefficients

In the first example, we define a pure fourth-order moving average (MA) process via the control parameter Responses:

>>> responses(((), (0.2, 0.4, 0.3, 0.1)))

This configuration leads to a usual “unit hydrograph” convolution result, where all inflow “impulses” are separated into the actual and the three subsequent time steps:

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

ARMA coefficients

Now, we set the order of the MA process to the smallest possible value, which is one. The autoregression (AR) process is of order two. Note that negative AR coefficients are allowed (also note the opposite signs of the coefficients in contrast to the statistical literature):

>>> responses(((1.1, -0.3), (0.2,)))

Due to the AR process, the maximum time delay of some fractions of each input impulse is theoretically infinite:

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

Increased delay

This example is identical to the second one, except for the additional time delay of exactly one hour due to the changed MA process:

>>> responses(((1.1, -0.3), (0.0, 0.2)))
>>> test("arma_rimorido_delay")
Click to see the table
Click to see the graph

Negative discharge

In some hydrological applications, the inflow into a channel might sometimes be lower than 0 m³/s. arma_rimorido generally routes such negative discharges using the response function with the lowest discharge threshold. When we repeat the calculation of the Increased delay example with inflow constantly decreased by 3 m³/s, the outflow is also constantly decreased by 3 m³/s, and many simulated values are negative:

>>> nodes.input1.sequences.sim.series -= 3.0
>>> test.inits = ((logs.login, -1.0),
...               (logs.logout, -1.0))
>>> test("arma_rimorido_negative_discharge")
Click to see the table
Click to see the graph
>>> nodes.input1.sequences.sim.series += 3.0
>>> test.inits = ((logs.login, 2.0),
...               (logs.logout, 2.0))

Plausibility

Be aware that neither parameter Responses checks the assigned coefficients nor does arma_rimorido check the calculated outflow for plausibility (one can use the features provided in modules iuhtools and armatools to calculate reliable coefficients). The fourth example increases the span of the AR coefficients used in the third example. The complete ARMA process is still mass conservative, but some response values of the recession curve are negative:

>>> responses(((1.5, -0.7), (0.0, 0.2)))
>>> test("arma_rimorido_plausibility")
Click to see the table
Click to see the graph

Nonlinearity

In the following example, we combine the coefficients of the first two examples. Between inflow values of 0 and 7 m³/s, the pure AR process is applied. For inflow discharges exceeding 7 m³/s, inflow is separated. The AR process is still applied on the portion of 7 m³/s, but for the inflow exceeding this threshold, the mixed ARMA model is applied:

>>> responses(_0=((), (0.2, 0.4, 0.3, 0.1)),
...           _7=((1.1, -0.3), (0.2,)))

To start from stationary conditions again, one has to apply different values to both log sequences. The base flow value of 2 m³/s is only given to the (low flow) MA model; the (high flow) ARMA model is initialized with zero values instead:

>>> test.inits.login = [[2.0], [0.0]]
>>> test.inits.logout = [[2.0], [0.0]]

The separate handling of the inflow can be studied by inspecting the columns of sequence QPIn and sequence QPOut. The respective left columns show the input and output of the MA model, and the respective right columns show the input and output of the ARMA model:

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

Bases: AdHocModel

HydPy-ARMA-RIMO/RIDO (nonlinear routing by multiple ARMA processes).

The following “inlet update methods” are called in the given sequence at the beginning of each simulation step:
The following “run methods” are called in the given sequence during each simulation step:
  • Calc_QPIn_V1 Calculate the input discharge portions of the different response functions.

  • Update_LogIn_V1 Refresh the input log sequence for the different MA processes.

  • Calc_QMA_V1 Calculate the discharge responses of the different MA processes.

  • Calc_QAR_V1 Calculate the discharge responses of the different AR processes.

  • Calc_QPOut_V1 Calculate the ARMA results for the different response functions.

  • Update_LogOut_V1 Refresh the log sequence for the different AR processes.

  • Calc_QOut_V1 Sum up the results of the different response functions.

The following “outlet update methods” are called in the given sequence at the end of each simulation step:
DOCNAME: DocName = ('ARMA-RIMO/RIDO', 'nonlinear routing by multiple ARMA processes')
REUSABLE_METHODS: ClassVar[tuple[type[ReusableMethod], ...]] = ()
class hydpy.models.arma_rimorido.ControlParameters(master: Parameters, cls_fastaccess: type[FastAccessParameter] | None = None, cymodel: CyModelProtocol | None = None)

Bases: SubParameters

Control parameters of model arma_rimorido.

The following classes are selected:
  • Responses() Assigns different ARMA models to different discharge thresholds.

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

Bases: SubParameters

Derived parameters of model arma_rimorido.

The following classes are selected:
  • Nmb() Number of response functions [-].

  • MaxQ() Maximum discharge values of the respective ARMA models [m³/s].

  • DiffQ() Differences between the values of MaxQ [m³/s].

  • AR_Order() Number of AR coefficients of the different responses [-].

  • MA_Order() Number of MA coefficients of the different responses [-].

  • AR_Coefs() AR coefficients of the different responses [-].

  • MA_Coefs() MA coefficients of the different responses [-].

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

Bases: FluxSequences

Flux sequences of model arma_rimorido.

The following classes are selected:
  • QIn() Total inflow [m³/s].

  • QPIn() Inflow portions corresponding to the different thresholds [m³/s].

  • QMA() MA result for the different thresholds [m³/s].

  • QAR() AR result for the different thresholds [m³/s].

  • QPOut() Outflow portions corresponding to the different thresholds [m³/s].

  • QOut() Total outflow [m³/s].

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

Bases: InletSequences

Inlet sequences of model arma_rimorido.

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

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

Bases: LogSequences

Log sequences of model arma_rimorido.

The following classes are selected:
  • LogIn() The recent and the past inflow portions for the application of the different MA processes [m³/s].

  • LogOut() The past outflow portions for the application of the different AR processes [m³/s].

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

Bases: OutletSequences

Outlet sequences of model arma_rimorido.

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