sequencetools

This module implements tools for defining and handling different kinds of hydrological model sequences (time series).

Module sequencetools implements the following members:


class hydpy.core.sequencetools.FastAccessIOSequence[source]

Bases: FastAccess

Provides fast access to the values of the IOSequence objects of a specific subgroup and supports handling time series data during simulations.

The following details are of relevance for HydPy developers only.

FastAccessIOSequence is applied in Python mode only. When working in Cython mode, it is replaced by model-specific Cython extension classes, which are computationally more efficient. For compatibility with these extension classes, FastAccessIOSequence objects work with dynamically set instance members. For example, suppose there is a sequence named seq1, which is 2-dimensional, then its associated attributes are:

  • seq1 (NDArrayFloat): The actual sequence value(s).

  • _seq1_ndim (int): The number of dimensions.

  • _seq1_length_0 (int): Length in the first dimension.

  • _seq1_length_1 (int): Length in the second dimension.

  • _seq1_ramflag (bool): Handle time series data in RAM?

  • _seq1_array (NDArrayFloat): Time-series data (when handled in RAM).

  • _seq1_diskflag_reading (bool): Read data from a NetCDF file during simulation?

  • _seq1_diskflag_writing (bool): Write data to a NetCDF file during simulation?

  • _seq1_ncarray (NDArrayFloat): An array connected with the data slice of the NetCDF file relevant for seq1.

Note that the respective IOSequences and IOSequence objects initialise, change, and apply these dynamical attributes. To handle them directly is error-prone and thus not recommended.

load_data(idx: int) None[source]

Load the data of certain sequences from the defined sources.

The following flags specify the data source (listed in the order of their priority):

  • inputflag (bool): Take the data from an “input node”.

  • diskflag_reading (bool): Read the data “on the fly” from a NetCDF file during a simulation run.

  • ramflag (bool): Take the data from the time series handled by the series attribute of the respective IOSequence object.

If, for example, diskflag_reading and ramflag are both activated, load_data() prefers the data available within the NetCDF file.

save_data(idx: int) None[source]

Save the data of certain sequences to the defined sources.

The following flags the data targets:

  • diskflag_writing (bool): Write the data “on the fly” to a NetCDF file during a simulation run.

  • ramflag (bool): Give the data to the time series handled by the series attribute of the respective IOSequence object.

It is possible to write data to a NetCDF file and pass it to series simultaneously.

class hydpy.core.sequencetools.FastAccessInputSequence[source]

Bases: FastAccessIOSequence

FastAccessIOSequence subclass specialised for input sequences.

set_pointerinput(name: str, pdouble: PDouble) None[source]

Use the given PDouble object as the pointer for the 0-dimensional InputSequence object with the given name.

class hydpy.core.sequencetools.FastAccessOutputSequence[source]

Bases: FastAccessIOSequence

FastAccessIOSequence subclass specialised for output sequences.

set_pointeroutput(name: str, pdouble: PDouble) None[source]

Use the given PDouble object as the pointer for the 0-dimensional OutputSequence object with the given name.

update_outputs() None[source]

Pass the data of all sequences with an activated output flag.

class hydpy.core.sequencetools.FastAccessLinkSequence[source]

Bases: FastAccess

FastAccessIOSequence subclass specialised for link sequences.

alloc(name: str, length: int) None[source]

Allocate enough memory for the given vector length of the LinkSequence object with the given name.

Cython extension classes need to define alloc() if the model handles at least one 1-dimensional LinkSequence subclass.

dealloc(name: str) None[source]

Free the previously allocated memory of the LinkSequence object with the given name.

Cython extension classes need to define dealloc() if the model handles at least one 1-dimensional LinkSequence subclass.

set_pointer0d(name: str, value: Double) None[source]

Define a pointer referencing the given Double object for the 0-dimensional LinkSequence object with the given name.

Cython extension classes need to define set_pointer0d() if the model handles at least one 0-dimensional LinkSequence subclasses.

set_pointer1d(name: str, value: Double, idx: int) None[source]

Define a pointer referencing the given Double object for the 1-dimensional LinkSequence object with the given name.

The given index defines the vector position of the defined pointer.

Cython extension classes need to define set_pointer1d() if the model handles at least one 1-dimensional LinkSequence subclasses.

get_value(name: str) float | ndarray[Any, dtype[float64]][source]

Return the actual value(s) referenced by the pointer(s) of the LinkSequence object with the given name.

set_value(name: str, value: float | Iterable[float]) None[source]

Set the actual value(s) referenced by the pointer(s) of the LinkSequence object with the given name.

class hydpy.core.sequencetools.FastAccessNodeSequence[source]

Bases: FastAccessIOSequence

FastAccessIOSequence subclass specialised for Node objects.

In contrast to other FastAccessIOSequence subclasses, FastAccessNodeSequence only needs to handle a fixed number of sequences, Sim and Obs. It thus can define the related attributes explicitly.

sim: Double
obs: Double
load_simdata(idx: int) None[source]

Load the next sim sequence value from a NetCDF file or, with second priority, from the series attribute of the current Sim object.

save_simdata(idx: int) None[source]

Save the next sim sequence value to a NetCDF file and/or to the series attribute of the Sim object.

load_obsdata(idx: int) None[source]

Load the next sim sequence value from a NetCDF file or, with second priority, from the series attribute of the Obs object.

save_obsdata(idx: int) None[source]

Save the next sim sequence value to a NetCDF file and/or to the series attribute of the Obs object.

load_data(idx: int) None[source]

Call both method load_simdata() and method load_obsdata().

save_data(idx: int) None[source]

Call both method save_simdata() and method save_obsdata().

reset(idx: int = 0) None[source]

Set the actual value of the simulation sequence to zero.

fill_obsdata(idx: int = 0) None[source]

Use the current sim value for the current obs value if obs is nan.

reset_obsdata(idx: int = 0) None[source]

Reset the current obs value to nan if modified beforehand by method fill_obsdata().

class hydpy.core.sequencetools.InfoArray(array: ndarray[Any, dtype[float64]], aggregation: Literal['unmodified', 'mean'] | None = None)[source]

Bases: ndarray[Any, dtype[float64]]

numpy ndarray subclass with an additional attribute describing the (potential) aggregation of the handled data.

>>> from hydpy.core.sequencetools import InfoArray
>>> array = InfoArray([1.0, 2.0], aggregation="mean")
>>> array
InfoArray([1., 2.])
>>> array.aggregation
'mean'
>>> subarray = array[:1]
>>> subarray
InfoArray([1.])
>>> subarray.aggregation
'mean'
aggregation: Literal['unmodified', 'mean'] | None
class hydpy.core.sequencetools.StandardInputNames(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: StrEnum

Standard names for the InputSequence subclasses of the various models.

One can use these names instead of the model-specific sequence names for reading input time series from or to files. For further information, see the introductory documentation on class HydPy.

The suffix “_HRU” refers to 1-dimensional sequences for which the different entries correspond to different spatial units (typically hydrological response units).

AIR_TEMPERATURE = 'air_temperature'

Air temperature 2 m above the ground [°C].

ALBEDO_HRU = 'albedo_hru'

Surface albedo [-].

ARTIFICIAL_GROUNDWATER_RECHARGE = 'artificial_groundwater_recharge'

Artificial/additional groundwater recharge [mm/T].

ARTIFICIAL_SURFACE_WATER_SUPPLY = 'artificial_surface_water_supply'

Artificial/additional surface water supply [mm/T].

ATMOSPHERIC_PRESSURE = 'atmospheric_pressure'

Atmospheric pressure [hPa].

CAPILLARY_RISE = 'capillary_rise'

Capillary rise [mm/T].

CLEAR_SKY_SOLAR_RADIATION = 'clear_sky_solar_radiation'

Clear sky solar radiation [W/m²].

EVAPOTRANSPIRATION = 'evapotranspiration'

Actual evapotranspiration [mm/T].

GLOBAL_RADIATION = 'global_radiation'

Global radiation [W/m²].

INTERCEPTED_WATER_HRU = 'intercepted_water_hru'

Amount of intercepted water [mm].

NORMAL_AIR_TEMPERATURE = 'normal_air_temperature'

Normal air temperature 2 m above the ground [°C].

NORMAL_EVAPOTRANSPIRATION = 'normal_evapotranspiration'

Normal evapotranspiration [mm/T].

POSSIBLE_SUNSHINE_DURATION = 'possible_sunshine_duration'

Possible sunshine duration [h].

POTENTIAL_EVAPOTRANSPIRATION = 'potential_evapotranspiration'

Potential evapotranspiration [mm/T].

PRECIPITATION = 'precipitation'

Precipitation [mm/T].

RELATIVE_HUMIDITY = 'relative_humidity'

Relative humidity [%].

SNOW_COVER_DEGREE_CANOPY_HRU = 'snow_cover_degree_canopy_hru'

Snow cover degree in the canopies of tree-like vegetation [-].

SNOW_COVER_DEGREE_HRU = 'snow_cover_degree_hru'

Snow cover degree [-].

SOIL_WATER_HRU = 'soil_water_hru'

Amount of soil water [mm].

SUNSHINE_DURATION = 'sunshine_duration'

Sunshine duration [h].

WIND_SPEED = 'wind_speed'

Wind speed [m/s].

class hydpy.core.sequencetools.Sequences(model: modeltools.Model, *, cls_inlets: type[InletSequences] | None = None, cls_receivers: type[ReceiverSequences] | None = None, cls_inputs: type[InputSequences] | None = None, cls_factors: type[FactorSequences] | None = None, cls_fluxes: type[FluxSequences] | None = None, cls_states: type[StateSequences] | None = None, cls_logs: type[LogSequences] | None = None, cls_aides: type[AideSequences] | None = None, cls_outlets: type[OutletSequences] | None = None, cls_senders: type[SenderSequences] | None = None, cymodel: CyModelProtocol | None = None, cythonmodule: types.ModuleType | None = None)[source]

Bases: object

Base class for handling all sequences of a specific model.

Sequences objects handle nine sequence subgroups as attributes such as the inlets and the receivers subsequences:

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> sequences = hp.elements.land_dill_assl.model.sequences
>>> bool(sequences.inlets)
False
>>> bool(sequences.fluxes)
True

Iteration makes only the non-empty subgroups available that handle Sequence_ objects:

>>> for subseqs in sequences:
...     print(subseqs.name)
inputs
factors
fluxes
states
aides
outlets
>>> len(sequences)
6

Keyword access provides a type-safe way to query a subgroup via a string:

>>> type(sequences["inputs"]).__name__
'InputSequences'
>>> type(sequences["wrong"])
Traceback (most recent call last):
...
TypeError: There is no sequence subgroup named `wrong`.
>>> sequences["model"]
Traceback (most recent call last):
...
TypeError: Attribute `model` is of type `Model`, which is not a subtype of class `SubSequences`.

Class Sequences provides some methods related to reading and writing time series data, which (directly or indirectly) call the corresponding methods of the handled IOSequence objects. In most cases, users should prefer to use the related methods of class HydPy, but using the ones of class Sequences can be more convenient when analysing a specific model in-depth.

To introduce these methods, we first change two IO-related settings:

>>> from hydpy import round_
>>> pub.options.checkseries = False
>>> pub.sequencemanager.overwrite = True

Method prepare_series() can both enable and disable the handling of time series in rapid access memory (RAM), and both enable and disable the reading of input data from NetCDF files and the writing of NetCDF files “on the fly” during simulation runs:

>>> from hydpy import attrready
>>> sequences.prepare_series(allocate_ram=False, jit=False)
>>> sequences.inputs.t.ramflag
False
>>> attrready(sequences.inputs.t, "series")
False
>>> sequences.inputs.t.diskflag
False
>>> sequences.inputs.t.diskflag_reading
False
>>> sequences.states.sm.diskflag_writing
False
>>> sequences.prepare_series()
>>> sequences.inputs.t.ramflag
True
>>> attrready(sequences.inputs.t, "series")
True
>>> sequences.inputs.t.diskflag
False
>>> sequences.inputs.t.diskflag_reading
False
>>> sequences.states.sm.diskflag_writing
False
>>> sequences.prepare_series(allocate_ram=False, jit=True)
>>> sequences.inputs.t.ramflag
False
>>> attrready(sequences.inputs.t, "series")
False
>>> sequences.inputs.t.diskflag
True
>>> sequences.inputs.t.diskflag_reading
True
>>> sequences.states.sm.diskflag_writing
True

After applying prepare_series(), you can use the methods load_series() and save_series() to read or write the time series of the relevant InputSequence, FactorSequence, FluxSequence, and StateSequence object, as the following technical test suggests. The documentation on class IOSequence explains the underlying functionalities of in more detail.

>>> from unittest.mock import patch
>>> template = "hydpy.core.sequencetools.%s.load_series"
>>> with patch(template % "InputSequences") as inputs, patch(template % "FactorSequences") as factors, patch(template % "FluxSequences") as fluxes, patch(template % "StateSequences") as states:
...     sequences.load_series()
...     inputs.assert_called_with()
...     factors.assert_called_with()
...     fluxes.assert_called_with()
...     states.assert_called_with()
>>> template = "hydpy.core.sequencetools.%s.save_series"
>>> with patch(template % "InputSequences") as inputs, patch(template % "FactorSequences") as factors, patch(template % "FluxSequences") as fluxes, patch(template % "StateSequences") as states:
...     sequences.save_series()
...     inputs.assert_called_with()
...     factors.assert_called_with()
...     fluxes.assert_called_with()
...     states.assert_called_with()
model: modeltools.Model
inlets: InletSequences
receivers: ReceiverSequences
inputs: InputSequences
factors: FactorSequences
fluxes: FluxSequences
states: StateSequences
logs: LogSequences
aides: AideSequences
outlets: OutletSequences
senders: SenderSequences
property iosubsequences: Iterator[InputSequences | FactorSequences | FluxSequences | StateSequences]

Yield all relevant IOSequences objects handled by the current Sequences object.

The currently available IO-subgroups are inputs, factors, fluxes, and states.

>>> from hydpy import prepare_model
>>> model = prepare_model("hland_96")
>>> for subseqs in model.sequences.iosubsequences:
...     print(subseqs.name)
inputs
factors
fluxes
states

However, not all models implement sequences for all these subgroups. Therefore, the iosubsequences property only yields those subgroups which are non-empty:

>>> model = prepare_model("musk_classic")
>>> for subseqs in model.sequences.iosubsequences:
...     print(subseqs.name)
fluxes
states
prepare_series(allocate_ram: bool = True, jit: bool = False) None[source]

Call method prepare_series() of attribute inputs with read_jit=jit and of attributes factors, fluxes, and states with write_jit=jit.

load_series()[source]

Call method load_series() of all handled IOSequences objects.

save_series()[source]

Call method save_series() of all handled IOSequences objects.

load_data(idx: int) None[source]

Call method load_data() of the handled InputSequences object.

save_data(idx: int) None[source]

Call method save_data() of the handled InputSequences, FactorSequences, FluxSequences, and StateSequences objects.

update_outputs() None[source]

Call the method update_outputs() of the subattributes factors, fluxes, and states.

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

reset() None[source]

Call method reset() of all handled ConditionSequence objects.

property conditionsequences: Iterator[ConditionSequence]

Generator object yielding all conditions (StateSequence and LogSequence objects).

property conditions: dict[str, dict[str, float | ndarray[Any, dtype[float64]]]]

A nested dictionary that contains the values of all condition sequences of a single model instance.

See the documentation on property conditions for further information.

trim_conditions() None[source]

Call method trim() of each handled ConditionSequence.

trim_conditions() is just a convenience function for calling method trim() of all StateSequence and LogSequence objects returned by property conditionsequences. We demonstrate its functionality by preparing an instance of application model lland_dd, using its available default values, and defining out-of-bound values of the soil moisture state sequence BoWa:

>>> from hydpy import prepare_model, pub
>>> pub.timegrids = "2000-01-01", "2000-01-10", "1d"
>>> with pub.options.usedefaultvalues(True):
...     model = prepare_model("lland_dd")
...     model.parameters.control.nhru(2)
>>> model.sequences.states.bowa = -100.0
>>> model.sequences.trim_conditions()
>>> model.sequences.states.bowa
bowa(0.0, 0.0)
class hydpy.core.sequencetools.SubSequences(master: TypeGroup_co, cls_fastaccess: type[TypeFastAccess_co] | None = None)[source]

Bases: SubVariables[TypeSequences, TypeSequence_co, TypeFastAccess_co]

Base class for handling subgroups of sequences.

Each SubSequences object has a fastaccess attribute, which is an instance of (a subclass of) class FastAccess when working in pure Python mode:

>>> from hydpy import classname, Node, prepare_model, pub
>>> with pub.options.usecython(False):
...     model = prepare_model("lland_dd")
>>> classname(model.sequences.logs.fastaccess)
'FastAccess'
>>> classname(model.sequences.inputs.fastaccess)
'FastAccessInputSequence'
>>> from hydpy.core.sequencetools import FastAccessNodeSequence
>>> with pub.options.usecython(False):
...     node = Node("test1")
>>> isinstance(node.sequences.fastaccess, FastAccessNodeSequence)
True

When working in Cython mode (the default and much faster than the pure Python mode), fastaccess is an object of the Cython extension class FastAccessNodeSequence of module sequenceutils or a Cython extension class specialised for the respective model and sequence group:

>>> with pub.options.usecython(True):
...     model = prepare_model("lland_dd")
>>> classname(model.sequences.inputs.fastaccess)
'InputSequences'
>>> from hydpy.cythons.sequenceutils import FastAccessNodeSequence
>>> with pub.options.usecython(True):
...     node = Node("test2")
>>> isinstance(Node("test2").sequences.fastaccess, FastAccessNodeSequence)
True

See the documentation of similar class SubParameters for further information. However, note the difference that model developers should not subclass SubSequences directly but specialised subclasses like FluxSequences or StateSequences instead.

property name: str

The class name in lowercase letters omitting the last eight characters (“equences”).

>>> from hydpy.core.sequencetools import StateSequences
>>> class StateSequences(StateSequences):
...     CLASSES = ()
>>> StateSequences(None).name
'states'
class hydpy.core.sequencetools.ModelSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)[source]

Bases: SubSequences[Sequences, TypeModelSequence_co, TypeFastAccess_co]

Base class for handling model-related subgroups of sequences.

class hydpy.core.sequencetools.SeriesMode(ramflag: bool, diskflag_reading: bool, diskflag_writing: bool)[source]

Bases: object

The type of property seriesmode of class IOSequence.

ramflag: bool

Corresponds to property ramflag of class IOSequence.

diskflag_reading: bool

Corresponds to property diskflag_reading of class IOSequence.

diskflag_writing: bool

Corresponds to property diskflag_writing of class IOSequence.

class hydpy.core.sequencetools.IOSequences(master: TypeGroup_co, cls_fastaccess: type[TypeFastAccess_co] | None = None)[source]

Bases: SubSequences[TypeSequences, TypeIOSequence_co, TypeFastAccessIOSequence_co]

Subclass of SubSequences, specialised for handling IOSequence objects.

prepare_series(allocate_ram: bool = True, read_jit: bool = False, write_jit: bool = False) None[source]

Call method prepare_series() of all handled IOSequence objects.

load_series() None[source]

Call method load_series() of all handled IOSequence objects with an activated ramflag.

save_series() None[source]

Call method save_series() of all handled IOSequence objects with an activated ramflag.

class hydpy.core.sequencetools.ModelIOSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)[source]

Bases: IOSequences[Sequences, TypeModelIOSequence_co, TypeFastAccessIOSequence_co], ModelSequences[TypeModelIOSequence_co, TypeFastAccessIOSequence_co]

Base class for handling model-related subgroups of IOSequence objects.

load_data(idx: int) None[source]

Call method load_data() of the FastAccessIOSequence object handled as attribute fastaccess.

save_data(idx: int) None[source]

Call method save_data() of the FastAccessIOSequence object handled as attribute fastaccess.

class hydpy.core.sequencetools.InputSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)[source]

Bases: ModelIOSequences[InputSequence, FastAccessInputSequence]

Base class for handling InputSequence objects.

class hydpy.core.sequencetools.OutputSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)[source]

Bases: ModelIOSequences[TypeOutputSequence_co, FastAccessOutputSequence]

Base class for handling OutputSequence objects.

update_outputs() None[source]

Call method update_outputs() of the FastAccessOutputSequence object handled as attribute fastaccess.

property numericsequences: Iterator[TypeOutputSequence_co]

Iterator for “numerical” sequences.

“numerical” means that the NUMERIC class attribute of the actual sequence is True:

>>> from hydpy import prepare_model
>>> model = prepare_model("dam_v001")
>>> len(model.sequences.fluxes)
16
>>> for seq in model.sequences.fluxes.numericsequences:
...     print(seq)
adjustedprecipitation(nan)
actualevaporation(nan)
inflow(nan)
actualrelease(nan)
flooddischarge(nan)
outflow(nan)
class hydpy.core.sequencetools.FactorSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)[source]

Bases: OutputSequences[FactorSequence]

Base class for handling FactorSequence objects.

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

Bases: OutputSequences[FluxSequence]

Base class for handling FluxSequence objects.

property name: str

Always return the string “fluxes”.

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

Bases: OutputSequences[StateSequence]

Base class for handling StateSequence objects.

new2old() None[source]

Call method new2old() of all handled StateSequence objects.

reset() None[source]

Call method reset() of all handled StateSequence objects.

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

Bases: ModelSequences[LogSequence, FastAccess]

Base class for handling LogSequence objects.

reset() None[source]

Call method reset() of all handled LogSequence objects.

class hydpy.core.sequencetools.AideSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)[source]

Bases: ModelSequences[AideSequence, FastAccess]

Base class for handling AideSequence objects.

class hydpy.core.sequencetools.LinkSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)[source]

Bases: ModelSequences[TypeLinkSequence_co, FastAccessLinkSequence]

Base class for handling LinkSequence objects.

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

Bases: LinkSequences[InletSequence]

Base class for handling “inlet” LinkSequence objects.

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

Bases: LinkSequences[OutletSequence]

Base class for handling “outlet” LinkSequence objects.

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

Bases: LinkSequences[ReceiverSequence]

Base class for handling “receiver” LinkSequence objects.

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

Bases: LinkSequences[SenderSequence]

Base class for handling “sender” LinkSequence objects.

class hydpy.core.sequencetools.Sequence_(subvars: SubVariables)[source]

Bases: Variable

Base class for defining different kinds of sequences.

Note that model developers should not derive their model-specific sequence classes from Sequence_ directly but from the “final” subclasses provided in module sequencetools (e.g. FluxSequence).

From the model developer perspective and especially from the user perspective, Sequence_ is only a small extension of its base class Variable. One relevant extension is that (only the) 0-dimensional sequence objects come with a predefined shape:

>>> from hydpy import prepare_model
>>> model = prepare_model("lland_dd")
>>> model.sequences.fluxes.qa.shape
()
>>> nkor = model.sequences.fluxes.nkor
>>> nkor.shape
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Shape information for variable `nkor` can only be retrieved after it has been defined.

For consistency with the usage of Parameter subclasses, Sequence_ objects are also “callable” for setting their values (but in a much less and flexible manner):

>>> nkor.shape = 3
>>> nkor(2.0)
>>> nkor
nkor(2.0, 2.0, 2.0)

Under the hood, class Sequence_ also prepares some attributes of its FastAccess object, used for performing the actual simulation calculations. Framework developers should note that the respective fastaccess attributes contain both the name of the sequence and the name of the original attribute in lowercase letters. We take NDIM as an example:

>>> nkor.fastaccess._nkor_ndim
1

Some of these attributes require updating in some situations. For example, other sequences than AideSequence objects require a “length” attribute, which needs updating each time the sequence’s shape changes:

>>> nkor.fastaccess._nkor_length
3
TYPE

alias of float

INIT: float = 0.0
NUMERIC: bool
strict_valuehandling: bool = False
property initinfo: tuple[float | Double, bool]

A tuple containing the initial value and True or a missing value and False, depending on the actual Sequence_ subclass and the actual value of option usedefaultvalues.

In the following, we do not explain property initinfo itself but show how it affects initialising new Sequence_ objects. Therefore, let us define a sequence test class and prepare a function for initialising it and connecting the resulting instance to a ModelSequences object:

>>> from hydpy.core.sequencetools import Sequence_, ModelSequences
>>> from hydpy.core.variabletools import FastAccess
>>> class Test(Sequence_):
...     NDIM = 0
...     _CLS_FASTACCESS_PYTHON = FastAccess
>>> class SubGroup(ModelSequences):
...     CLASSES = (Test,)
...     _CLS_FASTACCESS_PYTHON = FastAccess
>>> def prepare():
...     subseqs = SubGroup(None)
...     test = Test(subseqs)
...     test.__hydpy__connect_variable2subgroup__()
...     return test

By default, making use of the INIT attribute is disabled:

>>> prepare()
test(nan)

Enable it by setting usedefaultvalues to True:

>>> from hydpy import pub
>>> with pub.options.usedefaultvalues(True):
...     prepare()
test(0.0)

Attribute INIT of class Sequence_ comes with the value 0.0 by default, which should be reasonable for most Sequence_ subclasses. However, subclasses can define other values. Most importantly, note the possibility to set INIT to None for sequences that do not allow specifying a reasonabe initial value for all possible situations:

>>> Test.INIT = None
>>> prepare()
test(nan)
>>> with pub.options.usedefaultvalues(True):
...     prepare()
test(nan)
name: str = 'sequence_'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.IOSequence(subvars: SubVariables)[source]

Bases: Sequence_

Base class for sequences with input/output functionalities.

The documentation on modules filetools and netcdftools in some detail explains how to read and write time series files. However, due to efficiency, reading and writing time series files are disabled by default. Therefore, you must first prepare the series attribute of the relevant IOSequence objects. Typically, you call methods like prepare_inputseries() of class HydPy. Here, we instead use the related features of the IOSequence class itself.

We use the HydPy-H-Lahn example project and focus on the input, factor, fluxes, and state sequences:

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> inputs = hp.elements.land_lahn_marb.model.sequences.inputs
>>> factors = hp.elements.land_lahn_marb.model.sequences.factors
>>> fluxes = hp.elements.land_lahn_marb.model.sequences.fluxes
>>> states = hp.elements.land_lahn_marb.model.sequences.states

Each IOSequence object comes four flags, answering the following questions:

For input sequences as T, it is common to store their time series data (required for any simulation run) in RAM, which is much faster than (repeatedly) reading data “on the fly” and should be preferred, as long as limited available RAM is not an issue. For convenience, function prepare_full_example_2() prepared T (and the other input sequences) accordingly:

>>> inputs.t.ramflag
True
>>> inputs.t.diskflag_reading
False
>>> inputs.t.diskflag_writing
False
>>> inputs.t.diskflag
False
>>> from hydpy import round_
>>> round_(inputs.t.series)
-0.7, -1.5, -4.6, -8.2

prepare_full_example_2() also activated the ramflag of all factor, flux, and state sequences, which is unnecessary to perform a successful simulation. However, it is required to directly access the complete time series of simulated values afterwards (otherwise, only the last computed value(s) were available in RAM after a simulation run):

>>> factors.tc.ramflag
True
>>> factors.tc.diskflag
False
>>> round_(factors.tc.series[:, 0])
nan, nan, nan, nan

Use prepare_series() to force a sequence to handle time series data in RAM or to read or write it on the fly. We now activate the reading functionality of input sequence T (while still keeping its time series in RAM, which we set to zero beforehand) and the writing feature of the factor sequences ContriArea and TC (without handling their data in RAM) and the writing feature of the state sequences SM and SP (while handling their data in RAM simultaneously):

>>> inputs.t.series = 0.0
>>> inputs.t.prepare_series(allocate_ram=True, read_jit=True)
>>> factors.contriarea.prepare_series(allocate_ram=False, write_jit=True)
>>> factors.tc.prepare_series(allocate_ram=False, write_jit=True)
>>> states.sm.prepare_series(allocate_ram=True, write_jit=True)
>>> states.sp.prepare_series(allocate_ram=True, write_jit=True)

Use the properties ramflag, diskflag_reading, diskflag_writing, and diskflag for querying the current configuration of individual IOSequence objects:

>>> inputs.t.ramflag
True
>>> inputs.t.diskflag_reading
True
>>> inputs.t.diskflag_writing
False
>>> inputs.t.diskflag
True
>>> round_(inputs.t.series)
0.0, 0.0, 0.0, 0.0
>>> factors.contriarea.ramflag
False
>>> factors.contriarea.diskflag_reading
False
>>> factors.contriarea.diskflag_writing
True
>>> factors.contriarea.diskflag
True
>>> factors.contriarea.series
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Sequence `contriarea` of element `land_lahn_marb` is not requested to make any time series data available.
>>> states.sm.ramflag
True
>>> states.sm.diskflag_reading
False
>>> states.sm.diskflag_writing
True
>>> states.sm.diskflag
True
>>> round_(states.sm.series[:, 0])
nan, nan, nan, nan

Now we perform a simulation run. Note that we need to change the current working directory to the iotesting directory temporarily (by using class TestIO) because the relevant NetCDF files are now read and written on the fly:

>>> with TestIO():
...     hp.simulate()

After the simulation run, the read (T) and calculated (SM and SP) time series of the sequences with an activated ramflag are directly available:

>>> round_(inputs.t.series)
-0.7, -1.5, -4.6, -8.2
>>> round_(states.sm.series[:, 0])
99.1369, 99.01204, 98.93674, 98.91913
>>> round_(states.sp.series[:, 0, 0])
0.0, 0.0, 0.0, 0.0

To inspect the time series of ContriArea and TC, you must first activate their ramflag and then load their data manually with method load_series(). The latter requires some additional configuration effort (see the documentation on module netcdftools for further information):

>>> factors.contriarea.prepare_series()
>>> factors.tc.prepare_series()
>>> pub.sequencemanager.filetype = "nc"
>>> with TestIO():
...     pub.sequencemanager.open_netcdfreader()
...     factors.contriarea.load_series()
...     factors.tc.load_series()
...     pub.sequencemanager.close_netcdfreader()
>>> round_(factors.contriarea.series)
0.431311, 0.430524, 0.430049, 0.429938
>>> round_(factors.tc.series[:, 0])
0.453086, -0.346914, -3.446914, -7.046914

We also load time series of SM and SP to demonstrate that the data written to the respective NetCDF files are identical with the data directly stored in RAM:

>>> with TestIO():
...     pub.sequencemanager.open_netcdfreader()
...     states.sm.load_series()
...     states.sp.load_series()
...     pub.sequencemanager.close_netcdfreader()
>>> round_(states.sm.series[:, 0])
99.1369, 99.01204, 98.93674, 98.91913
>>> round_(states.sp.series[:, 0, 0])
0.0, 0.0, 0.0, 0.0

Writing the time series of input sequences on the fly is supported but not simultaneously with reading them (at best, one would overwrite the same file with the same data; at worst, one could corrupt it):

>>> inputs.t.prepare_series(read_jit=True, write_jit=True)
Traceback (most recent call last):
...
ValueError: Reading from and writing into the same NetCDF file "just in time" during a simulation run is not supported but tried for sequence `t` of element `land_lahn_marb`.

For simplifying the following examples, we now handle all model time series in RAM:

>>> pub.sequencemanager.filetype = "asc"
>>> hp.prepare_modelseries()
>>> with TestIO():
...     hp.load_inputseries()

You cannot only access the time series data of individual IOSequence objects, but you can also modify it. See, for example, the simulated time series for flux sequence PC (adjusted precipitation), which is zero because the values of input sequence P (given precipitation) are also zero:

>>> round_(fluxes.pc.series[:, 0])
0.0, 0.105611, 0.0, 0.0

We can assign different values to attribute series of sequence P, perform a new simulation run, and see that the newly calculated time series of sequence PC reflects our data modification:

>>> inputs.p.series = 10.0
>>> hp.simulate()
>>> round_(fluxes.pc.series[:, 0])
9.154557, 10.561131, 10.665633, 10.665633

Another convenience property is seriesshape, which combines the length of the simulation period with the shape of the individual IOSequence object:

>>> inputs.p.seriesshape
(4,)
>>> fluxes.pc.seriesshape
(4, 13)

Note that resetting the shape of an IOSequence object does not change how it handles its internal time series data but results in a loss of current information:

>>> factors.tc.seriesshape
(4, 13)
>>> factors.fastaccess._tc_length
13
>>> round_(factors.tc.series[:, 0], 1)
0.5, -0.3, -3.4, -7.0
>>> factors.tc.shape = 2,
>>> factors.tc.seriesshape
(4, 2)
>>> factors.fastaccess._tc_length
2
>>> round_(factors.tc.series[:, 0])
nan, nan, nan, nan

Resetting the shape of IOSequence objects with a deactivated ramflag data works likewise:

>>> fluxes.pc.prepare_series(allocate_ram=False)
>>> fluxes.pc.seriesshape
(4, 13)
>>> fluxes.fastaccess._pc_length
13
>>> fluxes.pc.series
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Sequence `pc` of element `land_lahn_marb` is not requested to make any time series data available.
>>> fluxes.pc.shape = (2,)
>>> fluxes.pc.seriesshape
(4, 2)
>>> fluxes.fastaccess._pc_length
2
>>> fluxes.pc.series = 1.0
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Sequence `pc` of element `land_lahn_marb` is not requested to make any time series data available.
filetype

“Ending of the time series data file.

Usually, IOSequence objects query the current file type from the SequenceManager object available in the global pub module:

>>> from hydpy import pub
>>> from hydpy.core.filetools import SequenceManager
>>> pub.sequencemanager = SequenceManager()
>>> from hydpy.core.sequencetools import InputSequence
>>> inputsequence = InputSequence(None)
>>> inputsequence.filetype
'asc'

Alternatively, you can specify the file type for each IOSequence object individually:

>>> inputsequence.filetype = "npy"
>>> inputsequence.filetype
'npy'
>>> inputsequence.filetype = "nc"
>>> inputsequence.filetype
'nc'

Use the del statement to reset the object-specific setting:

>>> del inputsequence.filetype
>>> inputsequence.filetype
'asc'

If neither a specific definition nor a SequenceManager object is available, property filetype raises the following error:

>>> del pub.sequencemanager
>>> inputsequence.filetype
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Sequence `inputsequence` does not know its file type.  Either set it manually or prepare `pub.sequencemanager` correctly.
aggregation

Type of aggregation for writing the time series to a data file.

Usually, IOSequence objects query the current aggregation mode from the SequenceManager object available in the global pub module:

>>> from hydpy import pub
>>> from hydpy.core.filetools import SequenceManager
>>> pub.sequencemanager = SequenceManager()
>>> from hydpy.core.sequencetools import FluxSequence
>>> fluxsequence = FluxSequence(None)
>>> fluxsequence.aggregation
'none'

Alternatively, you can specify the aggregation for each IOSequence object individually:

>>> fluxsequence.aggregation = "mean"
>>> fluxsequence.aggregation
'mean'

Use the del statement to reset the object-specific setting:

>>> del fluxsequence.aggregation
>>> fluxsequence.aggregation
'none'

If neither a specific definition nor a SequenceManager object is available, property aggregation raises the following error:

>>> del pub.sequencemanager
>>> fluxsequence.aggregation
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Sequence `fluxsequence` does not know its aggregation mode.  Either set it manually or prepare `pub.sequencemanager` correctly.
overwrite

True/False flag indicating if overwriting an existing data file is allowed or not.

Usually, IOSequence objects query the current overwrite flag from the SequenceManager object available in the global pub module:

>>> from hydpy import pub
>>> from hydpy.core.filetools import SequenceManager
>>> pub.sequencemanager = SequenceManager()
>>> from hydpy.core.sequencetools import FluxSequence
>>> fluxsequence = FluxSequence(None)
>>> fluxsequence.overwrite
0

Alternatively, you can specify the overwrite flag for each IOSequence object individually:

>>> fluxsequence.overwrite = True
>>> fluxsequence.overwrite
1

Use the del statement to reset the object-specific setting:

>>> del fluxsequence.overwrite
>>> fluxsequence.overwrite
0

If neither a specific definition nor a SequenceManager object is available, property overwrite raises the following error:

>>> del pub.sequencemanager
>>> fluxsequence.overwrite
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Sequence `fluxsequence` does not know its overwrite flag.  Either set it manually or prepare `pub.sequencemanager` correctly.
filename

The filename of the relevant time series file.

By default, the filenames of file types that store time series of single sequence instance consists of descr_device, descr_sequence, and filetype:

>>> from hydpy.core.sequencetools import StateSequence
>>> class S(StateSequence):
...     descr_device = "device"
...     descr_sequence = "group_sequence"
...     filetype = "npy"
...     aggregation = "none"
>>> s = S(None)
>>> s.filename
'device_group_sequence.npy'

For file types that store time series of multiple sequence instances, descr_device is omitted:

>>> s.filetype = "nc"
>>> s.filename
'group_sequence.nc'

When dealing with aggregated time series, the aggregation mode is suffixed:

>>> s.aggregation = "mean"
>>> s.filename
'group_sequence_mean.nc'
>>> s.filetype = "asc"
>>> s.filename
'device_group_sequence_mean.asc'
dirpath

The absolute path to the time series directory.

As long as it is not overwritten, dirpath is identical to the attribute currentpath of the SequenceManager object available in module pub:

>>> from hydpy import pub, repr_
>>> from hydpy.core.filetools import SequenceManager
>>> class SM(SequenceManager):
...     currentpath = "temp"
>>> pub.sequencemanager = SM()
>>> from hydpy.core.sequencetools import StateSequence
>>> repr_(StateSequence(None).dirpath)
'temp'
filepath

The absolute path to the time series file.

The path pointing to the file consists of dirpath and filename:

>>> from hydpy.core.sequencetools import StateSequence
>>> seq = StateSequence(None)
>>> seq.dirpath = "path"
>>> seq.filename = "file.npy"
>>> from hydpy import repr_
>>> repr_(seq.filepath)
'path/file.npy'
update_fastaccess() None[source]

Update the FastAccessIOSequence object handled by the actual IOSequence object.

Users do not need to apply the method update_fastaccess() directly. The following information should be relevant for framework developers only.

The main documentation on class Sequence_ mentions that the FastAccessIOSequence attribute handles some information about its sequences, but it needs to be kept up-to-date by the sequences themselves. This updating is the task of method update_fastaccess(), being called by some other methods class IOSequence call. We show this via the hidden attribute length, which is 0 after initialisation, and automatically set to another value when assigning it to property shape of IOSequence subclasses as NKor:

>>> from hydpy import prepare_model
>>> model = prepare_model("lland_dd")
>>> nkor = model.sequences.fluxes.nkor
>>> nkor.fastaccess._nkor_length
0
>>> nkor.shape = (3,)
>>> nkor.fastaccess._nkor_length
3
connect_netcdf(ncarray: ndarray[Any, dtype[float64]]) None[source]

Connect the current IOSequence object to the given buffer array for reading from or writing to a NetCDF file on the fly during a simulation run.

prepare_series(allocate_ram: bool | None = True, read_jit: bool | None = False, write_jit: bool | None = False) None[source]

Define how to handle the time series data of the current IOSequence object.

See the main documentation on class IOSequence for general information on method prepare_series(). Here, we only discuss the special case of passing None to it to preserve predefined settings.

When leaving out certain arguments, prepare_series() takes their boolean defaults. That means subsequent calls overwrite previous ones:

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> t = hp.elements.land_lahn_marb.model.sequences.inputs.t
>>> t.prepare_series(allocate_ram=False, read_jit=True)
>>> t.ramflag, t.diskflag_reading, t.diskflag_writing
(False, True, False)
>>> t.prepare_series(write_jit=True)
>>> t.ramflag, t.diskflag_reading, t.diskflag_writing
(True, False, True)

If you want to change one setting without modifying the others, pass None to the latter:

>>> t.prepare_series(allocate_ram=False, read_jit=None, write_jit=None)
>>> t.ramflag, t.diskflag_reading, t.diskflag_writing
(False, False, True)
>>> t.prepare_series(allocate_ram=None, read_jit=True, write_jit=False)
>>> t.ramflag, t.diskflag_reading, t.diskflag_writing
(False, True, False)
>>> t.prepare_series(allocate_ram=None, read_jit=None, write_jit=None)
>>> t.ramflag, t.diskflag_reading, t.diskflag_writing
(False, True, False)

The check for configurations attempting to both read and write “just in time” takes predefined flags into account:

>>> t.prepare_series(read_jit=None, write_jit=True)
Traceback (most recent call last):
...
ValueError: Reading from and writing into the same NetCDF file "just in time" during a simulation run is not supported but tried for sequence `t` of element `land_lahn_marb`.
>>> t.prepare_series(read_jit=False, write_jit=True)
>>> t.prepare_series(read_jit=True, write_jit=None)
Traceback (most recent call last):
...
ValueError: Reading from and writing into the same NetCDF file "just in time" during a simulation run is not supported but tried for sequence `t` of element `land_lahn_marb`.
property ramflag: bool

A flag telling if the actual IOSequence object makes its time series data directly available in RAM.

See the main documentation on class IOSequence for further information.

property diskflag_reading: bool

A flag telling if the actual IOSequence reads its time series data on the fly from a NetCDF file during a simulation run.

See the main documentation on class IOSequence for further information.

property diskflag_writing: bool

A flag telling if the actual IOSequence writes its time series data on the fly to a NetCDF file during a simulation run.

See the main documentation on class IOSequence for further information.

property diskflag: bool

A flag telling if diskflag_reading and/or diskflag_writing of the current IOSequence object is True:

>>> from hydpy.core.sequencetools import StateSequence
>>> for reading in (False, True):
...     for writing in (False, True):
...         class S(StateSequence):
...             diskflag_reading = reading
...             diskflag_writing = writing
...         print(reading, writing, S(None).diskflag)
False False False
False True True
True False True
True True True
property memoryflag: bool

A flag telling if either ramflag and/or diskflag of the current IOSequence object is True:

>>> from hydpy.core.sequencetools import StateSequence
>>> for ram in (False, True):
...     for disk in (False, True):
...         class S(StateSequence):
...             ramflag = ram
...             diskflag = disk
...         print(ram, disk, S(None).memoryflag)
False False False
False True True
True False True
True True True
property seriesmode: SeriesMode

A combination of property ramflag, diskflag_reading, and diskflag_writing.

seriesmode allows querying and changing all mentioned properties in one step:

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> t = hp.elements.land_lahn_marb.model.sequences.inputs.t
>>> t.prepare_series(read_jit=True)
>>> sm_t = t.seriesmode
>>> sm_t
SeriesMode(ramflag=True, diskflag_reading=True, diskflag_writing=False)
>>> p = hp.elements.land_lahn_marb.model.sequences.inputs.p
>>> p.prepare_series(allocate_ram=False, write_jit=True)
>>> sm_p = p.seriesmode
>>> sm_p
SeriesMode(ramflag=False, diskflag_reading=False, diskflag_writing=True)
>>> t.seriesmode = sm_p
>>> t.seriesmode
SeriesMode(ramflag=False, diskflag_reading=False, diskflag_writing=True)
>>> p.seriesmode = sm_t
>>> p.seriesmode
SeriesMode(ramflag=True, diskflag_reading=True, diskflag_writing=False)
shape

A tuple containing the actual lengths of all dimensions.

When setting a new shape of an IOSequence object, one automatically calls method update_fastaccess() and, if necessary, prepares the new internal series array.

See the main documentation on class IOSequence for further information.

property seriesshape: tuple[int, ...]

The shape of the whole time series (time being the first dimension).

property series: InfoArray

The complete time series data of the current IOSequence object within an InfoArray covering the whole initialisation period (defined by the init Timegrid of the global Timegrids object available in module pub).

property simseries: InfoArray

Read and write access to the subset of the data of property series covering the actual simulation period (defined by the sim Timegrid of the global Timegrids object available in module pub).

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> t = hp.elements.land_lahn_marb.model.sequences.inputs.t
>>> pub.timegrids.sim.dates = "1996-01-02", "1996-01-04"
>>> from hydpy import print_vector
>>> print_vector(t.series)
-0.7, -1.5, -4.6, -8.2
>>> print_vector(t.simseries)
-1.5, -4.6
>>> t.simseries = 1.0, 2.0
>>> print_vector(t.series)
-0.7, 1.0, 2.0, -8.2
property evalseries: InfoArray

Read and write access to the subset of the data of property | IOSequence.series| covering the actual evaluation period (defined by the eval_ Timegrid of the global Timegrids object available in module pub).

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> t = hp.elements.land_lahn_marb.model.sequences.inputs.t
>>> pub.timegrids.eval_.dates = "1996-01-02", "1996-01-04"
>>> from hydpy import print_vector
>>> print_vector(t.series)
-0.7, -1.5, -4.6, -8.2
>>> print_vector(t.evalseries)
-1.5, -4.6
>>> t.evalseries = 1.0, 2.0
>>> print_vector(t.series)
-0.7, 1.0, 2.0, -8.2
load_series() None[source]

Read time series data from a file.

Method load_series() only calls method load_file() of class SequenceManager passing itself as the only argument. Hence, see the documentation on the class SequenceManager for further information. The following example only shows the error messages when load_file() is missing due to incomplete project configurations:

>>> from hydpy.core.sequencetools import StateSequence
>>> StateSequence(None).load_series()
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: While trying to load the time series data of `statesequence`, the following error occurred: Attribute sequencemanager of module `pub` is not defined at the moment.
adjust_series(timegrid_data: timetools.Timegrid, values: NDArrayFloat) NDArrayFloat[source]

Adjust a time series to the current initialisation period.

Note that, in most HydPy applications, method adjust_series() is called by other methods related to reading data from files and does not need to be called by the user directly. However, if you want to call it directly for some reason, you need to make sure that the shape of the given numpy ndarray fits the given Timegrid object.

Often, time series data available in data files cover a longer period than required for an actual simulation run. Method adjust_series() selects the relevant data by comparing the initialisation Timegrid available in module pub and the given “data” Timegrid object. We explain this behaviour by using the HydPy-H-Lahn example project and focussing on the Obs sequence of Node dill_assl:

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> obs = hp.nodes.dill_assl.sequences.obs

With identical initialisation and data time grids, method adjust_series() returns the given data completely:

>>> from hydpy import print_vector, Timegrid
>>> import numpy
>>> with TestIO(), pub.options.checkseries(False):
...     print_vector(obs.adjust_series(
...         Timegrid("1996-01-01", "1996-01-05", "1d"),
...         numpy.arange(4, dtype=float)))
0.0, 1.0, 2.0, 3.0

For “too long” data, it only returns the relevant one:

>>> with TestIO(), pub.options.checkseries(False):
...     print_vector(obs.adjust_series(
...         Timegrid("1995-12-31", "1996-01-07", "1d"),
...         numpy.arange(7, dtype=float)))
1.0, 2.0, 3.0, 4.0

For “too short” data, the behaviour differs depending on option checkseries. With checkseries being enabled, method adjust_series() raises a RuntimeError. With checkseries being disabled, it extends the given array with nan values (using method adjust_short_series()):

>>> with TestIO(), pub.options.checkseries(True):
...     obs.adjust_series(Timegrid("1996-01-02", "1996-01-04", "1d"),
...                       numpy.zeros((3,)))  
Traceback (most recent call last):
...
RuntimeError: For sequence `obs` of node `dill_assl` the initialisation time grid (Timegrid("1996-01-01 00:00:00", "1996-01-05 00:00:00", "1d")) does not define a subset of the time grid of the data file `...dill_assl_obs_q.asc` (Timegrid("1996-01-02 00:00:00", "1996-01-04 00:00:00", "1d")).
>>> with TestIO(), pub.options.checkseries(False):
...     print_vector(obs.adjust_series(
...         Timegrid("1996-01-02", "1996-01-04", "1d"), numpy.zeros((2,))))
nan, 0.0, 0.0, nan

Additional checks raise errors in case of non-matching shapes or time information:

>>> with TestIO():
...     obs.adjust_series(Timegrid("1996-01-01", "1996-01-05", "1d"),
...                       numpy.zeros((5, 2)))  
Traceback (most recent call last):
...
RuntimeError: The shape of sequence `obs` of node `dill_assl` is `()` but according to the data file `...dill_assl_obs_q.asc` it should be `(2,)`.
>>> with TestIO():
...     obs.adjust_series(Timegrid("1996-01-01", "1996-01-05", "1h"),
...                       numpy.zeros((24*5,)))  
Traceback (most recent call last):
...
RuntimeError: According to data file `...dill_assl_obs_q.asc`, the date time step of sequence `obs` of node `dill_assl` is `1h` but the actual simulation time step is `1d`.
adjust_short_series(timegrid: timetools.Timegrid, values: NDArrayFloat) NDArrayFloat[source]

Adjust a short time series to a longer time grid.

Mostly, time series data to be read from files should span (at least) the whole initialisation period of a HydPy project. However, incomplete time series might also be helpful for some variables used only for comparison (e.g. observed runoff used for calibration). Method adjust_short_series() adjusts such incomplete series to the public initialisation time grid stored in module pub. It is automatically called in method adjust_series() when necessary, provided that the option checkseries is disabled.

Assume the initialisation period of a HydPy project spans five days:

>>> from hydpy import pub
>>> pub.timegrids = "2000.01.10", "2000.01.15", "1d"

Prepare a node series object for observational data:

>>> from hydpy.core.sequencetools import Obs
>>> obs = Obs(None)

Prepare a test function that expects the time grid of the data and the data itself, which returns the adjusted array through invoking the method adjust_short_series():

>>> import numpy
>>> def test(timegrid):
...     values = numpy.ones(len(timegrid))
...     return obs.adjust_short_series(timegrid, values)

The following calls to the test function show the arrays returned for different kinds of misalignments:

>>> from hydpy import print_vector, Timegrid
>>> print_vector(test(Timegrid("2000.01.05", "2000.01.20", "1d")))
1.0, 1.0, 1.0, 1.0, 1.0
>>> print_vector(test(Timegrid("2000.01.12", "2000.01.15", "1d")))
nan, nan, 1.0, 1.0, 1.0
>>> print_vector(test(Timegrid("2000.01.12", "2000.01.17", "1d")))
nan, nan, 1.0, 1.0, 1.0
>>> print_vector(test(Timegrid("2000.01.10", "2000.01.13", "1d")))
1.0, 1.0, 1.0, nan, nan
>>> print_vector(test(Timegrid("2000.01.08", "2000.01.13", "1d")))
1.0, 1.0, 1.0, nan, nan
>>> print_vector(test(Timegrid("2000.01.12", "2000.01.13", "1d")))
nan, nan, 1.0, nan, nan
>>> print_vector(test(Timegrid("2000.01.05", "2000.01.10", "1d")))
nan, nan, nan, nan, nan
>>> print_vector(test(Timegrid("2000.01.05", "2000.01.08", "1d")))
nan, nan, nan, nan, nan
>>> print_vector(test(Timegrid("2000.01.15", "2000.01.18", "1d")))
nan, nan, nan, nan, nan
>>> print_vector(test(Timegrid("2000.01.16", "2000.01.18", "1d")))
nan, nan, nan, nan, nan

After enabling option usedefaultvalues, the missing values are initialised with zero instead of nan:

>>> with pub.options.usedefaultvalues(True):
...     print_vector(test(Timegrid("2000.01.12", "2000.01.17", "1d")))
0.0, 0.0, 1.0, 1.0, 1.0
apply_adjusted_series(timegrid_data: timetools.Timegrid, series: NDArrayFloat) None[source]

Take the values of the given “adjusted series”.

The “adjusted series” is usually returned by method adjust_series(). The behaviour of method apply_adjusted_series() depends on option reset. By default, “resetting” is enabled, meaning that nan values due to incomplete time series files overwrite previously available data. We demonstrate this using the NetCDF data provided by function prepare_full_example_2() but changing the initialisation period (only advised for testing purposes):

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> pub.timegrids.init.firstdate = "1989-10-30"
>>> pub.timegrids.init.lastdate = "1989-11-03"
>>> t = hp.elements.land_dill_assl.model.sequences.inputs.t
>>> t.series = -99.9
>>> opt = pub.options
>>> sm = pub.sequencemanager
>>> with TestIO(), sm.filetype("nc"), opt.checkseries(False):
...     with sm.netcdfreading():
...         t.load_series()
>>> from hydpy import round_
>>> round_(t.series)
nan, nan, 10.1, 10.0

With option reset disabled, method apply_adjusted_series() keeps the already available data:

>>> t.series = 99.9
>>> with TestIO(), sm.reset(False), sm.filetype("nc"), opt.checkseries(False):
...     with sm.netcdfreading():
...         t.load_series()
>>> from hydpy import round_
>>> round_(t.series)
99.9, 99.9, 10.1, 10.0
check_completeness() None[source]

Raise a RuntimeError if the series contains at least one nan value and if the option checkseries is enabled.

>>> from hydpy import pub
>>> pub.timegrids = "2000-01-01", "2000-01-11", "1d"
>>> from hydpy.core.sequencetools import StateSequence, StateSequences
>>> class Seq(StateSequence):
...     NDIM = 0
...     NUMERIC = False
>>> class StateSequences(StateSequences):
...     CLASSES = (Seq,)
>>> seq = Seq(StateSequences(None))
>>> seq.__hydpy__connect_variable2subgroup__()
>>> seq.prepare_series()
>>> seq.check_completeness()
Traceback (most recent call last):
...
RuntimeError: The series array of sequence `seq` contains 10 nan values.
>>> seq.series = 1.0
>>> seq.check_completeness()
>>> seq.series[3] = numpy.nan
>>> seq.check_completeness()
Traceback (most recent call last):
...
RuntimeError: The series array of sequence `seq` contains 1 nan value.
>>> with pub.options.checkseries(False):
...     seq.check_completeness()
save_series() None[source]

Write the time series data of the current IOSequence object to a file.

Method save_series() only calls method save_file() of class SequenceManager, passing itself as the only argument. Hence, see the documentation on class the SequenceManager for further information. The following example only shows the error messages when save_file() is missing due to incomplete project configurations:

>>> from hydpy.core.sequencetools import StateSequence
>>> StateSequence(None).save_series()
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: While trying to save the time series data of `statesequence`, the following error occurred: Attribute sequencemanager of module `pub` is not defined at the moment.
save_mean(*args, **kwargs) None[source]

Average the time series data with method average_series() of class IOSequence and write the result to file using method save_file() of class SequenceManager.

The main documentation on class SequenceManager provides some examples.

property seriesmatrix: ndarray[Any, dtype[float64]]

The actual IOSequence object’s time series, arranged in a 2-dimensional matrix.

For a 1-dimensional sequence object, property seriesmatrix returns the original values without modification:

>>> from hydpy import pub
>>> pub.timegrids = "2000-01-01", "2000-01-04", "1d"
>>> from hydpy.models.hland import *
>>> parameterstep("1d")
>>> nmbzones(2)
>>> fluxes.pc.prepare_series()
>>> fluxes.pc.series = [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]
>>> from hydpy import print_vector
>>> for values in fluxes.pc.seriesmatrix:
...     print_vector(values)
1.0, 2.0
3.0, 4.0
5.0, 6.0

For all other sequences, seriesmatrix raises the following error by default:

>>> inputs.p.seriesmatrix
Traceback (most recent call last):
...
NotImplementedError: Sequence `p` does not implement a method for converting its series to a 2-dimensional matrix.
average_series(*args, **kwargs) InfoArray[source]

Average the actual time series of the IOSequence object for all time points.

Method average_series() works similarly to method average_values() of class Variable, from which we borrow some examples. However, we must first prepare a Timegrids object to define the series length:

>>> from hydpy import pub
>>> pub.timegrids = "2000-01-01", "2000-01-04", "1d"

As shown for method average_values(), for 0-dimensional IOSequence objects, the result of method average_series() equals series itself:

>>> from hydpy.core.sequencetools import StateSequence, StateSequences
>>> class SoilMoisture(StateSequence):
...     NDIM = 0
...     NUMERIC = False
>>> class StateSequences(StateSequences):
...     CLASSES = (SoilMoisture,)
>>> sm = SoilMoisture(StateSequences(None))
>>> sm.__hydpy__connect_variable2subgroup__()
>>> sm.prepare_series()
>>> import numpy
>>> sm.series = numpy.array([190.0, 200.0, 210.0])
>>> sm.average_series()
InfoArray([190., 200., 210.])

We require a weighting parameter for IOSequence objects with an increased dimensionality:

>>> SoilMoisture.NDIM = 1
>>> sm.shape = 3
>>> sm.prepare_series()
>>> sm.series = ([190.0, 390.0, 490.0],
...              [200.0, 400.0, 500.0],
...              [210.0, 410.0, 510.0])
>>> from hydpy.core.parametertools import Parameter
>>> class Area(Parameter):
...     NDIM = 1
...     shape = (3,)
...     value = numpy.array([1.0, 1.0, 2.0])
>>> area = Area(None)
>>> SoilMoisture.refweights = property(lambda self: area)
>>> sm.average_series()
InfoArray([390., 400., 410.])

The documentation on method average_values() provides many examples of using different masks in different ways. Here, we only show the results of method average_series() for a mask selecting the first two entries, for a mask selecting no entry at all, and for an ill-defined mask:

>>> from hydpy.core.masktools import DefaultMask
>>> class Soil(DefaultMask):
...     @classmethod
...     def new(cls, variable, **kwargs):
...         return cls.array2mask(maskvalues)
>>> SoilMoisture.mask = Soil()
>>> maskvalues = [True, True, False]
>>> sm.average_series()
InfoArray([290., 300., 310.])
>>> maskvalues = [False, False, False]
>>> sm.average_series()
InfoArray([nan, nan, nan])
>>> maskvalues = [True, True]
>>> sm.average_series()  
Traceback (most recent call last):
...
IndexError: While trying to calculate the mean value of the internal time series of sequence `soilmoisture`, the following error occurred: While trying to access the value(s) of variable `area` with key `[ True  True]`, the following error occurred: boolean index did not match indexed array ...
aggregate_series(*args, **kwargs) InfoArray[source]

Aggregate the time series data based on the actual aggregation attribute of the current IOSequence object.

We prepare some nodes and elements with the help of method prepare_io_example_1() and select a 1-dimensional flux sequence of type NKor:

>>> from hydpy.core.testtools import prepare_io_example_1
>>> nodes, elements = prepare_io_example_1()
>>> seq = elements.element3.model.sequences.fluxes.nkor

If aggregation is none, the original time series values are returned:

>>> seq.aggregation
'none'
>>> seq.aggregate_series()
InfoArray([[24., 25., 26.],
           [27., 28., 29.],
           [30., 31., 32.],
           [33., 34., 35.]])

If aggregation is mean, method aggregate_series() is called:

>>> seq.aggregation = "mean"
>>> seq.aggregate_series()
InfoArray([25., 28., 31., 34.])

In case the state of the sequence is invalid:

>>> seq.aggregation = "nonexistent"
>>> seq.aggregate_series()
Traceback (most recent call last):
...
RuntimeError: Unknown aggregation mode `nonexistent` for sequence `nkor` of element `element3`.

The following technical test confirms the propr passing of all potential positional and keyword arguments:

>>> seq.aggregation = "mean"
>>> from unittest import mock
>>> seq.average_series = mock.MagicMock()
>>> _ = seq.aggregate_series(1, x=2)
>>> seq.average_series.assert_called_with(1, x=2)
abstract property descr_sequence: str

Description of the IOSequence object and its context.

abstract property descr_device: str

Description of the Device object the IOSequence object belongs to.

name: str = 'iosequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.ModelSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: Sequence_

Base class for sequences to be handled by Model objects.

property descr_sequence: str

Description of the ModelSequence object itself and the Model type and SubSequences group it belongs to.

>>> from hydpy import prepare_model
>>> from hydpy.models import test_stiff0d
>>> model = prepare_model(test_stiff0d)
>>> model.sequences.fluxes.q.descr_sequence
'test_stiff0d_flux_q'
property descr_model: str

Description of the Model the ModelSequence object belongs to.

>>> from hydpy import prepare_model
>>> from hydpy.models import test, test_stiff0d
>>> model = prepare_model(test)
>>> model.sequences.fluxes.q.descr_model
'test'
>>> model = prepare_model(test_stiff0d)
>>> model.sequences.fluxes.q.descr_model
'test_stiff0d'
property descr_device: str

Description of the Element object the ModelSequence object belongs to.

>>> from hydpy import prepare_model, pub, Element
>>> element = Element("my_element", outlets="outlet")
>>> from hydpy.models.lland_knauf import *
>>> parameterstep()
>>> model.sequences.inputs.windspeed.descr_device
'?'
>>> element.model = model
>>> model.sequences.inputs.windspeed.descr_device
'my_element'
>>> from hydpy import pub
>>> pub.timegrids = "2000-01-01", "2001-01-02", "1d"
>>> nhru(1)
>>> ft(1.0)
>>> fhru(1.0)
>>> lnk(ACKER)
>>> measuringheightwindspeed(10.0)
>>> lai(10.0)
>>> wmax(300.0)
>>> with model.add_aetmodel_v1("evap_aet_morsim"):
...     pass
>>> model.aetmodel.sequences.inputs.windspeed.descr_device
'my_element'
property numericshape: tuple[int, ...]

The shape of the array of temporary values required for the relevant numerical solver.

The class ELSModel, being the base of the “dam” model, uses the “Explicit Lobatto Sequence” for solving differential equations and therefore requires up to eleven array fields for storing temporary values. Hence, the numericshape of the 0-dimensional sequence Inflow is eleven:

>>> from hydpy import prepare_model
>>> model = prepare_model("dam")
>>> model.sequences.fluxes.inflow.numericshape
(11,)

Changing the shape through a little trick (just for demonstration purposes) shows that there are eleven entries for each “normal” Inflow value:

>>> from hydpy.models.dam.dam_fluxes import Inflow
>>> shape = Inflow.shape
>>> Inflow.shape = (2,)
>>> model.sequences.fluxes.inflow.numericshape
(11, 2)
>>> Inflow.shape = shape

Erroneous configurations result in the following error:

>>> del model.numconsts
>>> model.sequences.fluxes.inflow.numericshape
Traceback (most recent call last):
...
AttributeError: The `numericshape` of a sequence like `inflow` depends on the configuration of the actual integration algorithm.  While trying to query the required configuration data `nmb_stages` of the model associated with element `?`, the following error occurred: 'Model' object has no attribute 'numconsts'
name: str = 'modelsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.ModelIOSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: ModelSequence, IOSequence

Base class for sequences with time series functionalities to be handled by Model objects.

name: str = 'modeliosequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.InputSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: ModelIOSequence

Base class for input sequences of Model objects.

InputSequence objects provide their master model with input data, which is possible in two ways: either by providing their individually managed data (usually read from a file) or data shared with an input node (usually calculated by another model). This flexibility allows, for example, to let application model hland_96 read already preprocessed precipitation time series or to couple it with application models like conv_nn, which interpolates precipitation during the simulation run.

The second mechanism (coupling InputSequence objects with input nodes) is relatively new, and we might adjust the relevant interfaces in the future. As soon as we finally settle things, we will improve the following example and place it more prominently. In short, it shows that working with both types of input data sources at the same time works well and that the different deploymode options are supported:

>>> from hydpy.core.testtools import prepare_full_example_1
>>> prepare_full_example_1()
>>> from hydpy import Element, FusedVariable, HydPy, Node, print_vector, pub, TestIO
>>> from hydpy.aliases import  hland_inputs_T, hland_inputs_P
>>> hp = HydPy("HydPy-H-Lahn")
>>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
>>> node_t = Node("node_t", variable=hland_inputs_T)
>>> node_p = Node("node_p", variable=FusedVariable("Precip", hland_inputs_P))
>>> node_q = Node("node_q")
>>> land_dill_assl = Element("land_dill_assl", inputs=[node_t, node_p],
...                          outlets=node_q)
>>> import os
>>> with TestIO():
...     os.chdir("HydPy-H-Lahn/control/default")
...     with open("land_dill_assl.py") as controlfile:
...         exec(controlfile.read(), {}, locals())
...     parameters.update()
...     land_dill_assl.model = model
>>> aetmodel = model.aetmodel
>>> petmodel = model.aetmodel.petmodel
>>> model.sequences.inputs.t.inputflag
True
>>> model.sequences.inputs.p.inputflag
True
>>> petmodel.sequences.inputs.normalevapotranspiration.inputflag
False
>>> hp.update_devices(nodes=[node_t, node_p, node_q], elements=land_dill_assl)
>>> hp.prepare_inputseries()
>>> hp.prepare_factorseries()
>>> hp.prepare_fluxseries()
>>> with TestIO():
...     hp.load_inputseries()
>>> hp.nodes.prepare_allseries()
>>> node_t.deploymode = "oldsim"
>>> node_t.sequences.sim.series = 1.0, 2.0, 3.0, 4.0, 5.0
>>> node_p.deploymode = "obs"
>>> node_p.sequences.obs.series = 0.0, 4.0, 0.0, 8.0, 0.0
>>> hp.simulate()
>>> print_vector(model.sequences.inputs.t.series)
1.0, 2.0, 3.0, 4.0, 5.0
>>> print_vector(model.sequences.factors.tc.series[:, 0])
2.323207, 3.323207, 4.323207, 5.323207, 6.323207
>>> print_vector(model.sequences.inputs.p.series)
0.0, 4.0, 0.0, 8.0, 0.0
>>> print_vector(model.sequences.fluxes.pc.series[:, 0])
0.0, 3.2514, 0.0, 6.5028, 0.0
>>> print_vector(petmodel.sequences.inputs.normalevapotranspiration.series)
0.3, 0.3, 0.3, 0.3, 0.3
>>> print_vector(
...     aetmodel.sequences.fluxes.potentialsoilevapotranspiration.series[:, 0])
0.309, 0.317657, 0.369, 0.352975, 0.432
STANDARD_NAME: ClassVar[StandardInputNames]
property descr_sequence: str

Either a model-specific or a standard HydPy string describing the input sequence instance.

By default, the returned string equals those of other ModelSequence subclasses:

>>> from hydpy import pub
>>> from hydpy.core.filetools import SequenceManager
>>> pub.sequencemanager = SequenceManager()
>>> from hydpy.models.hland_96 import *
>>> parameterstep()
>>> inputs.t.descr_sequence
'hland_96_input_t'

When activating the standard “HydPy” convention instead of the “model-specific” convention, descr_sequence returns the standard name selected by the respective InputSequence subclass:

>>> with pub.sequencemanager.convention("HydPy"):
...     inputs.t.descr_sequence
<StandardInputNames.AIR_TEMPERATURE: 'air_temperature'>
set_pointer(double: Double) None[source]

Prepare a pointer referencing the given Double object.

Method set_pointer() should be relevant for framework developers and eventually for some model developers only.

property inputflag: bool

A flag telling if the actual InputSequence object queries its data from an input node (True) or uses individually managed data, usually read from a data file (False).

See the main documentation on class InputSequence for further information.

name: str = 'inputsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.OutputSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: ModelIOSequence

Base class for FactorSequence, FluxSequence and StateSequence.

OutputSequence subclasses implement an optional output mechanism. Generally, as all instances of ModelSequence subclasses, output sequences handle values calculated within a simulation time step. With an activated outputflag, they also pass their internal values to an output node (see the documentation on class Element), which makes them accessible to other models.

This output mechanism (coupling OutputSequence objects with output nodes) is relatively new, and we might adjust the relevant interfaces in the future. Additionally, it works for 0-dimensional output sequences only so far. As soon as we finally settle things, we will improve the following example and place it more prominently. In short, it shows that everything works well for the different deploymode options:

>>> from hydpy.core.testtools import prepare_full_example_1
>>> prepare_full_example_1()
>>> from hydpy import Element, HydPy, Node, print_vector, pub, Selection, TestIO
>>> from hydpy.aliases import (
...     hland_fluxes_Perc, hland_fluxes_Q0, hland_fluxes_Q1, hland_states_UZ)
>>> hp = HydPy("HydPy-H-Lahn")
>>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
>>> node_q0 = Node("node_q0", variable=hland_fluxes_Q0)
>>> node_q1 = Node("node_q1", variable=hland_fluxes_Q1)
>>> node_perc = Node("node_perc", variable=hland_fluxes_Perc)
>>> node_uz = Node("node_uz", variable=hland_states_UZ)
>>> node_q = Node("node_q")
>>> land_dill_assl = Element("land_dill_assl",
...                     outlets=node_q,
...                     outputs=[node_q0, node_q1, node_perc, node_uz])
>>> import os
>>> with TestIO():
...     os.chdir("HydPy-H-Lahn/control/default")
...     with open("land_dill_assl.py") as controlfile:
...         exec(controlfile.read(), {}, locals())
...     parameters.update()
...     land_dill_assl.model = model
>>> model.sequences.fluxes.q0.outputflag
True
>>> model.sequences.fluxes.q1.outputflag
True
>>> model.sequences.fluxes.perc.outputflag
True
>>> model.sequences.fluxes.qt.outputflag
False
>>> model.sequences.states.uz.outputflag
True
>>> model.sequences.states.lz.outputflag
False
>>> hp.update_devices(nodes=[node_q0, node_q1, node_perc, node_uz],
...                   elements=land_dill_assl)
>>> with TestIO():
...     hp.load_conditions()
>>> hp.prepare_inputseries()
>>> with TestIO():
...     hp.load_inputseries()
>>> hp.prepare_fluxseries()
>>> hp.prepare_stateseries()
>>> hp.nodes.prepare_allseries()
>>> node_q0.deploymode = "oldsim"
>>> node_q0.sequences.sim.series = 1.0
>>> node_q0.sequences.obs.series = 2.0
>>> node_q1.deploymode = "obs"
>>> node_q1.sequences.obs.series = 3.0
>>> node_perc.deploymode = "newsim"
>>> node_perc.sequences.obs.series = 4.0
>>> node_uz.sequences.obs.series = 5.0
>>> hp.simulate()
>>> print_vector(node_q0.sequences.sim.series)
1.0, 1.0, 1.0, 1.0, 1.0
>>> print_vector(node_q0.sequences.obs.series)
2.0, 2.0, 2.0, 2.0, 2.0
>>> print_vector(model.sequences.fluxes.q1.series)
0.530782, 0.539976, 0.548629, 0.556786, 0.564477
>>> print_vector(node_q1.sequences.sim.series)
0.530782, 0.539976, 0.548629, 0.556786, 0.564477
>>> print_vector(node_q1.sequences.obs.series)
3.0, 3.0, 3.0, 3.0, 3.0
>>> print_vector(model.sequences.fluxes.perc.series)
0.694084, 0.693611, 0.693239, 0.693098, 0.693012
>>> print_vector(node_perc.sequences.sim.series)
0.694084, 0.693611, 0.693239, 0.693098, 0.693012
>>> print_vector(node_perc.sequences.obs.series)
4.0, 4.0, 4.0, 4.0, 4.0
>>> print_vector(model.sequences.states.uz.series)
5.628278, 4.368269, 3.337343, 2.452946, 1.662766
>>> print_vector(node_uz.sequences.sim.series)
5.628278, 4.368269, 3.337343, 2.452946, 1.662766
>>> print_vector(node_uz.sequences.obs.series)
5.0, 5.0, 5.0, 5.0, 5.0
set_pointer(double: Double) None[source]

Prepare a pointer referencing the given Double object.

Method set_pointer() should be relevant for framework developers and eventually for some model developers only.

property outputflag: bool

A flag telling if the actual OutputSequence object passes its data to an output node (True) or not (False).

See the main documentation on class OutputSequence for further information.

name: str = 'outputsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.DependentSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: OutputSequence

Base class for FactorSequence and FluxSequence.

shape

A tuple containing the actual lengths of all dimensions.

FactorSequence and FluxSequence objects come with some additional fastaccess attributes, which should only be of interest to framework developers. One such attribute is the results array, handling the (intermediate or final) calculation results for factor and flux sequences, as shown in the following example for the 0-dimensional flux sequence RH of the wland_wag model:

>>> from hydpy import prepare_model, print_vector, pub
>>> model = prepare_model("wland_wag")
>>> print_vector(model.sequences.fluxes.rh.fastaccess._rh_results)
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0

For 1-dimensional numerical factor and flux sequences, the results attribute is None initially, as property numericshape is unknown. Setting the shape attribute of the respective FactorSequence or FluxSequence object (we select EI as an example) prepares all “fastaccess attributes” automatically:

>>> ei = model.sequences.fluxes.ei
>>> ei.fastaccess._ei_results
>>> ei.shape = (2,)
>>> ei.shape
(2,)
>>> ei.fastaccess._ei_results.shape
(11, 2)
name: str = 'dependentsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.FactorSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: DependentSequence

Base class for factor sequences of Model objects.

NUMERIC: bool = False
name: str = 'factorsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.FluxSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: DependentSequence

Base class for flux sequences of Model objects.

name: str = 'fluxsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.ConditionSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: ModelSequence

Base class for StateSequence and LogSequence.

Class ConditionSequence should not be subclassed by model developers directly. Inherit from StateSequence or LogSequence instead.

trim(lower=None, upper=None) bool[source]

Apply trim() of module variabletools.

reset()[source]

Reset the value of the actual StateSequence or LogSequence object to the last value defined by “calling” the object.

We use the lland_knauf application model, which handles sequences derived from StateSequence (taking Inzp as an example) and from LogSequence (taking LoggedSunshineDuration as an example):

>>> from hydpy import prepare_model, pub
>>> model = prepare_model("lland_knauf")

After defining their shapes, both sequences contain nan values:

>>> inzp = model.sequences.states.inzp
>>> inzp.shape = (2,)
>>> inzp
inzp(nan, nan)
>>> lsd = model.sequences.logs.loggedsunshineduration
>>> lsd.shape = 2
>>> lsd
loggedsunshineduration(nan, nan)

Before “calling” the sequences method reset() does nothing:

>>> inzp.values = 0.0
>>> inzp.reset()
>>> inzp
inzp(0.0, 0.0)
>>> lsd.values = 0.0
>>> lsd.reset()
>>> lsd
loggedsunshineduration(0.0, 0.0)

After “calling” the sequences method reset() reuses the respective arguments:

>>> with pub.options.warntrim(False):
...     inzp(0.0, 1.0)
>>> inzp.values = 0.0
>>> inzp
inzp(0.0, 0.0)
>>> with pub.options.warntrim(False):
...     inzp.reset()
>>> inzp
inzp(0.0, 1.0)
>>> lsd(1.0, 2.0)
>>> lsd.values = 3.0
>>> lsd
loggedsunshineduration(3.0, 3.0)
>>> lsd.reset()
>>> lsd
loggedsunshineduration(1.0, 2.0)
name: str = 'conditionsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.StateSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: OutputSequence, ConditionSequence

Base class for state sequences of Model objects.

Each StateSequence object can handle states at two different “time points”: at the beginning of a simulation step via property old and the end of a simulation step via property new. These properties are reflected by two different fastaccess attributes. fastaccess_new is an alias for the standard fastaccess attribute storing the customary information. fastaccess_old is an additional feature for keeping the supplemental information.

We demonstrate the above explanations using state sequence SM of the base model hland_96 with a shape of two:

>>> from hydpy import prepare_model, print_vector
>>> model = prepare_model("hland")
>>> model.parameters.control.fc.shape = (2,)
>>> model.parameters.control.fc = 100.0
>>> sm = model.sequences.states.sm
>>> sm.shape = (2,)

Initially, no values are available at all:

>>> sm
sm(nan, nan)
>>> print_vector(sm.values)
nan, nan
>>> print_vector(sm.new)
nan, nan
>>> print_vector(sm.old)
nan, nan

The typical way to define state values, especially within condition files, is to “call” state sequence objects, which sets both the “old” and the “new” states to the given value(s):

>>> sm(1.0)
>>> print_vector(sm.values)
1.0, 1.0
>>> print_vector(sm.new)
1.0, 1.0
>>> print_vector(sm.old)
1.0, 1.0

Alternatively, one can assign values to property new or property old (note that using new is identical with using the value property):

>>> sm.new = 2.0, 3.0
>>> sm
sm(2.0, 3.0)
>>> print_vector(sm.values)
2.0, 3.0
>>> print_vector(sm.new)
2.0, 3.0
>>> print_vector(sm.old)
1.0, 1.0
>>> sm.old = 200.0
>>> sm
sm(2.0, 3.0)
>>> print_vector(sm.values)
2.0, 3.0
>>> print_vector(sm.new)
2.0, 3.0
>>> print_vector(sm.old)
200.0, 200.0

If you assign problematic values to property old, it raises similar error messages as property value:

>>> sm.old = 1.0, 2.0, 3.0
Traceback (most recent call last):
...
ValueError: While trying to set the old value(s) of state sequence `sm`, the following error occurred: While trying to convert the value(s) `(1.0, 2.0, 3.0)` to a numpy ndarray with shape `(2,)` and type `float`, the following error occurred: could not broadcast input array from shape (3,) into shape (2,)

Just for completeness: Method new2old() effectively takes the new values as old ones, but more efficiently than using the properties new and old (the Python method new2old() is usually replaced by model-specific, cythonized version when working in Cython mode):

>>> sm.new2old()
>>> print_vector(sm.values)
2.0, 3.0
>>> print_vector(sm.new)
2.0, 3.0
>>> print_vector(sm.old)
2.0, 3.0
shape

A tuple containing the actual lengths of all dimensions.

StateSequence objects come with some additional fastaccess attributes, which should only be of interest to framework developers. One such attribute is the results array, handling the (intermediate or final) calculation results for state sequence, as shown in the following example for the 0-dimensional sequence HS of the wland_wag model:

>>> from hydpy import prepare_model, print_vector, pub
>>> model = prepare_model("wland_wag")
>>> print_vector(model.sequences.states.hs.fastaccess._hs_results)
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0

For 1-dimensional numerical state sequences, the results attribute is None initially, as property numericshape is unknown. Setting the shape attribute of the respective StateSequence object (we select IC as an example) prepares all “fastaccess attributes” automatically:

>>> ic = model.sequences.states.ic
>>> ic.fastaccess._ic_results
>>> ic.shape = (2,)
>>> ic.shape
(2,)
>>> ic.fastaccess._ic_results.shape
(11, 2)
property new

State(s) after calling a Model calculation method. (Alias for property value).

Property new handles, in contrast to property old, the newly calculated state values during each simulation step. It supports testing and debugging of individual Model methods but is typically irrelevant when scripting HydPy workflows.

property old

State(s) before calling a Model calculation method.

Note the similarity to property new. However, property old references the initial states of the respective simulation step, which should not be changed by Model calculation methods.

new2old() None[source]

Assign the new state values to the old values.

See the main documentation on class StateSequence for further information.

Note that method new2old() is replaced by a model-specific, cythonized method when working in Cython mode.

name: str = 'statesequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.LogSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: ConditionSequence

Base class for logging values required for later calculations.

Class LogSequence serves similar purposes as StateSequence but is less strict in its assumptions. While StateSequence objects always handle two states (the old and the new one), LogSequence objects are supposed to remember an arbitrary or sequence-specific number of values, which can be state values but, for example, also flux values. A typical use case is to store “old” values of effective precipitation to calculate “new” values of direct discharge using the unit hydrograph concept in later simulation steps.

It is up to the model developer to ensure that a LogSequence subclass has the correct dimensionality and shape to store the required information. By convention, the “memory” of each LogSequence should be placed on the first axis for non-scalar properties.

As StateSequence objects, LogSequence objects store relevant information to start a new simulation run where another one has ended and are thus written into and read from condition files.

name: str = 'logsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.LogSequenceFixed(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: LogSequence

Base class for log sequences with a fixed shape.

NDIM: int = 1
SHAPE: int
name: str = 'logsequencefixed'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

shape

Sequences derived from LogSequenceFixed initialise themselves with a predefined shape.

We take parameter LoggedRequiredRemoteRelease of base model dam as an example:

>>> from hydpy.models.dam import *
>>> parameterstep()
>>> logs.loggedrequiredremoterelease.shape
(1,)

Property shape results in the following exception when you try to set a new shape:

>>> logs.loggedrequiredremoterelease.shape = 2
Traceback (most recent call last):
...
AttributeError: The shape of sequence `loggedrequiredremoterelease` cannot be changed, but this was attempted for element `?`.

See the documentation on property shape of class Variable for further information.

class hydpy.core.sequencetools.AideSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: ModelSequence

Base class for aide sequences of Model objects.

Aide sequences store data only relevant for calculating an individual simulation time step but must be shared between different calculation methods of a Model object.

name: str = 'aidesequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.LinkSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: ModelSequence

Base class for link sequences of Model objects.

LinkSequence objects do not handle values themselves. Instead, they point to the values handled by NodeSequence objects, using the functionalities provided by the Cython module pointerutils. Multiple LinkSequence objects of different application models can query and modify the same NodeSequence values, allowing different Model objects to share information and interact with each other.

A note for developers: LinkSequence subclasses must be either 0-dimensional or 1-dimensional.

Users might encounter the following exception that is a safety measure to prevent segmentation faults, as the error message suggests:

>>> from hydpy.core.sequencetools import LinkSequence
>>> seq = LinkSequence(None)
>>> seq
linksequence(?)
>>> seq.value
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: While trying to query the value(s) of link sequence `linksequence` of element `?`, the following error occurred: Proper connections are missing (which could result in segmentation faults when using it, so please be careful).
name: str = 'linksequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

set_pointer(double: Double, idx: int = 0) None[source]

Prepare a pointer referencing the given Double object.

For 1-dimensional sequence objects, one also needs to specify the relevant index position of the pointer via argument idx.

Method set_pointer() should be relevant for framework developers and eventually for some model developers only.

property value

The actual value(s) the LinkSequence object is pointing at.

Changing a value of a LinkSequence object seems very much like changing a value of any other Variable object. However, be aware that you are changing a value handled by a NodeSequence object. We demonstrate this by using the HydPy-H-Lahn example project through invoking function prepare_full_example_2():

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()

We focus on the musk_classic application model stream_lahn_marb_lahn_leun routing inflow from node lahn_marb to node lahn_leun:

>>> model = hp.elements.stream_lahn_marb_lahn_leun.model

The first example shows that the 0-dimensional outlet sequence Q points to the Sim sequence of node lahn_leun:

>>> model.sequences.outlets.q
q(0.0)
>>> hp.nodes.lahn_leun.sequences.sim = 1.0
>>> model.sequences.outlets.q
q(1.0)
>>> model.sequences.outlets.q(2.0)
>>> hp.nodes.lahn_leun.sequences.sim
sim(2.0)

The second example shows that the 1-dimensional inlet sequence Q points to the Sim sequence of node lahn_marb:

>>> model.sequences.inlets.q
q(0.0)
>>> hp.nodes.lahn_marb.sequences.sim = 1.0
>>> model.sequences.inlets.q
q(1.0)
>>> model.sequences.inlets.q(2.0)
>>> hp.nodes.lahn_marb.sequences.sim
sim(2.0)

Direct querying the values of both link sequences shows that the value of the 0-dimensional outlet sequence is scalar, of course, and that the value of the 1-dimensional inlet sequence is one entry of a vector:

>>> from hydpy import print_vector, round_
>>> round_(model.sequences.outlets.q.value)
2.0
>>> print_vector(model.sequences.inlets.q.values)
2.0

Assigning incorrect data leads to the usual error messages:

>>> model.sequences.outlets.q.value = 1.0, 2.0
Traceback (most recent call last):
...
ValueError: While trying to assign the value(s) (1.0, 2.0) to link sequence `q` of element `stream_lahn_marb_lahn_leun`, the following error occurred: 2 values are assigned to the scalar variable `q` of element `stream_lahn_marb_lahn_leun`.
>>> model.sequences.inlets.q.values = 1.0, 2.0
Traceback (most recent call last):
...
ValueError: While trying to assign the value(s) (1.0, 2.0) to link sequence `q` of element `stream_lahn_marb_lahn_leun`, the following error occurred: While trying to convert the value(s) `(1.0, 2.0)` to a numpy ndarray with shape `(1,)` and type `float`, the following error occurred: could not broadcast input array from shape (2,) into shape (1,)

In the example above, the 1-dimensional inlet sequence Q only points a single NodeSequence value. We now prepare a exch_branch_hbv96 application model instance to show what happens when connecting a 1-dimensional LinkSequence object (Branched) with three NodeSequence objects (see the documentation of application model exch_branch_hbv96 for more details):

>>> from hydpy import Element, Nodes, prepare_model
>>> model = prepare_model("exch_branch_hbv96")
>>> nodes = Nodes("input1", "input2", "output1", "output2", "output3")
>>> branch = Element("branch",
...                  inlets=["input1", "input2"],
...                  outlets=["output1", "output2", "output3"])
>>> model.parameters.control.xpoints(
...     0.0, 2.0, 4.0, 6.0)
>>> model.parameters.control.ypoints(
...     output1=[0.0, 1.0, 2.0, 3.0],
...     output2=[0.0, 1.0, 0.0, 0.0],
...     output3=[0.0, 0.0, 2.0, 6.0])
>>> branch.model = model

Our third example demonstrates that each field of the values of a 1-dimensional LinkSequence objects points to another NodeSequence object:

>>> nodes.output1.sequences.sim = 1.0
>>> nodes.output2.sequences.sim = 2.0
>>> nodes.output3.sequences.sim = 3.0
>>> model.sequences.outlets.branched
branched(1.0, 2.0, 3.0)
>>> model.sequences.outlets.branched = 4.0, 5.0, 6.0
>>> nodes.output1.sequences.sim
sim(4.0)
>>> nodes.output2.sequences.sim
sim(5.0)
>>> nodes.output3.sequences.sim
sim(6.0)
shape

A tuple containing the actual lengths of all dimensions.

Property shape of class LinkSequence works similarly as the general shape property of class Variable. Still, you need to be extra careful due to the pointer mechanism underlying class LinkSequence. Change the shape of a link sequence for good reasons only. Please read the documentation on property value first and then see the following examples, which are, again, based on the HydPy-H-Lahn example project and application model musk_classic:

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> model = hp.elements.stream_lahn_marb_lahn_leun.model

The default mechanisms of HydPy prepare both 0-dimensional and 1-dimensional link sequences with a proper shape (which, for inlet sequence | musk_inlets.Q|, depends on the number of connected Node objects):

>>> model.sequences.outlets.q.shape
()
>>> model.sequences.inlets.q.shape
(1,)

Attempting to set the only possible shape of 0-dimensional link sequences or any different shape results in the standard behaviour:

>>> model.sequences.outlets.q.shape = ()
>>> model.sequences.outlets.q.shape = (1,)
Traceback (most recent call last):
...
ValueError: While trying to set the shape of link sequence`q` of element `stream_lahn_marb_lahn_leun`, the following error occurred: The shape information of 0-dimensional variables as `q` of element `stream_lahn_marb_lahn_leun` can only be `()`, but `(1,)` is given.

Changing the shape of 1-dimensional link sequences is supported but destroys the connection to the NodeSequence values of the respective nodes. Therefore, he following exception prevents segmentation faults until proper connections are available:

>>> model.sequences.inlets.q.shape = (2,)
>>> model.sequences.inlets.q.shape
(2,)
>>> model.sequences.inlets.q.shape = 1
>>> model.sequences.inlets.q.shape
(1,)
>>> model.sequences.inlets.q
Traceback (most recent call last):
...
RuntimeError: While trying to query the value(s) of link sequence `q` of element `stream_lahn_marb_lahn_leun`, the following error occurred: The pointer of the actual `PPDouble` instance at index `0` requested, but not prepared yet via `set_pointer`.
>>> model.sequences.inlets.q(1.0)
Traceback (most recent call last):
...
RuntimeError: While trying to assign the value(s) 1.0 to link sequence `q` of element `stream_lahn_marb_lahn_leun`, the following error occurred: The pointer of the actual `PPDouble` instance at index `0` requested, but not prepared yet via `set_pointer`.

Querying the shape of a link sequence should rarely result in errors. However, if we enforce it by deleting the fastaccess attribute, we get an error message:

>>> del model.sequences.inlets.q.fastaccess
>>> model.sequences.inlets.q.shape
Traceback (most recent call last):
...
AttributeError: While trying to query the shape of link sequence`q` of element `stream_lahn_marb_lahn_leun`, the following error occurred: 'Q' object has no attribute 'fastaccess'
class hydpy.core.sequencetools.InletSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: LinkSequence

Base class for inlet link sequences of Model objects.

name: str = 'inletsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.OutletSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: LinkSequence

Base class for outlet link sequences of Model objects.

name: str = 'outletsequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.ReceiverSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: LinkSequence

Base class for receiver link sequences of Model objects.

name: str = 'receiversequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.SenderSequence(subvars: ModelSequences[ModelSequence, FastAccess])[source]

Bases: LinkSequence

Base class for sender link sequences of Model objects.

name: str = 'sendersequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

class hydpy.core.sequencetools.NodeSequence(subvars: NodeSequences)[source]

Bases: IOSequence

Base class for all sequences to be handled by Node objects.

name: str = 'nodesequence'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

NDIM: int = 0
NUMERIC: bool = False
property initinfo: tuple[Double, bool]

Return a Double instead of a float object as the first tuple entry.

property descr_sequence: str

Description of the NodeSequence object, including the variable to be represented.

>>> from hydpy import Node
>>> Node("test_node_1", "T").sequences.sim.descr_sequence
'sim_t'
>>> from hydpy import FusedVariable
>>> from hydpy.aliases import hland_inputs_T, lland_inputs_TemL
>>> Temp = FusedVariable("Temp", hland_inputs_T, lland_inputs_TemL)
>>> Node("test_node_2", Temp).sequences.sim.descr_sequence
'sim_temp'
property descr_device: str

Description of the Node object the NodeSequence object belongs to.

>>> from hydpy import Node
>>> Node("test_node_2").sequences.sim.descr_device
'test_node_2'
property value

The actual sequence value.

For framework users, the property value of class NodeSequence works as usual (explained in the documentation on property shape of class Variable). However, framework developers should note that NodeSequence objects use Double objects for storing their values and making them accessible to PDouble and PPDouble objects as explained in detail in the documentation on class LinkSequence. This mechanism is hidden for framework users via conversions to type float for safety reasons:

>>> from hydpy import Node
>>> sim = Node("node").sequences.sim
>>> sim(1.0)
>>> sim
sim(1.0)
>>> sim.value
1.0
>>> sim.fastaccess.sim
Double(1.0)
>>> sim.value = 2.0
>>> sim
sim(2.0)

Node sequences return errors like the following if they receive misspecified values or are ill-configured:

>>> sim.value = 1.0, 2.0  
Traceback (most recent call last):
...
TypeError: While trying to assign the value `(1.0, 2.0)` to sequence `sim` of node `node`, the following error occurred: float() argument must be a string or a... number, not 'tuple'
>>> sim.name = None
>>> sim.value  
Traceback (most recent call last):
...
TypeError: While trying to query the value of sequence `None` of node `node`, the following error occurred: ...attribute name must be string...
property seriescomplete: bool

True/False flag indicating whether simulated or observed data is fully available or not.

We use the observation series of node dill_assl of the HydPy-H-Lahn project:

>>> from hydpy.core.testtools import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> obs = hp.nodes.dill_assl.sequences.obs

When the sequence does not handle any time series data, seriescomplete is False:

>>> obs.prepare_series(allocate_ram=False)
>>> obs.series
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Sequence `obs` of node `dill_assl` is not requested to make any time series data available.
>>> obs.seriescomplete
False

As long as any time series data is missing, seriescomplete is still False:

>>> obs.prepare_series()
>>> obs.series[:-1] = 1.0
>>> obs.series
InfoArray([ 1.,  1.,  1., nan])
>>> obs.seriescomplete
False

Only with all data being not nan, seriescomplete is True:

>>> obs.series[-1] = 1.0
>>> obs.seriescomplete
True
class hydpy.core.sequencetools.Sim(subvars: NodeSequences)[source]

Bases: NodeSequence

Class for handling those values of Node objects that are “simulated”, meaning calculated by hydrological models.

name: str = 'sim'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

load_series() None[source]

Read time series data like method load_series() of class IOSequence but with special handling of missing data.

The method’s “special handling” is to convert errors to warnings. We explain the reasons in the documentation on method load_series() of class Obs, from which we borrow the following examples. The only differences are that method load_series() of class Sim does not disable property memoryflag and uses the option warnmissingsimfile instead of warnmissingobsfile:

>>> from hydpy.core.testtools import prepare_full_example_1
>>> prepare_full_example_1()
>>> from hydpy import HydPy, pub, TestIO
>>> hp = HydPy("HydPy-H-Lahn")
>>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
>>> with TestIO():
...     hp.prepare_network()
...     hp.prepare_models()
...     hp.prepare_simseries()
>>> sim = hp.nodes.dill_assl.sequences.sim
>>> with TestIO():
...     sim.load_series()  
Traceback (most recent call last):
...
UserWarning: While trying to load the time series data of sequence `sim` of node `dill_assl`, the following error occurred: [Errno 2] No such file or directory: '...dill_assl_sim_q.asc'
>>> sim.series
InfoArray([nan, nan, nan, nan, nan])
>>> sim.series = 1.0
>>> with TestIO():
...     sim.save_series()
>>> sim.series = 0.0
>>> with TestIO():
...     sim.load_series()
>>> sim.series
InfoArray([1., 1., 1., 1., 1.])
>>> import numpy
>>> sim.series[2] = numpy.nan
>>> with TestIO(), pub.sequencemanager.overwrite(True):
...     sim.save_series()
>>> with TestIO():
...     sim.load_series()
Traceback (most recent call last):
...
UserWarning: While trying to load the time series data of sequence `sim` of node `dill_assl`, the following error occurred: The series array of sequence `sim` of node `dill_assl` contains 1 nan value.
>>> sim.series
InfoArray([ 1.,  1., nan,  1.,  1.])
>>> sim.series = 0.0
>>> with TestIO(), pub.options.warnmissingsimfile(False):
...         sim.load_series()
>>> sim.series
InfoArray([ 1.,  1., nan,  1.,  1.])
class hydpy.core.sequencetools.Obs(subvars: NodeSequences)[source]

Bases: NodeSequence

Class for handling those values of Node objects that are observed, meaning read from data files.

name: str = 'obs'

Name of the variable in lowercase letters.

unit: str = '?'

Unit of the variable.

load_series() None[source]

Read time series data like method load_series() of class IOSequence but with special handling of missing data.

When reading incomplete time series data, HydPy usually raises a RuntimeError to prevent from performing erroneous calculations. This functionality makes sense for meteorological input data that is a strict requirement for hydrological simulations. However, the same often does not hold for the time series of Obs sequences, e.g. representing measured discharge. Measured discharge is often an optional input or only used for comparison purposes.

According to this reasoning, HydPy raises (at most) a UserWarning in case of missing or incomplete external time series data of Obs sequences. The following examples show this based on the HydPy-H-Lahn project, mainly focussing on the Obs sequence of node dill_assl, which is ready for handling time series data at the end of the following steps:

>>> from hydpy.core.testtools import prepare_full_example_1
>>> prepare_full_example_1()
>>> from hydpy import HydPy, pub, TestIO
>>> hp = HydPy("HydPy-H-Lahn")
>>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
>>> with TestIO():
...     hp.prepare_network()
...     hp.prepare_models()
...     hp.prepare_obsseries()
>>> obs = hp.nodes.dill_assl.sequences.obs
>>> obs.ramflag
True

Trying to read non-existing data raises the following warning and disables the sequence’s ability to handle time series data:

>>> import os
>>> with TestIO():
...     os.remove(hp.nodes.dill_assl.sequences.obs.filepath)
...     hp.load_obsseries()  
Traceback (most recent call last):
...
UserWarning: The `memory flag` of sequence `obs` of node `dill_assl` had to be set to `False` due to the following problem: While trying to load the time series data of sequence `obs` of node `dill_assl`, the following error occurred: [Errno 2] No such file or directory: '...dill_assl_obs_q.asc'
>>> obs.ramflag
False

After writing a complete data file, everything works fine:

>>> obs.prepare_series()
>>> obs.series = 1.0
>>> with TestIO():
...     obs.save_series()
>>> obs.series = 0.0
>>> with TestIO():
...     obs.load_series()
>>> obs.series
InfoArray([1., 1., 1., 1., 1.])

Reading incomplete data also results in a warning message, but does not disable the memoryflag:

>>> import numpy
>>> obs.series[2] = numpy.nan
>>> with TestIO(), pub.sequencemanager.overwrite(True):
...     obs.save_series()
>>> with TestIO():
...     obs.load_series()
Traceback (most recent call last):
...
UserWarning: While trying to load the time series data of sequence `obs` of node `dill_assl`, the following error occurred: The series array of sequence `obs` of node `dill_assl` contains 1 nan value.
>>> obs.memoryflag
True

Option warnmissingobsfile allows disabling the warning messages without altering the functionalities described above:

>>> hp.prepare_obsseries()
>>> with TestIO():
...     os.remove(hp.nodes.lahn_marb.sequences.obs.filepath)
...     with pub.options.warnmissingobsfile(False):
...         hp.load_obsseries()
>>> obs.series
InfoArray([ 1.,  1., nan,  1.,  1.])
>>> hp.nodes.lahn_marb.sequences.obs.memoryflag
False
class hydpy.core.sequencetools.NodeSequences(master: devicetools.Node, cls_fastaccess: type[FastAccessNodeSequence] | None = None, cymodel: CyModelProtocol | None = None)[source]

Bases: IOSequences[devicetools.Node, NodeSequence, FastAccessNodeSequence]

Base class for handling Sim and Obs sequence objects.

Basically, NodeSequences works like the different ModelSequences subclasses used for handling ModelSequence objects. The main difference is that they do not reference a Sequences object (which is only handled by Element objects but not by Node objects). Instead, they directly reference their master Node object via the attribute node:

>>> from hydpy import Node
>>> node = Node("node")
>>> node.sequences.node
Node("node", variable="Q")

The implemented methods just call the same method of the underlying fastaccess attribute, which is an instance of (a Cython extension class of) the Python class FastAccessNodeSequence.

sim: Sim
obs: Obs
node: devicetools.Node
load_data(idx: int) None[source]

Call method load_data() of the current fastaccess attribute.

>>> from hydpy import Node, pub
>>> with pub.options.usecython(False):
...     node = Node("node")
>>> from unittest import mock
>>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.load_data"
>>> with mock.patch(method) as mocked:
...     node.sequences.load_data(5)
>>> mocked.call_args_list
[call(5)]
load_simdata(idx: int) None[source]

Call method load_simdata() of the current fastaccess attribute.

>>> from hydpy import Node, pub
>>> with pub.options.usecython(False):
...     node = Node("node")
>>> from unittest import mock
>>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.load_simdata"
>>> with mock.patch(method) as mocked:
...     node.sequences.load_simdata(5)
>>> mocked.call_args_list
[call(5)]
load_obsdata(idx: int) None[source]

Call method load_obsdata() of the current fastaccess attribute.

>>> from hydpy import Node, pub
>>> with pub.options.usecython(False):
...     node = Node("node")
>>> from unittest import mock
>>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.load_obsdata"
>>> with mock.patch(method) as mocked:
...     node.sequences.load_obsdata(5)
>>> mocked.call_args_list
[call(5)]
save_data(idx: int) None[source]

Call method save_data() of the current fastaccess attribute.

>>> from hydpy import Node, pub
>>> with pub.options.usecython(False):
...     node = Node("node")
>>> from unittest import mock
>>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.save_data"
>>> with mock.patch(method) as mocked:
...     node.sequences.save_data(5)
>>> mocked.call_args_list
[call(5)]
save_simdata(idx: int) None[source]

Call method save_simdata() of the current fastaccess attribute.

>>> from hydpy import Node, pub
>>> with pub.options.usecython(False):
...     node = Node('node')
>>> from unittest import mock
>>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.save_simdata"
>>> with mock.patch(method) as mocked:
...     node.sequences.save_simdata(5)
>>> mocked.call_args_list
[call(5)]