itemtools

This module implements so-called exchange items, simplifying the modification of the values of Parameter and Sequence_ objects.

Module itemtools implements the following members:


class hydpy.core.itemtools.ExchangeSpecification(master: str, variable: str)[source]

Bases: object

Specification of a specific Parameter or Sequence_ type.

ExchangeSpecification is a helper class for ExchangeItem and its subclasses. Its constructor interprets two strings (without any checks on plausibility) and makes their information available as attributes. The following tests list the expected cases:

>>> from hydpy.core.itemtools import ExchangeSpecification
>>> ExchangeSpecification("hland_v1", "fluxes.qt")
ExchangeSpecification("hland_v1", "fluxes.qt")
>>> ExchangeSpecification("hland_v1", "fluxes.qt.series")
ExchangeSpecification("hland_v1", "fluxes.qt.series")
>>> ExchangeSpecification("node", "sim")
ExchangeSpecification("node", "sim")
>>> ExchangeSpecification("node", "sim.series")
ExchangeSpecification("node", "sim.series")

The following attributes are accessible:

>>> spec = ExchangeSpecification("hland_v1", "fluxes.qt")
>>> spec
ExchangeSpecification("hland_v1", "fluxes.qt")
>>> spec.master
'hland_v1'
>>> spec.subgroup
'fluxes'
>>> spec.variable
'qt'
>>> spec.series
False
entries: List[str]
master: str
series: bool
subgroup: Optional[str]
property specstring

The string corresponding to the current values of subgroup, state, and variable.

>>> from hydpy.core.itemtools import ExchangeSpecification
>>> spec = ExchangeSpecification("hland_v1", "fluxes.qt")
>>> spec.specstring
'fluxes.qt'
>>> spec.series = True
>>> spec.specstring
'fluxes.qt.series'
>>> spec.subgroup = None
>>> spec.specstring
'qt.series'
class hydpy.core.itemtools.ExchangeItem[source]

Bases: object

Base class for exchanging values with multiple Parameter or Sequence_ objects of a certain type.

master: str
targetspecs: hydpy.core.itemtools.ExchangeSpecification
device2target: Dict[Union[hydpy.core.devicetools.Node, hydpy.core.devicetools.Element], hydpy.core.variabletools.Variable[Any, Any]]
ndim: int
collect_variables(selections: hydpy.core.selectiontools.Selections)None[source]

Apply method insert_variables() to collect the relevant target variables handled by the devices of the given Selections object.

We prepare the LahnH example project to be able to use its Selections object:

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

We change the type of a specific application model to the type of its base model for reasons explained later:

>>> from hydpy.models.hland import Model
>>> hp.elements.land_lahn_3.model.__class__ = Model

We prepare a SetItem as an example, handling all Ic sequences corresponding to any application models derived from hland:

>>> from hydpy import SetItem
>>> item = SetItem("ic", "hland", "states.ic", 0)
>>> item.targetspecs
ExchangeSpecification("hland", "states.ic")

Applying method collect_variables() connects the SetItem object with all four relevant Ic objects:

>>> item.collect_variables(pub.selections)
>>> land_dill = hp.elements.land_dill
>>> sequence = land_dill.model.sequences.states.ic
>>> item.device2target[land_dill] is sequence
True
>>> for element in sorted(item.device2target, key=lambda x: x.name):
...     print(element)
land_dill
land_lahn_1
land_lahn_2
land_lahn_3

Asking for Ic objects corresponding to application model hland_v1 only, results in skipping the Element land_lahn_3 (handling the hland base model due to the hack above):

>>> item = SetItem("ic", "hland_v1", "states.ic", 0)
>>> item.collect_variables(pub.selections)
>>> for element in sorted(item.device2target, key=lambda x: x.name):
...     print(element)
land_dill
land_lahn_1
land_lahn_2

Selecting a series of a variable instead of the variable itself only affects the targetspec attribute:

>>> item = SetItem("t", "hland_v1", "inputs.t.series", 0)
>>> item.collect_variables(pub.selections)
>>> item.targetspecs
ExchangeSpecification("hland_v1", "inputs.t.series")
>>> sequence = land_dill.model.sequences.inputs.t
>>> item.device2target[land_dill] is sequence
True

To pass an ill-defined subgroup name results in the following error:

>>> from hydpy import SetItem
>>> item = SetItem("ic", "hland", "wrong_group.wrong_variable", 0)
>>> item.collect_variables(pub.selections)
Traceback (most recent call last):
...
RuntimeError: Model `hland_v1` of element `land_dill` does neither handle a parameter of sequence subgroup named `wrong_group.

It is both possible to address sequences of Node objects, as well as their time series, by arguments “node” and “nodes”:

>>> item = SetItem("sim", "node", "sim", 0)
>>> item.collect_variables(pub.selections)
>>> dill = hp.nodes.dill
>>> item.targetspecs
ExchangeSpecification("node", "sim")
>>> item.device2target[dill] is dill.sequences.sim
True
>>> for node in sorted(item.device2target, key=lambda x: x.name):
...  print(node)
dill
lahn_1
lahn_2
lahn_3
>>> item = SetItem("sim", "nodes", "sim.series", 0)
>>> item.collect_variables(pub.selections)
>>> item.targetspecs
ExchangeSpecification("nodes", "sim.series")
>>> for node in sorted(item.device2target, key=lambda x: x.name):
...  print(node)
dill
lahn_1
lahn_2
lahn_3
insert_variables(device2variable: Dict[Union[hydpy.core.devicetools.Node, hydpy.core.devicetools.Element], hydpy.core.variabletools.Variable[Any, Any]], exchangespec: hydpy.core.itemtools.ExchangeSpecification, selections: hydpy.core.selectiontools.Selections)None[source]

Determine the relevant target or base variables (as defined by the given ExchangeSpecification object) handled by the given Selections object and insert them into the given device2variable dictionary.

class hydpy.core.itemtools.ChangeItem[source]

Bases: hydpy.core.itemtools.ExchangeItem

Base class for changing the values of multiple Parameter or Sequence_ objects of a specific type.

name: str
ndim: int
device2target: Dict[Union[hydpy.core.devicetools.Node, hydpy.core.devicetools.Element], hydpy.core.variabletools.Variable[Any, Any]]
property shape

The shape of the target variables.

Trying to access property shape before calling method collect_variables() results in the following error:

>>> from hydpy import SetItem
>>> SetItem("name", "master", "target", 0).shape
Traceback (most recent call last):
...
RuntimeError: The shape of SetItem `name` has not been determined so far.

See method collect_variables() for further information.

property value

The value(s) that can be used to change the values of target variables through applying method update_variables() of class ChangeItem.

>>> from hydpy.examples import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> from hydpy import SetItem
>>> item = SetItem("ic", "hland", "states.ic", 0)
>>> item.collect_variables(pub.selections)
>>> item.value = 1
>>> item.value
array(1.0)
>>> item.value = 1, 2
Traceback (most recent call last):
...
ValueError: When trying to convert the value(s) `(1, 2)` assigned to SetItem `ic` to a numpy array of shape `()` and type `float`, the following error occurred: could not broadcast input array from shape (2,) into shape ()
update_variables()None[source]

Subclasses must define a mathematical operation for updating the values of target variables.

>>> from hydpy.core.itemtools import ChangeItem
>>> ChangeItem().update_variables()
Traceback (most recent call last):
...
NotImplementedError
collect_variables(selections: hydpy.core.selectiontools.Selections)None[source]

Apply method collect_variables() of the base class ExchangeItem and determine the shape of the current ChangeItem object afterwards, depending on its dimensionality and eventually on the shape of its target variables.

For the following examples, we prepare the LahnH example project:

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

0-dimensional change-items do not have a variable shape, which is indicated by an empty tuple:

>>> from hydpy import SetItem
>>> item = SetItem("ic", "hland", "states.ic", 0)
>>> item.collect_variables(pub.selections)
>>> item.shape
()

1-dimensional change-items take the shape of their target variables, which must be equal for all instances:

>>> item = SetItem("ic", "hland", "states.ic", 1)
>>> item.collect_variables(pub.selections)
Traceback (most recent call last):
...
RuntimeError: SetItem `ic` cannot handle target variables of different shapes.
>>> for element in hp.elements.catchment:
...     element.model.parameters.control.nmbzones(3)
>>> item = SetItem("ic", "hland", "states.ic", 1)
>>> item.collect_variables(pub.selections)
>>> item.shape
(3,)

Passing a Selections object not containing any relevant target variables results in the following error:

>>> item = SetItem("ic", "hland", "states.ic", 1)
>>> from hydpy import Selections
>>> item.collect_variables(Selections())
Traceback (most recent call last):
...
RuntimeError: Cannot determine the shape of the actual `SetItem` object, as the given `Selections` object does not handle any relevant target variables.
update_variable(variable: hydpy.core.variabletools.Variable[Any, Any], value: numpy.ndarray)None[source]

Assign the given value(s) to the given target or base variable.

If the assignment fails, update_variable() raises an error like the following:

>>> from hydpy.examples import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> item = SetItem("alpha", "hland_v1", "control.alpha", 0)
>>> item.collect_variables(pub.selections)
>>> item.update_variables()    
Traceback (most recent call last):
...
TypeError: When trying to update a target variable of SetItem `alpha` with the value(s) `None`, the following error occurred: While trying to set the value(s) of variable `alpha` of element `...`, the following error occurred: The given value `None` cannot be converted to type `float`.
class hydpy.core.itemtools.SetItem(name: str, master: str, target: str, ndim: int)[source]

Bases: hydpy.core.itemtools.ChangeItem

Item for assigning value to multiple Parameter or Sequence_ objects of a specific type.

name: str
targetspecs: hydpy.core.itemtools.ExchangeSpecification
ndim: int
device2target: Dict[Union[hydpy.core.devicetools.Node, hydpy.core.devicetools.Element], hydpy.core.variabletools.Variable[Any, Any]]
update_variables()None[source]

Assign the current objects value to the values of the target variables.

We use the LahnH project in the following:

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

In the first example, a 0-dimensional SetItem changes the value of the 0-dimensional parameter Alpha:

>>> from hydpy.core.itemtools import SetItem
>>> item = SetItem("alpha", "hland_v1", "control.alpha", 0)
>>> item
SetItem("alpha", "hland_v1", "control.alpha", 0)
>>> item.collect_variables(pub.selections)
>>> item.value is None
True
>>> land_dill = hp.elements.land_dill
>>> land_dill.model.parameters.control.alpha
alpha(1.0)
>>> item.value = 2.0
>>> item.value
array(2.0)
>>> land_dill.model.parameters.control.alpha
alpha(1.0)
>>> item.update_variables()
>>> land_dill.model.parameters.control.alpha
alpha(2.0)

In the second example, a 0-dimensional SetItem changes the values of the 1-dimensional parameter FC:

>>> item = SetItem("fc", "hland_v1", "control.fc", 0)
>>> item.collect_variables(pub.selections)
>>> item.value = 200.0
>>> land_dill.model.parameters.control.fc
fc(278.0)
>>> item.update_variables()
>>> land_dill.model.parameters.control.fc
fc(200.0)

In the third example, a 1-dimensional SetItem changes the values of the 1-dimensional sequence Ic:

>>> for element in hp.elements.catchment:
...     element.model.parameters.control.nmbzones(5)
...     element.model.parameters.control.icmax(4.0)
>>> item = SetItem("ic", "hland_v1", "states.ic", 1)
>>> item.collect_variables(pub.selections)
>>> land_dill.model.sequences.states.ic
ic(nan, nan, nan, nan, nan)
>>> item.value = 2.0
>>> item.update_variables()
>>> land_dill.model.sequences.states.ic
ic(2.0, 2.0, 2.0, 2.0, 2.0)
>>> item.value = 1.0, 2.0, 3.0, 4.0, 5.0
>>> item.update_variables()
>>> land_dill.model.sequences.states.ic
ic(1.0, 2.0, 3.0, 4.0, 4.0)
master: str
class hydpy.core.itemtools.MathItem(name: str, master: str, target: str, base: str, ndim: int)[source]

Bases: hydpy.core.itemtools.ChangeItem

Base class for performing some mathematical operations on the given values before assigning them to the handled target variables.

Subclasses of MathItem like AddItem handle not only target variables but also base variables:

>>> from hydpy import AddItem
>>> item = AddItem(
...     "sfcf", "hland_v1", "control.sfcf", "control.rfcf", 0)
>>> item
AddItem("sfcf", "hland_v1", "control.sfcf", "control.rfcf", 0)
>>> item.targetspecs
ExchangeSpecification("hland_v1", "control.sfcf")
>>> item.basespecs
ExchangeSpecification("hland_v1", "control.rfcf")

Generally, a MathItem calculates the target variable of a specific Device object by using its current value and the value(s) of the base variable of the same Device.

basespecs: hydpy.core.itemtools.ExchangeSpecification
device2base: Dict[Union[hydpy.core.devicetools.Node, hydpy.core.devicetools.Element], hydpy.core.variabletools.Variable[Any, Any]]
collect_variables(selections: hydpy.core.selectiontools.Selections)None[source]

Apply method collect_variables() of the base class ChangeItem and also apply method insert_variables() of class ExchangeItem to collect the relevant base variables handled by the devices of the given Selections object.

>>> from hydpy.examples import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> from hydpy import AddItem
>>> item = AddItem("alpha", "hland_v1", "control.sfcf", "control.rfcf", 0)
>>> item.collect_variables(pub.selections)
>>> land_dill = hp.elements.land_dill
>>> control = land_dill.model.parameters.control
>>> item.device2target[land_dill] is control.sfcf
True
>>> item.device2base[land_dill] is control.rfcf
True
>>> for device in sorted(item.device2base, key=lambda x: x.name):
...     print(device)
land_dill
land_lahn_1
land_lahn_2
land_lahn_3
class hydpy.core.itemtools.AddItem(name: str, master: str, target: str, base: str, ndim: int)[source]

Bases: hydpy.core.itemtools.MathItem

MathItem subclass performing additions.

update_variables()None[source]

Add the general value with the Device specific base variable and assign the result to the respective target variable.

>>> from hydpy.examples import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> from hydpy.models.hland_v1 import FIELD
>>> for element in hp.elements.catchment:
...     control = element.model.parameters.control
...     control.nmbzones(3)
...     control.zonetype(FIELD)
...     control.rfcf(1.1)
>>> from hydpy.core.itemtools import AddItem
>>> item = AddItem(
...     "sfcf", "hland_v1", "control.sfcf", "control.rfcf", 1)
>>> item.collect_variables(pub.selections)
>>> land_dill = hp.elements.land_dill
>>> land_dill.model.parameters.control.sfcf
sfcf(?)
>>> item.value = -0.1, 0.0, 0.1
>>> item.update_variables()
>>> land_dill.model.parameters.control.sfcf
sfcf(1.0, 1.1, 1.2)
>>> land_dill.model.parameters.control.rfcf.shape = 2
>>> land_dill.model.parameters.control.rfcf = 1.1
>>> item.update_variables()    
Traceback (most recent call last):
...
ValueError: When trying to add the value(s) `[-0.1  0.   0.1]` of AddItem `sfcf` and the value(s) `[ 1.1  1.1]` of variable `rfcf` of element `land_dill`, the following error occurred: operands could not be broadcast together with shapes (2,) (3,)...
basespecs: hydpy.core.itemtools.ExchangeSpecification
device2base: Dict[Union[hydpy.core.devicetools.Node, hydpy.core.devicetools.Element], hydpy.core.variabletools.Variable[Any, Any]]
name: str
ndim: int
device2target: Dict[Union[hydpy.core.devicetools.Node, hydpy.core.devicetools.Element], hydpy.core.variabletools.Variable[Any, Any]]
master: str
targetspecs: hydpy.core.itemtools.ExchangeSpecification
class hydpy.core.itemtools.GetItem(master: str, target: str)[source]

Bases: hydpy.core.itemtools.ExchangeItem

Base class for querying the values of multiple Parameter or Sequence_ objects of a specific type.

collect_variables(selections: hydpy.core.selectiontools.Selections)None[source]

Apply method collect_variables() of the base class ExchangeItem and determine the ndim attribute of the current ChangeItem object afterwards.

The value of ndim depends on whether the values of the target variable or its time series are of interest:

>>> from hydpy.examples import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> from hydpy.core.itemtools import SetItem
>>> for target in ("states.lz", "states.lz.series",
...                "states.sm", "states.sm.series"):
...     item = GetItem("hland_v1", target)
...     item.collect_variables(pub.selections)
...     print(item, item.ndim)
GetItem("hland_v1", "states.lz") 0
GetItem("hland_v1", "states.lz.series") 1
GetItem("hland_v1", "states.sm") 1
GetItem("hland_v1", "states.sm.series") 2
yield_name2value(idx1: Optional[int] = None, idx2: Optional[int] = None)Iterator[Tuple[str, str]][source]

Sequentially return name-value-pairs describing the current state of the target variables.

The names are automatically generated and contain both the name of the Device of the respective Variable object and the target description:

>>> from hydpy.examples import prepare_full_example_2
>>> hp, pub, TestIO = prepare_full_example_2()
>>> from hydpy.core.itemtools import SetItem
>>> item = GetItem("hland_v1", "states.lz")
>>> item.collect_variables(pub.selections)
>>> hp.elements.land_dill.model.sequences.states.lz = 100.0
>>> for name, value in item.yield_name2value():
...     print(name, value)
land_dill_states_lz 100.0
land_lahn_1_states_lz 8.18711
land_lahn_2_states_lz 10.14007
land_lahn_3_states_lz 7.52648
>>> item = GetItem("hland_v1", "states.sm")
>>> item.collect_variables(pub.selections)
>>> hp.elements.land_dill.model.sequences.states.sm = 2.0
>>> for name, value in item.yield_name2value():
...     print(name, value)    
land_dill_states_sm [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0]
land_lahn_1_states_sm [99.27505, ..., 142.84148]
...

When querying time series, one can restrict the span of interest by passing index values:

>>> item = GetItem("nodes", "sim.series")
>>> item.collect_variables(pub.selections)
>>> hp.nodes.dill.sequences.sim.series = 1.0, 2.0, 3.0, 4.0
>>> for name, value in item.yield_name2value():
...     print(name, value)    
dill_sim_series [1.0, 2.0, 3.0, 4.0]
lahn_1_sim_series [nan, ...
...
>>> for name, value in item.yield_name2value(2, 3):
...     print(name, value)    
dill_sim_series [3.0]
lahn_1_sim_series [nan]
...