# -*- coding: utf-8 -*-
# pylint: disable=missing-module-docstring
# import...
# ...from site-packages
import numpy
# ...from HydPy
from hydpy import pub
from hydpy.core import devicetools
from hydpy.core import exceptiontools
from hydpy.core import objecttools
from hydpy.core import parametertools
[docs]
class Delta(parametertools.MonthParameter):
"""Monthly varying difference for increasing or decreasing the input [e.g. m³/s]."""
TYPE, TIME, SPAN = float, None, (None, None)
INIT = 0.0
[docs]
class Minimum(parametertools.Parameter):
"""The allowed minimum value of the adjusted input [e.g. m³/s]."""
NDIM, TYPE, TIME, SPAN = 0, float, None, (None, None)
INIT = 0.0
[docs]
class XPoints(parametertools.Parameter):
"""Supporting points for the independent input variable [e.g. m³/s].
There must be at least two supporting points, and they must be strictly monotonous.
If not, |XPoints| raises the following errors:
>>> from hydpy.models.hbranch import *
>>> parameterstep()
>>> xpoints(1.0, 2.0)
>>> xpoints
xpoints(1.0, 2.0)
>>> xpoints(1.0)
Traceback (most recent call last):
...
ValueError: Branching via linear interpolation requires at least \
two supporting points, but parameter `xpoints` of element `?` \
received 1 value(s).
>>> xpoints(1.0, 2.0, 2.0, 3.0)
Traceback (most recent call last):
...
ValueError: The values of parameter `xpoints` of element `?` must be \
arranged strictly monotonous, which is not the case for the given values \
`1.0, 2.0, 2.0, and 3.0`.
"""
NDIM, TYPE, TIME, SPAN = 1, float, None, (None, None)
def __call__(self, *args, **kwargs) -> None:
# pylint: disable=unsubscriptable-object
# due to a pylint bug (see https://github.com/PyCQA/pylint/issues/870)
self.shape = len(args)
if self.shape[0] < 2:
raise ValueError(
f"Branching via linear interpolation requires at least two supporting "
f"points, but parameter {objecttools.elementphrase(self)} received "
f"{self.shape[0]} value(s)."
)
super().__call__(*args, **kwargs)
if min(numpy.diff(self)) <= 0.0:
raise ValueError(
f"The values of parameter {objecttools.elementphrase(self)} must be "
f"arranged strictly monotonous, which is not the case for the given "
f"values `{objecttools.enumeration(self)}`."
)
[docs]
class YPoints(parametertools.Parameter):
"""Supporting points for the dependent output variables [e.g. m³/s].
Preparing parameter |YPoints| requires consistency with parameter |XPoints| and the
currently available |Node| objects.
.. testsetup::
>>> from hydpy import reverse_model_wildcard_import
>>> reverse_model_wildcard_import()
>>> from hydpy.models.hbranch import *
>>> parameterstep("1d")
>>> ypoints
ypoints(?)
You need to prepare the parameter |XPoints| first:
>>> ypoints(1.0, 2.0)
Traceback (most recent call last):
...
RuntimeError: The shape of parameter `ypoints` of element `?` depends on the \
shape of parameter `xpoints`, which has not been defined so far.
>>> xpoints(1.0, 2.0, 3.0)
You need to supply the names of the output |Node| objects as keyword arguments:
>>> ypoints(1.0, 2.0)
Traceback (most recent call last):
...
ValueError: For parameter `ypoints` of element `?` no branches are defined. Do \
this via keyword arguments as explained in the documentation.
The number of x and y supporting points must agree for all branches:
>>> ypoints(branch1=[1.0, 2.0],
... branch2=[2.0, 4.0])
Traceback (most recent call last):
...
ValueError: Each branch requires the same number of supporting points as given for \
parameter `xpoints`, which is 3, but for branch `branch1` of parameter `ypoints` of \
element `?` 2 values are given.
>>> xpoints(1.0, 2.0)
When working in on actual project (indicated by a predefined project name), each
branch name must correspond to a |Node| name:
>>> from hydpy import pub, Nodes
>>> pub.projectname = "test"
>>> nodes = Nodes("branch1")
>>> ypoints(branch1=[1.0, 2.0],
... branch2=[2.0, 4.0])
Traceback (most recent call last):
...
RuntimeError: Parameter `ypoints` of element `?` is supposed to branch to node \
`branch2`, but such a node is not available.
We use the following general exception message for some unexpected errors:
>>> nodes = Nodes("branch1", "branch2")
>>> ypoints(branch1=[1.0, 2.0],
... branch2="xy")
Traceback (most recent call last):
...
ValueError: While trying to set the values for branch `branch2` of parameter \
`ypoints` of element `?`, the following error occurred: could not convert string to \
float: 'xy'
Changing the number of branches during runtime might result in erroneous
connections to the |Node| objects:
>>> ypoints(branch1=[1.0, 2.0],
... branch2=[2.0, 4.0])
>>> ypoints
ypoints(branch1=[1.0, 2.0],
branch2=[2.0, 4.0])
>>> ypoints(branch1=[1.0, 2.0])
Traceback (most recent call last):
...
RuntimeError: The number of branches of the hbranch model should not be changed \
during run time. If you really need to do this, first initialize a new `branched` \
sequence and connect it to the respective outlet nodes properly.
"""
NDIM, TYPE, TIME, SPAN = 2, float, None, (None, None)
def __call__(self, *args, **kwargs):
try:
shape = (len(kwargs), self.subpars.xpoints.shape[0])
except exceptiontools.AttributeNotReady:
raise RuntimeError(
f"The shape of parameter {objecttools.elementphrase(self)} "
f"depends on the shape of parameter `xpoints`, which has "
f"not been defined so far."
) from None
if shape[0] == 0:
raise ValueError(
f"For parameter {objecttools.elementphrase(self)} "
f"no branches are defined. Do this via keyword "
f"arguments as explained in the documentation."
)
branched = self.subpars.pars.model.sequences.outlets.branched
if (branched.shape[0] != 0) and (branched.shape[0] != shape[0]):
raise RuntimeError(
"The number of branches of the hbranch model should not "
"be changed during run time. If you really need to do "
"this, first initialize a new `branched` sequence and "
"connect it to the respective outlet nodes properly."
)
self.shape = shape
self.values = numpy.nan
for idx, (key, value) in enumerate(sorted(kwargs.items())):
if key not in devicetools.Node.query_all():
try:
pub.projectname
except RuntimeError:
pass
else:
raise RuntimeError(
f"Parameter {objecttools.elementphrase(self)} is "
f"supposed to branch to node `{key}`, but such a "
f"node is not available."
)
try:
self.values[idx] = value
except BaseException:
if shape[1] != len(value):
raise ValueError(
f"Each branch requires the same number of supporting "
f"points as given for parameter `xpoints`, which is "
f"{shape[1]}, but for branch `{key}` of parameter "
f"{objecttools.elementphrase(self)} {len(value)} "
f"values are given."
) from None
objecttools.augment_excmessage(
f"While trying to set the values for branch `{key}` "
f"of parameter {objecttools.elementphrase(self)}"
)
if branched.shape == (0,):
branched.shape = shape[0]
self.subpars.pars.model.sequences.fluxes.outputs.shape = shape[0]
self.subpars.pars.model.nodenames.clear()
for idx, key in enumerate(sorted(kwargs.keys())):
setattr(self, key, self.values[idx])
self.subpars.pars.model.nodenames.append(key)
def __repr__(self) -> str:
try:
lines = self.commentrepr
names = self.subpars.pars.model.nodenames
for idx, (name, values) in enumerate(zip(names, self)):
line = f"{name}={repr(list(values))},"
if not idx:
lines.append(f"ypoints({line}")
else:
lines.append(f" {line}")
lines[-1] = f"{lines[-1][:-1]})"
return "\n".join(lines)
except BaseException:
return "ypoints(?)"