modeltools

This module provides features for applying and implementing hydrological models.

Module modeltools implements the following members:

  • AideSequences Aide sequences of model modeltools.

  • FluxSequences Flux sequences of model modeltools.

  • InletSequences Inlet sequences of model modeltools.

  • InputSequences Input sequences of model modeltools.

  • LogSequences Log sequences of model modeltools.

  • OutletSequences Outlet sequences of model modeltools.

  • ReceiverSequences Receiver sequences of model modeltools.

  • SenderSequences Sender sequences of model modeltools.

  • StateSequences State sequences of model modeltools.

  • Method Base class for defining (hydrological) calculation methods.

  • IndexProperty Base class for index descriptors like Idx_Sim.

  • Idx_Sim The simulation step index.

  • Idx_HRU The hydrological response unit index.

  • Model Base class for all hydrological models.

  • AdHocModel Base class for models solving the underlying differential equations in an “ad hoc manner”.

  • SolverModel Base class for hydrological models which solve ordinary differential equations with numerical integration algorithms.

  • NumConstsELS Configuration options for using the “Explicit Lobatto Sequence” implemented by class ELSModel.

  • NumVarsELS Intermediate results of the “Explicit Lobatto Sequence” implemented by class ELSModel.

  • ELSModel Base class for hydrological models using the “Explicit Lobatto Sequence” for solving ordinary differential equations.

  • Submodel Base class for implementing “submodels” that serve to deal with (possibly complicated) general mathematical algorithms (e.g. root-finding algorithms) within hydrological model methods.


class hydpy.core.modeltools.Method[source]

Bases: object

Base class for defining (hydrological) calculation methods.

class hydpy.core.modeltools.IndexProperty[source]

Bases: object

Base class for index descriptors like Idx_Sim.

class hydpy.core.modeltools.Idx_Sim[source]

Bases: hydpy.core.modeltools.IndexProperty

The simulation step index.

Some model methods require to know the index of the current simulation step (with respect to the initialisation period), which one usually updates by passing it to simulate(). However, you are allowed to change it manually via the Idx_Sim descriptor, which is often beneficial during testing:

>>> from hydpy.models.hland_v1 import *
>>> parameterstep("1d")
>>> model.idx_sim
0
>>> model.idx_sim = 1
>>> model.idx_sim
1

Like other objects of IndexProperty subclasses, Idx_Sim objects are aware of their name:

>>> Model.idx_sim.name
'idx_sim'
class hydpy.core.modeltools.Idx_HRU[source]

Bases: hydpy.core.modeltools.IndexProperty

The hydrological response unit index.

See class Idx_HRU for an explanation on the purpose and handling of objects of IndexProperty subclasses.

class hydpy.core.modeltools.Model[source]

Bases: object

Base class for all hydrological models.

Class Model provides everything to create a usable application model, except method simulate(). See class AdHocModel and ELSModel, which implement this method.

Each final Model object has two attributes named parameters and sequences, providing access to all parameter and sequence values, respectively. For example, the application model hland_v1 so provides access to the control parameter NmbZones and the input sequence P:

>>> from hydpy.models.hland_v1 import *
>>> parameterstep("1d")
>>> model.parameters.control.nmbzones
nmbzones(?)
>>> model.sequences.inputs.p
p(nan)

Both attributes are dynamic. You need to add them manually whenever you want to prepare a workable Model object on your own (see the factory functions prepare_model() and parameterstep(), which do this regularly). In case you forget to do so, you get the following error message:

>>> from hydpy.models.hland_v1 import Model
>>> Model().parameters
Traceback (most recent call last):
...
AttributeError: The dynamic attribute `parameters` of `hland_v1` of element `?` is not available at the moment.
>>> Model().sequences
Traceback (most recent call last):
...
AttributeError: The dynamic attribute `sequences` of `hland_v1` of element `?` is not available at the moment.

Other wrong attribute names result in the familiar error message:

>>> Model().wrong
Traceback (most recent call last):
...
AttributeError: 'Model' object has no attribute 'wrong'

Similar to parameters and sequences, there is also the dynamic masks attribute, making all predefined masks of the actual model type available within in a Masks objects:

>>> model.masks
complete of module hydpy.models.hland.hland_masks
land of module hydpy.models.hland.hland_masks
noglacier of module hydpy.models.hland.hland_masks
soil of module hydpy.models.hland.hland_masks
field of module hydpy.models.hland.hland_masks
forest of module hydpy.models.hland.hland_masks
ilake of module hydpy.models.hland.hland_masks
glacier of module hydpy.models.hland.hland_masks

You can use these masks, for example, to average the zone-specific precipitation values handled by sequence PC. When passing no argument, method average_values() applies the complete mask. Pass mask land to average the values of all zones except those of type ILAKE:

>>> nmbzones(4)
>>> zonetype(FIELD, FOREST, GLACIER, ILAKE)
>>> zonearea(1.0)
>>> fluxes.pc = 1.0, 3.0, 5.0, 7.0
>>> fluxes.pc.average_values()
4.0
>>> fluxes.pc.average_values(model.masks.land)
3.0

Attribute masks is, in contrast to attributes parameters and sequences, optional, which we indicate by a different error message:

>>> from hydpy import prepare_model
>>> prepare_model("test_v1").masks
Traceback (most recent call last):
...
AttributeError: Model ``test_v1` of element `?`` does not handle a group of masks (at the moment).
parameters: hydpy.core.parametertools.Parameters
sequences: hydpy.core.sequencetools.Sequences
masks: masktools.Masks
idx_sim

The simulation step index.

Some model methods require to know the index of the current simulation step (with respect to the initialisation period), which one usually updates by passing it to simulate(). However, you are allowed to change it manually via the Idx_Sim descriptor, which is often beneficial during testing:

>>> from hydpy.models.hland_v1 import *
>>> parameterstep("1d")
>>> model.idx_sim
0
>>> model.idx_sim = 1
>>> model.idx_sim
1

Like other objects of IndexProperty subclasses, Idx_Sim objects are aware of their name:

>>> Model.idx_sim.name
'idx_sim'
METHOD_GROUPS: ClassVar[Tuple[Type[hydpy.core.modeltools.Method], ]]
cymodel: Optional[hydpy.core.typingtools.CyModelProtocol]
element: Optional[devicetools.Element]
connect()None[source]

Connect all LinkSequence objects and the selected InputSequence and OutputSequence objects of the actual model to the corresponding NodeSequence objects.

You cannot connect any sequences until the Model object itself is connected to an Element object referencing the required Node objects:

>>> from hydpy import prepare_model
>>> prepare_model("hstream_v1").connect()
Traceback (most recent call last):
...
AttributeError: While trying to build the node connection of the `input` sequences of the model handled by element `?`, the following error occurred: 'NoneType' object has no attribute 'inputs'

The application model hstream_v1 can receive inflow from an arbitrary number of upstream nodes and passes its outflow to a single downstream node (note that property model of class Element calls method connect() automatically):

>>> from hydpy import Element, Node
>>> in1 = Node("in1", variable="Q")
>>> in2 = Node("in2", variable="Q")
>>> out1 = Node("out1", variable="Q")
>>> element1 = Element("element1", inlets=(in1, in2), outlets=out1)
>>> element1.model = prepare_model("hstream_v1")

Now all connections work as expected:

>>> in1.sequences.sim = 1.0
>>> in2.sequences.sim = 2.0
>>> out1.sequences.sim = 3.0
>>> element1.model.sequences.inlets.q
q(1.0, 2.0)
>>> element1.model.sequences.outlets.q
q(3.0)
>>> element1.model.sequences.inlets.q *= 2.0
>>> element1.model.sequences.outlets.q *= 2.0
>>> in1.sequences.sim
sim(2.0)
>>> in2.sequences.sim
sim(4.0)
>>> out1.sequences.sim
sim(6.0)

To show some possible errors and related error messages, we define three additional nodes, two handling variables different from discharge (Q):

>>> in3 = Node("in3", variable="X")
>>> out2 = Node("out2", variable="Q")
>>> out3 = Node("out3", variable="X")

Link sequence names must match the variable a node is handling:

>>> element2 = Element("element2", inlets=(in1, in2), outlets=out3)
>>> element2.model = prepare_model("hstream_v1")
Traceback (most recent call last):
...
RuntimeError: While trying to build the node connection of the `outlet` sequences of the model handled by element `element2`, the following error occurred: Sequence `q` of element `element2` cannot be connected due to no available node handling variable `Q`.

One can connect a 0-dimensional link sequence to a single node sequence only:

>>> element3 = Element("element3", inlets=(in1, in2), outlets=(out1, out2))
>>> element3.model = prepare_model("hstream_v1")
Traceback (most recent call last):
...
RuntimeError: While trying to build the node connection of the `outlet` sequences of the model handled by element `element3`, the following error occurred: Sequence `q` cannot be connected as it is 0-dimensional but multiple nodes are available which are handling variable `Q`.

Method connect() generally reports about unusable node sequences:

>>> element4 = Element("element4", inlets=(in1, in2), outlets=(out1, out3))
>>> element4.model = prepare_model("hstream_v1")
Traceback (most recent call last):
...
RuntimeError: While trying to build the node connection of the `outlet` sequences of the model handled by element `element4`, the following error occurred: The following nodes have not been connected to any sequences: out3.
>>> element5 = Element("element5", inlets=(in1, in2, in3), outlets=out1)
>>> element5.model = prepare_model("hstream_v1")
Traceback (most recent call last):
...
RuntimeError: While trying to build the node connection of the `inlet` sequences of the model handled by element `element5`, the following error occurred: The following nodes have not been connected to any sequences: in3.
>>> element6 = Element("element6", inlets=in1, outlets=out1, receivers=in2)
>>> element6.model = prepare_model("hstream_v1")
Traceback (most recent call last):
...
RuntimeError: While trying to build the node connection of the `receiver` sequences of the model handled by element `element6`, the following error occurred: The following nodes have not been connected to any sequences: in2.
>>> element7 = Element("element7", inlets=in1, outlets=out1, senders=in2)
>>> element7.model = prepare_model("hstream_v1")
Traceback (most recent call last):
...
RuntimeError: While trying to build the node connection of the `sender` sequences of the model handled by element `element7`, the following error occurred: The following nodes have not been connected to any sequences: in2.

The above examples explain how to connect link sequences to their nodes. Such connections are relatively hard requirements (hstream_v1 definitively needs inflow provided from a node, which the node itself typically receives from another model). In contrast, connections between input or output sequences and nodes are optional. If one defines such a connection for an input sequence, it receives data from the related node; otherwise, it uses its individually managed data, usually read from a file. If one defines such a connection for an output sequence, it passes its internal data to the related node; otherwise, nothing happens.

We demonstrate this functionality by focussing on the input sequences T and P and the output sequences Q0 and UZ of application model hland_v1. T uses its own data (which we define manually, but we could read it from a file as well), whereas P gets its data from node inp1. Flux sequence Q0 and state sequence UZ pass their data to two separate output nodes, whereas all other fluxes and states do not. This functionality requires to tell each node which sequence it should connect to, which we do by passing the sequence types (or the globally available aliases hland_P, hland_Q0, and hland_UZ) to the variable keyword of different node objects:

>>> from hydpy import pub
>>> from hydpy.inputs import hland_P
>>> from hydpy.outputs import hland_Q0, hland_UZ
>>> pub.timegrids = "2000-01-01", "2000-01-06", "1d"
>>> inp1 = Node("inp1", variable=hland_P)
>>> outp1 = Node("outp1", variable=hland_Q0)
>>> outp2 = Node("outp2", variable=hland_UZ)
>>> element8 = Element("element8",
...                    outlets=out1,
...                    inputs=inp1,
...                    outputs=[outp1, outp2])
>>> element8.model = prepare_model("hland_v1")
>>> element8.prepare_inputseries()
>>> element8.model.idx_sim = 2
>>> element8.model.sequences.inputs.t.series = 1.0, 2.0, 3.0, 4.0, 5.0
>>> inp1.sequences.sim(9.0)
>>> element8.model.load_data()
>>> element8.model.sequences.inputs.t
t(3.0)
>>> element8.model.sequences.inputs.p
p(9.0)
>>> element8.model.sequences.fluxes.q0 = 99.0
>>> element8.model.sequences.states.uz = 999.0
>>> element8.model.update_outputs()
>>> outp1.sequences.sim
sim(99.0)
>>> outp2.sequences.sim
sim(999.0)

Instead of using single InputSequence and OutputSequence subclasses, one can create and apply fused variables, combining multiple subclasses (see the documentation on class FusedVariable for more information and a more realistic example):

>>> from hydpy import FusedVariable
>>> from hydpy.inputs import lland_Nied
>>> from hydpy.outputs import lland_QDGZ
>>> Precip = FusedVariable("Precip", hland_P, lland_Nied)
>>> inp2 = Node("inp2", variable=Precip)
>>> FastRunoff = FusedVariable("FastRunoff", hland_Q0, lland_QDGZ)
>>> outp3 = Node("outp3", variable=FastRunoff)
>>> element9 = Element("element9",
...                    outlets=out1,
...                    inputs=inp2,
...                    outputs=outp3)
>>> element9.model = prepare_model("hland_v1")
>>> inp2.sequences.sim(9.0)
>>> element9.model.load_data()
>>> element9.model.sequences.inputs.p
p(9.0)
>>> element9.model.sequences.fluxes.q0 = 99.0
>>> element9.model.update_outputs()
>>> outp3.sequences.sim
sim(99.0)

Method connect() reports if one of the given fused variables does not find a fitting sequence:

>>> from hydpy.inputs import lland_TemL
>>> Wrong = FusedVariable("Wrong", lland_Nied, lland_TemL)
>>> inp3 = Node("inp3", variable=Wrong)
>>> element10 = Element("element10",
...                     outlets=out1,
...                     inputs=inp3)
>>> element10.model = prepare_model("hland_v1")
Traceback (most recent call last):
...
TypeError: While trying to build the node connection of the `input` sequences of the model handled by element `element10`, the following error occurred: None of the input sequences of model `hland_v1` is among the sequences of the fused variable `Wrong` of node `inp3`.
>>> outp4 = Node("outp4", variable=Wrong)
>>> element11 = Element("element11",
...                     outlets=out1,
...                     outputs=outp4)
>>> element11.model = prepare_model("hland_v1")
Traceback (most recent call last):
...
TypeError: While trying to build the node connection of the `output` sequences of the model handled by element `element11`, the following error occurred: None of the output sequences of model `hland_v1` is among the sequences of the fused variable `Wrong` of node `outp4`.

Selecting wrong sequences results in the following errors messages:

>>> outp5 = Node("outp5", variable=hland_Q0)
>>> element12 = Element("element12",
...                     outlets=out1,
...                     inputs=outp5)
>>> element12.model = prepare_model("hland_v1")
Traceback (most recent call last):
...
TypeError: While trying to build the node connection of the `input` sequences of the model handled by element `element12`, the following error occurred: No input sequence of model hland_v1` is named `q0`.
>>> inp5 = Node("inp5", variable=hland_P)
>>> element13 = Element("element13",
...                     outlets=out1,
...                     outputs=inp5)
>>> element13.model = prepare_model("hland_v1")
Traceback (most recent call last):
...
TypeError: While trying to build the node connection of the `output` sequences of the model handled by element `element13`, the following error occurred: No flux or state sequence of model `hland_v1` is named `p`.

So far, you can build connections to 0-dimensional output sequences only:

>>> from hydpy.models.hland.hland_fluxes import PC
>>> outp6 = Node("outp6", variable=PC)
>>> element14 = Element("element14",
...                     outlets=out1,
...                     outputs=outp6)
>>> element14.model = prepare_model("hland_v1")
Traceback (most recent call last):
...
TypeError: While trying to build the node connection of the `output` sequences of the model handled by element `element14`, the following error occurred: Only connections with 0-dimensional output sequences are supported, but sequence `pc` is 1-dimensional.
property name

Name of the model type.

For base models, name corresponds to the package name:

>>> from hydpy import prepare_model
>>> hland = prepare_model("hland")
>>> hland.name
'hland'

For application models, name corresponds the module name:

>>> hland_v1 = prepare_model("hland_v1")
>>> hland_v1.name
'hland_v1'

This last example has only technical reasons:

>>> hland.name
'hland'
abstract simulate(idx: int)None[source]

Perform a simulation run over a single simulation time step.

load_data()None[source]

Call method load_data() of attribute sequences.

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

save_data(idx: int)None[source]

Call method save_data() of attribute sequences.

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

update_inlets()None[source]

Call all methods defined as “INLET_METHODS” in the defined order.

>>> from hydpy.core.modeltools import AdHocModel, Method
>>> class print_1(Method):
...     @staticmethod
...     def __call__(self):
...         print(1)
>>> class print_2(Method):
...     @staticmethod
...     def __call__(self):
...         print(2)
>>> class Test(AdHocModel):
...     INLET_METHODS = print_1, print_2
>>> Test().update_inlets()
1
2

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

update_outlets()None[source]

Call all methods defined as “OUTLET_METHODS” in the defined order.

>>> from hydpy.core.modeltools import AdHocModel, Method
>>> class print_1(Method):
...     @staticmethod
...     def __call__(self):
...         print(1)
>>> class print_2(Method):
...     @staticmethod
...     def __call__(self):
...         print(2)
>>> class Test(AdHocModel):
...     OUTLET_METHODS = print_1, print_2
>>> Test().update_outlets()
1
2

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

update_receivers(idx: int)None[source]

Call all methods defined as “RECEIVER_METHODS” in the defined order.

>>> from hydpy.core.modeltools import AdHocModel, Method
>>> class print_1(Method):
...     @staticmethod
...     def __call__(self):
...        print(test.idx_sim+1)
>>> class print_2(Method):
...     @staticmethod
...     def __call__(self):
...         print(test.idx_sim+2)
>>> class Test(AdHocModel):
...     RECEIVER_METHODS = print_1, print_2
>>> test = Test()
>>> test.update_receivers(1)
2
3

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

update_senders(idx: int)None[source]

Call all methods defined as “SENDER_METHODS” in the defined order.

>>> from hydpy.core.modeltools import AdHocModel, Method
>>> class print_1(Method):
...     @staticmethod
...     def __call__(self):
...        print(test.idx_sim+1)
>>> class print_2(Method):
...     @staticmethod
...     def __call__(self):
...         print(test.idx_sim+2)
>>> class Test(AdHocModel):
...     SENDER_METHODS = print_1, print_2
>>> test = Test()
>>> test.update_senders(1)
2
3

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

new2old()None[source]

Call method new2old() of subattribute sequences.states.

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

update_outputs()None[source]

Call method update_outputs() of subattributes sequences.fluxes and sequences.states.

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

classmethod get_methods()Iterator[hydpy.core.modeltools.Method][source]

Convenience method for iterating through all methods selected by a Model subclass.

>>> from hydpy.models import hland_v1
>>> for method in hland_v1.Model.get_methods():
...     print(method.__name__)   
Calc_TC_V1
Calc_TMean_V1
...
Calc_QT_V1
Pass_Q_v1

Note that function get_methods() returns the “raw” Method objects instead of the modified Python or Cython functions used for performing calculations.

class hydpy.core.modeltools.AdHocModel[source]

Bases: hydpy.core.modeltools.Model

Base class for models solving the underlying differential equations in an “ad hoc manner”.

“Ad hoc” stands for the classical approaches in hydrology, to calculate individual fluxes separately (often sequentially) and without error control (see Clark and Kavetski).

METHOD_GROUPS: ClassVar[Tuple[Type[Method], ]] = ('RECEIVER_METHODS', 'INLET_METHODS', 'RUN_METHODS', 'ADD_METHODS', 'OUTLET_METHODS', 'SENDER_METHODS')
simulate(idx: int)None[source]

Perform a simulation run over a single simulation time step.

The required argument idx corresponds to property idx_sim (see the main documentation on class Model).

You can integrate method simulate() into your workflows for tailor-made simulation runs. Method simulate() is complete enough to allow for consecutive calls. However, note that it does neither call save_data(), update_receivers(), nor update_senders(). Also, one would have to reset the related node sequences, as done in the following example:

>>> from hydpy.examples import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> model = hp.elements.land_dill.model
>>> for idx in range(4):
...     model.simulate(idx)
...     print(hp.nodes.dill.sequences.sim)
...     hp.nodes.dill.sequences.sim = 0.0
sim(11.78038)
sim(8.901179)
sim(7.131072)
sim(6.017787)
>>> hp.nodes.dill.sequences.sim.series
InfoArray([ nan,  nan,  nan,  nan])

The results above are identical to those of method simulate() of class HydPy, which is the standard method to perform simulation runs (except that method simulate() of class HydPy also performs the steps neglected by method simulate() of class Model mentioned above):

>>> from hydpy import round_
>>> hp.reset_conditions()
>>> hp.simulate()
>>> round_(hp.nodes.dill.sequences.sim.series)
11.78038, 8.901179, 7.131072, 6.017787

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

run()None[source]

Call all methods defined as “RUN_METHODS” in the defined order.

>>> from hydpy.core.modeltools import AdHocModel, Method
>>> class print_1(Method):
...     @staticmethod
...     def __call__(self):
...         print(1)
>>> class print_2(Method):
...     @staticmethod
...     def __call__(self):
...         print(2)
>>> class Test(AdHocModel):
...     RUN_METHODS = print_1, print_2
>>> Test().run()
1
2

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

class hydpy.core.modeltools.SolverModel[source]

Bases: hydpy.core.modeltools.Model

Base class for hydrological models which solve ordinary differential equations with numerical integration algorithms.

abstract solve()None[source]

Solve all FULL_ODE_METHODS in parallel.

class hydpy.core.modeltools.NumConstsELS[source]

Bases: object

Configuration options for using the “Explicit Lobatto Sequence” implemented by class ELSModel.

You can change the following solver options at your own risk.

>>> from hydpy.core.modeltools import NumConstsELS
>>> consts = NumConstsELS()

The maximum number of Runge Kutta submethods to be applied (the higher, the better the theoretical accuracy, but also the worse the time spent unsuccessful when the theory does not apply):

>>> consts.nmb_methods
10

The number of entries to handle the stages of the highest order method (must agree with the maximum number of methods):

>>> consts.nmb_stages
11

The maximum increase of the integration step size in case of success:

>>> consts.dt_increase
2.0

The maximum decrease of the integration step size in case of failure:

>>> consts.dt_decrease
10.0

The Runge Kutta coefficients, one matrix for each submethod:

>>> consts.a_coefs.shape
(11, 12, 11)
a_coeffs: numpy.ndarray
nmb_methods: int
nmb_stages: int
dt_increase: float
dt_decrease: float
class hydpy.core.modeltools.NumVarsELS[source]

Bases: object

Intermediate results of the “Explicit Lobatto Sequence” implemented by class ELSModel.

Class NumVarsELS should be of relevance for model developers, as it helps to evaluate how efficient newly implemented models are solved (see the documentation on method solve() of class ELSModel as an example).

use_relerror: bool
nmb_calls: int
t0: float
t1: float
dt_est: float
dt: float
idx_method: int
idx_stage: int
abserror: float
relerror: float
last_abserror: float
last_relerror: float
extrapolated_abserror: float
extrapolated_relerror: float
f0_ready: bool
class hydpy.core.modeltools.ELSModel[source]

Bases: hydpy.core.modeltools.SolverModel

Base class for hydrological models using the “Explicit Lobatto Sequence” for solving ordinary differential equations.

The “Explicit Lobatto Sequence” is a variable order Runge Kutta method combining different Lobatto methods. Its main idea is to first calculate a solution with a lower order method, then to use these results to apply the next higher-order method, and to compare both results. If they are close enough, the latter results are accepted. If not, the next higher-order method is applied (or, if no higher-order method is available, the step size is decreased, and the algorithm restarts with the method of the lowest order). So far, the thorough description of the algorithm is available in German only.

Note the strengths and weaknesses of class ELSModel discussed in the documentation on method solve(). Model developers should not derive from class ELSModel when trying to implement models with a high potential for stiff parameterisations. Discontinuities should be regularised, for example by the “smoothing functions” provided by module smoothtools. Model users should be careful not to define two small smoothing factors, to avoid needlessly long simulation times.

METHOD_GROUPS: ClassVar[Tuple[Type[Method], ]] = ('RECEIVER_METHODS', 'INLET_METHODS', 'PART_ODE_METHODS', 'FULL_ODE_METHODS', 'ADD_METHODS', 'OUTLET_METHODS', 'SENDER_METHODS')
numconsts: hydpy.core.modeltools.NumConstsELS
numvars: hydpy.core.modeltools.NumVarsELS
simulate(idx: int)None[source]

Similar to method simulate() of class AdHocModel but calls method solve() instead of run().

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

solve()None[source]

Solve all FULL_ODE_METHODS in parallel.

Implementing numerical integration algorithms that (hopefully) always work well in practice is a tricky task. The following exhaustive examples show how well our “Explicit Lobatto Sequence” algorithm performs for the numerical test models test_v1 and test_v2. We hope to cover all possible corner-cases. Please tell us if you find one we missed.

First, we set the value of parameter K to zero, resulting in no changes at all, and thus defining the simplest test case possible:

>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> k(0.0)

Second, we assign values to the solver parameters AbsErrorMax, RelDTMin, and RelDTMax to specify the required numerical accuracy and the smallest and largest internal integration step size allowed:

>>> solver.abserrormax(0.1)
>>> solver.reldtmin(0.001)
>>> solver.reldtmax(1.0)

Additionally, we set RelErrorMax to nan, which disables taking relative errors into account:

>>> solver.relerrormax(nan)

Calling method solve() correctly calculates zero discharge (Q) and thus does not change the water storage (S):

>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(1.0)
>>> fluxes.q
q(0.0)

The achieve the above result, ELSModel requires two function calls, one for the initial guess (using the Explicit Euler Method) and the other one (extending the Explicit Euler method to the Explicit Heun method) to confirm the first guess meets the required accuracy:

>>> model.numvars.idx_method
2
>>> model.numvars.dt
1.0
>>> model.numvars.nmb_calls
2

With moderate changes due to setting the value of parameter K to 0.1, two method calls are still sufficient:

>>> k(0.1)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.905)
>>> fluxes.q
q(0.095)
>>> model.numvars.idx_method
2
>>> model.numvars.nmb_calls
2

Calculating the analytical solution shows ELSModel did not exceed the given tolerance value:

>>> import numpy
>>> from hydpy import round_
>>> round_(numpy.exp(-k))
0.904837

After decreasing the allowed error by one order of magnitude, ELSModel requires four method calls (again, one for the first order and one for the second-order method, and two additional calls for the third-order method):

>>> solver.abserrormax(0.001)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.904833)
>>> fluxes.q
q(0.095167)
>>> model.numvars.idx_method
3
>>> model.numvars.nmb_calls
4

After decreasing AbsErrorMax by a factor of ten again, ELSModel needs one further higher-order method, which requires three additional calls, making a sum of seven:

>>> solver.abserrormax(0.0001)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.904837)
>>> fluxes.q
q(0.095163)
>>> model.numvars.idx_method
4
>>> model.numvars.nmb_calls
7

ELSModel achieves even a very extreme numerical precision (just for testing, way beyond hydrological requirements), in one single step, but now requires a total of 29 method calls:

>>> solver.abserrormax(1e-12)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.904837)
>>> fluxes.q
q(0.095163)
>>> model.numvars.dt
1.0
>>> model.numvars.idx_method
8
>>> model.numvars.nmb_calls
29

With a more dynamical parameterisation, where the storage decreases by about 40% per time step, ELSModel needs seven method calls to meet a “normal” error tolerance:

>>> solver.abserrormax(0.01)
>>> k(0.5)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.606771)
>>> fluxes.q
q(0.393229)
>>> model.numvars.idx_method
4
>>> model.numvars.nmb_calls
7
>>> round_(numpy.exp(-k))
0.606531

Being an explicit integration method, the “Explicit Lobatto Sequence” can be inefficient for solving stiff initial value problems. Setting K to 2.0 forces ELSModel to solve the problem in two substeps, requiring a total of 22 method calls:

>>> k(2.0)
>>> round_(numpy.exp(-k))
0.135335
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.134658)
>>> fluxes.q
q(0.865342)
>>> round_(model.numvars.dt)
0.3
>>> model.numvars.nmb_calls
22

Increasing the stiffness of the initial value problem further can increase computation times rapidly:

>>> k(4.0)
>>> round_(numpy.exp(-k))
0.018316
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.019774)
>>> fluxes.q
q(0.980226)
>>> round_(model.numvars.dt)
0.3
>>> model.numvars.nmb_calls
44

If we prevent ELSModel from compensating its problems by disallowing it to reduce its integration step size, it does not achieve satisfying results:

>>> solver.reldtmin(1.0)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.09672)
>>> fluxes.q
q(0.90328)
>>> round_(model.numvars.dt)
1.0
>>> model.numvars.nmb_calls
46

You can restrict the allowed maximum integration step size, which can help to prevent from loosing to much performance due to trying to solve too stiff problems, repeatedly:

>>> solver.reldtmin(0.001)
>>> solver.reldtmax(0.25)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.016806)
>>> fluxes.q
q(0.983194)
>>> round_(model.numvars.dt)
0.25
>>> model.numvars.nmb_calls
33

Alternatively, you can restrict the available number of Lobatto methods. Using two methods only is an inefficient choice for the given initial value problem, but at least solves it with the required accuracy:

>>> solver.reldtmax(1.0)
>>> model.numconsts.nmb_methods = 2
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.020284)
>>> fluxes.q
q(0.979716)
>>> round_(model.numvars.dt)
0.156698
>>> model.numvars.nmb_calls
74

In the above examples, we control numerical accuracies based on absolute error estimates only via parameter AbsErrorMax. After assigning an actual value to parameter RelErrorMax, ELSModel also takes relative errors into account. We modify some of the above examples to show how this works.

Generally, it is sufficient to meet one of both criteria. If we repeat the second example with a relaxed absolute but a strict relative tolerance, we reproduce the original result due to our absolute criteria being the relevant one:

>>> solver.abserrormax(0.1)
>>> solver.relerrormax(0.000001)
>>> k(0.1)
>>> states.s(1.0)
>>> model.solve()
>>> states.s
s(0.905)
>>> fluxes.q
q(0.095)

The same holds for the opposite case of a strict absolute but a relaxed relative tolerance:

>>> solver.abserrormax(0.000001)
>>> solver.relerrormax(0.1)
>>> k(0.1)
>>> states.s(1.0)
>>> model.solve()
>>> states.s
s(0.905)
>>> fluxes.q
q(0.095)

Reiterating the “more dynamical parameterisation” example results in slightly different but also correct results:

>>> k(0.5)
>>> states.s(1.0)
>>> model.solve()
>>> states.s
s(0.607196)
>>> fluxes.q
q(0.392804)

Reiterating the stiffest example with a relative instead of an absolute error tolerance of 0.1 achieves higher accuracy, as to be expected due to the value of S being far below 1.0 for some time:

>>> k(4.0)
>>> states.s(1.0)
>>> model.solve()
>>> states.s
s(0.0185)
>>> fluxes.q
q(0.9815)

Besides its weaknesses with stiff problems, ELSModel cannot solve discontinuous problems well. We use the test_v1 example model to demonstrate how ELSModel behaves when confronted with such a problem.

>>> from hydpy import reverse_model_wildcard_import
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v2 import *
>>> parameterstep()

Everything works fine, as long as the discontinuity does not affect the considered simulation step:

>>> k(0.5)
>>> solver.abserrormax(0.01)
>>> solver.reldtmin(0.001)
>>> solver.reldtmax(1.0)
>>> solver.relerrormax(nan)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(0.5)
>>> fluxes.q
q(0.5)
>>> model.numvars.idx_method
2
>>> model.numvars.dt
1.0
>>> model.numvars.nmb_calls
2

The occurrence of a discontinuity within the simulation step often increases computation times more than a stiff parameterisation:

>>> k(2.0)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(-0.006827)
>>> fluxes.q
q(1.006827)
>>> model.numvars.nmb_calls
58
>>> k(2.1)
>>> states.s(1.0)
>>> model.numvars.nmb_calls = 0
>>> model.solve()
>>> states.s
s(-0.00072)
>>> fluxes.q
q(1.00072)
>>> model.numvars.nmb_calls
50

When working in Cython mode, the standard model import overrides this generic Python version with a model-specific Cython version.

calculate_single_terms()None[source]

Apply all methods stored in the PART_ODE_METHODS tuple.

>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> k(0.25)
>>> states.s = 1.0
>>> model.calculate_single_terms()
>>> fluxes.q
q(0.25)
calculate_full_terms()None[source]

Apply all methods stored in the FULL_ODE_METHODS tuple.

>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> k(0.25)
>>> states.s.old = 1.0
>>> fluxes.q = 0.25
>>> model.calculate_full_terms()
>>> states.s.old
1.0
>>> states.s.new
0.75
get_point_states()None[source]

Load the states corresponding to the actual stage.

>>> from hydpy import round_
>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> states.s.old = 2.0
>>> states.s.new = 2.0
>>> model.numvars.idx_stage = 2
>>> points = numpy.asarray(states.fastaccess._s_points)
>>> points[:4] = 0.0, 0.0, 1.0, 0.0
>>> model.get_point_states()
>>> round_(states.s.old)
2.0
>>> round_(states.s.new)
1.0
>>> from hydpy import reverse_model_wildcard_import, print_values
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> states.sv.old = 3.0, 3.0
>>> states.sv.new = 3.0, 3.0
>>> model.numvars.idx_stage = 2
>>> points = numpy.asarray(states.fastaccess._sv_points)
>>> points[:4, 0] = 0.0, 0.0, 1.0, 0.0
>>> points[:4, 1] = 0.0, 0.0, 2.0, 0.0
>>> model.get_point_states()
>>> print_values(states.sv.old)
3.0, 3.0
>>> print_values(states.sv.new)
1.0, 2.0
set_point_states()None[source]

Save the states corresponding to the actual stage.

>>> from hydpy import print_values
>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> states.s.old = 2.0
>>> states.s.new = 1.0
>>> model.numvars.idx_stage = 2
>>> points = numpy.asarray(states.fastaccess._s_points)
>>> points[:] = 0.
>>> model.set_point_states()
>>> print_values(points[:4])
0.0, 0.0, 1.0, 0.0
>>> from hydpy import reverse_model_wildcard_import
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> states.sv.old = 3.0, 3.0
>>> states.sv.new = 1.0, 2.0
>>> model.numvars.idx_stage = 2
>>> points = numpy.asarray(states.fastaccess._sv_points)
>>> points[:] = 0.
>>> model.set_point_states()
>>> print_values(points[:4, 0])
0.0, 0.0, 1.0, 0.0
>>> print_values(points[:4, 1])
0.0, 0.0, 2.0, 0.0
set_result_states()None[source]

Save the final states of the actual method.

>>> from hydpy import print_values
>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> states.s.old = 2.0
>>> states.s.new = 1.0
>>> model.numvars.idx_method = 2
>>> results = numpy.asarray(states.fastaccess._s_results)
>>> results[:] = 0.0
>>> model.set_result_states()
>>> print_values(results[:4])
0.0, 0.0, 1.0, 0.0
>>> from hydpy import reverse_model_wildcard_import
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> states.sv.old = 3.0, 3.0
>>> states.sv.new = 1.0, 2.0
>>> model.numvars.idx_method = 2
>>> results = numpy.asarray(states.fastaccess._sv_results)
>>> results[:] = 0.0
>>> model.set_result_states()
>>> print_values(results[:4, 0])
0.0, 0.0, 1.0, 0.0
>>> print_values(results[:4, 1])
0.0, 0.0, 2.0, 0.0
get_sum_fluxes()None[source]

Get the sum of the fluxes calculated so far.

>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> fluxes.q = 0.0
>>> fluxes.fastaccess._q_sum = 1.0
>>> model.get_sum_fluxes()
>>> fluxes.q
q(1.0)
>>> from hydpy import reverse_model_wildcard_import, print_values
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> fluxes.qv = 0.0, 0.0
>>> numpy.asarray(fluxes.fastaccess._qv_sum)[:] = 1.0, 2.0
>>> model.get_sum_fluxes()
>>> fluxes.qv
qv(1.0, 2.0)
set_point_fluxes()None[source]

Save the fluxes corresponding to the actual stage.

>>> from hydpy import print_values
>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> fluxes.q = 1.0
>>> model.numvars.idx_stage = 2
>>> points = numpy.asarray(fluxes.fastaccess._q_points)
>>> points[:] = 0.0
>>> model.set_point_fluxes()
>>> print_values(points[:4])
0.0, 0.0, 1.0, 0.0
>>> from hydpy import reverse_model_wildcard_import
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> fluxes.qv = 1.0, 2.0
>>> model.numvars.idx_stage = 2
>>> points = numpy.asarray(fluxes.fastaccess._qv_points)
>>> points[:] = 0.0
>>> model.set_point_fluxes()
>>> print_values(points[:4, 0])
0.0, 0.0, 1.0, 0.0
>>> print_values(points[:4, 1])
0.0, 0.0, 2.0, 0.0
set_result_fluxes()None[source]

Save the final fluxes of the actual method.

>>> from hydpy import print_values
>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> fluxes.q = 1.0
>>> model.numvars.idx_method = 2
>>> results = numpy.asarray(fluxes.fastaccess._q_results)
>>> results[:] = 0.0
>>> model.set_result_fluxes()
>>> from hydpy import round_
>>> print_values(results[:4])
0.0, 0.0, 1.0, 0.0
>>> from hydpy import reverse_model_wildcard_import
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> fluxes.qv = 1.0, 2.0
>>> model.numvars.idx_method = 2
>>> results = numpy.asarray(fluxes.fastaccess._qv_results)
>>> results[:] = 0.0
>>> model.set_result_fluxes()
>>> print_values(results[:4, 0])
0.0, 0.0, 1.0, 0.0
>>> print_values(results[:4, 1])
0.0, 0.0, 2.0, 0.0
integrate_fluxes()None[source]

Perform a dot multiplication between the fluxes and the A coefficients associated with the different stages of the actual method.

>>> from hydpy import print_values
>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> model.numvars.idx_method = 2
>>> model.numvars.idx_stage = 1
>>> model.numvars.dt = 0.5
>>> points = numpy.asarray(fluxes.fastaccess._q_points)
>>> points[:4] = 15.0, 2.0, -999.0, 0.0
>>> model.integrate_fluxes()
>>> from hydpy import round_
>>> from hydpy import pub
>>> print_values(numpy.asarray(model.numconsts.a_coefs)[1, 1, :2])
0.375, 0.125
>>> fluxes.q
q(2.9375)
>>> from hydpy import reverse_model_wildcard_import
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> model.numvars.idx_method = 2
>>> model.numvars.idx_stage = 1
>>> model.numvars.dt = 0.5
>>> points = numpy.asarray(fluxes.fastaccess._qv_points)
>>> points[:4, 0] = 1.0, 1.0, -999.0, 0.0
>>> points[:4, 1] = 15.0, 2.0, -999.0, 0.0
>>> model.integrate_fluxes()
>>> print_values(numpy.asarray(model.numconsts.a_coefs)[1, 1, :2])
0.375, 0.125
>>> fluxes.qv
qv(0.25, 2.9375)
reset_sum_fluxes()None[source]

Set the sum of the fluxes calculated so far to zero.

>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> fluxes.fastaccess._q_sum = 5.0
>>> model.reset_sum_fluxes()
>>> fluxes.fastaccess._q_sum
0.0
>>> from hydpy import reverse_model_wildcard_import, print_values
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> import numpy
>>> sums = numpy.asarray(fluxes.fastaccess._qv_sum)
>>> sums[:] = 5.0, 5.0
>>> model.reset_sum_fluxes()
>>> print_values(fluxes.fastaccess._qv_sum)
0.0, 0.0
addup_fluxes()None[source]

Add up the sum of the fluxes calculated so far.

>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> fluxes.fastaccess._q_sum = 1.0
>>> fluxes.q(2.0)
>>> model.addup_fluxes()
>>> fluxes.fastaccess._q_sum
3.0
>>> from hydpy import reverse_model_wildcard_import, print_values
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> sums = numpy.asarray(fluxes.fastaccess._qv_sum)
>>> sums[:] = 1.0, 2.0
>>> fluxes.qv(3.0, 4.0)
>>> model.addup_fluxes()
>>> print_values(sums)
4.0, 6.0
calculate_error()None[source]

Estimate the numerical error based on the relevant fluxes calculated by the current and the last method.

“Relevant fluxes” are those contained within the SOLVERSEQUENCES tuple. If this tuple is empty, method calculate_error() selects all flux sequences of the respective model with a True NUMERIC attribute.

>>> from hydpy import round_
>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> results = numpy.asarray(fluxes.fastaccess._q_results)
>>> results[:5] = 0.0, 0.0, 3.0, 4.0, 4.0
>>> model.numvars.use_relerror = False
>>> model.numvars.idx_method = 3
>>> model.calculate_error()
>>> round_(model.numvars.abserror)
1.0
>>> model.numvars.relerror
inf
>>> model.numvars.use_relerror = True
>>> model.calculate_error()
>>> round_(model.numvars.abserror)
1.0
>>> round_(model.numvars.relerror)
0.25
>>> model.numvars.idx_method = 4
>>> model.calculate_error()
>>> round_(model.numvars.abserror)
0.0
>>> round_(model.numvars.relerror)
0.0
>>> model.numvars.idx_method = 1
>>> model.calculate_error()
>>> round_(model.numvars.abserror)
0.0
>>> model.numvars.relerror
inf
>>> from hydpy import reverse_model_wildcard_import
>>> reverse_model_wildcard_import()
>>> from hydpy.models.test_v3 import *
>>> parameterstep()
>>> n(2)
>>> model.numvars.use_relerror = True
>>> model.numvars.idx_method = 3
>>> results = numpy.asarray(fluxes.fastaccess._qv_results)
>>> results[:5, 0] = 0.0, 0.0, -4.0, -2.0, -2.0
>>> results[:5, 1] = 0.0, 0.0, -8.0, -4.0, -4.0
>>> model.calculate_error()
>>> round_(model.numvars.abserror)
4.0
>>> round_(model.numvars.relerror)
1.0
>>> model.numvars.idx_method = 4
>>> model.calculate_error()
>>> round_(model.numvars.abserror)
0.0
>>> round_(model.numvars.relerror)
0.0
>>> model.numvars.idx_method = 1
>>> model.calculate_error()
>>> round_(model.numvars.abserror)
0.0
>>> model.numvars.relerror
inf
extrapolate_error()None[source]
Estimate the numerical error expected when applying all methods

available based on the results of the current and the last method.

Note that you cannot apply this extrapolation strategy on the first method. If the current method is the first one, method extrapolate_error() returns -999.9:

>>> from hydpy.models.test_v1 import *
>>> parameterstep()
>>> model.numvars.use_relerror = False
>>> model.numvars.abserror = 0.01
>>> model.numvars.last_abserror = 0.1
>>> model.numvars.idx_method = 10
>>> model.extrapolate_error()
>>> from hydpy import round_
>>> round_(model.numvars.extrapolated_abserror)
0.01
>>> model.numvars.extrapolated_relerror
inf
>>> model.numvars.use_relerror = True
>>> model.numvars.relerror = 0.001
>>> model.numvars.last_relerror = 0.01
>>> model.extrapolate_error()
>>> round_(model.numvars.extrapolated_abserror)
0.01
>>> round_(model.numvars.extrapolated_relerror)
0.001
>>> model.numvars.idx_method = 9
>>> model.extrapolate_error()
>>> round_(model.numvars.extrapolated_abserror)
0.001
>>> round_(model.numvars.extrapolated_relerror)
0.0001
>>> model.numvars.relerror = inf
>>> model.extrapolate_error()
>>> round_(model.numvars.extrapolated_relerror)
inf
>>> model.numvars.abserror = 0.0
>>> model.extrapolate_error()
>>> round_(model.numvars.extrapolated_abserror)
0.0
>>> round_(model.numvars.extrapolated_relerror)
0.0
class hydpy.core.modeltools.AideSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.ModelSequences[AideSequence, hydpy.core.variabletools.FastAccess]

Aide sequences of model modeltools.

class hydpy.core.modeltools.FluxSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.OutputSequences[FluxSequence]

Flux sequences of model modeltools.

class hydpy.core.modeltools.InletSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.LinkSequences[InletSequence]

Inlet sequences of model modeltools.

class hydpy.core.modeltools.InputSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.ModelIOSequences[InputSequence, hydpy.core.sequencetools.FastAccessInputSequence]

Input sequences of model modeltools.

class hydpy.core.modeltools.LogSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.ModelSequences[LogSequence, hydpy.core.variabletools.FastAccess]

Log sequences of model modeltools.

class hydpy.core.modeltools.OutletSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.LinkSequences[OutletSequence]

Outlet sequences of model modeltools.

class hydpy.core.modeltools.ReceiverSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.LinkSequences[ReceiverSequence]

Receiver sequences of model modeltools.

class hydpy.core.modeltools.SenderSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.LinkSequences[SenderSequence]

Sender sequences of model modeltools.

class hydpy.core.modeltools.StateSequences(master: hydpy.core.sequencetools.Sequences, cls_fastaccess: Optional[Type[FastAccessType]] = None, cymodel: Optional[hydpy.core.typingtools.CyModelProtocol] = None)

Bases: hydpy.core.sequencetools.OutputSequences[StateSequence]

State sequences of model modeltools.

class hydpy.core.modeltools.Submodel(model: hydpy.core.modeltools.Model)[source]

Bases: object

Base class for implementing “submodels” that serve to deal with (possibly complicated) general mathematical algorithms (e.g. root-finding algorithms) within hydrological model methods.

You might find class Submodel useful when trying to implement algorithms requiring some interaction with the respective model without any Python overhead. See the modules roottools and rootutils as an example, implementing Python interfaces and Cython implementations of a root-finding algorithms, respectively.

METHODS: ClassVar[Tuple[Type[hydpy.core.modeltools.Method], ]]
CYTHONBASECLASS: ClassVar[Type]
PYTHONCLASS: ClassVar[Type]