!! Perform discharge routing
!|author: Giovanni Ravazzani
! license: GPL
!
!### History
!
! current version 2.3 - 2nd December 2024
!
! | version | date | comment |
! |----------|-------------|----------|
! | 1.0 | 07/Oct/2016 | Original code |
! | 1.1 | 08/Feb/2023 | DischargePointInit and DischargePointExport added |
! | 1.2 | 12/Feb/2023 | routing parameters are no more stored in stream network. No more need of HydroNetwork module |
! | 1.3 | 25/Jan/2024 | module name changed from SurfaceRouting to DischargeRouting |
! | 1.4 | 26/Jan/2024 | module adjusted to cope with new module Diversions |
! | 1.5 | 10/Apr/2024 | observed reservoir downstream discharge read from file |
! | 1.6 | 11/Apr/2024 | observed diverted discharge read from file |
! | 1.7 | 18/Apr/2024 | discharge routing parameters assigned for different subbasins |
! | 1.8 | 08/May/2024 | Qin written to output instead of Qout, to fix the bug of zero discharge in outlet cell |
! | 1.9 | 06/Jun/2024 | log message bug fixed when diversion is outside channel|
! | 2.0 | 14/Jun/2024 | back to Qout written to output file. Qout is defined in last downstream cell|
! | 2.1 | 03/Jul/2024 | Diverted discharge from diversions included in Qlateral of Muskingum |
! | 2.2 | 04/Sep/2024 | pools variable moved to Reservoirs module |
! | 2.3 | 02/Dec/2024 | excess of water retained in snow added to Qlateral of Muskingum |
!
!### License
! license: GNU GPL
!
!### Module Description
! Route discharge in river network.
!
! River network includes different elements:
!
! - hillslope reaches
! - channel reaches
! - reservoirs
! - diversions
!
! The user sets the elements to simulate and their properties
! with a configuration file like in the following example
!
!```
!export-channel-grid = 0
!
!masks-number = 2 # number of masks to assign channel parameters (at least 1 for the base-mask must exist)
!
![reservoirs]
! file = ./conf/reservoirs.ini
! dt = 10 # if dt = 0 reservoirs are not solved
! dt-out = 3600 #optional, default = 0
!
![diversions]
! file = ./conf/diversions.ini
! dt = 0 # 0 = suppresses diversion simulation. otherwise dt is set equals to discharge routing dt
! dt-out = 3600
!
![discharge-in]
! scalar = 0.0
!
![discharge-out]
! scalar = 0.0
!
![discharge-lat]
! scalar = 0.0
!
![base-mask]
! channel-initiation-method = area #area, ask, curvature
! channel-initiation-threshold = 4000000 # ask 600000 m^2 area 3500000
! hillslope-width = 200 # cross section width (m)
! hillslope-alpha = 45. # slope of trapezoidal section side bank (degree)
! hillslope-ks = 2 #Strickler roughness coefficient m^1/3 s^-1
!
![mask1]
! file = ./data/subbasin1.asc
! format = esri-ascii
! epsg = 32633
! channel-initiation-method = area #area, ask, curvature
! channel-initiation-threshold = 400000 # ask 600000 m^2 area 3500000
! hillslope-width = 200 # cross section width (m)
! hillslope-alpha = 45. # slope of trapezoidal section side bank (degree)
! hillslope-ks = 3 #Strickler roughness coefficient m^1/3 s^-1
!
!Table Start
!Title: channel properties
!Id: base-mask
!#threshold indicates the basin area below which values of parameters are applied
!Columns: [count] [threshold] [width] [alpha] [ks]
!Units: [-] [m^2] [m] [deg] [m^1/3s^-1]
! 1 5000000 5 45 20
! 2 10000000 7 45 25
! 3 15000000 10 45 30
! 4 20000000 20 45 35
! 5 30000000 25 45 40
! 6 100000000 35 45 45
! 7 500000000 50 45 45
! 8 1000000000 65 45 45
! 9 2000000000 80 45 45
!Table End
!
!Table Start
!Title: channel properties
!Id: mask1
!#threshold indicates the basin area below which values of parameters are applied
!Columns: [count] [threshold] [width] [alpha] [ks]
!Units: [-] [m^2] [m] [deg] [m^1/3s^-1]
! 1 5000000 5 45 15
! 2 10000000 9 45 20
! 3 15000000 20 45 25
! 4 20000000 25 45 30
! 5 30000000 30 45 35
! 6 100000000 35 45 40
! 7 500000000 40 45 40
! 8 2000000000 45 45 45
!Table End
!
!```
!
MODULE DischargeRouting
! Modules used:
USE DataTypeSizes, ONLY : &
! Imported Parameters:
float, &
short
USE Loglib, ONLY : &
!Imported routines:
Catch
USE GridLib, ONLY : &
!imported definitions:
grid_real, &
grid_integer, &
!imported routines:
NewGrid, &
GridDestroy, &
ExportGrid, &
ESRI_ASCII
USE GridOperations, ONLY : &
!Imported routines:
GridByIni, &
CRSisEqual, &
CellArea, &
!Imported operators:
ASSIGNMENT (=)
USE IniLib, ONLY: &
!Imported routines:
IniOpen, &
IniClose, &
SectionIsPresent, &
KeyIsPresent, &
IniReadString, &
IniReadInt,&
IniReadReal, &
!Imported type definitions:
IniList
USE StringManipulation, ONLY : &
!Imported routines:
ToString, &
StringCompact, &
StringToUpper
USE Morphology, ONLY : &
!Imported rituines:
DownstreamCell, &
CheckOutlet
USE Reservoirs, ONLY : &
!imported definition:
Reservoir, &
!imported routines:
InitReservoirs, &
GetReservoirById, &
CountReservoirs, &
OutReservoirsInit, &
OutReservoirs, &
!imported variables:
pools
USE Diversions, ONLY : &
!imported definition:
Diversion, &
!Imported routines:
InitDiversions, &
OutDiversionsInit, &
GetDiversionById, &
OutDiversions, &
!Imported variables:
dtDiversion, &
dtOutDiversion, &
diversionChannels
USE Chronos, ONLY : &
!imported definition:
DateTime, &
!Imported operators:
OPERATOR (==), &
OPERATOR (+), &
ASSIGNMENT( = )
USE ObservationalNetworks, ONLY : &
!Imported routines:
ReadData, &
ReadMetadata, &
WriteData, &
WriteMetadata, &
CopyNetwork, &
DestroyNetwork, &
AssignDataFromGrid, &
!Imported defined variable:
ObservationalNetwork
USE RoutingModels, ONLY : &
!Imported routines:
LevelPool, &
DivertFlow, &
MuskingumCungeTodini, &
MuskingumCunge
USE TableLib, ONLY: &
!imported definitions:
Table, &
TableCollection, &
!Imported routines:
TableNew, &
TableGetValue
USE Irrigation, ONLY: &
!imported variables:
Qirrigation
USE Snow, ONLY: &
!imported variables:
excessWaterSnowFlux
USE Utilities, ONLY : &
!Imported routines:
GetUnit
USE MorphologicalProperties, ONLY : &
!imported variables:
flowAccumulation, &
flowDirection, &
dem, &
streamNetwork, &
streamNetworkCreated
USE RiverDrainage, ONLY : &
!imported routines:
ChannelInitiation
IMPLICIT NONE
!Public routines
PUBLIC :: DischargeRoute
PUBLIC :: InitDischargeRouting
PUBLIC :: DischargePointInit
!public declarations:
TYPE(grid_real) :: Qin !! inlet discharge at time t (current)
TYPE(grid_real) :: Qout !! outlet discharge at time t (current)
TYPE(grid_real) :: Pin !! inlet discharge at time t-1 (previous)
TYPE(grid_real) :: Pout !! outlet discharge at time t-1 (previous)
TYPE(grid_real) :: Plat !! lateral discharge at time t-1 (previous)
TYPE(grid_real) :: waterDepth !! water depth from thalweg (m)
TYPE(grid_real) :: topWidth !! channel top width (m)
INTEGER (KIND = short) :: dtDischargeRouting
!Local routines
PRIVATE :: DischargePointExport
!Local declarations:
INTEGER (KIND = short), PRIVATE, &
PARAMETER :: MISSING_DEF_INT = -9999
REAL (KIND = float) , PRIVATE, &
PARAMETER :: MISSING_DEF_REAL = -9999.9
INTEGER (KIND = short), PRIVATE, &
PARAMETER :: SLOPES = 0
INTEGER (KIND = short), PRIVATE, &
PARAMETER :: MCT = 1 !Muskingum-Cunge-Todini
INTEGER (KIND = short), PRIVATE, &
PARAMETER :: MUSKINGUM = 2 !Muskingum
INTEGER (KIND = short), PRIVATE, &
PARAMETER :: INSTANTANEOUS = 3 !Infinite celerity (Lake)
INTEGER (KIND = short), PRIVATE, &
PARAMETER :: MC = 4 !Muskingum-Cunge
TYPE (ObservationalNetwork), PRIVATE :: sites
TYPE (ObservationalNetwork), PRIVATE :: sitesDischarge
INTEGER (KIND = short), PRIVATE :: fileUnitPointDischarge
TYPE (DateTime), PRIVATE :: timePointExport
TYPE (DateTime), PRIVATE :: timePoolsExport
TYPE (DateTime), PRIVATE :: timeDiversionsExport
INTEGER (KIND = short) :: dtOutReservoirs
TYPE (grid_integer), PRIVATE :: channel
TYPE (grid_integer), PRIVATE :: dams !!location of reservoirs in raster map
TYPE (grid_integer), PRIVATE :: divChannels !!location of diversions in raster map
TYPE (grid_real), PRIVATE :: manning !! Manning roughness (s / m^1/3)
TYPE (grid_real), PRIVATE :: bankSlope !!river bank slope (degree)
TYPE (grid_real), PRIVATE :: sectionWidth !!river section width (m)
TYPE (TableCollection), PRIVATE :: channelParameters
!=======
CONTAINS
!=======
! Define procedures contained in this module.
!==============================================================================
!| Description:
! Initialize surface routing
SUBROUTINE InitDischargeRouting &
!
(fileini, time, path_out, flowdirection, domain, &
storage, netRainfall, dtrk )
IMPLICIT NONE
!Arguments with intent in:
CHARACTER (LEN = *), INTENT(IN) :: fileini !!name of configuration file
TYPE(DateTime), INTENT(IN) :: time
CHARACTER (LEN = *), INTENT(IN) :: path_out !!path of output folder
TYPE(grid_integer), INTENT(IN) :: flowdirection
TYPE(grid_integer), INTENT(IN) :: domain
!Arguments with intent out:
TYPE(grid_real), INTENT(OUT) :: storage !water stored in channel cell
TYPE(grid_real), INTENT(OUT) :: netRainfall ! net rainfall rate [m/s]
INTEGER(KIND = short), INTENT(OUT) :: dtrk
!local declarations:
TYPE (IniList) :: ini
INTEGER (KIND = short) :: i, j, k
!CHARACTER (LEN=5) :: ic !!option for initial condition
TYPE(Reservoir), POINTER :: p !!point to current reservoir
TYPE(Diversion), POINTER :: d !!point to current diversion channel
CHARACTER (LEN = 30) :: channelInitiationMethod !!method to assign channel initiation
REAL (KIND = float) :: channelInitiationThreshold !!channel initiation threshold (m2)
REAL (KIND = float) :: hillslopelWidth !!hillslope section width (m)
REAL (KIND = float) :: hillslopeKs !!hillslope roughness (m^1/3 s-1)
REAL (KIND = float) :: hillslopeBankSlope !!hillslope bank slope (degree)
REAL (KIND = float) :: area !! basin area (m2)
REAL (KIND = float) :: channelValue
INTEGER (KIND = short) :: exportChannelGrid !!set channel grid export
REAL (KIND = float) :: scalar
INTEGER (KIND = short) :: nmasks !! number of masks to assign routing parameters
TYPE (grid_integer) :: maskTmp
CHARACTER (LEN = 100) :: stringTmp
!-------------------------------end of declaration-----------------------------
!check stream network
IF ( .NOT. streamNetworkCreated ) THEN
CALL Catch ('error', 'DischargeRouting', &
'stream network is not available, check morphological properties' )
END IF
!load configuration file
CALL IniOpen (fileini, ini)
!number of masks to assign discharge routing parameters
IF ( KeyIsPresent ('masks-number', ini) ) THEN
nmasks = IniReadInt ('masks-number', ini)
ELSE
CALL Catch ('error', 'DischargeRouting', &
'masks-number missing in configuration file' )
END IF
!channel initiation
!------------------
!allocate channel grid
CALL NewGrid ( channel, domain, 0)
!set option to export channel grid
exportChannelGrid = IniReadInt ('export-channel-grid', ini)
!first use base mask
channelInitiationMethod = IniReadString ('channel-initiation-method', &
ini, section = 'base-mask')
channelInitiationThreshold = IniReadReal ('channel-initiation-threshold', &
ini, section = 'base-mask')
CALL ChannelInitiation (method = channelInitiationMethod, &
threshold = channelInitiationThreshold, &
mask = domain, flowAcc = flowAccumulation,&
flowDir = flowDirection, dem = dem, &
channel = channel )
! modify channel initiation grid if additional masks are required
DO k = 1, nmasks - 1
stringTmp = 'mask' // ToString (k)
channelInitiationMethod = IniReadString ('channel-initiation-method', &
ini, section = stringTmp)
channelInitiationThreshold = IniReadReal ('channel-initiation-threshold', &
ini, section = stringTmp)
CALL GridByIni (ini, maskTmp, section = stringTmp)
CALL ChannelInitiation (method = channelInitiationMethod, &
threshold = channelInitiationThreshold, &
mask = maskTmp, flowAcc = flowAccumulation,&
flowDir = flowDirection, dem = dem, &
channel = channel )
CALL GridDestroy (maskTmp)
END DO
!export channel grid
IF ( exportChannelGrid == 1 ) THEN
CALL ExportGrid (channel, 'channel.asc', ESRI_ASCII)
END IF
!create discharge parameter maps
!-------------------------------
!initialize empty maps
CALL NewGrid (layer = manning, grid = domain, initial = 0. )
CALL NewGrid (layer = bankSlope, grid = domain, initial = 0. )
CALL NewGrid (layer = sectionWidth, grid = domain, initial = 0. )
!read channel parameter table(s)
CALL TableNew ( fileini, channelParameters )
!assign parameters to base mask
hillslopelWidth = IniReadReal ('hillslope-width', ini, section = 'base-mask')
hillslopeKs = IniReadReal ('hillslope-ks', ini, section = 'base-mask')
hillslopeBankSlope = IniReadReal ('hillslope-alpha', ini, section = 'base-mask')
DO i = 1, domain % idim
DO j = 1, domain % jdim
IF ( domain % mat (i,j) /= domain % nodata ) THEN
area = flowAccumulation % mat (i,j) * CellArea (domain,i,j)
IF ( channel % mat (i,j) == 0 ) THEN !hillslope cell
manning % mat (i,j) = 1. / hillslopeKs
bankSlope % mat (i,j) = hillslopeBankSlope
sectionWidth % mat (i,j) = hillslopelWidth
ELSE !channel cell
!set manning roughness coefficient
CALL TableGetValue ( valueIn = area, &
tables = channelParameters, &
id = 'base-mask', keyIn = 'threshold', &
keyOut ='ks', match = 'linear', &
valueOut = channelValue, &
bound = 'extendlinear' )
manning % mat (i,j) = 1. / channelValue
!set river bank slope
CALL TableGetValue ( valueIn = area, &
tables = channelParameters, &
id = 'base-mask', keyIn = 'threshold', &
keyOut ='alpha', match = 'linear', &
valueOut = channelValue, &
bound = 'extendlinear' )
bankSlope % mat (i,j) = channelValue
!set section width
CALL TableGetValue ( valueIn = area, &
tables = channelParameters, &
id = 'base-mask', keyIn = 'threshold', &
keyOut ='width', match = 'linear', &
valueOut = channelValue, &
bound = 'extendlinear' )
sectionWidth % mat (i,j) = channelValue
END IF
END IF
END DO
END DO
!assign parameters to sub masks
DO k = 1, nmasks - 1
stringTmp = 'mask' // ToString (k)
hillslopelWidth = IniReadReal ('hillslope-width', ini, &
section = stringTmp)
hillslopeKs = IniReadReal ('hillslope-ks', ini, &
section = stringTmp)
hillslopeBankSlope = IniReadReal ('hillslope-alpha', ini, &
section = stringTmp)
CALL GridByIni (ini, maskTmp, section = stringTmp)
DO i = 1, domain % idim
DO j = 1, domain % jdim
IF ( maskTmp % mat (i,j) /= maskTmp % nodata ) THEN
area = flowAccumulation % mat (i,j) * CellArea (domain,i,j)
IF ( channel % mat (i,j) == 0 ) THEN !hillslope cell
manning % mat (i,j) = 1. / hillslopeKs
bankSlope % mat (i,j) = hillslopeBankSlope
sectionWidth % mat (i,j) = hillslopelWidth
ELSE !channel cell
!set manning roughness coefficient
CALL TableGetValue ( valueIn = area, &
tables = channelParameters, &
id = stringTmp, keyIn = 'threshold', &
keyOut ='ks', match = 'linear', &
valueOut = channelValue, &
bound = 'extendlinear' )
manning % mat (i,j) = 1. / channelValue
!set river bank slope
CALL TableGetValue ( valueIn = area, &
tables = channelParameters, &
id = stringTmp, keyIn = 'threshold', &
keyOut ='alpha', match = 'linear', &
valueOut = channelValue, &
bound = 'extendlinear' )
bankSlope % mat (i,j) = channelValue
!set section width
CALL TableGetValue ( valueIn = area, &
tables = channelParameters, &
id = stringTmp, keyIn = 'threshold', &
keyOut ='width', match = 'linear', &
valueOut = channelValue, &
bound = 'extendlinear' )
sectionWidth % mat (i,j) = channelValue
END IF
END IF
END DO
END DO
CALL GridDestroy (maskTmp)
END DO
!initial condition
!-----------------
!input discharge
IF (SectionIsPresent('discharge-in', ini )) THEN
IF (KeyIsPresent ('scalar', ini, 'discharge-in') ) THEN
scalar = IniReadReal ('scalar', ini, 'discharge-in')
CALL NewGrid (Pin, domain, scalar)
ELSE
CALL GridByIni (ini, Pin, section = 'discharge-in')
END IF
ELSE !grid is optional: set to default = 0
CALL NewGrid ( Pin, domain, 0. )
END IF
!output discharge
IF (SectionIsPresent('discharge-out', ini )) THEN
IF (KeyIsPresent ('scalar', ini, 'discharge-out') ) THEN
scalar = IniReadReal ('scalar', ini, 'discharge-out')
CALL NewGrid (Pout, domain, scalar)
ELSE
CALL GridByIni (ini, Pout, section = 'discharge-out')
END IF
ELSE !grid is optional: set to default = 0
CALL NewGrid ( Pout, domain, 0. )
END IF
!lateral discharge
IF (SectionIsPresent('discharge-lat', ini )) THEN
IF (KeyIsPresent ('scalar', ini, 'discharge-lat') ) THEN
scalar = IniReadReal ('scalar', ini, 'discharge-lat')
CALL NewGrid (Plat, domain, scalar)
ELSE
CALL GridByIni (ini, Plat, section = 'discharge-lat')
END IF
ELSE !grid is optional: set to default = 0
CALL NewGrid ( Plat, domain, 0. )
END IF
!allocate variables
CALL NewGrid (layer = Qin, grid = domain, initial = 0. )
CALL NewGrid (layer = Qout, grid = domain, initial = 0. )
CALL NewGrid (layer = storage, grid = domain, initial = 0. )
!net rainfall grid
CALL NewGrid (layer = netRainfall, grid = domain, initial = 0.)
!water depth grid
CALL NewGrid (layer = waterDepth, grid = domain, initial = 0.)
!topwidth
CALL NewGrid (layer = topWidth, grid = domain, initial = 0.)
!diversions
IF (SectionIsPresent('diversions', ini)) THEN
dtDiversion = IniReadInt ('dt', ini, section = 'diversions')
IF ( dtDiversion /= 0 ) THEN
!set dtDiversion = dtDischargeRouting
dtDiversion = dtDischargeRouting
END IF
IF (dtDiversion > 0) THEN
CALL InitDiversions ( IniReadString('file', ini, &
section = 'diversions') )
!create grid with diversion location
CALL NewGrid (divChannels, domain, domain % nodata )
d => diversionChannels
DO
i = d % r
j = d % c
divChannels % mat (i,j) = d % id
IF ( channel % mat (i,j) == 0 ) THEN
CALL Catch ('warning', 'DischargeRouting', &
'diversion is not on channel: ', argument = ToString(d%id) )
END IF
IF ( .NOT. ASSOCIATED (d % next) ) THEN !found last element of list
EXIT
END IF
!set next diversion
d => d % next
END DO
!initialize files for output
IF ( KeyIsPresent ('dt-out', ini, 'diversions' ) ) THEN
dtOutDiversion = IniReadInt ('dt-out', ini, 'diversions')
IF ( dtOutDiversion > 0) THEN
timeDiversionsExport = time
CALL OutDiversionsInit ( path_out )
END IF
END IF
END IF
ELSE !section is mandatory: stop the program if missing
CALL Catch ('error', 'DischargeRouting', &
'error loading diversions: ' , &
argument = 'section not defined in ini file' )
END IF
!reservoirs
IF (SectionIsPresent('reservoirs', ini)) THEN
!create grid with reservoirs location
CALL NewGrid (dams, domain, domain % nodata )
dtrk = IniReadInt ('dt', ini, section = 'reservoirs')
IF (dtrk > 0) THEN
CALL InitReservoirs (IniReadString('file', ini, &
section = 'reservoirs'), &
time, domain, pools)
p => pools
DO
i = p % r
j = p % c
dams % mat (i,j) = p % id
IF ( channel % mat (i,j) == 0 ) THEN
CALL Catch ('warning', 'DischargeRouting', &
'reservoir is not on channel: ', argument = ToString(p%id) )
END IF
IF ( .NOT. ASSOCIATED (p % next) ) THEN !found last element of list
EXIT
END IF
p => p % next
END DO
!initialize files for output
IF ( KeyIsPresent ('dt-out', ini, 'reservoirs' ) ) THEN
dtOutReservoirs = IniReadInt ('dt-out', ini, 'reservoirs')
IF ( dtOutReservoirs > 0) THEN
timePoolsExport = time
CALL OutReservoirsInit (pools, path_out)
END IF
END IF
END IF
ELSE !section is optional
dtrk = 0
END IF
!finished configuration, deallocate memory
CALL IniClose (ini)
END SUBROUTINE InitDischargeRouting
!==============================================================================
!| Description:
! route discharge in surface network
SUBROUTINE DischargeRoute &
!
(dt, time, flowdir, runoff, dtrk, riverToGroundwater, &
groundwaterToRiver, storage )
IMPLICIT NONE
!Arguments with intent(in):
INTEGER(KIND = short), INTENT(IN) :: dt !!time step [s]
TYPE(DateTime), INTENT(IN) :: time
TYPE(grid_integer), INTENT(IN) :: flowdir !!flow direction
TYPE(grid_real), INTENT(IN) :: runoff !! net rainfall [m/s]
INTEGER(KIND = short), INTENT(IN) :: dtrk !!time step for reservoirs
TYPE(grid_real), INTENT(IN) :: riverToGroundwater !!discharge
!!from river to groundwater (m3/s)
TYPE(grid_real), INTENT(IN) :: groundwaterToRiver !!discharge
!!from groundwater to river (m3/s)
!Arguments with intent inout
TYPE(grid_real), INTENT(INOUT) :: storage !!volume stored on channel cell
!local declarations:
INTEGER(KIND = short) :: k, iin, jin, is, js
REAL (KIND = float) :: ddx
REAL (KIND = float) :: Qlateral !!lateral discharge (m3/s)
REAL (KIND = float) :: QlateralChannel !!lateral discharge in a diversion channel (m3/s)
REAL (KIND = float) :: Qdownstream !! discharge downstream a diversion channel (m3/s)
REAL (KIND = float) :: Qdiverted !! discharge diverted from a diversion channel (m3/s)
REAL (KIND = float) :: runoff_ij !!runoff of single cell
REAL (KIND = float) :: wDepth
REAL (KIND = float) :: tWidth
REAL (KIND = float) :: area
TYPE (Reservoir), POINTER :: p !!pointer to current reservoir
TYPE (Diversion), POINTER :: d, dnest !!pointer to current diversion
!-------------------------------end of declaration-----------------------------
!reset Qin
Qin = 0.
!loop troughout hydro network
DO k = 1, streamNetwork % nreach
iin = streamNetwork % branch (k) % i0
jin = streamNetwork % branch (k) % j0
!follow the branch
DO WHILE ( .NOT.( jin == streamNetwork % branch (k) % j1 .AND. &
iin == streamNetwork % branch (k) % i1 ) )
!set runoff_ij
runoff_ij = runoff % mat(iin,jin)
!find downstream cell
CALL DownstreamCell (iin, jin, flowdir % mat(iin,jin), &
is,js, ddx, flowdir)
! looking for reservoir
IF ( dtrk > 0) THEN
IF ( dams % mat (iin,jin) /= dams % nodata ) THEN
!set current reservoir
p => GetReservoirById (list = pools, id = dams % mat (iin,jin) )
IF (time == p % tReadNewStage) THEN
!read new stage value from file
CALL ReadData (network = p % network, &
fileunit = p % unit, time = time)
!update target level
p % stageTarget = p % network % obs (1) % value
p % tReadNewStage = p % network % time
!update time of next reading of target stage
p % tReadNewStage = p % tReadNewStage + &
p % network % timeIncrement
END IF
IF (p % dischargeDownstream .AND. &
time == p % tReadNewDischargeDownstream ) THEN
!read new downstream discharge value from file
CALL ReadData (network = p % networkDischargeDownstream, &
fileunit = p % unitDischargeDownstream, time = time)
!update time of next reading of downstream discharge
p % tReadNewDischargeDownstream = &
p % tReadNewDischargeDownstream + &
p % networkDischargeDownstream % timeIncrement
END IF
IF (p % dischargeDiverted .AND. &
time == p % tReadNewDischargeDiverted ) THEN
!read new diverted discharge value from file
CALL ReadData (network = p % networkDischargeDiverted, &
fileunit = p % unitDischargeDiverted, time = time)
!update time of next reading of diverted discharge
p % tReadNewDischargeDiverted = &
p % tReadNewDischargeDiverted + &
p % networkDischargeDiverted % timeIncrement
END IF
!reservoir routing
Qin % mat(iin,jin) = Qin % mat(iin,jin) + &
runoff_ij * &
CellArea(runoff,iin,jin)
CALL LevelPool (time, dt, dtrk, Pin % mat(iin,jin), &
Qin % mat(iin,jin), p)
!check and simulate diversion from reservoir
IF ( p % bypassIsPresent ) THEN
!divert flow from river
d => p % bypass
CALL DivertFlow (time, d, p % Qout, p % Qout )
!overwrite diversion inlet discharge when observation is available
IF ( p % dischargeDiverted ) THEN
IF ( p % networkDischargeDiverted % obs (1) % value /= &
p % networkDischargeDiverted % nodata ) THEN
d % QinChannel = p % networkDischargeDiverted % obs (1) % value
END IF
END IF
!route discharge into channel
Qlateral = 0.
CALL MuskingumCungeTodini ( dt, &
d % channelLenght, &
d % QinChannel, &
d % PinChannel, &
d % PoutChannel, &
Qlateral, Qlateral, &
d % channelWidth, &
d % channelBankSlope, &
d % channelSlope, &
d % channelManning, &
d % QoutChannel, &
wDepth, tWidth )
!copy data of current step for next step
d % PinChannel = d % QinChannel
d % PoutChannel = d % QoutChannel
END IF
!set Qout
SELECT CASE ( p % typ )
CASE ( 'on' )
IF ( p % dischargeDownstream ) THEN
IF ( p % networkDischargeDownstream % obs (1) % value /= &
p % networkDischargeDownstream % nodata ) THEN
!overwrite reservoir downstream discharge
p % Qout = p % networkDischargeDownstream % obs (1) % value
END IF
END IF
Qout % mat(iin,jin) = p % Qout
CASE ( 'off' ) !off-stream reservoir DA RIVEDERE!!
!compute off-stream pool outflow
CALL TableGetValue ( valueIn = p % stage, tab = p % geometry, keyIn = 'h', &
keyOut ='Qout', match = 'linear', &
valueOut = p % Qout_off, bound = 'extendlinear' )
p % Pout_off = p % Qout_off
Qout % mat(iin,jin) = p % Qout
!assign outflow to pool out cell
!IF (p % rout /= MISSING_DEF_INT .AND. p % cout /= MISSING_DEF_INT ) THEN
!Qin % mat(p % rout,p % cout) = Qin % mat(p % rout,p % cout) + &
! p % Qout_off
!END IF
END SELECT
Pin % mat(iin,jin) = Qin % mat(iin,jin)
Pout % mat(iin,jin) = Qout % mat(iin,jin)
jin = js
iin = is
CYCLE
END IF
END IF
!Muskingum-Cunge-Todini
area = CellArea(runoff,iin,jin)
!set Qlateral
Qlateral = runoff_ij * area
!remove irrigation
IF ( ALLOCATED (Qirrigation % mat) ) THEN
Qlateral = Qlateral - Qirrigation % mat (iin, jin)
END IF
!include river-groundwater exchange
IF ( ALLOCATED ( riverToGroundwater % mat ) ) THEN
IF ( riverToGroundwater % mat (iin,jin) /= &
riverToGroundwater % nodata ) THEN
Qlateral = Qlateral + groundwaterToRiver % mat (iin,jin) - &
riverToGroundwater % mat (iin,jin)
END IF
END IF
!add excess of water retained in snow
IF ( ALLOCATED ( excessWaterSnowFlux % mat ) ) THEN
Qlateral = Qlateral + excessWaterSnowFlux % mat (iin, jin) * area
END IF
!add outflow from by-pass channel or off-stream basin
p => pools
DO WHILE (ASSOCIATED(p)) !loop trough all reservoirs
IF ( p % typ == 'off' ) THEN
IF ( iin == p % rout .AND. jin == p % cout ) THEN
Qlateral = Qlateral + p % Pout_off
END IF
END IF
IF (p % bypassIsPresent) THEN
IF ( iin == p % bypass % rout .AND. jin == p % bypass % cout ) THEN
Qlateral = Qlateral + p % bypass % PoutChannel
END IF
END IF
p => p % next
END DO
!remove diverted discharge from diversion channel
IF (dtDiversion > 0) THEN
IF ( divChannels % mat (iin,jin) /= divChannels % nodata ) THEN
!divert flow from river
d => GetDiversionById (list = diversionChannels, &
id = divChannels % mat (iin,jin) )
CALL DivertFlow (time, d, Qin % mat(iin,jin), Qdownstream )
Qdiverted = Qin % mat (iin, jin) - Qdownstream
!update Qlateral
Qlateral = Qlateral - Qdiverted
!route discharge into channel
QlateralChannel = 0.
CALL MuskingumCungeTodini ( dtDiversion, &
d % channelLenght, &
d % QinChannel, &
d % PinChannel, &
d % PoutChannel, &
QlateralChannel, QlateralChannel, &
d % channelWidth, &
d % channelBankSlope, &
d % channelSlope, &
d % channelManning, &
d % QoutChannel, &
wDepth, tWidth )
!copy data of current step for next step
d % PinChannel = d % QinChannel
d % PoutChannel = d % QoutChannel
END IF
END IF
!add outflow from diversion channel
d => diversionChannels
DO WHILE (ASSOCIATED(d)) !loop trough all diversion channels
IF ( iin == d % rout .AND. jin == d % cout ) THEN
! As the flood routing is solved from upstream to downstream
! PoutChannel contains the current or the previous time step discharge according to the
! Horton order of the outlet section respect to the horton order of onlet section
Qlateral = Qlateral + d % PoutChannel
END IF
d => d % next
END DO
CALL MuskingumCungeTodini ( dt, ddx, Qin % mat(iin,jin), &
Pin % mat(iin,jin), &
Pout % mat(iin,jin), &
Qlateral, Plat % mat(iin,jin), &
sectionWidth % mat (iin,jin), &
bankSlope % mat (iin,jin), &
streamNetwork % branch (k) % slope, &
manning % mat (iin,jin), &
Qout % mat(iin,jin), &
waterDepth % mat(iin,jin), &
topWidth % mat(iin,jin) )
storage % mat (iin,jin) = storage % mat (iin,jin) + &
( Qin % mat(iin,jin) + Qlateral - Qout % mat(iin,jin) ) * &
dt / CellArea(Qin,iin,jin)
IF ( storage % mat (iin,jin) < 0. ) THEN
!storage % mat (iin,jin) = 0.
!Qin % mat(iin,jin) = 0.
!Qout % mat(iin,jin) = 0.
END IF
IF (isnan (Qout % mat(iin,jin)) ) THEN
Qout % mat(iin,jin) = Pout % mat(iin,jin)
END IF
!computed Qout becomes Qin for the downstream section.
!Take account of junctions, sum to existing discharge
Qin % mat(is,js) = Qin % mat(is,js) + Qout % mat(iin,jin)
!store previous values for next time step
Pin % mat(iin,jin) = Qin % mat(iin,jin)
Pout % mat(iin,jin) = Qout % mat(iin,jin)
Plat % mat(iin,jin) = Qlateral
!store cell indexes and select downstream cell for next loop
jin = js
iin = is
!check next cell is outlet cell
CALL DownstreamCell (iin, jin, flowdir % mat(iin,jin), &
is,js, ddx, flowdir)
IF ( CheckOutlet (iin,jin,is,js,flowdir) ) THEN
!next cell will not be computed, set approximate value of Qout
Qout % mat (iin, jin) = Qin % mat (iin, jin) + &
runoff % mat(iin,jin) * CellArea(runoff,iin,jin)
END IF
END DO
END DO
!write results
IF ( time == timePointExport ) THEN
CALL DischargePointExport ( time )
timePointExport = timePointExport + sitesDischarge % timeIncrement
END IF
IF ( time == timePoolsExport ) THEN
CALL OutReservoirs ( pools, time, Qin, Qout )
timePoolsExport = timePoolsExport + dtOutReservoirs
END IF
IF ( time == timeDiversionsExport ) THEN
CALL OutDiversions ( diversionChannels, time, Qin, Qout )
timeDiversionsExport = timeDiversionsExport + dtOutDiversion
END IF
RETURN
END SUBROUTINE DischargeRoute
!==============================================================================
!| Description:
! Initialize export of point site data
SUBROUTINE DischargePointInit &
!
( pointfile, path_out, time )
IMPLICIT NONE
!Arguments with intent (in):
CHARACTER (LEN = *), INTENT(IN) :: pointfile !!file containing coordinate of points
CHARACTER (LEN = *), INTENT(IN) :: path_out !!path of output folder
TYPE (DateTime), INTENT(IN) :: time !!start time
!local declarations
INTEGER (KIND = short) :: err_io
INTEGER (KIND = short) :: fileunit
!-------------------------end of declarations----------------------------------
timePointExport = time
!open point file
fileunit = GetUnit ()
OPEN ( unit = fileunit, file = pointfile(1:LEN_TRIM(pointfile)), &
status='OLD', iostat = err_io )
IF ( err_io /= 0 ) THEN
!file does not exist
CALL Catch ('error', 'DischargeRouting', 'out point file does not exist')
END IF
!Read metadata
CALL ReadMetadata (fileunit, sites)
!check dt
IF (.NOT. ( MOD ( sites % timeIncrement, dtDischargeRouting ) == 0 ) ) THEN
CALL Catch ('error', 'DischargeRouting', &
'dt out sites must be multiple of dtDischargeRouting ')
END IF
CLOSE ( fileunit )
!create virtual network and initialize file for output
fileUnitPointDischarge = GetUnit ()
OPEN ( unit = fileUnitPointDischarge, &
file = TRIM(path_out) // 'point_discharge.fts' )
CALL CopyNetwork ( sites, sitesDischarge )
sitesDischarge % description = 'discharge data exported from FEST'
sitesDischarge % unit = 'm3/s'
sitesDischarge % offsetZ = 0.
CALL WriteMetadata ( network = sitesDischarge, &
fileunit = fileUnitPointDischarge )
CALL WriteData (sitesDischarge, fileUnitPointDischarge, .TRUE.)
! destroy sites
CALL DestroyNetwork ( sites )
RETURN
END SUBROUTINE DischargePointInit
!==============================================================================
!| Description:
! Export of point site data
SUBROUTINE DischargePointExport &
!
( time )
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT (IN) :: time
!local declarations:
INTEGER (KIND = short) :: i
!-------------------------end of declarations----------------------------------
!set current time
sitesDischarge % time = time
!populate data
!CALL AssignDataFromGrid (Qin, sitesDischarge )
!debug
CALL AssignDataFromGrid (Qout, sitesDischarge )
!write data
CALL WriteData (sitesDischarge, fileUnitPointDischarge)
RETURN
END SUBROUTINE DischargePointExport
END MODULE DischargeRouting