Source code for hydpy.models.musk.musk_model

# -*- coding: utf-8 -*-
# pylint: disable=missing-module-docstring

# import...
# ...from HydPy
from hydpy.core import modeltools
from hydpy.cythons import modelutils
from hydpy.auxs import roottools
from hydpy.interfaces import routinginterfaces
from hydpy.core.typingtools import *

from hydpy.models.musk import musk_control
from hydpy.models.musk import musk_derived
from hydpy.models.musk import musk_solver
from hydpy.models.musk import musk_fluxes
from hydpy.models.musk import musk_factors
from hydpy.models.musk import musk_states
from hydpy.models.musk import musk_inlets
from hydpy.models.musk import musk_outlets


[docs] class Pick_Inflow_V1(modeltools.Method): """Assign the actual value of the inlet sequence to the inflow sequence.""" REQUIREDSEQUENCES = (musk_inlets.Q,) RESULTSEQUENCES = (musk_fluxes.Inflow,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: inl = model.sequences.inlets.fastaccess flu = model.sequences.fluxes.fastaccess flu.inflow = 0.0 for idx in range(inl.len_q): flu.inflow += inl.q[idx][0]
[docs] class Update_Discharge_V1(modeltools.Method): r"""Assign the inflow to the start point of the first channel segment. Example: >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(3) >>> fluxes.inflow = 2.0 >>> model.update_discharge_v1() >>> states.discharge discharge(2.0, nan, nan, nan) """ REQUIREDSEQUENCES = (musk_fluxes.Inflow,) UPDATEDSEQUENCES = (musk_states.Discharge,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: flu = model.sequences.fluxes.fastaccess sta = model.sequences.states.fastaccess sta.discharge[0] = flu.inflow
[docs] class Calc_Discharge_V1(modeltools.Method): r"""Apply the routing equation with fixed coefficients. Basic equation: :math:`Q_{space+1,time+1} = Coefficients_0 \cdot Discharge_{space,time+1} + Coefficients_1 \cdot Discharge_{space,time} + Coefficients_2 \cdot Discharge_{space+1,time}` Examples: First, define a channel divided into four segments: >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(4) The following coefficients correspond to pure translation without diffusion: >>> coefficients(0.0, 1.0, 0.0) The initial flow is 2 m³/s: >>> states.discharge.old = 2.0 >>> states.discharge.new = 2.0 Successive invocations of method |Calc_Discharge_V1| shift the given inflows to the next lower endpoints at each time step: >>> states.discharge[0] = 5.0 >>> model.run_segments(model.calc_discharge_v1) >>> model.new2old() >>> states.discharge discharge(5.0, 2.0, 2.0, 2.0, 2.0) >>> states.discharge[0] = 8.0 >>> model.run_segments(model.calc_discharge_v1) >>> model.new2old() >>> states.discharge discharge(8.0, 5.0, 2.0, 2.0, 2.0) >>> states.discharge[0] = 6.0 >>> model.run_segments(model.calc_discharge_v1) >>> model.new2old() >>> states.discharge discharge(6.0, 8.0, 5.0, 2.0, 2.0) We repeat the example with strong wave diffusion: >>> coefficients(0.5, 0.0, 0.5) >>> states.discharge.old = 2.0 >>> states.discharge.new = 2.0 >>> states.discharge[0] = 5.0 >>> model.run_segments(model.calc_discharge_v1) >>> model.new2old() >>> states.discharge discharge(5.0, 3.5, 2.75, 2.375, 2.1875) >>> states.discharge[0] = 8.0 >>> model.run_segments(model.calc_discharge_v1) >>> model.new2old() >>> states.discharge discharge(8.0, 5.75, 4.25, 3.3125, 2.75) >>> states.discharge[0] = 6.0 >>> model.run_segments(model.calc_discharge_v1) >>> model.new2old() >>> states.discharge discharge(6.0, 5.875, 5.0625, 4.1875, 3.46875) """ CONTROLPARAMETERS = (musk_control.Coefficients,) UPDATEDSEQUENCES = (musk_states.Discharge,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: con = model.parameters.control.fastaccess new = model.sequences.states.fastaccess_new old = model.sequences.states.fastaccess_old i = model.idx_segment new.discharge[i + 1] = ( con.coefficients[0] * new.discharge[i] + con.coefficients[1] * old.discharge[i] + con.coefficients[2] * old.discharge[i + 1] )
[docs] class Calc_ReferenceDischarge_V1(modeltools.Method): r"""Estimate the reference discharge according to :cite:t:`ref-Todini2007`. Basic equations (equations 45 and 46): :math:`ReferenceDischarge_{next, new} = \frac{Discharge_{last, new} + Discharge^*_{next, new}}{2}` :math:`Discharge^*_{next, new} = Discharge_{next, old} + (Discharge_{last, new} - Discharge_{last, old})` Examples: The Muskingum-Cunge-Todini method requires an initial guess for the new discharge value at the segment endpoint, which other methods have to improve later. However, the final discharge value will still depend on the initial estimate. Hence, :cite:t:`ref-Todini2007` suggests an iterative refinement by repeating all relevant methods. Method |Calc_ReferenceDischarge_V1| plays a significant role in controlling this refinement. It calculates the initial estimate as defined in the basic equationsDuring the first run (when the index property |Idx_Run| is zero): >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(1) >>> states.discharge.old = 3.0, 2.0 >>> states.discharge.new = 4.0, 5.0 >>> model.idx_run = 0 >>> model.calc_referencedischarge_v1() >>> fluxes.referencedischarge referencedischarge(3.5) However, subsequent runs use the already available estimate calculated in the last iteration.: >>> model.idx_run = 1 >>> model.calc_referencedischarge_v1() >>> fluxes.referencedischarge referencedischarge(4.5) """ REQUIREDSEQUENCES = (musk_states.Discharge,) RESULTSEQUENCES = (musk_fluxes.ReferenceDischarge,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: flu = model.sequences.fluxes.fastaccess old = model.sequences.states.fastaccess_old new = model.sequences.states.fastaccess_new i = model.idx_segment if model.idx_run == 0: est: float = old.discharge[i + 1] + new.discharge[i] - old.discharge[i] else: est = new.discharge[i + 1] flu.referencedischarge[i] = (new.discharge[i] + est) / 2.0
[docs] class Return_Discharge_CrossSectionModel_V1(modeltools.Method): """Let a submodel that follows the |CrossSectionModel_V1| submodel interface calculate the discharge for the given water depth and return it. See the documentation on method |Return_ReferenceDischargeError_V1| for an example. """ @staticmethod def __call__( model: modeltools.SegmentModel, wqmodel: routinginterfaces.CrossSectionModel_V1, waterdepth: float, ) -> float: wqmodel.use_waterdepth(waterdepth) return wqmodel.get_discharge()
[docs] class Return_ReferenceDischargeError_V1(modeltools.Method): r"""Calculate the difference between the discharge corresponding to the given water depth and the reference discharge. Basic equation: :math:`Return\_Discharge\_V1(waterdepth) - ReferenceDischarge` Example: We use the submodel |wq_trapeze_strickler| as an example: >>> from hydpy.models.musk_mct import * >>> parameterstep() >>> nmbsegments(1) >>> bottomslope(0.01) >>> with model.add_wqmodel_v1("wq_trapeze_strickler"): ... nmbtrapezes(1) ... bottomlevels(0.0) ... bottomwidths(2.0) ... sideslopes(2.0) ... stricklercoefficients(20.0) >>> fluxes.referencedischarge = 50.0 >>> from hydpy import round_ >>> round_(model.return_referencedischargeerror_v1(3.0)) 14.475285 """ SUBMETHODS = (Return_Discharge_CrossSectionModel_V1,) REQUIREDSEQUENCES = (musk_fluxes.ReferenceDischarge,) @staticmethod def __call__(model: modeltools.SegmentModel, waterdepth: float) -> float: flu = model.sequences.fluxes.fastaccess i = model.idx_segment return ( model.return_discharge_crosssectionmodel_v1( cast(routinginterfaces.CrossSectionModel_V1, model.wqmodel), waterdepth ) - flu.referencedischarge[i] )
[docs] class Calc_ReferenceWaterDepth_V1(modeltools.Method): r"""Find the reference water depth via |Pegasus| iteration. Examples: The following test calculation extends the example of the documentation on method |Return_ReferenceDischargeError_V1|. The first and the last channel segments demonstrate that method |Calc_ReferenceWaterDepth_V1| restricts the Pegasus search to the lowest water depth of 0 m and the highest water depth of 1000 m: >>> from hydpy.models.musk_mct import * >>> parameterstep() >>> catchmentarea(100.0) >>> nmbsegments(5) >>> bottomslope(0.01) >>> with model.add_wqmodel_v1("wq_trapeze_strickler"): ... nmbtrapezes(1) ... bottomlevels(0.0) ... bottomwidths(2.0) ... sideslopes(2.0) ... stricklercoefficients(20.0) >>> solver.tolerancewaterdepth.update() >>> solver.tolerancedischarge.update() >>> fluxes.referencedischarge = -10.0, 0.0, 64.475285, 1000.0, 1000000000.0 >>> model.run_segments(model.calc_referencewaterdepth_v1) >>> factors.referencewaterdepth referencewaterdepth(0.0, 0.0, 3.0, 9.199035, 1000.0) Repeated applications of |Calc_ReferenceWaterDepth_V1| should always yield the same results but are often more efficient than the initial calculation because they use old reference water depth estimates to gain more precise initial search intervals: >>> model.run_segments(model.calc_referencewaterdepth_v1) >>> factors.referencewaterdepth referencewaterdepth(0.0, 0.0, 3.0, 9.199035, 1000.0) The Pegasus algorithm stops when the search interval is smaller than the tolerance value defined by the |ToleranceWaterDepth| parameter or if the difference to the target discharge is less than the tolerance value defined by the |ToleranceDischarge| parameter. By default, the water depth-related tolerance is zero; hence, the discharge-related tolerance must stop the iteration: >>> solver.tolerancewaterdepth tolerancewaterdepth(0.0) >>> solver.tolerancedischarge tolerancedischarge(0.0001) Increase at least one parameter to reduce computation time: >>> solver.tolerancewaterdepth(0.1) >>> factors.referencewaterdepth = nan >>> model.run_segments(model.calc_referencewaterdepth_v1) >>> factors.referencewaterdepth referencewaterdepth(0.0, 0.0, 3.000295, 9.196508, 1000.0) """ SUBMETHODS = (Return_ReferenceDischargeError_V1,) SOLVERPARAMETERS = (musk_solver.ToleranceWaterDepth, musk_solver.ToleranceDischarge) REQUIREDSEQUENCES = (musk_fluxes.ReferenceDischarge,) RESULTSEQUENCES = (musk_factors.ReferenceWaterDepth,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: sol = model.parameters.solver.fastaccess fac = model.sequences.factors.fastaccess i = model.idx_segment wl: float = fac.referencewaterdepth[i] if modelutils.isnan(wl) or modelutils.isinf(wl): mn: float = 0.0 mx: float = 2.0 elif wl <= 0.001: mn, mx = 0.0, 0.01 else: mn, mx = 0.9 * wl, 1.1 * wl fac.referencewaterdepth[i] = model.pegasusreferencewaterdepth.find_x( mn, mx, 0.0, 1000.0, sol.tolerancewaterdepth, sol.tolerancedischarge, 100 )
[docs] class Calc_WettedArea_SurfaceWidth_Celerity_CrossSectionModel_V1(modeltools.Method): """Let a submodel that follows the |CrossSectionModel_V1| interface calculate all its properties based on the current reference water level and query the wetted area, the surface width, and the celerity.""" SUBMETHODS = () REQUIREDSEQUENCES = (musk_factors.ReferenceWaterDepth,) RESULTSEQUENCES = ( musk_factors.WettedArea, musk_factors.SurfaceWidth, musk_factors.Celerity, ) @staticmethod def __call__( model: modeltools.SegmentModel, wqmodel: routinginterfaces.CrossSectionModel_V1 ) -> None: fac = model.sequences.factors.fastaccess i = model.idx_segment wqmodel.use_waterdepth(fac.referencewaterdepth[i]) fac.wettedarea[i] = wqmodel.get_wettedarea() fac.surfacewidth[i] = wqmodel.get_surfacewidth() fac.celerity[i] = wqmodel.get_celerity()
[docs] class Calc_WettedArea_SurfaceWidth_Celerity_V1(modeltools.Method): """Let a submodel that follows the |CrossSectionModel_V1| interface calculate all its properties based on the current reference water level and query the wetted area, the surface width, and the celerity. Example: We use the submodel |wq_trapeze_strickler| as an example: >>> from hydpy.models.musk_mct import * >>> parameterstep() >>> nmbsegments(3) >>> bottomslope(0.01) >>> with model.add_wqmodel_v1("wq_trapeze_strickler"): ... nmbtrapezes(1) ... bottomlevels(0.0) ... bottomwidths(2.0) ... sideslopes(0.0) ... stricklercoefficients(20.0) >>> factors.referencewaterdepth = 1.0, 2.0, 3.0 >>> model.run_segments(model.calc_wettedarea_surfacewidth_celerity_v1) >>> factors.wettedarea wettedarea(2.0, 4.0, 6.0) >>> factors.surfacewidth surfacewidth(2.0, 2.0, 2.0) >>> factors.celerity celerity(1.679895, 1.86546, 1.926124) """ SUBMETHODS = (Calc_WettedArea_SurfaceWidth_Celerity_CrossSectionModel_V1,) REQUIREDSEQUENCES = (musk_factors.ReferenceWaterDepth,) RESULTSEQUENCES = ( musk_factors.WettedArea, musk_factors.SurfaceWidth, musk_factors.Celerity, ) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: if model.wqmodel_typeid == 1: model.calc_wettedarea_surfacewidth_celerity_crosssectionmodel_v1( cast(routinginterfaces.CrossSectionModel_V1, model.wqmodel) )
[docs] class Calc_CorrectingFactor_V1(modeltools.Method): r"""Calculate the correcting factor according to :cite:t:`ref-Todini2007`. Basic equation (equation 49): :math:`CorrectingFactor = \frac{Celerity \cdot WettedArea}{ReferenceDischarge}` Example: The last segment shows that |Calc_CorrectingFactor_V1| prevents zero divisions by setting the correcting factor to one when necessary: >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(3) >>> factors.celerity = 1.0 >>> factors.wettedarea = 2.0, 2.0, 2.0 >>> fluxes.referencedischarge = 4.0, 2.0, 0.0 >>> model.run_segments(model.calc_correctingfactor_v1) >>> factors.correctingfactor correctingfactor(0.5, 1.0, 1.0) """ REQUIREDSEQUENCES = ( musk_factors.Celerity, musk_factors.WettedArea, musk_fluxes.ReferenceDischarge, ) RESULTSEQUENCES = (musk_factors.CorrectingFactor,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: fac = model.sequences.factors.fastaccess flu = model.sequences.fluxes.fastaccess i = model.idx_segment if flu.referencedischarge[i] == 0.0: fac.correctingfactor[i] = 1.0 else: fac.correctingfactor[i] = ( fac.celerity[i] * fac.wettedarea[i] / flu.referencedischarge[i] )
[docs] class Calc_CourantNumber_V1(modeltools.Method): r"""Calculate the Courant number according to :cite:t:`ref-Todini2007`. Basic equation (equation 50): :math:`CourantNumber = \frac{Celerity \cdot Seconds}{CorrectingFactor \cdot 1000 \cdot SegmentLength}` Example: The last segment shows that |Calc_CourantNumber_V1| prevents zero divisions by setting the courant number to zero when necessary: >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(5) >>> derived.seconds(1000.0) >>> derived.segmentlength(4.0) >>> factors.celerity = 2.0 >>> factors.correctingfactor = 0.0, 0.5, 1.0, 2.0, inf >>> model.run_segments(model.calc_courantnumber_v1) >>> states.courantnumber courantnumber(0.0, 1.0, 0.5, 0.25, 0.0) """ DERIVEDPARAMETERS = (musk_derived.Seconds, musk_derived.SegmentLength) REQUIREDSEQUENCES = (musk_factors.Celerity, musk_factors.CorrectingFactor) RESULTSEQUENCES = (musk_states.CourantNumber,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: der = model.parameters.derived.fastaccess fac = model.sequences.factors.fastaccess sta = model.sequences.states.fastaccess i = model.idx_segment if fac.correctingfactor[i] == 0.0: sta.courantnumber[i] = 0.0 else: sta.courantnumber[i] = (fac.celerity[i] / fac.correctingfactor[i]) * ( der.seconds / (1000.0 * der.segmentlength) )
[docs] class Calc_ReynoldsNumber_V1(modeltools.Method): r"""Calculate the cell Reynolds number according to :cite:t:`ref-Todini2007`. Basic equation (equation 51): :math:`ReynoldsNumber = \frac{ReferenceDischarge}{CorrectingFactor \cdot SurfaceWidth \cdot BottomSlope \cdot Celerity \cdot 1000 \cdot SegmentLength}` Example: The last segment shows that |Calc_ReynoldsNumber_V1| prevents zero divisions by setting the cell reynolds number to zero when necessary: >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(5) >>> bottomslope(0.01) >>> derived.segmentlength(4.0) >>> factors.surfacewidth = 5.0 >>> factors.celerity = 2.0 >>> factors.correctingfactor = 0.0, 0.5, 1.0, 2.0, inf >>> fluxes.referencedischarge = 10.0 >>> model.run_segments(model.calc_reynoldsnumber_v1) >>> states.reynoldsnumber reynoldsnumber(0.0, 0.05, 0.025, 0.0125, 0.0) """ CONTROLPARAMETERS = (musk_control.BottomSlope,) DERIVEDPARAMETERS = (musk_derived.SegmentLength,) REQUIREDSEQUENCES = ( musk_fluxes.ReferenceDischarge, musk_factors.CorrectingFactor, musk_factors.SurfaceWidth, musk_factors.Celerity, ) RESULTSEQUENCES = (musk_states.ReynoldsNumber,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: con = model.parameters.control.fastaccess der = model.parameters.derived.fastaccess fac = model.sequences.factors.fastaccess flu = model.sequences.fluxes.fastaccess sta = model.sequences.states.fastaccess i = model.idx_segment denom: float = ( fac.correctingfactor[i] * fac.surfacewidth[i] * con.bottomslope * fac.celerity[i] * (1000.0 * der.segmentlength) ) if denom == 0.0: sta.reynoldsnumber[i] = 0.0 else: sta.reynoldsnumber[i] = flu.referencedischarge[i] / denom
[docs] class Calc_Coefficient1_Coefficient2_Coefficient3_V1(modeltools.Method): r"""Calculate the coefficients of the Muskingum working formula according to :cite:t:`ref-Todini2007`. Basic equations (equation 52, corrigendum): :math:`Coefficient1 = \frac {-1 + CourantNumber_{new} + ReynoldsNumber_{new}} {1 + CourantNumber_{new} + ReynoldsNumber_{new}}` :math:`Coefficient2 = \frac {1 + CourantNumber_{old} - ReynoldsNumber_{old}} {1 + CourantNumber_{new} + ReynoldsNumber_{new}} \cdot \frac{CourantNumber_{new}}{CourantNumber_{old}}` :math:`Coefficient3 = \frac {1 - CourantNumber_{old} + ReynoldsNumber_{old}} {1 + CourantNumber_{new} + ReynoldsNumber_{new}} \cdot \frac{CourantNumber_{new}}{CourantNumber_{old}}` Examples: We make some effort to calculate consistent "old" and "new" Courant and Reynolds numbers: >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(5) >>> bottomslope(0.01) >>> derived.seconds(1000.0) >>> derived.segmentlength(4.0) >>> factors.celerity = 2.0 >>> factors.surfacewidth = 5.0 >>> factors.correctingfactor = 0.0, 0.5, 1.0, 2.0, inf >>> fluxes.referencedischarge = 10.0 >>> model.run_segments(model.calc_courantnumber_v1) >>> model.run_segments(model.calc_reynoldsnumber_v1) >>> states.courantnumber.new2old() >>> states.reynoldsnumber.new2old() >>> fluxes.referencedischarge = 11.0 >>> model.run_segments(model.calc_courantnumber_v1) >>> model.run_segments(model.calc_reynoldsnumber_v1) Due to the consistency of its input data, |Calc_Coefficient1_Coefficient2_Coefficient3_V1| calculates the three working coefficients so that their sum is one: >>> model.run_segments(model.calc_coefficient1_coefficient2_coefficient3_v1) >>> factors.coefficient1 coefficient1(-1.0, 0.026764, -0.309329, -0.582591, -1.0) >>> factors.coefficient2 coefficient2(1.0, 0.948905, 0.96563, 0.979228, 1.0) >>> factors.coefficient3 coefficient3(1.0, 0.024331, 0.343699, 0.603363, 1.0) >>> from hydpy import print_vector >>> print_vector( ... factors.coefficient1 + factors.coefficient2 + factors.coefficient3) 1.0, 1.0, 1.0, 1.0, 1.0 Note that the "old" Courant numbers of the first and the last segment are zero. >>> print_vector(states.courantnumber.old) 0.0, 1.0, 0.5, 0.25, 0.0 To prevent zero divisions, |Calc_Coefficient1_Coefficient2_Coefficient3_V1| assumes the ratio between the new and the old Courant number to be one in such cases. """ REQUIREDSEQUENCES = (musk_states.CourantNumber, musk_states.ReynoldsNumber) UPDATEDSEQUENCES = ( musk_factors.Coefficient1, musk_factors.Coefficient2, musk_factors.Coefficient3, ) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: fac = model.sequences.factors.fastaccess new = model.sequences.states.fastaccess_new old = model.sequences.states.fastaccess_old i = model.idx_segment f: float = 1.0 / (1.0 + new.courantnumber[i] + new.reynoldsnumber[i]) fac.coefficient1[i] = (new.courantnumber[i] + new.reynoldsnumber[i] - 1.0) * f if old.courantnumber[i] != 0.0: f *= new.courantnumber[i] / old.courantnumber[i] fac.coefficient2[i] = (1 + old.courantnumber[i] - old.reynoldsnumber[i]) * f fac.coefficient3[i] = (1 - old.courantnumber[i] + old.reynoldsnumber[i]) * f
[docs] class Calc_Discharge_V2(modeltools.Method): r"""Apply the routing equation with discharge-dependent coefficients. Basic equation: :math:`Discharge_{next, new} = Coefficient0 \cdot Discharge_{last, new} + Coefficient1 \cdot Discharge_{last, old} + Coefficient2 \cdot Discharge_{next, old}` Examples: First, we define a channel divided into four segments: >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(4) The following coefficients correspond to pure translation without diffusion: >>> factors.coefficient1 = 0.0 >>> factors.coefficient2 = 1.0 >>> factors.coefficient3 = 0.0 The initial flow is 2 m³/s: >>> states.discharge.old = 2.0 >>> states.discharge.new = 2.0 Successive invocations of method |Calc_Discharge_V2| shift the given inflows to the next lower endpoints at each time step: >>> states.discharge[0] = 5.0 >>> model.run_segments(model.calc_discharge_v2) >>> model.new2old() >>> states.discharge discharge(5.0, 2.0, 2.0, 2.0, 2.0) >>> states.discharge[0] = 8.0 >>> model.run_segments(model.calc_discharge_v2) >>> model.new2old() >>> states.discharge discharge(8.0, 5.0, 2.0, 2.0, 2.0) >>> states.discharge[0] = 6.0 >>> model.run_segments(model.calc_discharge_v2) >>> model.new2old() >>> states.discharge discharge(6.0, 8.0, 5.0, 2.0, 2.0) We repeat the example with strong wave diffusion: >>> factors.coefficient1 = 0.5 >>> factors.coefficient2 = 0.0 >>> factors.coefficient3 = 0.5 >>> states.discharge.old = 2.0 >>> states.discharge.new = 2.0 >>> states.discharge[0] = 5.0 >>> model.run_segments(model.calc_discharge_v2) >>> model.new2old() >>> states.discharge discharge(5.0, 3.5, 2.75, 2.375, 2.1875) >>> states.discharge[0] = 8.0 >>> model.run_segments(model.calc_discharge_v2) >>> model.new2old() >>> states.discharge discharge(8.0, 5.75, 4.25, 3.3125, 2.75) >>> states.discharge[0] = 6.0 >>> model.run_segments(model.calc_discharge_v2) >>> model.new2old() >>> states.discharge discharge(6.0, 5.875, 5.0625, 4.1875, 3.46875) """ REQUIREDSEQUENCES = ( musk_factors.Coefficient1, musk_factors.Coefficient2, musk_factors.Coefficient3, ) UPDATEDSEQUENCES = (musk_states.Discharge,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: fac = model.sequences.factors.fastaccess new = model.sequences.states.fastaccess_new old = model.sequences.states.fastaccess_old i = model.idx_segment if new.discharge[i] == old.discharge[i] == old.discharge[i + 1]: new.discharge[i + 1] = new.discharge[i] else: new.discharge[i + 1] = ( fac.coefficient1[i] * new.discharge[i] + fac.coefficient2[i] * old.discharge[i] + fac.coefficient3[i] * old.discharge[i + 1] ) new.discharge[i + 1] = max(new.discharge[i + 1], 0.0)
[docs] class Calc_Outflow_V1(modeltools.Method): r"""Take the discharge at the last segment endpoint as the channel's outflow. Basic equation: :math:`Outflow = Discharge_{NmbSegments}` Example: >>> from hydpy.models.musk import * >>> parameterstep() >>> nmbsegments(2) >>> states.discharge.new = 1.0, 2.0, 3.0 >>> model.calc_outflow_v1() >>> fluxes.outflow outflow(3.0) """ CONTROLPARAMETERS = (musk_control.NmbSegments,) REQUIREDSEQUENCES = (musk_states.Discharge,) RESULTSEQUENCES = (musk_fluxes.Outflow,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: con = model.parameters.control.fastaccess flu = model.sequences.fluxes.fastaccess sta = model.sequences.states.fastaccess_new flu.outflow = sta.discharge[con.nmbsegments]
[docs] class Pass_Outflow_V1(modeltools.Method): """Pass the channel's outflow to the outlet sequence.""" REQUIREDSEQUENCES = (musk_fluxes.Outflow,) RESULTSEQUENCES = (musk_outlets.Q,) @staticmethod def __call__(model: modeltools.SegmentModel) -> None: flu = model.sequences.fluxes.fastaccess out = model.sequences.outlets.fastaccess out.q[0] += flu.outflow
[docs] class PegasusReferenceWaterDepth(roottools.Pegasus): """Pegasus iterator for finding the correct reference water depth.""" METHODS = (Return_ReferenceDischargeError_V1,)
[docs] class Model(modeltools.SegmentModel): """|musk.DOCNAME.complete|.""" DOCNAME = modeltools.DocName(short="Musk") __HYDPY_ROOTMODEL__ = None SOLVERPARAMETERS = ( musk_solver.NmbRuns, musk_solver.ToleranceWaterDepth, musk_solver.ToleranceDischarge, ) INLET_METHODS = (Pick_Inflow_V1, Update_Discharge_V1) RECEIVER_METHODS = () RUN_METHODS = ( Calc_Discharge_V1, Calc_ReferenceDischarge_V1, Calc_ReferenceWaterDepth_V1, Calc_WettedArea_SurfaceWidth_Celerity_V1, Calc_CorrectingFactor_V1, Calc_CourantNumber_V1, Calc_ReynoldsNumber_V1, Calc_Coefficient1_Coefficient2_Coefficient3_V1, Calc_Discharge_V2, ) ADD_METHODS = ( Return_Discharge_CrossSectionModel_V1, Return_ReferenceDischargeError_V1, Calc_WettedArea_SurfaceWidth_Celerity_CrossSectionModel_V1, ) OUTLET_METHODS = (Calc_Outflow_V1, Pass_Outflow_V1) SENDER_METHODS = () SUBMODELINTERFACES = () SUBMODELS = (PegasusReferenceWaterDepth,) wqmodel = modeltools.SubmodelProperty(routinginterfaces.CrossSectionModel_V1) wqmodel_is_mainmodel = modeltools.SubmodelIsMainmodelProperty() wqmodel_typeid = modeltools.SubmodelTypeIDProperty()