# pylint: disable=missing-module-docstring
import itertools
import numpy
from hydpy.core import exceptiontools
from hydpy.core import objecttools
from hydpy.core import parametertools
from hydpy.models.whmod.whmod_constants import *
from hydpy.models.whmod import whmod_constants
from hydpy.models.whmod import whmod_masks
from hydpy.models.whmod import whmod_parameters
[docs]
class Area(parametertools.Parameter):
"""Total area [m²]."""
NDIM, TYPE, TIME, SPAN = 0, float, None, (1e-10, None)
[docs]
class NmbZones(parametertools.Parameter):
"""Number of zones (hydrological response units) in a subbasin [-].
|NmbZones| determines the length of most 1-dimensional parameters and sequences.
Usually, you should first prepare |NmbZones| and define the values of all
1-dimensional parameters and sequences afterwards:
>>> from hydpy.models.whmod import *
>>> parameterstep()
>>> nmbzones(5)
>>> availablefieldcapacity.shape
(5,)
>>> states.soilmoisture.shape
(5,)
Changing the value of |NmbZones| later reshapes the affected parameters and
sequences and makes it necessary to reset their values:
>>> availablefieldcapacity(2.0)
>>> availablefieldcapacity
availablefieldcapacity(2.0)
>>> nmbzones(3)
>>> availablefieldcapacity
availablefieldcapacity(?)
Re-defining the same value does not delete the already available data:
>>> availablefieldcapacity(2.0)
>>> nmbzones(3)
>>> availablefieldcapacity
availablefieldcapacity(2.0)
"""
NDIM, TYPE, TIME, SPAN = 0, int, None, (1, None)
def __call__(self, *args, **kwargs):
old = exceptiontools.getattr_(self, "value", None)
super().__call__(*args, **kwargs)
new = self.value
if new != old:
model = self.subpars.pars.model
for subvars in itertools.chain(model.parameters, model.sequences):
for var in subvars:
if var.NDIM == 1:
var.shape = new
[docs]
class ZoneArea(whmod_parameters.LandTypeCompleteParameter):
"""Zone area [m²]."""
TYPE, TIME, SPAN = float, None, (0.0, None)
[docs]
class LandType(parametertools.NameParameter):
"""Land cover type [-]."""
constants = whmod_constants.LANDTYPE_CONSTANTS
[docs]
class SoilType(parametertools.NameParameter):
"""Soil type [-].
>>> from hydpy.models.whmod import *
>>> parameterstep()
>>> nmbzones(9)
>>> landtype(GRASS, DECIDUOUS, CORN, CONIFER, SPRINGWHEAT, WINTERWHEAT, SUGARBEETS,
... SEALED, WATER)
>>> soiltype(SAND, SAND_COHESIVE, LOAM, CLAY, SILT, PEAT, SAND, NONE, NONE)
>>> soiltype
soiltype(SAND, SAND_COHESIVE, LOAM, CLAY, SILT, PEAT, SAND, NONE, NONE)
>>> soiltype(SAND, SAND_COHESIVE, LOAM, CLAY, SILT, PEAT, NONE, NONE, NONE)
Traceback (most recent call last):
...
ValueError: While trying to set the values of parameter `soiltype` of element `?`, \
the following error occurred: The soil type of land type(s) SUGARBEETS must not be NONE.
>>> soiltype
soiltype(?)
>>> soiltype(SAND, SAND_COHESIVE, LOAM, CLAY, SILT, PEAT, SAND, SAND, SAND)
Traceback (most recent call last):
...
ValueError: While trying to set the values of parameter `soiltype` of element `?`, \
the following error occurred: The soil type of land type(s) SEALED and WATER must be \
NONE.
>>> soiltype
soiltype(?)
"""
constants = whmod_constants.SOILTYPE_CONSTANTS
mask = whmod_masks.LandTypeSoil()
def __call__(self, *args, **kwargs) -> None:
def _make_names() -> str:
value2name = whmod_constants.LANDTYPE_CONSTANTS.value2name
return objecttools.enumeration([value2name[l] for l in landtype[jdxs]])
try:
super().__call__(*args, **kwargs)
landtype = self.subpars.landtype.values
should_be_soil = (landtype != SEALED) * (landtype != WATER)
is_soil = self.values != NONE
if numpy.any(jdxs := should_be_soil * ~is_soil):
self._valueready = False
raise ValueError(
f"The soil type of land type(s) {_make_names()} must not be NONE."
)
if numpy.any(jdxs := ~should_be_soil * is_soil):
self._valueready = False
raise ValueError(
f"The soil type of land type(s) {_make_names()} must be NONE."
)
except BaseException:
objecttools.augment_excmessage(
"While trying to set the values of parameter "
f"{objecttools.elementphrase(self)}"
)
[docs]
class CisternSource(whmod_parameters.LandTypeNonWaterParameter):
"""A flag that indicates whether a zone's excess water (surface runoff or
percolation) is channelled into the cistern [-].."""
TYPE, TIME, SPAN = bool, None, (False, True)
[docs]
class CisternCapacity(parametertools.Parameter):
"""Maximum water amount that can be collected in the cistern [m³]."""
NDIM, TYPE, TIME, SPAN = 0, float, None, (0.0, None)
[docs]
class InterceptionCapacity(parametertools.KeywordParameter2D):
"""Maximum interception storage [mm]."""
TYPE, TIME, SPAN = float, None, (0.0, None)
columnnames = parametertools.MonthParameter.entrynames
rownames = whmod_constants.LANDTYPE_CONSTANTS.get_sortednames(
relevant=whmod_masks.LandTypeNonWater.relevant
)
[docs]
class DegreeDayFactor(whmod_parameters.LandTypeNonWaterParameter):
"""Degree day factor for snow melting [mm/T/K]."""
TYPE, TIME, SPAN = float, True, (0.0, None)
[docs]
class AvailableFieldCapacity(whmod_parameters.SoilTypeParameter):
"""Maximum relative soil moisture content [-]."""
TYPE, TIME, SPAN = float, None, (0.0, None)
[docs]
class RootingDepth(whmod_parameters.LandTypeSoilParameter):
"""Maximum rooting depth [m]."""
TYPE, TIME, SPAN = float, None, (0.0, None)
[docs]
class GroundwaterDepth(whmod_parameters.SoilTypeParameter):
"""Average groundwater depth [m]."""
TYPE, TIME, SPAN = float, None, (0.0, None)
[docs]
class WithCapillaryRise(parametertools.Parameter):
"""Flag to turn on/off capillary rise [-]."""
NDIM, TYPE, TIME = 0, bool, None
[docs]
class CapillaryThreshold(whmod_parameters.SoilTypeParameter):
"""Relative soil moisture where the capillary rise starts [-]."""
TYPE, TIME, SPAN = float, None, (0.0, None)
[docs]
class CapillaryLimit(whmod_parameters.SoilTypeParameter):
"""Relative soil moisture where the capillary rise reaches its maximum [-]."""
TYPE, TIME, SPAN = float, None, (0.0, None)
[docs]
class IrrigationTrigger(parametertools.KeywordParameter2D):
"""Relative soil moisture below which irrigation starts [-]."""
TYPE, TIME, SPAN = float, None, (0.0, 1.0)
columnnames = parametertools.MonthParameter.entrynames
rownames = whmod_constants.LANDTYPE_CONSTANTS.get_sortednames(
relevant=whmod_masks.LandTypeSoil.relevant
)
[docs]
def trim(self, lower=None, upper=None) -> bool:
r"""Trim |IrrigationTrigger| following
:math:`0 \leq IrrigationTrigger \leq IrrigationTarget \leq 1`.
>>> from hydpy.models.whmod import *
>>> parameterstep()
>>> trigger = irrigationtrigger
>>> trigger(
... grass=0.0,
... deciduous=0.0,
... corn=[0.0, 0.0, 0.0, 0.0, 0.0, 0.7, 1.0, 1.3, 0.0, 0.0, 0.0, 0.0],
... conifer=0.0,
... springwheat=0.0,
... winterwheat=0.0,
... sugarbeets=0.0,
... )
>>> trigger.corn_jun, trigger.corn_jul, trigger.corn_aug
(0.7, 1.0, 1.0)
>>> irrigationtarget.corn_jun = 0.7
>>> irrigationtarget.corn_jul = 0.7
>>> trigger(
... grass=0.0,
... deciduous=0.0,
... corn=[0.0, 0.0, 0.0, 0.0, 0.0, 0.7, 1.0, 1.3, 0.0, 0.0, 0.0, 0.0],
... conifer=0.0,
... springwheat=0.0,
... winterwheat=0.0,
... sugarbeets=0.0,
... )
>>> trigger = irrigationtrigger
>>> trigger.corn_jun, trigger.corn_jul, trigger.corn_aug
(0.7, 0.7, 1.0)
"""
if upper is None:
upper = self.subpars.irrigationtarget.values.copy()
upper[numpy.isnan(upper)] = 1.0
return super().trim(lower, upper)
[docs]
class IrrigationTarget(parametertools.KeywordParameter2D):
"""Relative soil moisture content at which irrigation ends [-]."""
TYPE, TIME, SPAN = float, None, (0.0, 1.0)
columnnames = parametertools.MonthParameter.entrynames
rownames = whmod_constants.LANDTYPE_CONSTANTS.get_sortednames(
relevant=whmod_masks.LandTypeSoil.relevant
)
[docs]
def trim(self, lower=None, upper=None) -> bool:
r"""Trim |IrrigationTarget| following
:math:`0 \leq IrrigationTrigger \leq IrrigationTarget \leq 1`.
>>> from hydpy.models.whmod import *
>>> parameterstep()
>>> target = irrigationtarget
>>> target(
... grass=0.0,
... deciduous=0.0,
... corn=[0.0, 0.0, 0.0, 0.0, 0.0, 0.7, 0.3, -0.1, 0.0, 0.0, 0.0, 0.0],
... conifer=0.0,
... springwheat=0.0,
... winterwheat=0.0,
... sugarbeets=0.0,
... )
>>> target.corn_jun, target.corn_jul, target.corn_aug
(0.7, 0.3, 0.0)
>>> irrigationtrigger.corn_jun = 0.7
>>> irrigationtrigger.corn_jul = 0.7
>>> target(
... grass=0.0,
... deciduous=0.0,
... corn=[0.0, 0.0, 0.0, 0.0, 0.0, 0.7, 0.3, -0.1, 0.0, 0.0, 0.0, 0.0],
... conifer=0.0,
... springwheat=0.0,
... winterwheat=0.0,
... sugarbeets=0.0,
... )
>>> target = irrigationtarget
>>> target.corn_jun, target.corn_jul, target.corn_aug
(0.7, 0.7, 0.0)
"""
if lower is None:
lower = self.subpars.irrigationtrigger.values.copy()
lower[numpy.isnan(lower)] = 0.0
return super().trim(lower, upper)
[docs]
class WithExternalIrrigation(parametertools.Parameter):
"""Flag to turn on/off external irrigation [-]."""
NDIM, TYPE, TIME = 0, bool, None
[docs]
class BaseflowIndex(whmod_parameters.LandTypeGroundwaterParameter):
"""Baseflow index [-]."""
TYPE, TIME, SPAN = float, None, (0.0, 1.0)
[docs]
class RechargeDelay(parametertools.Parameter):
"""Delay between soil percolation and groundwater recharge [T]."""
NDIM, TYPE, TIME, SPAN = 0, float, False, (0.0, None)