Implement models for discharge routing
!! Implement models for discharge routing !|author: <a href="mailto:giovanni.ravazzani@polimi.it">Giovanni Ravazzani</a> ! license: <a href="http://www.gnu.org/licenses/">GPL</a> ! !### History ! ! current version 1.4 - 25th April 2025 ! ! | version | date | comment | ! |----------|-------------|----------| ! | 1.0 | 03/Oct/2016 | Original code | ! | 1.1 | 05/May/2023 | bug corrected in RungeKutta third order | ! | 1.2 | 28/Nov/2023 | manage reservoir when level is high | ! | 1.3 | 12/Dec/2023 | ecological flow is defined at daily scale | ! | 1.4 | 24/Apr/2025 | added 'noleap' when computing doy in DiverFlow | ! !### License ! license: GNU GPL <http://www.gnu.org/licenses/> ! !### Module Description ! This module includes models for discharge routing ! MODULE RoutingModels ! Modules used: USE DataTypeSizes, ONLY : & ! Imported Parameters: float, short USE Units, ONLY : & ! Imported parameters: degToRad USE Loglib, ONLY : & !Imported routines: Catch USE Chronos, ONLY : & !Imported definitions: DateTime , & !Imported routines: DayOfYear USE TableLib, ONLY : & !Imported definitions: Table, & !Imported routines: TableGetValue USE StringManipulation, ONLY : & !Imported routines: ToString USE Utilities, ONLY : & !Imported routines: LinearInterp USE Reservoirs, ONLY: & !Imported definition: Reservoir USE Diversions, ONLY : & !Imported definition: Diversion IMPLICIT NONE ! Global (i.e. public) Declarations: !Public routines PUBLIC :: MuskingumCungeTodini PUBLIC :: LevelPool PUBLIC :: DivertFlow !Local routines PRIVATE :: WettedArea PRIVATE :: WettedPerimeter PRIVATE :: TopWidth PRIVATE :: Discharge PRIVATE :: Velocity PRIVATE :: Celerity PRIVATE :: Beta PRIVATE :: NormalDepth PRIVATE :: DepthFromArea PRIVATE :: RungeKutta !======= CONTAINS !======= ! Define procedures contained in this module. !============================================================================== !| Description: ! Solve Muskingum-Cunge-Todini equation. When parameters k and x ! are passed, C1, C2, and C3 are computed using the passed values. ! When x = 0, linear reservoir model. When x = 0.5 and k = dt translation only ! ! References: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. ! ! Ponce, V.M., (1994) Engineering Hydrology: Principles and Practices, ! Prentice Hall SUBROUTINE MuskingumCungeTodini & ! ( dt, dx, Qin, Pin, Pout, Qlat, Plat, B0, alpha, slope, n, Qout, depth, width, k, x ) !arguments with intent in: INTEGER (KIND = short), INTENT (IN) :: dt !!time increment (s) REAL (KIND = float), INTENT (IN) :: dx !!reach length (m) REAL (KIND = float), INTENT (IN) :: Qin !!input discharge at current time (m3/s) REAL (KIND = float), INTENT (IN) :: Pin, Pout !!input and output at previous time (m3/s) REAL (KIND = float), INTENT (IN) :: Qlat, Plat !!lateral flow at current and previous time (m3/s) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) REAL (KIND = float), INTENT (IN) :: slope !!bed slope (m/m) REAL (KIND = float), INTENT (IN) :: n !!manning roughness coefficient (s m^(-1/3)) REAL (KIND = float), OPTIONAL, INTENT(IN) :: k !!flood wave travel time used to compute C1, C2, and C3 (s) REAL (KIND = float), OPTIONAL, INTENT(IN) :: x !!used to compute C1, C2, and C3 (-) !arguments with intent out: REAL (KIND = float), INTENT (OUT) :: Qout !!output discharge at current time REAL (KIND = float), INTENT (OUT) :: depth !average water depth REAL (KIND = float), INTENT (OUT) :: width !wetted width !local declarations REAL (KIND = float) :: Cstar !!dimensionless Courant number at time t+deltat REAL (KIND = float) :: Dstar !!Cell Reynolds number at time t+deltat REAL (KIND = float) :: PCstar !!dimensionless Courant number at time t REAL (KIND = float) :: PDstar !!Cell Reynolds number at time t REAL (KIND = float) :: cel !!kinematic celerity (m/s) REAL (KIND = float) :: normal_depth !!normal flow depth (m) REAL (KIND = float) :: Qref !!reference discharge at time t+deltat REAL (KIND = float) :: Pref !!reference discharge at time t REAL (KIND = float) :: betaf !!correction factor REAL (KIND = float) :: B ! topwidth REAL (KIND = float) :: C1, C2, C3, C4 REAL (KIND = float) :: tol = 0.000001 !tolerance for checking convergence REAL (KIND = float) :: Qoutp !Qout of the previous iteration step REAL (KIND = float) :: storage !storage at time t + deltat REAL (KIND = float) :: aveWetArea !average wetted area in the reach INTEGER :: maxiter = 20 !maximum number of iterations INTEGER :: i !-------------------------------end of declaration----------------------------- IF (PRESENT (x) .AND. PRESENT (k) ) THEN C1 = (dt-2.*k*x) / (2.*k*(1.-x) + dt) C2 = (dt+2.*k*x) / (2.*k*(1.-x) + dt) C3 = (2.*k*(1.-x) - dt) / (2.*k*(1.-x) + dt) C4 = (2.*dt) / (2.*k*(1.-x) + dt) Qout = C1 * Qin + C2 * Pin + C3 * Pout + C4 * 0.5 * (Qlat + Plat) !Compute the storage storage = k * x * Qin + k * (1. - x) * Qout IF (storage > 0.) THEN !Compute the average wetted area aveWetArea = storage / dx !Compute average water depth depth = DepthFromArea (aveWetArea, B0, alpha) !Compute width width = TopWidth ( depth, B0, alpha ) ELSE depth = 0. width = 0. END IF RETURN !no need to go on END IF !compute reference discharge Pref = (Pin + Pout) / 2. IF ( (Qin > 0. .OR. Qlat > 0. ) .AND. Pref == 0.) THEN !prevent division by zero Pref = 0.001 END IF IF (Pref > 0.) THEN !compute normal flow depth normal_depth = NormalDepth ( Pref, B0, alpha, slope, n ) !compute topwidth B = TopWidth ( normal_depth, B0, alpha ) !compute celerity cel = Celerity ( normal_depth, B0, alpha, slope, n ) !compute beta betaf = Beta ( normal_depth, B0, alpha, slope, n ) !compute dimensionless Courant number PCstar = cel * dt / (dx * betaf) !Compute cell Reynolds number IF ( (B * slope * cel * betaf * dx) > 0.) THEN PDstar = Pref / (B * slope * cel * betaf * dx) ELSE PDstar = 0. END IF END IF !first guess estimate Qout = Pout + (Qin + Qlat - Pin) IF (Qout < 0.) THEN Qout = 0. END IF IF (Qin > 0. .OR. Qlat > 0.) THEN DO i = 1, maxiter Qoutp = Qout !save previous iteration value !compute reference discharge Qref = (Qin + Qout) / 2. !compute normal flow depth normal_depth = NormalDepth ( Qref, B0, alpha, slope, n ) !compute topwidth B = TopWidth ( normal_depth, B0, alpha ) !compute celerity cel = Celerity ( normal_depth, B0, alpha, slope, n ) !compute beta betaf = Beta ( normal_depth, B0, alpha, slope, n ) !compute dimensionless Courant number IF (dx * betaf > 0.) THEN Cstar = cel * dt / (dx * betaf) ELSE Cstar = 0. END IF !Compute cell Reynolds number IF ( (B * slope * cel * betaf * dx) > 0.) THEN Dstar = Qref / (B * slope * cel * betaf * dx) ELSE Dstar = 0. END IF !M-C coefficients C1 = (-1. + Cstar + Dstar) / (1. + Cstar + Dstar) C2 = (1. + PCstar - PDstar) / (1. + Cstar + Dstar) * Cstar / PCstar C3 = (1. - PCstar + PDstar) / (1. + Cstar + Dstar) * Cstar / PCstar C4 = 2. * Cstar / (1. + Cstar + Dstar) Qout = C1 * Qin + C2 * Pin + C3 * Pout + C4 * 0.5 * (Qlat + Plat) !IF (Qout < 0.) THEN ! Qout = 0. !END IF !check if other iteration is needed IF (ABS (Qout - Qoutp) / Qoutp <= tol) EXIT END DO !last check IF (Qout < 0.) then Qout = 0. END IF !Compute the storage storage = (1. - Dstar) * dt * Qin / (2. * Cstar) + & (1. + Dstar) * dt * Qout / (2. * Cstar) IF (storage > 0.) THEN !Compute the average wetted area aveWetArea = storage / dx !Compute average water depth depth = DepthFromArea (aveWetArea, B0, alpha) !Compute width width = TopWidth ( depth, B0, alpha ) ELSE depth = 0. width = 0. END IF ELSE !M-C coefficients C1 = 1. / 3. C2 = 1. / 3. C3 = 1. / 3. Qout = C1 * Qin + C2 * Pin + C3 * Pout depth = 0. END IF RETURN END SUBROUTINE MuskingumCungeTodini !============================================================================== !| Description: ! Solve Muskingum-Cunge equation. ! ! References: ! ! Ponce, V.M., (1994) Engineering Hydrology: Principles and Practices, ! Prentice Hall SUBROUTINE MuskingumCunge & ! ( dt, dx, Qin, Pin, Pout, Qlat, Plat, B0, alpha, slope, n, Qout, depth, width, k, x) !arguments with intent in: INTEGER (KIND = short), INTENT (IN) :: dt !!time increment (s) REAL (KIND = float), INTENT (IN) :: dx !!reach length (m) REAL (KIND = float), INTENT (IN) :: Qin !!input discharge at current time (m3/s) REAL (KIND = float), INTENT (IN) :: Pin, Pout !!input and output at previous time (m3/s) REAL (KIND = float), INTENT (IN) :: Qlat, Plat !!lateral flow at current and previous time (m3/s) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) REAL (KIND = float), INTENT (IN) :: slope !!bed slope (m/m) REAL (KIND = float), INTENT (IN) :: n !!manning roughness coefficient (s m^(-1/3)) REAL (KIND = float), OPTIONAL, INTENT(IN) :: k !!flood wave travel time used to compute C1, C2, and C3 (s) REAL (KIND = float), OPTIONAL, INTENT(IN) :: x !!used to compute C1, C2, and C3 (-) !arguments with intent out: REAL (KIND = float), INTENT (OUT) :: Qout !!output discharge at current time REAL (KIND = float), INTENT (OUT) :: depth !average water depth REAL (KIND = float), INTENT (OUT) :: width !wetted width !local declarations REAL (KIND = float) :: xx, kk !!used for computing C1, C2, and C3 REAL (KIND = float) :: cel !!kinematic celerity (m/s) REAL (KIND = float) :: normal_depth !!normal flow depth (m) REAL (KIND = float) :: Qref !!reference discharge at time t+deltat REAL (KIND = float) :: B ! topwidth REAL (KIND = float) :: C1, C2, C3, C4 REAL (KIND = float) :: storage !storage at time t + deltat REAL (KIND = float) :: aveWetArea !average wetted area in the reach !-------------------------------end of declaration----------------------------- IF (PRESENT (x) .AND. PRESENT (k) ) THEN C1 = (dt-2.*k*x) / (2.*k*(1.-x) + dt) C2 = (dt+2.*k*x) / (2.*k*(1.-x) + dt) C3 = (2.*k*(1.-x) - dt) / (2.*k*(1.-x) + dt) C4 = (2.*dt) / (2.*k*(1.-x) + dt) Qout = C1 * Qin + C2 * Pin + C3 * Pout + C4 * 0.5 * (Qlat + Plat) !Compute the storage storage = k * x * Qin + k * (1. - x) * Qout IF (storage > 0.) THEN !Compute the average wetted area aveWetArea = storage / dx !Compute average water depth depth = DepthFromArea (aveWetArea, B0, alpha) !Compute width width = TopWidth ( depth, B0, alpha ) ELSE depth = 0. width = 0. END IF RETURN !no need to go on END IF !compute reference discharge Qref = (Qin + Pin + Pout) / 3. IF (Qref == 0. .AND. Qlat > 0.) THEN Qref = Qlat END IF IF (Qref > 0.) THEN !compute normal flow depth normal_depth = NormalDepth ( Qref, B0, alpha, slope, n ) !compute topwidth B = TopWidth ( normal_depth, B0, alpha ) !compute celerity cel = Celerity ( normal_depth, B0, alpha, slope, n ) !compute k and x kk = dx / cel xx = 0.5 * ( 1. - Qref / (B * cel * slope * dx) ) !compute weigths C1 = (dt-2.*kk*xx) / (2.*kk*(1.-xx)+dt) C2 = (dt+2.*kk*xx) / (2.*kk*(1.-xx)+dt) C3 = (2.*kk*(1.-xx)-dt) / (2.*kk*(1.-xx)+dt) C4 = 2.*dt / (2.*kk*(1.-xx)+dt) !compute Qout Qout = C1 * Qin + C2 * Pin + C3 * Pout + C4 * (Qlat+Plat) / 2. !Qout = C1 * Qin + C2 * Pin + C3 * Pout + C4 * Qlat !last check IF (Qout < 0.) then Qout = 0. END IF !compute the storage storage = kk *xx * Qin + kk * (1. - xx) * Qout !compute the stage IF (storage > 0.) THEN !Compute the average wetted area aveWetArea = storage / dx !Compute average water depth depth = DepthFromArea (aveWetArea, B0, alpha) !Compute width width = TopWidth ( depth, B0, alpha ) ELSE depth = 0. width = 0. END IF ELSE Qout = 0. depth = 0. END IF RETURN END SUBROUTINE MuskingumCunge !============================================================================== !| Description: ! Discharge routing through diversion. Discharge is split ! between river and diversion channel SUBROUTINE DivertFlow & ! ( time, div, Qin, Qout) !arguments with intent in: TYPE(DateTime), INTENT(IN) :: time REAL(KIND = float), INTENT(IN) :: Qin !!Input discharge at time t + dt (m3/s) !arguments with intent inout: TYPE (Diversion), INTENT(INOUT) :: div !arguments with intent out: REAL(KIND = float), INTENT(OUT) :: Qout !!downstream discharge at time t + dt (m3/s) !local declarations INTEGER (KIND = short) :: doy !!day of year CHARACTER (LEN = 3) :: QdivertedDOY !-------------------------------end of declaration----------------------------- !compute day of year (used to set ecological flow and weir function) doy = DayOfYear (time, 'noleap') !set doy for weir function QdivertedDOY = ToString ( div % weirDOY (doy) ) CALL TableGetValue ( valueIn = Qin, tab = div % weir, keyIn = 'Qstream', & keyOut = QdivertedDOY, match = 'linear', & valueOut = div % QinChannel, bound = 'extendconstant' ) !check environmental flow and set Qout div % QinChannel = MIN ( Qin - div % eFlow (doy), div % QinChannel) IF ( div % QinChannel < 0. ) THEN !Qin is less than eflow div % QinChannel = 0. END IF !set Qout Qout = Qin - div % QinChannel RETURN END SUBROUTINE DivertFlow !============================================================================== !| Description: ! Discharge routing through reservoir. Given Qin, find Qout and stage ! References: SUBROUTINE LevelPool & ! (time, dt, dtrk, Qin_t, Qin_tplusdt, pool) !arguments with intent in: TYPE(DateTime), INTENT(IN) :: time INTEGER, INTENT(IN) :: dt !!main time step (s) INTEGER, INTENT(IN) :: dtrk !!time step to run runge-kutta (s) REAL(KIND = float), INTENT(IN) :: Qin_t !!Input discharge at time t (m3/s) REAL(KIND = float), INTENT(IN) :: Qin_tplusdt !!Input discharge at time t + dt (m3/s) !arguments with intent inout: TYPE(Reservoir), INTENT(INOUT) :: pool !arguments with intent out: !REAL(KIND = float), INTENT(OUT) :: Qout !!downstream discharge at time t + dt (m3/s) !local declarations INTEGER :: nstep !!number of sub-steps REAL(KIND = float) :: Qin_stepbegin, Qin_stepend !(m3/s) REAL(KIND = float) :: Qinpool_t !!Actual input dicharge to the pool at time t (m3/s) REAL(KIND = float) :: Qinpool_tplusdt !!Actual input dicharge to the pool at time t+dt (m3/s) REAL(KIND = float) :: h !!current stage (m) INTEGER :: j LOGICAL :: qinIsRising INTEGER (KIND = short) :: doy !!day of year CHARACTER (LEN = 3) :: Qdiverted CHARACTER (LEN = 3) :: QoutDOY !!doy to compute Qout !-------------------------------end of declaration----------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! ! ! ! * Qinpool_tplusdt ! | ! ! | ! ! Qin_stepend O | ! ! | | ! ! | | ! ! Qin_stepbegin O | | ! ! . | | | ! ! . | | | ! ! . | | | ! ! . | | | ! ! . | | | ! ! O | | | ! ! | | | | ! ! | | | | ! ! Qinpool_t * ____|...........|_____|_____| ! ! ! ! <-----> ! ! dtrk ! ! t <-----------------------------> t + dt ! ! dt ! ! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !initialize variables to run runge-kutta SELECT CASE ( TRIM(pool % typ) ) CASE ( 'off' ) !diverted discharge by weir is the input to the pool ! time t CALL TableGetValue ( valueIn = Qin_t, tab = pool % weir, keyIn = 'Qstream', & keyOut ='Qdiverted', match = 'linear', & valueOut = Qinpool_t, bound = 'extendconstant' ) ! time t+dt CALL TableGetValue ( valueIn = Qin_tplusdt, tab = pool % weir, keyIn = 'Qstream', & keyOut ='Qdiverted', match = 'linear', & valueOut = Qinpool_tplusdt, bound = 'extendconstant' ) !if pool is full discharge can not enter anymore IF (pool % stage > pool % stageMax) THEN Qinpool_t = 0. Qinpool_tplusdt = 0. END IF CASE ('on') !discharge from stream enters pool Qinpool_t = Qin_t Qinpool_tplusdt = Qin_tplusdt IF ( Qinpool_tplusdt > Qinpool_t ) THEN qinIsRising = .TRUE. ELSE qinIsRising = .FALSE. END IF CASE DEFAULT CALL Catch ('error', 'LevelPool', 'wrong reservoir type in pool: ', & argument = TRIM(pool % name)) END SELECT !initialize stage h = pool % stage !compute day of year (used to set ecological flow) doy = DayOfYear (time, 'noleap') !set doy for computing Qout QoutDOY = ToString ( pool % geometryDOY (doy) ) ! manage high level IF ( pool % highLevel .AND. h > pool % fullReservoirLevel ) THEN IF ( pool % rising) THEN IF ( qInIsRising ) THEN SELECT CASE ( pool % qoutRule ) CASE ( 1 ) !qout = qin pool % Qout = Qinpool_tplusdt END SELECT RETURN END IF ELSE SELECT CASE ( pool % qoutRule ) CASE ( 1 ) !qout = qin pool % Qout = Qinpool_tplusdt END SELECT RETURN END IF END IF !loop throughout steps nstep = dt / dtrk DO j = 1, nstep Qin_stepbegin = LinearInterp (0., REAL(dt), Qinpool_t, & Qinpool_tplusdt, 1.*(j-1)*dtrk) Qin_stepend = LinearInterp (0., REAL(dt), Qinpool_t, & Qinpool_tplusdt, 1.*j*dtrk) !update level pool h = RungeKutta ( hini = h, Qin_begin = Qin_stepbegin, & Qin_end = Qin_stepend, dt = dtrk, & geometry = pool % geometry, & rk = pool % rk, typOut = pool % typOut, & hTarget = pool % stageTarget, & eFlow = pool % eFlow (doy), & freeFlow = pool % freeFlow, & freeFlowElevation = pool % freeFlowElevation, & QoutDOY = QoutDOY ) END DO !downstream discharge SELECT CASE ( TRIM (pool % typ ) ) CASE ('on') !on-stream reservoir IF (pool % typOut == 2 .AND. h < pool % stageTarget) THEN pool % Qout = MIN( pool % eFlow (doy), Qin_tplusdt) ELSE IF ( pool % typOut == 2 .AND. h >= pool % stageTarget) THEN CALL TableGetValue ( valueIn = h, tab = pool % geometry, keyIn = 'h', & keyOut ='Qout', match = 'linear', & valueOut = pool % Qout, bound = 'extendconstant' ) IF ( pool % eFlow (doy) > 0. ) THEN pool % Qout = MAX (pool % eFlow (doy), pool % Qout) END IF ELSE IF ( pool % stage <= pool % freeFlowElevation .AND. & Qin_tplusdt < pool % freeFlow ) THEN pool % Qout = Qin_tplusdt ELSE CALL TableGetValue ( valueIn = h, tab = pool % geometry, keyIn = 'h', & keyOut ='Qout', match = 'linear', & valueOut = pool % Qout, bound = 'extendconstant' ) END IF END IF CASE ('off') !off-stream reservoir pool % Qout = Qin_tplusdt - Qinpool_tplusdt END SELECT !update stage pool % stage = h RETURN END SUBROUTINE LevelPool !============================================================================== !| Description: ! returns the wetted area of a triangular, rectangular or trapezoidal cross ! section ! ! Reference: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION WettedArea & ( y, B0, alpha ) & RESULT (a) IMPLICIT NONE ! Function arguments ! Scalar arguments with intent(in): REAL (KIND = float), INTENT (IN) :: y !!water stage (m) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) ! Scalar arguments with intent(OUT): REAL (KIND = float) :: a REAL (KIND = float) :: calpha !!cotangent of angle alpha !------------end of declaration------------------------------------------------ calpha = 1. / TAN(alpha*degToRad) a = (B0 + y * calpha) * y RETURN END FUNCTION WettedArea !============================================================================== !| Description: ! returns the wetted perimeter of a triangular, rectangular or trapezoidal cross ! section ! ! Reference: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION WettedPerimeter & ( y, B0, alpha ) & RESULT (p) IMPLICIT NONE ! Function arguments ! Scalar arguments with intent(in): REAL (KIND = float), INTENT (IN) :: y !!water stage (m) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) ! Scalar arguments with intent(OUT): REAL (KIND = float) :: p REAL (KIND = float) :: salpha !!sine of angle alpha !------------end of declaration------------------------------------------------ salpha = SIN(alpha*degToRad) p = B0 + 2 * y / salpha RETURN END FUNCTION WettedPerimeter !============================================================================== !| Description: ! returns the topwidth of a triangular, rectangular or trapezoidal cross ! section ! ! Reference: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION TopWidth & ( y, B0, alpha ) & RESULT (b) IMPLICIT NONE ! Function arguments ! Scalar arguments with intent(in): REAL (KIND = float), INTENT (IN) :: y !!water stage (m) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) ! Scalar arguments with intent(OUT): REAL (KIND = float) :: b REAL (KIND = float) :: calpha !!cotangent of angle alpha !------------end of declaration------------------------------------------------ calpha = 1. / TAN(alpha*degToRad) b = B0 + 2 * y * calpha RETURN END FUNCTION TopWidth !============================================================================== !| Description: ! returns the normal discharge (m3/s) with Chezy equation. ! ! Reference: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION Discharge & ( y, B0, alpha, slope, n ) & RESULT (Q) IMPLICIT NONE ! Function arguments ! Scalar arguments with intent(in): REAL (KIND = float), INTENT (IN) :: y !!water stage (m) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section(m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) REAL (KIND = float), INTENT (IN) :: slope !!bed slope (m/m) REAL (KIND = float), INTENT (IN) :: n !!manning riughness coefficient (s m^(-1/3)) ! Scalar arguments with intent(OUT): REAL (KIND = float) :: Q !------------end of declaration------------------------------------------------ Q = slope**0.5 / n * & WettedArea ( y, B0, alpha )**(5./3.) / WettedPerimeter ( y, B0, alpha )**(2./3.) RETURN END FUNCTION Discharge !============================================================================== !| Description: ! returns the normal velocity (m/s) with Chezy equation ! ! Reference: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION Velocity & ( y, B0, alpha, slope, n ) & RESULT (v) IMPLICIT NONE ! Function arguments ! Scalar arguments with intent(in): REAL (KIND = float), INTENT (IN) :: y !!water stage (m) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) REAL (KIND = float), INTENT (IN) :: slope !!bed slope (m/m) REAL (KIND = float), INTENT (IN) :: n !!manning riughness coefficient (s m^(-1/3)) ! Scalar arguments with intent(OUT): REAL (KIND = float) :: v !------------end of declaration------------------------------------------------ v = slope**0.5 / n * & WettedArea ( y, B0, alpha )**(2./3.) / WettedPerimeter ( y, B0, alpha )**(2./3.) RETURN END FUNCTION Velocity !============================================================================== !| Description: ! returns the celerity (m/s) ! ! Reference: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION Celerity & ( y, B0, alpha, slope, n ) & RESULT (c) IMPLICIT NONE ! Function arguments ! Arguments with intent(in): REAL (KIND = float), INTENT (IN) :: y !!water stage (m) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane [deg) REAL (KIND = float), INTENT (IN) :: slope !!bed slope (m/m) REAL (KIND = float), INTENT (IN) :: n !!manning riughness coefficient (s m^(-1/3)) ! Arguments with intent(OUT): REAL (KIND = float) :: c !local declaration: REAL (KIND = float) :: A !!wetted area (m2) REAL (KIND = float) :: P !!wetted perimeter (m) REAL (KIND = float) :: B !!top width (m) REAL (KIND = float) :: salpha !!sine of angle alpha !------------end of declaration------------------------------------------------ A = WettedArea ( y, B0, alpha ) P = WettedPerimeter ( y, B0, alpha ) B = TopWidth ( y, B0, alpha ) salpha = SIN(alpha*degToRad) c = 5./3. * slope**0.5 / n * A**(2./3.) / P**(2./3.) * & ( 1. - 4./5. * A / (B * P * salpha)) RETURN END FUNCTION Celerity !============================================================================== !| Description: ! returns the correction factor to be used in MCT algorithm (-) ! ! Reference: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION Beta & ( y, B0, alpha, slope, n ) & RESULT (cf) IMPLICIT NONE ! Function arguments ! Arguments with intent(in): REAL (KIND = float), INTENT (IN) :: y !!water stage (m) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) REAL (KIND = float), INTENT (IN) :: slope !!bed slope (m/m) REAL (KIND = float), INTENT (IN) :: n !!manning riughness coefficient (s m^(-1/3)) ! Arguments with intent(OUT): REAL (KIND = float) :: cf !local declarations REAL (KIND = float) :: A !!wetted area (m2) REAL (KIND = float) :: P !!wetted perimeter (m) REAL (KIND = float) :: B !!top width (m) REAL (KIND = float) :: salpha !!sine of angle alpha !------------end of declaration------------------------------------------------ IF (y == 0.) THEN cf = 0. RETURN END IF A = WettedArea ( y, B0, alpha ) P = WettedPerimeter ( y, B0, alpha ) B = TopWidth ( y, B0, alpha ) salpha = SIN(alpha*degToRad) cf = 5./3. * ( 1. - 4./5. * A / (B * P * salpha)) RETURN END FUNCTION Beta !============================================================================== !| Description: ! returns the normal depth (m) using Newton-Raphson. ! ! References: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION NormalDepth & ( Q, B0, alpha, slope, n ) & RESULT (nd) IMPLICIT NONE ! Function arguments ! Arguments with intent(in): REAL (KIND = float), INTENT (IN) :: Q !!discharge (m3/s) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) REAL (KIND = float), INTENT (IN) :: slope !!bed slope (m/m) REAL (KIND = float), INTENT (IN) :: n !!manning riughness coefficient (s m^(-1/3)) ! Arguments with intent(OUT): REAL (KIND = float) :: nd !local declarations INTEGER :: i INTEGER :: maxiter = 100 REAL (KIND = float) :: nd1 REAL (KIND = float) :: Q0 REAL (KIND = float) :: dQdy !first derivative REAL (KIND = float) :: tol = 0.001 !------------end of declaration------------------------------------------------ IF (Q == 0.) THEN nd = 0. END IF !first guess, nd = 1 m nd = 1. DO i = 1, maxiter Q0 = Discharge ( nd, B0, alpha, slope, n ) dQdy = TopWidth ( nd, B0, alpha ) * Celerity ( nd, B0, alpha, slope, n ) nd1 = nd - (Q0 - Q) / dQdy !test conf IF ( ABS(nd1 - nd) / nd <= tol) THEN !root found nd = nd1 RETURN END IF nd = nd1 END DO RETURN RETURN END FUNCTION NormalDepth !============================================================================== !| Description: ! returns water depth given wetted area (m) using Newton-Raphson. ! ! References: ! ! Todini, E. (2007) A mass conservative and water storage consistent variable ! parameter Muskingum-Cunge approach, Hydrol. Earth Syst. Sci.,1645-1659. FUNCTION DepthFromArea & ( A, B0, alpha ) & RESULT (y) IMPLICIT NONE ! Function arguments ! Arguments with intent(in): REAL (KIND = float), INTENT (IN) :: A !!area (m2) REAL (KIND = float), INTENT (IN) :: B0 !!bottom width, = 0 for triangular section (m) REAL (KIND = float), INTENT (IN) :: alpha !!angle formed by dykes over a horizontal plane (deg) ! Arguments with intent(OUT): REAL (KIND = float) :: y !local declaration: INTEGER :: i INTEGER :: maxiter = 100 REAL (KIND = float) :: y1 REAL (KIND = float) :: A0 REAL (KIND = float) :: dAdy !first derivative REAL (KIND = float) :: tol = 0.001 REAL (KIND = float) :: calpha !!cotangent of angle alpha !------------end of declaration------------------------------------------------ calpha = 1. / TAN(alpha*degToRad) !first guess, y = 1 m y = 1. DO i = 1, maxiter A0 = WettedArea ( y, B0, alpha ) dAdy = calpha * y + (B0 + y * calpha) y1 = y - (A0 - A) / dAdy !test conf IF ( ABS(y1 - y) / y <= tol) THEN !root found y = y1 RETURN END IF y = y1 END DO RETURN RETURN END FUNCTION DepthFromArea !============================================================================== !| Description: ! solve mass continuity of reservoir using runge kutta method ! ! References: ! ! Chow, V. T., Maidment, D. R. &Mays, L. W. (1988) Applied Hydrology. ! McGraw-Hill, New York, USA ! ! Fenton, J.D. (1992) Reservoir routing, Hydrological Sciences Journal, ! 37(3), 233-246, DOI: 10.1080/02626669209492584 FUNCTION RungeKutta & ( hini, Qin_begin, Qin_end, dt, geometry, rk, typOut, hTarget, & eFlow, freeFlow, freeFlowElevation, QoutDOY ) & RESULT (stage) IMPLICIT NONE ! Function arguments ! Arguments with intent(in): REAL (KIND = float), INTENT(IN) :: hini !!initial stage value (m) REAL (KIND = float), INTENT(IN) :: Qin_begin !!input discharge at the beginning of time step (m3/s) REAL (KIND = float), INTENT(IN) :: Qin_end !!input discharge at the end of time step (m3/s) INTEGER, INTENT(IN) :: dt !!time step (s) TYPE (Table), INTENT(IN) :: geometry !!geometry of reservoir INTEGER, INTENT(IN) :: rk !! solution order 3 or 4 INTEGER, INTENT(IN) :: typOut !!type ofoutflow: 1=free flow 2=target level REAL (KIND = float), INTENT(IN) :: hTarget !!follow a target (observed) stage (m) REAL (KIND = float), INTENT(IN) :: eFlow !!environmental flow (m3/s) REAL (KIND = float), INTENT(IN) :: freeFlow !! free flow (m3/s) REAL (KIND = float), INTENT(IN) :: freeFlowElevation !! free flow elevation (m asl) CHARACTER (LEN = 3), INTENT(IN) :: QoutDOY !!doy to compute Qout ! Arguments with intent(OUT): REAL (KIND = float) :: stage !local declaration: REAL(KIND = float) :: h1, h2 REAL(KIND = float) :: h3 !used only if rk = 4 REAL(KIND = float) :: dh1, dh2, dh3 REAL(KIND = float) :: dh4 !used only if rk = 4 REAL(KIND = float) :: dh REAL(KIND = float) :: Q1third, Q2third REAL(KIND = float) :: Qout REAL(KIND = float) :: area !(m2) !------------end of declaration------------------------------------------------ SELECT CASE (rk) CASE(3) IF ( typOut == 2 .AND. hini < hTarget) THEN Qout = MIN (eFlow, Qin_begin) !step 1 !find area in table geometry CALL TableGetValue ( valueIn = hini, tab = geometry, keyIn = 'h', & keyOut='area', match='linear', & valueOut=area, bound = 'extendconstant' ) !compute dh1 dh1 = (Qin_begin - Qout) * dt / area !update h1 h1 = hini + dh1 / 3.0 !step 2 Q1third = LinearInterp (0., REAL(dt), Qin_begin, Qin_end, 1.0/3.0*dt) !find area in table geometry CALL TableGetValue ( valueIn = h1, tab = geometry, keyIn = 'h', & keyOut='area', match='linear', & valueOut=area, bound = 'extendconstant' ) !compute dh2 dh2 = (Q1third - Qout) * dt / area !update h2 h2 = hini + 2.0 * dh2 / 3.0 !step 3 Q2third = LinearInterp (0., real(dt), Qin_begin, Qin_end, 2.0/3.0*dt) !find area in table geometry CALL TableGetValue ( valueIn = h2, tab = geometry, keyIn = 'h', & keyOut='area', match='linear', & valueOut=area, bound = 'extendconstant' ) !compute dh3 dh3 = (Q2third - Qout) * dt / area ELSE !step 1 !find Qout IF ( hini <= freeFlowElevation .AND. Qin_begin < freeFlow ) THEN Qout = Qin_begin ELSE CALL TableGetValue ( valueIn = hini, tab = geometry, keyIn = 'h', & keyOut = QoutDOY, match = 'linear', & valueOut = Qout, bound = 'extendconstant' ) IF ( eFlow > 0.) THEN Qout = MAX (eFlow, Qout) END IF END IF !find area in table geometry CALL TableGetValue ( valueIn = hini, tab = geometry, keyIn = 'h', & keyOut = 'area', match = 'linear', & valueOut = area, bound = 'extendconstant' ) !compute dh1 dh1 = (Qin_begin - Qout) * dt / area !update h1 h1 = hini + dh1 / 3.0 !step2 Q1third = LinearInterp (0., REAL(dt), Qin_begin, Qin_end, 1.0/3.0*dt) !find Qout IF ( h1 <= freeFlowElevation .AND. Q1third < freeFlow ) THEN Qout = Q1third ELSE CALL TableGetValue ( valueIn = h1, tab = geometry, keyIn = 'h', & keyOut = QoutDOY, match = 'linear', & valueOut = Qout, bound = 'extendconstant' ) END IF !find area in table geometry CALL TableGetValue ( valueIn = h1, tab = geometry, keyIn = 'h', & keyOut = 'area', match = 'linear', & valueOut = area, bound = 'extendconstant' ) !compute dh2 dh2 = (Q1third - Qout) * dt / area !update h2 h2 = hini + 2.0 * dh2 / 3.0 !step3 Q2third = LinearInterp (0., real(dt), Qin_begin, Qin_end, 2.0/3.0*dt) !find Qout IF ( h2 <= freeFlowElevation .AND. Q2third < freeFlow ) THEN Qout = Q2third ELSE CALL TableGetValue ( valueIn = h2, tab = geometry, keyIn = 'h', & keyOut = QoutDOY, match = 'linear', & valueOut = Qout, bound = 'extendconstant' ) END IF !find area in table geometry CALL TableGetValue ( valueIn = h2, tab = geometry, keyIn = 'h', & keyOut = 'area', match = 'linear', & valueOut = area, bound = 'extendconstant' ) !compute dh3 dh3 = (Q2third - Qout) * dt / area END IF dh = dh1 / 4.0 + 3.0 * dh3 / 4.0 CASE(4) !IF ( typOut == 2 .AND. hini < hTarget) THEN ! ! Qout = MIN (eFlow, Qin_begin) ! ! !step 1 ! !find area in table geometry ! CALL TableGetValue ( valueIn = hini, tab = geometry, keyIn = 'h', & ! keyOut='area', match='linear', & ! valueOut=area, bound = 'extendlinear' ) ! ! !compute dh1 ! dh1 = (Qin_begin - Qout) * area ! !write(*,*) dh1, Qout, eflow, Qin_begin, hini, htarget ! ! !update h1 ! h1 = hini + dh1 / 3.0 ! ! !step 2 ! Q1third = LinearInterp (0., REAL(dt), Qin_begin, Qin_end, 1.0/3.0*dt) ! ! !find area in table geometry ! CALL TableGetValue ( valueIn = h1, tab = geometry, keyIn = 'h', & ! keyOut='area', match='linear', & ! valueOut=area, bound = 'extendlinear' ) ! ! !compute dh2 ! dh2 = (Q1third - Qout) * dt / area ! ! !update h2 ! h2 = hini - 1.0 / 3.0 * dh1 + dh2 ! ! !step 3 ! Q2third = LinearInterp (0., real(dt), Qin_begin, Qin_end, 2.0/3.0*dt) ! ! !find area in table geometry ! CALL TableGetValue ( valueIn = h2, tab = geometry, keyIn = 'h', & ! keyOut='area', match='linear', & ! valueOut=area, bound = 'extendlinear' ) ! ! !compute dh3 ! dh3 = (Q2third - Qout) * dt / area ! ! !update h3 ! h3 = hini + dh1 - dh2 + dh3 ! ! !step4 ! !find area in table geometry ! CALL TableGetValue ( valueIn = h3, tab = geometry, keyIn = 'h', & ! keyOut='area', match='linear', & ! valueOut=area, bound = 'extendlinear' ) ! ! !compute dh4 ! dh4 = (Qin_end - Qout) * dt / area ! !ELSE !step 1 !find Qout IF ( typOut == 2 .AND. hini < hTarget) THEN Qout = MIN (eFlow, Qin_begin) ELSE IF ( typOut == 2 .AND. hini >= hTarget) THEN CALL TableGetValue ( valueIn = hini, tab = geometry, keyIn = 'h', & keyOut = QoutDOY, match = 'linear', & valueOut = Qout, bound = 'extendconstant' ) IF ( eFlow > 0. ) THEN Qout = MAX (eFlow, Qout) END IF ELSE IF ( hini <= freeFlowElevation .AND. Qin_begin < freeFlow ) THEN Qout = Qin_begin ELSE CALL TableGetValue ( valueIn = hini, tab = geometry, keyIn = 'h', & keyOut = QoutDOY, match = 'linear', & valueOut = Qout, bound = 'extendconstant' ) END IF !find area in table geometry CALL TableGetValue ( valueIn = hini, tab = geometry, keyIn = 'h', & keyOut = 'area', match = 'linear', & valueOut = area, bound = 'extendconstant' ) !compute dh1 dh1 = (Qin_begin - Qout) * dt / area !update h1 h1 = hini + dh1 / 3.0 !step2 Q1third = LinearInterp (0., REAL(dt), Qin_begin, Qin_end, 1.0/3.0*dt) IF ( typOut == 1 ) THEN !update Qout IF ( h1 <= freeFlowElevation .AND. Q1third < freeFlow ) THEN Qout = Q1third ELSE CALL TableGetValue ( valueIn = h1, tab = geometry, keyIn = 'h', & keyOut = QoutDOY, match = 'linear', & valueOut = Qout, bound = 'extendconstant' ) END IF END IF !find area in table geometry CALL TableGetValue ( valueIn = h1, tab = geometry, keyIn = 'h', & keyOut = 'area', match = 'linear', & valueOut = area, bound = 'extendconstant' ) !compute dh2 dh2 = (Q1third - Qout) * dt / area !update h2 h2 = hini - 1.0 / 3.0 * dh1 + dh2 !step3 Q2third = LinearInterp (0., real(dt), Qin_begin, Qin_end, 2.0/3.0*dt) IF ( typOut == 1 ) THEN !update Qout IF ( h2 <= freeFlowElevation .AND. Q2third < freeFlow ) THEN Qout = Q2third ELSE CALL TableGetValue ( valueIn = h2, tab = geometry, keyIn = 'h', & keyOut = QoutDOY, match = 'linear', & valueOut = Qout, bound = 'extendconstant' ) END IF END IF !find area in table geometry CALL TableGetValue ( valueIn = h2, tab = geometry, keyIn = 'h', & keyOut = 'area', match = 'linear', & valueOut = area, bound = 'extendconstant' ) !compute dh3 dh3 = (Q2third - Qout) * dt / area !update h3 h3 = hini + dh1 - dh2 + dh3 !step 4 IF ( typOut == 1 ) THEN !update Qout IF ( h3 <= freeFlowElevation .AND. Qin_end < freeFlow ) THEN Qout = Qin_end ELSE CALL TableGetValue ( valueIn = h3, tab = geometry, keyIn = 'h', & keyOut = QoutDOY, match = 'linear', & valueOut = Qout, bound = 'extendconstant' ) END IF END IF !find area in table geometry CALL TableGetValue ( valueIn = h3, tab = geometry, keyIn = 'h', & keyOut = 'area', match = 'linear', & valueOut = area, bound = 'extendconstant' ) !compute dh4 dh4 = (Qin_end - Qout) * dt / area !END IF dh = dh1 / 8.0 + 3.0 * dh2 / 8.0 + 3.0 * dh3 / 8.0 + dh4 / 8.0 CASE DEFAULT CALL Catch ('error', 'RungeKutta', 'order Runge-Kutta not valid: ', & argument = ToString(rk)) STOP END SELECT !update reservoir stage stage = hini + dh RETURN END FUNCTION RungeKutta END MODULE RoutingModels