# -*- coding: utf-8 -*-
# pylint: disable=missing-docstring
# pylint: enable=missing-docstring
# import...
# ...from site-packages
import numpy
# ...from HydPy
import hydpy
from hydpy.core import exceptiontools
from hydpy.core import objecttools
from hydpy.core import parametertools
# ...from lland
from hydpy.models.lland import lland_control
from hydpy.models.lland import lland_fixed
from hydpy.models.lland import lland_parameters
from hydpy.models.lland.lland_constants import LAUBW, MISCHW, NADELW
[docs]class MOY(parametertools.MOYParameter):
"""References the "global" month of the year index array [-]."""
[docs]class DOY(parametertools.DOYParameter):
"""References the "global" day of the year index array [-]."""
[docs]class Seconds(parametertools.SecondsParameter):
"""The length of the actual simulation step size in seconds [s]."""
[docs]class Hours(parametertools.HoursParameter):
"""The length of the actual simulation step size in hours [h]."""
[docs]class Days(parametertools.DaysParameter):
"""The length of the actual simulation step size in days [d]."""
[docs]class SCT(parametertools.SCTParameter):
"""References the "global" standard clock time array [-]."""
[docs]class UTCLongitude(parametertools.UTCLongitudeParameter):
"""Longitude of the centre of the local time zone [°]."""
[docs]class NmbLogEntries(parametertools.Parameter):
"""The number of log entries required for a memory duration of 24 hours
[-]."""
NDIM, TYPE, TIME, SPAN = 0, int, None, (1, None)
[docs] def update(self):
"""Calculate the number of entries and adjust the shape of all
relevant log sequences.
The aimed memory duration is one day. Hence, the number of the
required log entries depends on the simulation step size:
>>> from hydpy.models.lland import *
>>> parameterstep()
>>> from hydpy import pub
>>> nhru(2)
>>> pub.timegrids = "2000-01-01", "2000-01-02", "1h"
>>> derived.nmblogentries.update()
>>> derived.nmblogentries
nmblogentries(24)
>>> logs
wet0([[nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]])
loggedteml(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan)
loggedrelativehumidity(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan)
loggedsunshineduration(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan)
loggedglobalradiation(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan)
loggedwindspeed2m(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan)
To prevent from loosing information, updating parameter |NmbLogEntries|
resets the shape of the relevant log sequences only when necessary:
>>> logs.wet0 = 1.0
>>> logs.loggedteml = 2.0
>>> logs.loggedrelativehumidity.shape = (6,)
>>> logs.loggedrelativehumidity = 3.0
>>> derived.nmblogentries.update()
>>> logs # doctest: +ELLIPSIS
wet0([[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]])
loggedteml(2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0)
loggedrelativehumidity(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan)
...
There is an explicit check for inappropriate simulation step sizes:
>>> pub.timegrids = "2000-01-01 00:00", "2000-01-01 10:00", "5h"
>>> derived.nmblogentries.update()
Traceback (most recent call last):
...
ValueError: The value of parameter `nmblogentries` of element `?` \
cannot be determined for a the current simulation step size. The fraction of \
the memory period (1d) and the simulation step size (5h) leaves a remainder.
.. testsetup::
>>> del pub.timegrids
"""
nmb = "1d" / hydpy.pub.options.simulationstep
if nmb % 1:
raise ValueError(
f"The value of parameter {objecttools.elementphrase(self)} "
f"cannot be determined for a the current simulation step "
f"size. The fraction of the memory period (1d) and the "
f"simulation step size ({hydpy.pub.timegrids.stepsize}) "
f"leaves a remainder."
)
self(nmb)
nmb = int(nmb)
logs = self.subpars.pars.model.sequences.logs
for seq in logs:
shape = exceptiontools.getattr_(seq, "shape", (None,))
if nmb != shape[-1]:
seq.shape = nmb
[docs]class LatitudeRad(parametertools.Parameter):
"""The latitude [rad]."""
NDIM, TYPE, TIME, SPAN = 0, float, None, (-1.5708, 1.5708)
CONTROLPARAMETERS = (lland_control.Latitude,)
[docs] def update(self):
"""Update |LatitudeRad| based on parameter |Latitude|.
>>> from hydpy import round_
>>> from hydpy.models.lland import *
>>> parameterstep()
>>> for value in (-90.0, -45.0, 0.0, 45.0, 90.0):
... latitude(value)
... derived.latituderad.update()
... round_(latitude.value, end=": ")
... round_(derived.latituderad.value)
-90.0: -1.570796
-45.0: -0.785398
0.0: 0.0
45.0: 0.785398
90.0: 1.570796
"""
self.value = 3.141592653589793 / 180.0 * self.subpars.pars.control.latitude
[docs]class AbsFHRU(lland_parameters.ParameterComplete):
"""Flächen der Hydrotope (areas of the respective HRUs) [km²]."""
NDIM, TYPE, TIME, SPAN = 1, float, None, (0.0, None)
CONTROLPARAMETERS = (
lland_control.FT,
lland_control.FHRU,
)
[docs] def update(self):
"""Update |AbsFHRU| based on |FT| and |FHRU|.
>>> from hydpy.models.lland import *
>>> parameterstep("1d")
>>> nhru(2)
>>> lnk(ACKER)
>>> ft(100.0)
>>> fhru(0.2, 0.8)
>>> derived.absfhru.update()
>>> derived.absfhru
absfhru(20.0, 80.0)
"""
control = self.subpars.pars.control
self.value = control.ft * control.fhru
[docs]class KInz(lland_parameters.LanduseMonthParameter):
"""Interzeptionskapazität bezogen auf die Bodenoberfläche (interception
capacity normalized to the soil surface area) [mm]."""
NDIM, TYPE, TIME, SPAN = 2, float, None, (0.0, None)
CONTROLPARAMETERS = (
lland_control.HInz,
lland_control.LAI,
)
[docs] def update(self):
"""Update |KInz| based on |HInz| and |LAI| :cite:`ref-LARSIM`
(based on :cite:`ref-Dickinson1984`).
>>> from hydpy.models.lland import *
>>> parameterstep("1d")
>>> nhru(2)
>>> hinz(0.2)
>>> lai.acker_jun = 1.0
>>> lai.vers_dec = 2.0
>>> derived.kinz.update()
>>> from hydpy import round_
>>> round_(derived.kinz.acker_jun)
0.2
>>> round_(derived.kinz.vers_dec)
0.4
"""
con = self.subpars.pars.control
self.value = con.hinz * con.lai
[docs]class HeatOfFusion(lland_parameters.ParameterLand):
"""Heat which is necessary to melt the frozen soil water content."""
NDIM, TYPE, TIME, SPAN = 1, float, None, (0.0, None)
FIXEDPARAMETERS = (
lland_fixed.BoWa2Z,
lland_fixed.RSchmelz,
)
[docs] def update(self):
"""Update |HeatOfFusion| based on |RSchmelz| and |BoWa2Z|.
Basic equation:
:math:`HeatOfFusion = RSchmelz \\cdot BoWa2Z`
>>> from hydpy.models.lland import *
>>> parameterstep("1d")
>>> nhru(2)
>>> lnk(ACKER, LAUBW)
>>> derived.heatoffusion.update()
>>> derived.heatoffusion
heatoffusion(26.72)
"""
fixed = self.subpars.pars.fixed
self.value = fixed.rschmelz * fixed.bowa2z
[docs]class Fr(lland_parameters.LanduseMonthParameter):
"""Reduktionsfaktor für Strahlung :cite:`ref-LARSIM`
(basierend auf :cite:`ref-LUBWLUWG2015`) (reduction factor for short- and
long wave radiation) :cite:`ref-LARSIM` (based on :cite:`ref-LUBWLUWG2015`)
[-]."""
NDIM, TYPE, TIME, SPAN = 2, float, None, (0.0, None)
CONTROLPARAMETERS = (
lland_control.LAI,
lland_control.P1Strahl,
lland_control.P2Strahl,
)
[docs] def update(self):
"""Update |Fr| based on |LAI|, |P1Strahl| and |P2Strahl|.
Basic equation for forests:
:math:`Fr = P1Strahl - P2Strahl \\cdot LAI`
Note that |Fr| is one for all other land use classes than |LAUBW|,
|MISCHW|, and |NADELW|, and that we do not trim |Fr| to prevent
negative values for large leaf area index values:
>>> from hydpy.models.lland import *
>>> parameterstep("1d")
>>> p1strahl(0.5)
>>> p2strahl(0.1)
>>> lai.acker_jan = 1.0
>>> lai.laubw_feb = 3.0
>>> lai.mischw_mar = 5.0
>>> lai.nadelw_apr = 7.0
>>> derived.fr.update()
>>> from hydpy import round_
>>> round_(derived.fr.acker_jan)
1.0
>>> round_(derived.fr.laubw_feb)
0.2
>>> round_(derived.fr.mischw_mar)
0.0
>>> round_(derived.fr.nadelw_apr)
-0.2
"""
con = self.subpars.pars.control
values = self.values
for idx, lais in enumerate(con.lai.values):
if idx + 1 in (LAUBW, MISCHW, NADELW):
values[idx, :] = con.p1strahl - con.p2strahl * lais
else:
values[idx, :] = 1.0
[docs]class NFk(lland_parameters.ParameterSoil):
"""Nutzbare Feldkapazität (usable field capacity) [mm]."""
NDIM, TYPE, TIME, SPAN = 1, float, None, (0.0, None)
CONTROLPARAMETERS = (
lland_control.PWP,
lland_control.FK,
)
[docs] def update(self):
"""Update |NFk| based on |PWP| and |FK|.
>>> from hydpy.models.lland import *
>>> parameterstep("1d")
>>> nhru(1)
>>> lnk(ACKER)
>>> fk(100.0)
>>> pwp(20.0)
>>> derived.nfk.update()
>>> derived.nfk
nfk(80.0)
"""
con = self.subpars.pars.control
self.value = con.fk - con.pwp
[docs]class KB(parametertools.Parameter):
"""Konzentrationszeit des Basisabflusses (concentration time of baseflow)
[T]."""
NDIM, TYPE, TIME, SPAN = 0, float, False, (0.0, None)
CONTROLPARAMETERS = (
lland_control.EQB,
lland_control.TInd,
)
[docs] def update(self):
"""Update |KB| based on |EQB| and |TInd|.
>>> from hydpy.models.lland import *
>>> simulationstep("1h")
>>> parameterstep("1d")
>>> eqb(10.0)
>>> tind(10.0)
>>> derived.kb.update()
>>> derived.kb
kb(100.0)
"""
con = self.subpars.pars.control
self.value = con.eqb * con.tind
[docs]class KI1(parametertools.Parameter):
"""Konzentrationszeit des "unteren" Zwischenabflusses (concentration time
of the first interflow component) [T]."""
NDIM, TYPE, TIME, SPAN = 0, float, False, (0.0, None)
CONTROLPARAMETERS = (
lland_control.EQI1,
lland_control.TInd,
)
[docs] def update(self):
"""Update |KI1| based on |EQI1| and |TInd|.
>>> from hydpy.models.lland import *
>>> simulationstep("1h")
>>> parameterstep("1d")
>>> eqi1(5.0)
>>> tind(10.0)
>>> derived.ki1.update()
>>> derived.ki1
ki1(50.0)
"""
con = self.subpars.pars.control
self.value = con.eqi1 * con.tind
[docs]class KI2(parametertools.Parameter):
"""Konzentrationszeit des "oberen" Zwischenabflusses" (concentration time
of the second interflow component) [T]."""
NDIM, TYPE, TIME, SPAN = 0, float, False, (0.0, None)
CONTROLPARAMETERS = (
lland_control.EQI2,
lland_control.TInd,
)
[docs] def update(self):
"""Update |KI2| based on |EQI2| and |TInd|.
>>> from hydpy.models.lland import *
>>> simulationstep("1h")
>>> parameterstep("1d")
>>> eqi2(1.0)
>>> tind(10.0)
>>> derived.ki2.update()
>>> derived.ki2
ki2(10.0)
"""
con = self.subpars.pars.control
self.value = con.eqi2 * con.tind
[docs]class KD1(parametertools.Parameter):
"""Konzentrationszeit des "langsamen" Direktabflusses (concentration time
of the slower component of direct runoff) [T]."""
NDIM, TYPE, TIME, SPAN = 0, float, False, (0.0, None)
CONTROLPARAMETERS = (
lland_control.EQD1,
lland_control.TInd,
)
[docs] def update(self):
"""Update |KD1| based on |EQD1| and |TInd|.
>>> from hydpy.models.lland import *
>>> simulationstep("1h")
>>> parameterstep("1d")
>>> eqd1(0.5)
>>> tind(10.0)
>>> derived.kd1.update()
>>> derived.kd1
kd1(5.0)
"""
con = self.subpars.pars.control
self.value = con.eqd1 * con.tind
[docs]class KD2(parametertools.Parameter):
"""Konzentrationszeit des "schnellen" Direktabflusses (concentration time
of the faster component of direct runoff) [T]."""
NDIM, TYPE, TIME, SPAN = 0, float, False, (0.0, None)
CONTROLPARAMETERS = (
lland_control.EQD2,
lland_control.TInd,
)
[docs] def update(self):
"""Update |KD2| based on |EQD2| and |TInd|.
>>> from hydpy.models.lland import *
>>> simulationstep("1h")
>>> parameterstep("1d")
>>> eqd2(0.1)
>>> tind(10.0)
>>> derived.kd2.update()
>>> derived.kd2
kd2(1.0)
"""
con = self.subpars.pars.control
self.value = con.eqd2 * con.tind
[docs]class QBGAMax(parametertools.Parameter):
"""Maximaler Abfluss aus dem Basisabfluss-Gebietsspeicher (maximum outflow
from the storage compartment for base flow) [mm/T]."""
NDIM, TYPE, TIME, SPAN = 0, float, True, (0.0, None)
CONTROLPARAMETERS = (
lland_control.GSBMax,
lland_control.VolBMax,
)
DERIVEDPARAMETERS = (KB,)
[docs] def update(self):
r"""Update based on :math:`QBGAMax = (GSBMax \cdot VolBMax) / KB`.
>>> from hydpy.models.lland import *
>>> simulationstep("1h")
>>> parameterstep("1d")
>>> volbmax(100.0)
>>> gsbmax(2.0)
>>> derived.kb(50.0)
>>> derived.qbgamax.update()
>>> derived.qbgamax
qbgamax(4.0)
For zero runoff concentration time, we generally set |QBGAMax| to zero,
even if |VolBMax| is also to zero:
>>> volbmax(0.0)
>>> derived.kb(0.0)
>>> derived.qbgamax.update()
>>> derived.qbgamax
qbgamax(inf)
"""
con = self.subpars.pars.control
der = self.subpars
if der.kb > 0.0:
self.value = (con.gsbmax * con.volbmax) / der.kb
else:
self.value = numpy.inf
[docs]class QFactor(parametertools.Parameter):
"""Factor for converting mm/stepsize to m³/s."""
NDIM, TYPE, TIME, SPAN = 0, float, None, (0.0, None)
CONTROLPARAMETERS = (lland_control.FT,)
[docs] def update(self):
"""Update |QFactor| based on |FT| and the current simulation step size.
>>> from hydpy.models.lland import *
>>> parameterstep("1d")
>>> simulationstep("1d")
>>> ft(10.0)
>>> derived.qfactor.update()
>>> derived.qfactor
qfactor(0.115741)
"""
con = self.subpars.pars.control
self.value = con.ft * 1000.0 / hydpy.pub.options.simulationstep.seconds