# -*- coding: utf-8 -*-
# pylint: disable=missing-docstring
# pylint: enable=missing-docstring
# imports...
# ...from HydPy
from hydpy.core import modeltools
from hydpy.models.arma import arma_derived
from hydpy.models.arma import arma_fluxes
from hydpy.models.arma import arma_logs
from hydpy.models.arma import arma_inlets
from hydpy.models.arma import arma_outlets
[docs]class Calc_QPIn_V1(modeltools.Method):
"""Calculate the input discharge portions of the different response
functions.
Examples:
Initialise an |arma| model with three different response functions:
>>> from hydpy.models.arma import *
>>> parameterstep()
>>> derived.nmb = 3
>>> derived.maxq.shape = 3
>>> derived.diffq.shape = 2
>>> fluxes.qpin.shape = 3
Define the maximum discharge value of the respective response
functions and their successive differences:
>>> derived.maxq(0.0, 2.0, 6.0)
>>> derived.diffq(2.0, 4.0)
The first seven examples deal with inflow values ranging from
-1 to 12 m³/s (note that |arma| even routes negative discharges,
which are below the 0 m²/s threshold):
>>> from hydpy import UnitTest
>>> test = UnitTest(
... model, model.calc_qpin_v1,
... last_example=7,
... parseqs=(fluxes.qin, fluxes.qpin))
>>> test.nexts.qin = -1.0, 0.0, 1.0, 2.0, 4.0, 6.0, 12.0
>>> test()
| ex. | qin | qpin |
--------------------------------
| 1 | -1.0 | -1.0 0.0 0.0 |
| 2 | 0.0 | 0.0 0.0 0.0 |
| 3 | 1.0 | 1.0 0.0 0.0 |
| 4 | 2.0 | 2.0 0.0 0.0 |
| 5 | 4.0 | 2.0 2.0 0.0 |
| 6 | 6.0 | 2.0 4.0 0.0 |
| 7 | 12.0 | 2.0 4.0 6.0 |
The following two additional examples are demonstrate that method
|Calc_QPIn_V1| also functions properly if there is only one response
function, wherefore there is no need to divide the total discharge:
>>> derived.nmb = 1
>>> derived.maxq.shape = 1
>>> derived.diffq.shape = 0
>>> fluxes.qpin.shape = 1
>>> derived.maxq(0.0)
>>> test = UnitTest(
... model, model.calc_qpin_v1,
... first_example=8, last_example=10,
... parseqs=(fluxes.qin,
... fluxes.qpin))
>>> test.nexts.qin = -1.0, 0.0, 12.0
>>> test()
| ex. | qin | qpin |
---------------------
| 8 | -1.0 | -1.0 |
| 9 | 0.0 | 0.0 |
| 10 | 12.0 | 12.0 |
"""
DERIVEDPARAMETERS = (
arma_derived.Nmb,
arma_derived.MaxQ,
arma_derived.DiffQ,
)
REQUIREDSEQUENCES = (arma_fluxes.QIn,)
RESULTSEQUENCES = (arma_fluxes.QPIn,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
der = model.parameters.derived.fastaccess
flu = model.sequences.fluxes.fastaccess
for idx in range(der.nmb - 1):
if flu.qin < der.maxq[idx]:
if idx == 0:
flu.qpin[idx] = flu.qin
else:
flu.qpin[idx] = 0.0
elif flu.qin < der.maxq[idx + 1]:
flu.qpin[idx] = flu.qin - der.maxq[idx]
else:
flu.qpin[idx] = der.diffq[idx]
if der.nmb == 1:
flu.qpin[0] = flu.qin
else:
flu.qpin[der.nmb - 1] = max(flu.qin - der.maxq[der.nmb - 1], 0.0)
[docs]class Update_LogIn_V1(modeltools.Method):
"""Refresh the input log sequence for the different MA processes.
Example:
Assume there are three response functions, involving one, two and
three MA coefficients respectively:
>>> from hydpy.models.arma import *
>>> parameterstep()
>>> derived.nmb(3)
>>> derived.ma_order.shape = 3
>>> derived.ma_order = 1, 2, 3
>>> fluxes.qpin.shape = 3
>>> logs.login.shape = (3, 3)
The "memory values" of the different MA processes are defined as
follows (one row for each process):
>>> logs.login = ((1.0, nan, nan),
... (2.0, 3.0, nan),
... (4.0, 5.0, 6.0))
These are the new inflow discharge portions to be included into
the memories of the different processes:
>>> fluxes.qpin = 7.0, 8.0, 9.0
Through applying method |Update_LogIn_V1| all values already
existing are shifted to the right ("into the past"). Values,
which are no longer required due to the limited order or the
different MA processes, are discarded. The new values are
inserted in the first column:
>>> model.update_login_v1()
>>> logs.login
login([[7.0, nan, nan],
[8.0, 2.0, nan],
[9.0, 4.0, 5.0]])
"""
DERIVEDPARAMETERS = (
arma_derived.Nmb,
arma_derived.MA_Order,
)
REQUIREDSEQUENCES = (arma_fluxes.QPIn,)
UPDATEDSEQUENCES = (arma_logs.LogIn,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
der = model.parameters.derived.fastaccess
flu = model.sequences.fluxes.fastaccess
log = model.sequences.logs.fastaccess
for idx in range(der.nmb):
for jdx in range(der.ma_order[idx] - 2, -1, -1):
log.login[idx, jdx + 1] = log.login[idx, jdx]
for idx in range(der.nmb):
log.login[idx, 0] = flu.qpin[idx]
[docs]class Calc_QMA_V1(modeltools.Method):
"""Calculate the discharge responses of the different MA processes.
Examples:
Assume there are three response functions, involving one, two and
three MA coefficients respectively:
>>> from hydpy.models.arma import *
>>> parameterstep()
>>> derived.nmb(3)
>>> derived.ma_order.shape = 3
>>> derived.ma_order = 1, 2, 3
>>> derived.ma_coefs.shape = (3, 3)
>>> logs.login.shape = (3, 3)
>>> fluxes.qma.shape = 3
The coefficients of the different MA processes are stored in
separate rows of the 2-dimensional parameter `ma_coefs`:
>>> derived.ma_coefs = ((1.0, nan, nan),
... (0.8, 0.2, nan),
... (0.5, 0.3, 0.2))
The "memory values" of the different MA processes are defined as
follows (one row for each process). The current values are stored
in first column, the values of the last time step in the second
column, and so on:
>>> logs.login = ((1.0, nan, nan),
... (2.0, 3.0, nan),
... (4.0, 5.0, 6.0))
Applying method |Calc_QMA_V1| is equivalent to calculating the
inner product of the different rows of both matrices:
>>> model.calc_qma_v1()
>>> fluxes.qma
qma(1.0, 2.2, 4.7)
"""
DERIVEDPARAMETERS = (
arma_derived.Nmb,
arma_derived.MA_Order,
arma_derived.MA_Coefs,
)
REQUIREDSEQUENCES = (arma_logs.LogIn,)
RESULTSEQUENCES = (arma_fluxes.QMA,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
der = model.parameters.derived.fastaccess
flu = model.sequences.fluxes.fastaccess
log = model.sequences.logs.fastaccess
for idx in range(der.nmb):
flu.qma[idx] = 0.0
for jdx in range(der.ma_order[idx]):
flu.qma[idx] += der.ma_coefs[idx, jdx] * log.login[idx, jdx]
[docs]class Calc_QAR_V1(modeltools.Method):
"""Calculate the discharge responses of the different AR processes.
Examples:
Assume there are four response functions, involving zero, one, two,
and three AR coefficients respectively:
>>> from hydpy.models.arma import *
>>> parameterstep()
>>> derived.nmb(4)
>>> derived.ar_order.shape = 4
>>> derived.ar_order = 0, 1, 2, 3
>>> derived.ar_coefs.shape = (4, 3)
>>> logs.logout.shape = (4, 3)
>>> fluxes.qar.shape = 4
The coefficients of the different AR processes are stored in
separate rows of the 2-dimensional parameter `ma_coefs`.
Note the special case of the first AR process of zero order
(first row), which involves no autoregressive memory at all:
>>> derived.ar_coefs = ((nan, nan, nan),
... (1.0, nan, nan),
... (0.8, 0.2, nan),
... (0.5, 0.3, 0.2))
The "memory values" of the different AR processes are defined as
follows (one row for each process). The values of the last time
step are stored in first column, the values of the last time step
in the second column, and so on:
>>> logs.logout = ((nan, nan, nan),
... (1.0, nan, nan),
... (2.0, 3.0, nan),
... (4.0, 5.0, 6.0))
Applying method |Calc_QAR_V1| is equivalent to calculating the
inner product of the different rows of both matrices:
>>> model.calc_qar_v1()
>>> fluxes.qar
qar(0.0, 1.0, 2.2, 4.7)
"""
DERIVEDPARAMETERS = (
arma_derived.Nmb,
arma_derived.AR_Order,
arma_derived.AR_Coefs,
)
REQUIREDSEQUENCES = (arma_logs.LogOut,)
RESULTSEQUENCES = (arma_fluxes.QAR,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
der = model.parameters.derived.fastaccess
flu = model.sequences.fluxes.fastaccess
log = model.sequences.logs.fastaccess
for idx in range(der.nmb):
flu.qar[idx] = 0.0
for jdx in range(der.ar_order[idx]):
flu.qar[idx] += der.ar_coefs[idx, jdx] * log.logout[idx, jdx]
[docs]class Calc_QPOut_V1(modeltools.Method):
"""Calculate the ARMA results for the different response functions.
Examples:
Initialize an arma model with three different response functions:
>>> from hydpy.models.arma import *
>>> parameterstep()
>>> derived.nmb(3)
>>> fluxes.qma.shape = 3
>>> fluxes.qar.shape = 3
>>> fluxes.qpout.shape = 3
Define the output values of the MA and of the AR processes
associated with the three response functions and apply
method |Calc_QPOut_V1|:
>>> fluxes.qar = 4.0, 5.0, 6.0
>>> fluxes.qma = 1.0, 2.0, 3.0
>>> model.calc_qpout_v1()
>>> fluxes.qpout
qpout(5.0, 7.0, 9.0)
"""
DERIVEDPARAMETERS = (arma_derived.Nmb,)
REQUIREDSEQUENCES = (
arma_fluxes.QMA,
arma_fluxes.QAR,
)
RESULTSEQUENCES = (arma_fluxes.QPOut,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
der = model.parameters.derived.fastaccess
flu = model.sequences.fluxes.fastaccess
for idx in range(der.nmb):
flu.qpout[idx] = flu.qma[idx] + flu.qar[idx]
[docs]class Update_LogOut_V1(modeltools.Method):
"""Refresh the log sequence for the different AR processes.
Example:
Assume there are four response functions, involving zero, one, two
and three AR coefficients respectively:
>>> from hydpy.models.arma import *
>>> parameterstep()
>>> derived.nmb(4)
>>> derived.ar_order.shape = 4
>>> derived.ar_order = 0, 1, 2, 3
>>> fluxes.qpout.shape = 4
>>> logs.logout.shape = (4, 3)
The "memory values" of the different AR processes are defined as
follows (one row for each process). Note the special case of the
first AR process of zero order (first row), which is why there are
no autoregressive memory values required:
>>> logs.logout = ((nan, nan, nan),
... (0.0, nan, nan),
... (1.0, 2.0, nan),
... (3.0, 4.0, 5.0))
These are the new outflow discharge portions to be included into
the memories of the different processes:
>>> fluxes.qpout = 6.0, 7.0, 8.0, 9.0
Through applying method |Update_LogOut_V1| all values already
existing are shifted to the right ("into the past"). Values, which
are no longer required due to the limited order or the different
AR processes, are discarded. The new values are inserted in the
first column:
>>> model.update_logout_v1()
>>> logs.logout
logout([[nan, nan, nan],
[7.0, nan, nan],
[8.0, 1.0, nan],
[9.0, 3.0, 4.0]])
"""
DERIVEDPARAMETERS = (
arma_derived.Nmb,
arma_derived.AR_Order,
)
REQUIREDSEQUENCES = (arma_fluxes.QPOut,)
UPDATEDSEQUENCES = (arma_logs.LogOut,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
der = model.parameters.derived.fastaccess
flu = model.sequences.fluxes.fastaccess
log = model.sequences.logs.fastaccess
for idx in range(der.nmb):
for jdx in range(der.ar_order[idx] - 2, -1, -1):
log.logout[idx, jdx + 1] = log.logout[idx, jdx]
for idx in range(der.nmb):
if der.ar_order[idx] > 0:
log.logout[idx, 0] = flu.qpout[idx]
[docs]class Calc_QOut_V1(modeltools.Method):
"""Sum up the results of the different response functions.
Examples:
Initialize an arma model with three different response functions:
>>> from hydpy.models.arma import *
>>> parameterstep()
>>> derived.nmb(3)
>>> fluxes.qpout.shape = 3
Define the output values of the three response functions and
apply method |Calc_QOut_V1|:
>>> fluxes.qpout = 1.0, 2.0, 3.0
>>> model.calc_qout_v1()
>>> fluxes.qout
qout(6.0)
"""
DERIVEDPARAMETERS = (arma_derived.Nmb,)
REQUIREDSEQUENCES = (arma_fluxes.QPOut,)
RESULTSEQUENCES = (arma_fluxes.QOut,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
der = model.parameters.derived.fastaccess
flu = model.sequences.fluxes.fastaccess
flu.qout = 0.0
for idx in range(der.nmb):
flu.qout += flu.qpout[idx]
[docs]class Pick_Q_V1(modeltools.Method):
"""Update inflow."""
REQUIREDSEQUENCES = (arma_inlets.Q,)
RESULTSEQUENCES = (arma_fluxes.QIn,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
flu = model.sequences.fluxes.fastaccess
inl = model.sequences.inlets.fastaccess
flu.qin = 0.0
for idx in range(inl.len_q):
flu.qin += inl.q[idx][0]
[docs]class Pass_Q_V1(modeltools.Method):
"""Update outflow."""
REQUIREDSEQUENCES = (arma_fluxes.QOut,)
RESULTSEQUENCES = (arma_outlets.Q,)
@staticmethod
def __call__(model: modeltools.Model) -> None:
flu = model.sequences.fluxes.fastaccess
out = model.sequences.outlets.fastaccess
out.q[0] += flu.qout
[docs]class Model(modeltools.AdHocModel):
"""Base model ARMA."""
INLET_METHODS = (Pick_Q_V1,)
RECEIVER_METHODS = ()
RUN_METHODS = (
Calc_QPIn_V1,
Update_LogIn_V1,
Calc_QMA_V1,
Calc_QAR_V1,
Calc_QPOut_V1,
Update_LogOut_V1,
Calc_QOut_V1,
)
ADD_METHODS = ()
OUTLET_METHODS = (Pass_Q_V1,)
SENDER_METHODS = ()
SUBMODELS = ()