Source code for hydpy.models.wq.wq_derived

# pylint: disable=missing-module-docstring
# import...
# ...from standard library
import itertools

# ...from standard library
import math

# ...from site-packages
import numpy

# ...from HydPy
from hydpy.core import parametertools
from hydpy.auxs import smoothtools

# ...from wq
from hydpy.models.wq import wq_control
from hydpy.models.wq import wq_variables


[docs] class BottomDepths(wq_variables.MixinTrapezes, parametertools.Parameter): """The cumulated depth of a trapeze and its lower neighbours [m].""" TYPE, TIME, SPAN = float, None, (0.0, None) CONTROLPARAMETERS = (wq_control.BottomLevels,)
[docs] def update(self) -> None: r"""Calculate the depth values based on :math:`BottomDepths_i = BottomLevels_i - BottomLevels_0`. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbtrapezes(3) >>> bottomlevels(1.0, 4.0, 6.0) >>> derived.bottomdepths.update() >>> derived.bottomdepths bottomdepths(0.0, 3.0, 5.0) """ bottomlevels = self.subpars.pars.control.bottomlevels.values self.values = bottomlevels - bottomlevels[0]
[docs] class TrapezeHeights(wq_variables.MixinTrapezes, parametertools.Parameter): """The individual height of each trapeze [m]. The highest trapeze has no upper neighbour and is thus infinitely high. """ TYPE, TIME, SPAN = float, None, (0.0, None) CONTROLPARAMETERS = (wq_control.BottomLevels,)
[docs] def update(self) -> None: r"""Calculate the height values based on :math:`TrapezeHeights_i = BottomLevels_{i+1} - BottomLevels_i`. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbtrapezes(3) >>> bottomlevels(1.0, 4.0, 6.0) >>> derived.trapezeheights.update() >>> derived.trapezeheights trapezeheights(3.0, 2.0, inf) """ self.values = numpy.inf self.values[:-1] = numpy.diff(self.subpars.pars.control.bottomlevels.values)
[docs] class SlopeWidths(wq_variables.MixinTrapezes, parametertools.Parameter): """The total width of both side slopes of each trapeze. The highest trapeze has no upper neighbour and is thus infinitely high and potentially infinitely wide. """ TYPE, TIME, SPAN = float, None, (0.0, None) CONTROLPARAMETERS = (wq_control.SideSlopes,) DERIVEDPARAMETERS = (TrapezeHeights,)
[docs] def update(self) -> None: r"""Calculate the slope width values based on :math:`SlopeWidths = 2 \cdot SideSlopes \cdot TrapezeHeights`. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbtrapezes(3) >>> sideslopes(0.0, 2.0, 2.0) >>> derived.trapezeheights(2.0, 3.0, inf) >>> derived.slopewidths.update() >>> derived.slopewidths slopewidths(0.0, 12.0, inf) """ sideslopes = self.subpars.pars.control.sideslopes.values trapezeheights = self.subpars.trapezeheights.values self.values = numpy.inf self.values[:-1] = 2.0 * sideslopes[:-1] * trapezeheights[:-1]
[docs] class TrapezeAreas(wq_variables.MixinTrapezes, parametertools.Parameter): """The individual area of each trapeze [m]. The highest trapeze has no upper neighbour and is thus infinitely large. """ TYPE, TIME, SPAN = float, None, (0.0, None) CONTROLPARAMETERS = (wq_control.BottomWidths,) DERIVEDPARAMETERS = (TrapezeHeights, SlopeWidths)
[docs] def update(self) -> None: r"""Calculate the perimeter derivatives based on :math:`(BottomWidths + SlopeWidths / 2) \cdot TrapezeHeights`. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbtrapezes(4) >>> bottomlevels(1.0, 3.0, 4.0, 5.0) >>> bottomwidths(2.0, 0.0, 2.0, 2.0) >>> sideslopes(0.0, 2.0, 2.0, 2.0) >>> derived.trapezeheights.update() >>> derived.slopewidths.update() >>> derived.trapezeareas.update() >>> derived.trapezeareas trapezeareas(4.0, 4.0, 10.0, inf) """ wb = self.subpars.pars.control.bottomwidths.values ht = self.subpars.trapezeheights.values ws = self.subpars.slopewidths.values self.values = (wb + ws / 2.0) * ht w = numpy.cumsum(wb + ws) self.values[1:] += w[:-1] * ht[1:]
[docs] class PerimeterDerivatives(wq_variables.MixinTrapezes, parametertools.Parameter): """Change of the perimeter of each trapeze relative to a water level increase within the trapeze's range [-]. """ TYPE, TIME, SPAN = float, None, (0.0, None) CONTROLPARAMETERS = (wq_control.SideSlopes,)
[docs] def update(self) -> None: r"""Calculate the perimeter derivatives based on :math:`2 \cdot \sqrt{1 + SideSlopes^2}`. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbtrapezes(2) >>> sideslopes(0.0, 2.0) >>> derived.perimeterderivatives.update() >>> derived.perimeterderivatives perimeterderivatives(2.0, 4.472136) """ sideslopes = self.subpars.pars.control.sideslopes.value self.values = 2.0 * (1.0 + sideslopes**2.0) ** 0.5
class _SectorWidths(wq_variables.MixinSectorsAndWidths, parametertools.Parameter): TYPE, TIME, SPAN = float, None, (0.0, None) def _update(self, widths: wq_control.FlowWidths | wq_control.TotalWidths) -> None: control = self.subpars.pars.control n = control.nmbsectors.value t = control.transitions.values w = widths.values self.values = 0.0 s = self.values for i in range(n): w0 = 0.0 if i == 0 else w[t[i - 1]] w1 = w[-1] if i + 1 == n else w[t[i]] s[i, :] = numpy.clip(w - w0, 0.0, w1 - w0)
[docs] class SectorFlowWidths(_SectorWidths): """The sector-specific widths of those subareas of the cross section involved in water routing [m].""" CONTROLPARAMETERS = ( wq_control.NmbSectors, wq_control.Transitions, wq_control.Heights, wq_control.FlowWidths, )
[docs] def update(self) -> None: """Allocate the |wq_control.FlowWidths| parts to the respective cross-section sectors. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbwidths(9) >>> nmbsectors(4) >>> heights(1.0, 3.0, 4.0, 4.0, 4.0, 5.0, 6.0, 7.0, 8.0) >>> flowwidths(2.0, 4.0, 6.0, 14.0, 18.0, 18.0, 24.0, 28.0, 30.0) >>> transitions(2, 3, 5) >>> derived.sectorflowwidths.update() >>> derived.sectorflowwidths sectorflowwidths([[2.0, 4.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0], [0.0, 0.0, 0.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0], [0.0, 0.0, 0.0, 0.0, 4.0, 4.0, 4.0, 4.0, 4.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.0, 10.0, 12.0]]) """ flowwidths = self.subpars.pars.control.flowwidths assert isinstance(flowwidths, wq_control.FlowWidths) self._update(flowwidths)
[docs] class SectorTotalWidths(_SectorWidths): """The sector-specific widths of the total cross section [m].""" CONTROLPARAMETERS = ( wq_control.NmbSectors, wq_control.Transitions, wq_control.Heights, wq_control.TotalWidths, )
[docs] def update(self) -> None: """Allocate the |wq_control.TotalWidths| parts to the respective cross-section sectors. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbwidths(9) >>> nmbsectors(4) >>> heights(1.0, 3.0, 4.0, 4.0, 4.0, 5.0, 6.0, 7.0, 8.0) >>> totalwidths(2.0, 4.0, 6.0, 14.0, 18.0, 18.0, 24.0, 28.0, 30.0) >>> transitions(2, 3, 5) >>> derived.sectortotalwidths.update() >>> derived.sectortotalwidths sectortotalwidths([[2.0, 4.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0], [0.0, 0.0, 0.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0], [0.0, 0.0, 0.0, 0.0, 4.0, 4.0, 4.0, 4.0, 4.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.0, 10.0, 12.0]]) """ totalwidths = self.subpars.pars.control.totalwidths assert isinstance(totalwidths, wq_control.TotalWidths) self._update(totalwidths)
class _SectorAreas(wq_variables.MixinSectorsAndWidths, parametertools.Parameter): TYPE, TIME, SPAN = float, None, (0.0, None) def _update(self, widths: SectorFlowWidths | SectorTotalWidths) -> None: h = self.subpars.pars.control.heights.values w = widths.values h_diff = numpy.diff(h) w_mean = (w[:, :-1] + w[:, 1:]) / 2.0 self.values = 0.0 self.values[:, 1:] = numpy.cumsum(h_diff * w_mean, axis=1)
[docs] class SectorFlowAreas(_SectorAreas): """The sector-specific wetted areas of those subareas of the cross section involved in water routing [m²].""" CONTROLPARAMETERS = (wq_control.Heights,) DERIVEDPARAMETERS = (SectorFlowWidths,)
[docs] def update(self) -> None: """Calculate the cumulative sum of the individual trapeze areas defined by the height-width pairs of the individual sectors. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbwidths(9) >>> nmbsectors(4) >>> heights(1.0, 3.0, 4.0, 4.0, 4.0, 5.0, 6.0, 7.0, 8.0) >>> flowwidths(2.0, 4.0, 6.0, 14.0, 18.0, 18.0, 24.0, 28.0, 30.0) >>> transitions(2, 3, 5) >>> derived.sectorflowwidths.update() >>> derived.sectorflowareas.update() >>> derived.sectorflowareas sectorflowareas([[0.0, 6.0, 11.0, 11.0, 11.0, 17.0, 23.0, 29.0, 35.0], [0.0, 0.0, 0.0, 0.0, 0.0, 8.0, 16.0, 24.0, 32.0], [0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 8.0, 12.0, 16.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 11.0, 22.0]]) """ sectorflowwidths = self.subpars.sectorflowwidths assert isinstance(sectorflowwidths, SectorFlowWidths) self._update(sectorflowwidths)
[docs] class SectorTotalAreas(_SectorAreas): """The sector-specific wetted areas of the total cross section [m²].""" CONTROLPARAMETERS = (wq_control.Heights,) DERIVEDPARAMETERS = (SectorTotalWidths,)
[docs] def update(self) -> None: """Calculate the cumulative sum of the individual trapeze areas defined by the height-width pairs of the individual sectors. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbwidths(9) >>> nmbsectors(4) >>> heights(1.0, 3.0, 4.0, 4.0, 4.0, 5.0, 6.0, 7.0, 8.0) >>> totalwidths(2.0, 4.0, 6.0, 14.0, 18.0, 18.0, 24.0, 28.0, 30.0) >>> transitions(2, 3, 5) >>> derived.sectortotalwidths.update() >>> derived.sectortotalareas.update() >>> derived.sectortotalareas sectortotalareas([[0.0, 6.0, 11.0, 11.0, 11.0, 17.0, 23.0, 29.0, 35.0], [0.0, 0.0, 0.0, 0.0, 0.0, 8.0, 16.0, 24.0, 32.0], [0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 8.0, 12.0, 16.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 11.0, 22.0]]) """ sectortotalwidths = self.subpars.sectortotalwidths assert isinstance(sectortotalwidths, SectorTotalWidths) self._update(sectortotalwidths)
[docs] class SectorFlowPerimeters( wq_variables.MixinSectorsAndWidths, parametertools.Parameter ): """The sector-specific wetted perimeters of those subareas of the cross section involved in water routing [m].""" TYPE, TIME, SPAN = float, None, (0.0, None) CONTROLPARAMETERS = (wq_control.Heights,) DERIVEDPARAMETERS = (SectorFlowWidths,)
[docs] def update(self) -> None: """Calculate the cumulative sum of the individual trapeze perimeters defined by the height-width pairs of the individual sectors. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbwidths(9) >>> nmbsectors(4) >>> heights(1.0, 3.0, 4.0, 4.0, 4.0, 5.0, 6.0, 7.0, 8.0) >>> flowwidths(2.0, 4.0, 6.0, 14.0, 18.0, 18.0, 24.0, 28.0, 30.0) >>> transitions(2, 3, 5) >>> derived.sectorflowwidths.update() >>> derived.sectorflowperimeters.update() >>> derived.sectorflowperimeters sectorflowperimeters([[2.0, 6.472136, 9.300563, 9.300563, 9.300563, 11.300563, 13.300563, 15.300563, 17.300563], [0.0, 0.0, 0.0, 8.0, 8.0, 10.0, 12.0, 14.0, 16.0], [0.0, 0.0, 0.0, 0.0, 4.0, 6.0, 8.0, 10.0, 12.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.324555, 10.796691, 13.625118]]) """ control = self.subpars.pars.control h = control.heights.values w = self.subpars.sectorflowwidths.values dh = numpy.diff(h) dw = numpy.diff(w) p = numpy.sqrt(numpy.square(2.0 * dh) + numpy.square(dw)) self.values = 0.0 for i, t in enumerate(itertools.chain([0], control.transitions.values)): self.values[i, t:] = w[i, t] self.values[i, t + 1 :] += numpy.cumsum(p[i, t:])
[docs] class SectorFlowPerimeterDerivatives( wq_variables.MixinSectorsAndWidths, parametertools.Parameter ): """The sector-specific changes in the wetted perimeters of those subareas of the cross section involved in water routing with respect to water level increases [m].""" TYPE, TIME, SPAN = float, None, (0.0, None) CONTROLPARAMETERS = (wq_control.Heights,) DERIVEDPARAMETERS = (SectorFlowWidths,)
[docs] def update(self) -> None: r"""Calculate the flow perimeter derivatives based on :math:`2 \cdot \sqrt{1 + (dw/dh/2)^2}`. >>> from hydpy.models.wq import * >>> parameterstep() >>> nmbwidths(9) >>> nmbsectors(4) >>> heights(1.0, 3.0, 4.0, 4.0, 4.0, 5.0, 6.0, 7.0, 8.0) >>> flowwidths(2.0, 4.0, 6.0, 14.0, 18.0, 18.0, 24.0, 28.0, 30.0) >>> transitions(2, 3, 5) >>> derived.sectorflowwidths.update() >>> derived.sectorflowperimeterderivatives.update() >>> derived.sectorflowperimeterderivatives sectorflowperimeterderivatives([[2.236068, 2.828427, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0], [nan, nan, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0], [nan, nan, nan, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0], [nan, nan, nan, nan, nan, 6.324555, 4.472136, 2.828427, 2.0]]) """ control = self.subpars.pars.control n = control.nmbwidths.value dh = numpy.diff(control.heights.values) dw = numpy.diff(self.subpars.sectorflowwidths.values) self.values = numpy.nan v = self.values v[:, -1] = 2.0 for i, t in enumerate(itertools.chain([0], control.transitions.values)): d = 2.0 for j in range(n - 2, t - 1, -1): if dh[j] > 0.0: d = 2.0 * math.sqrt(1.0 + math.pow(dw[i, j] / dh[j] / 2.0, 2.0)) v[i, j] = d
[docs] class CrestHeightRegularisation(parametertools.Parameter): """Regularisation parameter related to the difference between the water depth and the crest height [m].""" NDIM, TYPE, TIME, SPAN = 0, float, None, (0.0, None) CONTROLPARAMETERS = (wq_control.CrestHeightTolerance,)
[docs] def update(self) -> None: """Calculate the smoothing parameter value. The documentation on module |smoothtools| explains the following example in some detail: >>> from hydpy.models.wq import * >>> from hydpy.cythons.smoothutils import smooth_logistic2 >>> from hydpy import round_ >>> parameterstep() >>> crestheighttolerance(0.0) >>> derived.crestheightregularisation.update() >>> round_(smooth_logistic2(0.0, derived.crestheightregularisation)) 0.0 >>> crestheighttolerance(0.0025) >>> derived.crestheightregularisation.update() >>> round_(smooth_logistic2(0.0025, derived.crestheightregularisation)) 0.00251 """ metapar = self.subpars.pars.control.crestheighttolerance.value self(smoothtools.calc_smoothpar_logistic2(1000.0 * metapar) / 1000.0)