!! Solve soil water and energy balance
!|author: Giovanni Ravazzani
! license: GPL
!
!### History
!
! current version 1.6 - 20th July 2023
!
! | version | date | comment |
! |----------|-------------|----------|
! | 1.0 | 28/Jun/2010 | Initial code. chiara corbari and davide rabuffetti |
! | 1.1 | 03/May/2011 | Correct energy fluxes when snow is present |
! | 1.2 | 10/Nov/2016 | Module rewritten to add new infiltration models and different soil properties management |
! | 1.3 | 25/Jul/2022 | subsurface flow changed from muskingum to darcy. Runon option removed |
! | 1.4 | 27/Jan/2023 | bug correction: percolationcell set to zero on hillslope and rivers in SetSoilDepth |
! | 1.5 | 10/Feb/2023 | modified to not use river network |
! | 1.6 | 20/Jul/2023 | SetSoilDepth renamed to PercolationAndCaprise and modified |
!
!### License
! license: GNU GPL
!
!### Module Description
! Solve soil water and energy balance to update soil moisture and
! compute actual evapotranspiration, runoff, and groundwater
! recharge.
! Soil column is assumed to be divided in two layers:
!
! * the surface layer where roots develop (root zone)
!
! * the lower unsaturated layer that transfers
! water to recharge groundwater in landplain (transmission zone)
!
MODULE SoilBalance
! Modules used:
USE DataTypeSizes, ONLY : &
! Imported Type Definitions:
short, float, double
USE DomainProperties, ONLY : &
!imported variables:
mask
USE StringManipulation, ONLY: &
!Imported routines:
ToString
USE GridLib, ONLY: &
!Imported definitions:
grid_integer, grid_real, &
!Imported routines:
NewGrid
USE GridOperations, ONLY : &
!imported routines:
GridByIni, CRSisEqual, CellArea, &
!Imported assignment:
ASSIGNMENT (=)
USE Chronos, ONLY: &
!Imported definitions:
DateTime, &
!Imported operators and assignment:
OPERATOR (>=), OPERATOR (<=), &
ASSIGNMENT( = )
USE LogLib, ONLY: &
!Imported routines:
Catch
USE IniLib, ONLY: &
!Imported derived types:
IniList, &
!Imported routines:
IniOpen, IniClose, &
IniReadInt, IniReadReal, IniReadDouble, &
IniReadString, SectionIsPresent, &
KeyIsPresent
USE SoilProperties, ONLY : &
!Imported definitions:
!SoilType, &
!imported routines:
UnsHydCond, Psi
USE Infiltration, ONLY : &
!imported routines:
InfiltrationInit, &
Sat2scn, SCScurveNumber, &
Philip, GreenAmpt, &
!imported variables:
infiltrationModel, &
soilDivisions, &
curveNumber, &
storativity, &
sEff, ism, cuminf, &
raincum, wiltingPoint, &
fieldCapacity, thetar, &
thetas, ksat, psdi, psic, &
abstractionRatio, phy, &
!imported parameters:
SCS_CN, PHILIPEQ, &
GREEN_AMPT, ROSS_BC, ROSS_VG
USE Richards, ONLY : &
!Imported routines
SolveRichardsBC, &
!imported variables :
nsteps, parameters, cell, &
dx, S, h0grid, drncum, &
infilcum, evapcum, runoffcum
USE Evapotranspiration, ONLY : &
!imported routines
ETinit, PETupdate, &
!imported variables:
pet, pe, pt
USE Morphology, ONLY: &
!Imported routines:
DownstreamCell, CheckOutlet
USE TableLib, ONLY: &
! Imported definitions:
Table, &
!Imported routines
TableNew, TableGetValue, TableGetNrows
USE StringManipulation, ONLY: &
!Imported routines:
StringToShort
USE MorphologicalProperties, ONLY: &
!imported variables:
dem
USE Snow, ONLY : &
!imported variables:
swe, waterInSnow, dtSnow
USE Glacier, ONLY : &
!imported variables:
icewe, waterInIce, dtIce
!Global declarations:
!public routines
PUBLIC :: InitSoilBalance
PUBLIC :: SolveSoilBalance
!public variables:
INTEGER (KIND = short) :: dtSoilBalance
!TYPE(NetworkSoilBalance):: soilNetwork !!define spatial scheme to solve balance
!parameters
TYPE (grid_real) :: ksat_sub ![m/s]
TYPE (grid_real) :: soilDepth !!soil depth [m]
TYPE (grid_real) :: soilDepthRZ !! root zone depth [m]
TYPE (grid_real) :: soilDepthTZ !!transmission zone depth [m]
!common state variables
TYPE (grid_real) :: soilSat !!mean soil relative saturation [0-1]
TYPE (grid_real) :: soilSatRZ !!root zone soil relative saturation [0-1]
TYPE (grid_real) :: soilSatTZ !!transmission zone soil relative saturation [0-1]
TYPE (grid_real) :: soilMoisture !!mean volumetric water content [m3/m3]
TYPE (grid_real) :: soilMoistureRZ !!root zone volumetric water content [m3/m3]
TYPE (grid_real) :: soilMoistureTZ !!transmission zone volumetric water content [m3/m3]
TYPE (grid_integer) :: rainFlag !!set if it is raining in a cell
TYPE (grid_integer) :: interstormDuration !!interstorm duration [s]
TYPE (grid_real) :: QinSoilSub !!input discharge in soil subsurface [m3/s]
TYPE (grid_real) :: QoutSoilSub !!output discharge from soil subsurface [m3/s]
TYPE (grid_real) :: balanceError !!balance error [mm]
TYPE (grid_real) :: deltaSoilMoisture !! time step soil mositure variation (m)
!vertical fluxes
TYPE (grid_real) :: rainBalance !!actual rainfall rate as input to soil balance [m/s]
TYPE (grid_real) :: infilt !!infiltration rate [m/s]
TYPE (grid_real) :: runoff !!runoff rate [m/s]
TYPE (grid_real) :: et !! actual evapotranspiration rate [m/s]
TYPE (grid_real) :: percolation !! deep percolation through soil [m/s]
TYPE (grid_real) :: percolationFactor !! deep percolation factor [-]
TYPE (grid_real) :: capRise !! capillary rise [m/s]
!energy balance fluxes
TYPE (grid_real) :: Ts
TYPE (grid_real) :: Rnetta
TYPE (grid_real) :: Xle
TYPE (grid_real) :: Hse
TYPE (grid_real) :: Ge
TYPE (grid_real) :: Ta_prec
TYPE (grid_real) :: Ts_prec
!Local routines
PRIVATE :: WetlandIsWet
PRIVATE :: SetInitialCondition
PRIVATE :: SetWetland
PRIVATE :: PercolationAndCaprise
PRIVATE :: UpdateSoilMoisture
!local declarations:
!parameters
INTEGER, PRIVATE, PARAMETER :: HILLSLOPE = 0
INTEGER, PRIVATE, PARAMETER :: CHANNEL = 1
INTEGER, PRIVATE, PARAMETER :: LAKE = 2
INTEGER, PRIVATE, PARAMETER :: LANDPLAIN = 3
!wetland
TYPE (grid_integer), PRIVATE :: wetland
INTEGER, ALLOCATABLE, PRIVATE :: wetCode (:)
TYPE (DateTime), ALLOCATABLE, PRIVATE :: wetBegin (:)
TYPE (DateTime), ALLOCATABLE, PRIVATE :: wetEnd (:)
!balance
REAL (KIND = float), PRIVATE :: thresholdStartEvent !threshold to consider storm initiated [m/s]
REAL (KIND = float), PRIVATE :: interstorm !duration of interstorm period to terminate an event [s]
REAL (KIND = float), PRIVATE :: isd !!initial saturation degree, used for cold start
TYPE (grid_integer), PRIVATE :: balanceId !!id code for solving water balance
LOGICAL , PRIVATE :: saturatedByGroundwater !!groundwater table intercepts root zone
!=======
CONTAINS
!=======
! Define procedures contained in this module.
!==============================================================================
!| Description:
! Initialize soil water balance
SUBROUTINE InitSoilBalance &
!
(inifile, flowDirection, time)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: inifile !!stores configuration information
TYPE (grid_integer), INTENT(IN) :: flowDirection !!flow direction map
TYPE (DateTime), INTENT(IN) :: time !!start time
!local declarations:
TYPE (IniList) :: iniDB
CHARACTER (LEN = 1000) :: filename
INTEGER (KIND = short) :: k, i, j, iin, jin, is, js
REAL (KIND = float) :: scalar
!------------end of declaration------------------------------------------------
!open and read configuration file
CALL IniOpen (inifile, iniDB)
!read balance id
IF (SectionIsPresent('balance-id', iniDB)) THEN
CALL GridByIni (iniDB, balanceId, section = 'balance-id')
ELSE !grid is mandatory: stop the program if not present
CALL Catch ('error', 'SoilBalance', &
'error in loading balance-id: ' , &
argument = 'section not defined in ini file' )
END IF
!read subsurface soil hydraulic conductivity
IF (SectionIsPresent('ksat-subsurface', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'ksat-subsurface') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'ksat-subsurface')
CALL NewGrid (ksat_sub, mask, scalar)
ELSE
CALL GridByIni (iniDB, ksat_sub, section = 'ksat-subsurface')
END IF
ELSE !grid is mandatory: stop the program if not present
CALL Catch ('error', 'SoilBalance', &
'error in loading subsurface conductivity: ' , &
argument = 'section not defined in ini file' )
END IF
!read soil depth (m)
IF (SectionIsPresent('soil-depth', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'soil-depth') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'soil-depth')
CALL NewGrid (soilDepth, mask, scalar)
ELSE
CALL GridByIni (iniDB, soilDepth, section = 'soil-depth')
END IF
ELSE !grid is mandatory: stop the program if not present
CALL Catch ('error', 'SoilBalance', &
'error in loading soil depth: ' , &
argument = 'section not defined in ini file' )
END IF
!read deep percolation factor (-)
IF (SectionIsPresent('deep-percolation-factor', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'deep-percolation-factor') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'deep-percolation-factor')
CALL NewGrid (percolationFactor, mask, scalar)
ELSE
CALL GridByIni (iniDB, percolationFactor, section = 'deep-percolation-factor')
END IF
ELSE !grid is optional: default = 1.
CALL NewGrid (percolationFactor, mask, 1.0)
END IF
!read root zone depth (m)
IF (SectionIsPresent('root-zone-depth', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'root-zone-depth') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'root-zone-depth')
CALL NewGrid (soilDepthRZ, mask, scalar)
ELSE
CALL GridByIni (iniDB, soilDepthRZ, section = 'root-zone-depth')
END IF
ELSE !grid is mandatory: stop the program if not present
CALL Catch ('error', 'SoilBalance', &
'error in loading root zone depth: ' , &
argument = 'section not defined in ini file' )
END IF
!compute transmission zone depth
CALL NewGrid ( soilDepthTZ, mask, 0.)
DO i = 1, mask % idim
DO j = 1, mask % jdim
IF ( mask % mat (i,j) /= mask % nodata ) THEN
soilDepthTZ % mat(i,j) = soilDepth % mat(i,j) - &
soilDepthRZ % mat(i,j)
IF ( soilDepthTZ % mat(i,j) <= 0. ) THEN
soilDepthTZ % mat(i,j) = 0.1
END IF
END IF
END DO
END DO
!configure evapotranspiration
IF (KeyIsPresent('evapotranspiration', iniDB)) THEN
filename = IniReadString ('evapotranspiration', iniDB)
CALL ETinit (filename, time)
ELSE
CALL Catch ('error', 'SoilBalance', &
'evapotranspiration not found in configuration file' )
END IF
!threshold to initiate storm period, read and convert from mm/h to m/s
thresholdStartEvent = IniReadReal ('threshold-storm-start', iniDB)
thresholdStartEvent = thresholdStartEvent / 1000. / 3600.
!interstorm duration to terminate an event, read and convert from hours to seconds
interstorm = IniReadReal ('interstorm', iniDB)
interstorm = interstorm * 3600.
!configure infiltration
IF (KeyIsPresent('infiltration', iniDB)) THEN
filename = IniReadString ('infiltration', iniDB)
CALL InfiltrationInit (filename, soilMoisture, soilDepth )
ELSE
CALL Catch ('error', 'SoilBalance', &
'infiltration not found in configuration file' )
END IF
!set initial condition
CALL SetInitialCondition (iniDB)
!set wetland
CALL SetWetland (iniDB, inifile)
!allocate coomon variables used to solve soil water and energy balance
!vertical fluxes
CALL NewGrid (rainBalance, mask, 0.)
CALL NewGrid (infilt, mask, 0.)
CALL NewGrid (runoff, mask, 0.)
CALL NewGrid (et, mask, 0.)
CALL NewGrid (percolation, mask, 0.)
CALL NewGrid (capRise, mask, 0.)
!energy balance
CALL NewGrid (Ts, mask, 0.)
CALL NewGrid (Rnetta, mask, 0.)
CALL NewGrid (Xle, mask, 0.)
CALL NewGrid (Hse, mask, 0.)
CALL NewGrid (Ge, mask, 0.)
CALL NewGrid (Ta_prec, mask, 0.)
CALL NewGrid (Ts_prec, mask, 0.)
!lateral fluxes at time t
CALL NewGrid (QinSoilSub, mask, 0.)
CALL NewGrid (QoutSoilSub, mask, 0.)
!balance error
CALL NewGrid (balanceError, mask, 0.)
!soil mositure variation
CALL NewGrid (deltaSoilMoisture, mask, 0.)
! Configuration terminated. Deallocate ini database
CALL IniClose (iniDB)
RETURN
END SUBROUTINE InitSoilBalance
!==============================================================================
!| Description:
! Solve soil water balance
SUBROUTINE SolveSoilBalance &
!
(time, rain, irrigation, flowdir, vf, vadose)
IMPLICIT NONE
!Arguments with intent(in):
!INTEGER (KIND = short), INTENT(IN) :: dt !!computation time step [s]
TYPE (DateTime), INTENT(IN) :: time !current date and time
TYPE (grid_real), INTENT(IN) :: rain !!rainfall rate [m/s]
TYPE (grid_real), INTENT(IN) :: irrigation !!irrigation rate [m/s]
TYPE (grid_integer), INTENT(IN) :: flowdir
TYPE (grid_real), INTENT(IN) :: vf !!vegetation fraction [0-1]
TYPE (grid_real), INTENT(IN) :: vadose !vadose zone depth [m]
!local declarations:
INTEGER (KIND = short) :: k, iin, jin, is, js, id, i, j
REAL (KIND = double) :: runoffcell !runoff of single cell [m/s]
REAL (KIND = float) :: percolationcellRZ !root zone percolation of single cell [m/s]
REAL (KIND = float) :: percolationcellTZ !transmission zone percolation of single cell [m/s]
REAL (KIND = float) :: Tdeep !deep surface temperature in lake
REAL (KIND = float) :: alpha, beta !!used to compute actual evapotranspiration [0-1]
REAL (KIND = float) :: sm, wp, fc !!soil moisture, wilting point and field capacity of current cell
REAL (KIND = float) :: actualE, actualT !!actual evaporation and transpiration of current cell
REAL (KIND = double) :: soilDepthCell !!soil depth of current cell [m]
REAL (KIND = float) :: prunoffcum !!cumulated runoff computed by Ross at previous timestep [cm]
REAL (KIND = float) :: pinfilcum !!cumulated infiltration computed by Ross at previous timestep [cm]
REAL (KIND = float) :: pdrncum !!cumulated drainage computed by Ross at previous timestep [cm]
REAL (KIND = float) :: pevapcum !!cumulated evapotranspiration used and possibly modified by Ross at previous timestep [cm]
REAL (KIND = float) :: water !!water in soil [m]
REAL (KIND = float) :: ddx !actual cell length (m)
REAL (KIND = float) :: meanHydCond !!mean hydraulic conductivity
!for computing lateral subsurface flow (m/s)
REAL (KIND = float) :: ds !!thickness of the saturated depth (m)
REAL (KIND = float) :: cellWidth !!cell width (m)
REAL (KIND = float) :: ijSlope
LOGICAL :: snowCovered, iceCovered
!------------end of declaration------------------------------------------------
!reset variables for the time step
QinSoilSub = 0.
QoutSoilSub = 0.
runoff = 0.
rainBalance = 0.
!update PET
!----------
CALL PETupdate (time)
!compute lateral subsurface intercell fluxes
!------------------------------------------
DO i = 1, mask % idim
DO j = 1, mask % jdim
IF ( mask % mat (i,j) == mask % nodata ) CYCLE
!check balance id
IF ( balanceId % mat (i,j) == LANDPLAIN .OR. &
balanceId % mat (i,j) == LAKE ) THEN
CYCLE
END IF
!set downstream cell (is,js)
CALL DownstreamCell(i, j, flowdir % mat(i,j), is, js, ddx, flowdir )
IF ( CheckOutlet (i, j, is, js, flowdir) ) CYCLE
!harmonic mean saturated conductivity
meanHydCond = 2. * ksat_sub % mat (i,j) * ksat_sub % mat (is,js) / &
( ksat_sub % mat (i,j) + ksat_sub % mat (is,js) )
!thickness of the saturated depth
ds = soilDepthTZ % mat(i,j) * soilMoistureTZ % mat (i,j) / thetas % mat (i,j)
!cell width
cellWidth = ( CellArea (soilMoisture, i, j) ) ** 0.5
!local slope
ijSlope = ( dem % mat (i,j) - dem % mat (is,js) ) / ddx
IF ( ijSlope <= 0. ) THEN
ijSlope = 0.0001
END IF
QoutSoilSub % mat (i,j) = cellWidth * ds * meanHydCond * ijSlope
!output becomes input of downstream cell
QinSoilSub % mat (is,js) = QinSoilSub % mat (is,js) + QoutSoilSub % mat (i,j)
END DO
END DO
!loop through mask
DO i = 1, mask % idim
DO j = 1, mask % jdim
IF ( mask % mat (i,j) == mask % nodata ) CYCLE
!reset fluxes of current cell
runoffcell = 0.
percolationcellRZ = 0.
percolationcellTZ = 0.
!set balance id
id = balanceId % mat (i,j)
!rain contributes to soil balance
rainBalance % mat (i,j) = rain % mat (i,j)
!add irrigation
IF ( ALLOCATED ( irrigation % mat ) ) THEN
rainBalance % mat (i,j) = rainBalance % mat (i,j) + irrigation % mat (i,j)
END IF
snowCovered = .FALSE.
iceCovered = .FALSE.
!check if snow and ice cover the current cell
IF ( dtIce > 0 ) THEN !glaciers are simulated
IF ( icewe % mat (i,j) <= 0. ) THEN !liquid water in ice
!contributes to soil balance
rainBalance % mat (i,j) = rainBalance % mat (i,j) + &
waterInIce % mat (i,j) / dtSoilBalance
waterInIce % mat (i,j) = 0.
IF ( dtSnow > 0 ) THEN !snow is simulated
IF ( swe % mat (i,j) <= 0. ) THEN !liquid water in snow
! contributes to soil balance
rainBalance % mat (i,j) = rainBalance % mat (i,j) + &
waterInSnow % mat (i,j) / dtSoilBalance
waterInSnow % mat (i,j) = 0.
ELSE
rainBalance % mat (i,j) = 0.
snowCovered = .TRUE.
END IF
END IF
ELSE !soil is frozen, no vertical fluxes in it
!runoff % mat(i,j) = 0.
!infilt % mat(i,j) = 0.
!percolation % mat(i,j) = 0.
!et % mat(i,j) = 0.
rainBalance % mat (i,j) = 0.
iceCovered = .TRUE.
END IF
END IF
IF ( dtSnow > 0 .AND. dtIce <= 0 ) THEN
IF ( swe % mat (i,j) > 0.) THEN
!soil is frozen, no vertical fluxes in it
!runoff % mat(i,j) = 0.
!infilt % mat(i,j) = 0.
!percolation % mat(i,j) = 0.
!et % mat(i,j) = 0.
rainBalance % mat (i,j) = 0.
snowCovered = .TRUE.
ELSE
rainBalance % mat (i,j) = rainBalance % mat (i,j) + &
waterInSnow % mat (i,j) / dtSoilBalance
waterInSnow % mat (i,j) = 0.
END IF
END IF
!decide if storm or interstorm period
IF ( rainFlag % mat(i,j) == 0 .AND. rainBalance % mat (i, j) >= thresholdStartEvent ) THEN
!storm starts
rainFlag % mat(i,j) = 1
interstormDuration % mat(i,j) = 0 !reset interstorm duration
IF (infiltrationModel == SCS_CN) THEN
CALL Sat2scn (curveNumber % mat(i,j), storativity % mat(i,j), &
soilSatRZ % mat(i,j), sEff % mat(i,j) )
END IF
IF (infiltrationModel == PHILIPEQ .OR. &
infiltrationModel == GREEN_AMPT) THEN
ism % mat(i,j) = soilMoistureRZ % mat(i,j)
END IF
IF (infiltrationModel == ROSS_BC .OR. &
infiltrationModel == ROSS_VG) THEN
runoffcum % mat(i,j) = 0.
drncum % mat(i,j) = 0.
infilcum % mat(i,j) = 0.
evapcum % mat(i,j) = 0.
END IF
ELSE IF ( rainFlag % mat(i,j) == 1 .AND. rainBalance % mat (i, j) >= thresholdStartEvent ) THEN
!storm continues
rainFlag % mat(i,j) = 2
ELSE IF ( rainBalance % mat (i, j) < thresholdStartEvent ) THEN
!update interstorm duration
interstormDuration % mat(i,j) = interstormDuration % mat(i,j) + dtSoilBalance
END IF
!check if storm event must be terminated
IF (interstormDuration % mat(i,j) >= interstorm) THEN
rainFlag % mat(i,j) = 0
IF (infiltrationModel == SCS_CN) THEN
!reset cumulated rainfall
raincum % mat(i,j) = 0.
END IF
IF (infiltrationModel == PHILIPEQ .OR. &
infiltrationModel == GREEN_AMPT ) THEN
!reset cumulated infiltration
cuminf % mat(i,j) = 0.
END IF
END IF
!compute vertical fluxes according to soil balance computation scheme
SELECT CASE ( id )
CASE (LAKE)
!Lake drains subsurface flux
!runoff can become < 0 when pet > rain + QinSoilSub
runoffcell = rainBalance % mat (i, j) - pet % mat(i,j) + &
QinSoilSub % mat(i,j) / CellArea (QinSoilSub, i, j)
et % mat(i,j) = pet % mat(i,j) !et is potential
percolationcellRZ = 0. ! percolation is zero
percolationcellTZ = 0.
!CASE (LAKE_EWB)
! Tdeep = 25. ![°C]
! GR TODO
!CALL ET_energy_nr_lago(SM%mat(iin,jin),albedo%mat(iin,jin),bpress%mat(iin,jin),&
! dtm%mat(iin,jin),temperatura_oss%mat(iin,jin),&
! radiazione_oss%mat(iin,jin),vento_oss%mat(iin,jin),umidita_oss%mat(iin,jin),&
! indice_nuv%mat(iin,jin),ET%mat(iin,jin),&
! Ts%mat(iin,jin),Rnetta%mat(iin,jin),Xle%mat(iin,jin),Hse%mat(iin,jin),&
! Ge%mat(iin,jin),Tdeep)
!runoffcell = rainBalance % mat (i, j) - et % mat(i,j) + &
! QinSoilSub % mat(i,j) / CellArea (QinSoilSub, i, j)
! percolationcell = 0. !deep percolation is zero
CASE DEFAULT ! all other cells(SLOPE, SLOPE_EWB, SLOPE_EWB_WET, CHANNEL, CHANNEL_EWB, &
!CHANNEL_EWB_WET, PLAIN, PLAIN_EWB, PLAIN_EWB_WET)
!compute infiltration and runoff during storm period
IF ( rainFlag % mat(i,j) /= 0 ) THEN
SELECT CASE (infiltrationModel)
CASE (SCS_CN) !use soil conservation services curve number method
infilt % mat (i,j) = SCScurveNumber (rain = rainBalance % mat (i, j), &
raincum = raincum % mat (i,j), &
c = abstractionRatio % mat(i,j), &
s = sEff % mat(i,j), &
dt = dtSoilBalance, runoff = runoffcell)
CASE (PHILIPEQ) !use Philips infiltration model
infilt % mat (i,j) = Philip (rain = rainBalance % mat (i, j), &
psic = psic % mat(i,j), &
ksat = ksat % mat(i,j), &
theta = soilMoistureRZ % mat(i,j), &
thetas = thetas % mat(i,j), &
thetar = thetar % mat(i,j), &
psdi = psdi % mat (i,j), &
itheta = ism % mat (i,j), dt = dtSoilBalance, &
cuminf = cuminf % mat (i,j) )
runoffcell = rainBalance % mat (i, j) - infilt % mat (i,j)
CASE (GREEN_AMPT) !use Green-Ampt infiltration model
infilt % mat (i,j) = GreenAmpt (rainFlag % mat(i,j), &
rain = rainBalance % mat (i, j), &
ksat = ksat % mat(i,j), &
theta = soilMoistureRZ % mat(i,j), &
thetas = thetas % mat(i,j), &
thetar = thetar % mat(i,j), &
phy = phy % mat(i,j), &
itheta = ism % mat (i,j), dt = dtSoilBalance, &
cuminf = cuminf % mat (i,j) )
runoffcell = rainBalance % mat (i, j) - infilt % mat (i,j)
!CASE (ROSS_BC)
!the subroutine SolveRichardsBC is always called even during interstorm period
END SELECT
ELSE !interstorm period, runoff is zero. Low intensity rainfall infiltrates
runoffcell = 0.
infilt % mat(i,j) = rainBalance % mat (i, j)
END IF
!compute actual evapotranspiration
!---------------------------------
!IF (id == SLOPE_EWB .OR. id == SLOPE_EWB_WET .OR. id == CHANNEL_EWB .OR. &
! id == CHANNEL_EWB_WET .OR. id == PLAIN_EWB .OR. id == PLAIN_EWB_WET) THEN
!solve energy balance to compute actual evapotranspiration
!IF ( WetlandIsWet (wetland, iin, jin, time) ) THEN
! TODO
! Tsprof=26.5
!
!CALL ET_energy_nr_wetland(SM%mat(iin,jin),albedo%mat(iin,jin),bpress%mat(iin,jin),&
! dtm%mat(iin,jin),temperatura_oss%mat(iin,jin),&
! radiazione_oss%mat(iin,jin),vento_oss%mat(iin,jin),umidita_oss%mat(iin,jin),&
! indice_nuv%mat(iin,jin),ET%mat(iin,jin),&
! Ts%mat(iin,jin),Rnetta%mat(iin,jin),Xle%mat(iin,jin),Hse%mat(iin,jin),&
! Ge%mat(iin,jin),Tsprof)
!
! if(ET%mat(iin,jin)<0)then
! ET%mat(iin,jin)=0
! end if
!ELSE
! CALL ET_ENERGETICO_suolo(SM%mat(iin,jin),SMsat%mat(iin,jin),SMres%mat(iin,jin),albedo%mat(iin,jin),&
! res_stom_min%mat(iin,jin),LAI%mat(iin,jin),h_veg%mat(iin,jin),bpress%mat(iin,jin),&
! dtm%mat(iin,jin),B%mat(iin,jin),fraz_veg%mat(iin,jin),temperatura_oss%mat(iin,jin),&
! radiazione_oss%mat(iin,jin),vento_oss%mat(iin,jin),umidita_oss%mat(iin,jin),&
! indice_nuv%mat(iin,jin),WP%mat(iin,jin),FC%mat(iin,jin),ET%mat(iin,jin),&
! Ts%mat(iin,jin),Rnetta%mat(iin,jin),Xle%mat(iin,jin),Hse%mat(iin,jin),&
! Ge%mat(iin,jin),Ta_prec%mat(iin,jin),Ts_prec%mat(iin,jin))
! Ta_prec%mat(iin,jin)=temperatura_oss%mat(iin,jin)
! Ts_prec%mat(iin,jin)=Ts%mat(iin,jin)
! IF(ET%mat(iin,jin)<0)THEN
! ET%mat(iin,jin)=0
! END IF
! IF (PRESENT(stato_neve)) THEN
!IF (stato_neve%mat(iin,jin) /= 0.0) THEN
! ET % mat(iin,jin) = 0.0
! IF(temperatura_oss%mat(iin,jin)<0.)THEN
! Ts%mat(iin,jin)=temperatura_oss%mat(iin,jin)
! ELSE
! Ts%mat(iin,jin)=0.
! END IF
! Rnetta%mat(iin,jin) = 0.0
! Xle%mat(iin,jin) = 0.0
! Ge%mat(iin,jin) = 0.0
! Hse%mat(iin,jin) = 0.0
! END IF
!END IF
!ELSE
IF ( snowCovered .OR. iceCovered ) THEN
et % mat(i,j) = 0.0
ELSE
!compute actual evaporation as a fraction alpha of potential
sm = soilMoistureRZ % mat(i,j)
wp = wiltingPoint % mat(i,j)
fc = fieldCapacity % mat(i,j)
alpha = 0.082 * sm + 9.173 * sm**2. - 9.815 * sm**3.
IF (alpha > 1.) alpha = 1.
actualE = pet % mat(i,j) * alpha
!compute actual transpiration as a fraction beta of potential
beta = (sm - wp) / (fc - wp)
IF (beta > 1.) beta = 1.
IF (beta < 0.) beta = 0.
actualT = pet % mat(i,j) * beta
!compute actual evapotranspiration
et % mat(i,j) = vf % mat(i,j) * actualT + (1. - vf % mat(i,j)) * actualE
IF ( et % mat(i,j) < 0.) THEN
et % mat(i,j) = 0.0
END IF
END IF
!END IF
!compute percolation and capilary rise
!------------------------------------------------------------------------
CALL PercolationAndCaprise (id, i, j, rainBalance % mat (i, j), vadose, pet, &
soilDepthCell, percolationcellRZ, &
percolationcellTZ, runoffcell)
!update soil moisture
!--------------------
SELECT CASE (infiltrationModel)
CASE (SCS_CN, PHILIPEQ, GREEN_AMPT)
CALL UpdateSoilMoisture (id, i, j, dtSoilBalance, &
soilDepthCell, &
percolationcellRZ, &
percolationcellTZ, &
runoffcell, &
QinSoilSub % mat (i,j), &
QoutSoilSub % mat (i,j) )
CASE (ROSS_BC)
!store values of cumulated variables at previous time step
pevapcum = evapcum % mat(i,j)
prunoffcum = runoffcum % mat(i,j)
pinfilcum = infilcum % mat(i,j)
pdrncum = drncum % mat(i,j)
!update soil moisture profile and cumulated fluxes in cm
CALL SolveRichardsBC (idt = dtSoilBalance, &
rain = rainBalance % mat (i, j) , &
pet = et % mat (i,j), &
p = parameters (cell % mat(i,j) ), &
Scell = S (cell % mat(i,j),:), &
dxcell = dx (cell % mat(i,j),:), &
nsol = 0, &
h0 = h0grid % mat(i,j), &
nsteps = nsteps % mat(i,j) , &
evap = evapcum % mat(i,j), &
runoff = runoffcum % mat(i,j), &
infil = infilcum % mat(i,j), &
drn = drncum % mat(i,j), &
watercontent = water )
!compute fluxes of this time step in m/s
runoffcell = ( runoffcum % mat(i,j) - prunoffcum ) / 100. / dtSoilBalance
IF (runoffcell < 0.) runoffcell = 0.
infilt % mat (i,j) = ( infilcum % mat(i,j) - pinfilcum ) / 100. / dtSoilBalance
IF (infilt % mat (i,j) < 0.) infilt % mat (i,j) = 0.
et % mat (i,j) = ( evapcum % mat(i,j) - pevapcum ) / 100. / dtSoilBalance
IF (et % mat (i,j) < 0.) et % mat (i,j) = 0.
percolationcellTZ = ( drncum % mat(i,j) - pdrncum ) / 100. / dtSoilBalance
IF (percolationcellTZ < 0.) percolationcellTZ = 0.
!compute average soil moisture
soilMoisture % mat (i,j) = water / soilDepthCell
END SELECT
END SELECT
!update percolation grid
percolation % mat (i,j) = percolationcellTZ
!update runoff grid
runoff % mat (i,j) = runoffcell
END DO
END DO
RETURN
END SUBROUTINE SolveSoilBalance
!==============================================================================
!| Description:
! return .TRUE. if wetland is flooded
FUNCTION WetlandIsWet &
!
(wetland,row,col,time) &
RESULT (wet)
IMPLICIT NONE
! function arguments
! Scalar arguments with intent(in):
TYPE (grid_integer):: wetland
INTEGER, INTENT (IN) :: row, col
TYPE (DateTime), INTENT (IN) :: time
!Local scalar:
LOGICAL :: wet
INTEGER :: code
INTEGER :: i
!------------end of declaration------------------------------------------------
IF (ALLOCATED (wetland % mat) ) THEN
code = wetland % mat(row,col)
wet = .FALSE.
DO i = 1, SIZE(wetCode)
IF ( code == wetCode (i) ) THEN
IF (time >= wetBegin (i) .AND. time <= wetEnd (i) ) THEN
wet = .TRUE.
EXIT
END IF
END IF
END DO
ELSE
wet = .FALSE.
END IF
END FUNCTION WetlandIsWet
!==============================================================================
!| Description:
! set initial condition for soilwater balance
SUBROUTINE SetInitialCondition &
!
(iniDB)
IMPLICIT NONE
! Arguments with intent(in):
TYPE (IniList), INTENT(IN) :: iniDB
!Local declaration:
INTEGER (KIND = short) :: i, j
REAL (KIND = float) :: scalar
!------------end of declaration------------------------------------------------
!mandatory variables
! root-zone soil saturation degree
IF (SectionIsPresent('saturation-rz', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'saturation-rz') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'saturation-rz')
CALL NewGrid (soilSatRZ, mask, scalar)
ELSE
CALL GridByIni (iniDB, soilSatRZ, section = 'saturation-rz')
END IF
ELSE !grid is mandatory: stop the program if not present
CALL Catch ('error', 'SoilBalance', &
'error in loading saturation-rz: ' , &
argument = 'section not defined in ini file' )
END IF
! transmission-zone soil saturation degree
IF (SectionIsPresent('saturation-tz', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'saturation-tz') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'saturation-tz')
CALL NewGrid (soilSatTZ, mask, scalar)
ELSE
CALL GridByIni (iniDB, soilSatTZ, section = 'saturation-tz')
END IF
ELSE !grid is mandatory: stop the program if not present
CALL Catch ('error', 'SoilBalance', &
'error in loading saturation-tz: ' , &
argument = 'section not defined in ini file' )
END IF
! allocate mean saturation map
CALL NewGrid (soilSat, mask, 0.)
!allocate and set soil moisture
CALL NewGrid (soilMoisture, mask)
CALL NewGrid (soilMoistureRZ, mask)
CALL NewGrid (soilMoistureTZ, mask)
DO j = 1, mask % jdim
DO i = 1, mask % idim
SELECT CASE ( balanceId % mat (i,j) )
CASE(LAKE) !lake cells are saturated by definition
soilSat % mat(i,j) = 1.
soilSatRZ % mat(i,j) = 1.
soilSatTZ % mat(i,j) = 1.
soilMoisture % mat(i,j) = 1.
soilMoistureRZ % mat(i,j) = 1.
soilMoistureTZ % mat(i,j) = 1.
CASE DEFAULT
soilMoistureRZ % mat(i,j) = thetar % mat(i,j) + &
soilSatRZ % mat(i,j) * &
(thetas % mat(i,j) - &
thetar % mat(i,j) )
soilMoistureTZ % mat(i,j) = thetar % mat(i,j) + &
soilSatTZ % mat(i,j) * &
(thetas % mat(i,j) - &
thetar % mat(i,j) )
END SELECT
END DO
END DO
!optional variables:
! interstorm duration
IF (SectionIsPresent('interstorm-duration', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'interstorm-duration') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'interstorm-duration')
CALL NewGrid (interstormDuration, mask, INT(scalar))
ELSE
CALL GridByIni (iniDB, interstormDuration, section = 'interstorm-duration')
END IF
ELSE !grid is optional: set to default = 0
CALL NewGrid ( interstormDuration, mask, 0 )
END IF
! precipitation status
IF (SectionIsPresent('precipitation-status', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'precipitation-status') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'precipitation-status')
CALL NewGrid (rainFlag, mask, INT(scalar))
ELSE
CALL GridByIni (iniDB, rainFlag, section = 'precipitation-status')
END IF
ELSE !grid is optional: set to default = 0
CALL NewGrid ( rainFlag, mask, 0 )
END IF
! variables for SCS-CN method
IF ( infiltrationModel == SCS_CN ) THEN
! effective soil retention capacity of SCS-CN method [mm]
IF (SectionIsPresent('soil-retention', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'soil-retention') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'soil-retention')
CALL NewGrid (sEff, mask, scalar )
ELSE
CALL GridByIni (iniDB, sEff, section = 'soil-retention')
END IF
ELSE !grid is optional: set to default = 0
CALL NewGrid ( sEff, mask, 0. )
END IF
! cumulative precipitation
IF (SectionIsPresent('cumulative-precipitation', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'cumulative-precipitation') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'cumulative-precipitation')
CALL NewGrid (cuminf, mask, scalar )
ELSE
CALL GridByIni (iniDB, cuminf, section = 'cumulative-precipitation')
END IF
ELSE !grid is optional: set to default = 0
CALL NewGrid ( cuminf, mask, 0. )
END IF
END IF
! variables for Philip and Green-Ampt methods
IF ( infiltrationModel == PHILIPEQ .OR. &
infiltrationModel == GREEN_AMPT ) THEN
! cumulative infiltration
IF (SectionIsPresent('cumulative-infiltration', iniDB)) THEN
IF (KeyIsPresent ('scalar', iniDB, 'cumulative-infiltration') ) THEN
scalar = IniReadReal ('scalar', iniDB, 'cumulative-infiltration')
CALL NewGrid (cuminf, mask, scalar )
ELSE
CALL GridByIni (iniDB, cuminf, section = 'cumulative-infiltration')
END IF
ELSE !grid is optional: set to default = 0
CALL NewGrid ( cuminf, mask, 0. )
END IF
END IF
!state variable initialization
!IF (SectionIsPresent('initial-saturation', iniDB)) THEN
!
! !cold start
! IF ( KeyIsPresent ('cold', iniDB, section = 'initial-saturation') ) THEN
! !allocate state variables
! CALL NewGrid (sEff, mask, 0.)
! CALL NewGrid (rainFlag, mask, 0)
! CALL NewGrid (interstormDuration, mask, 0)
!
! !initial saturation
! isd = IniReadReal ('cold', iniDB, section = 'initial-saturation')
!
! CALL Catch ('info', 'SoilBalance: ', &
! 'initial degree of saturation: ', &
! argument = ToString(isd))
!
! !same value for root and transmission zones
! CALL NewGrid (soilSat, mask, isd)
! CALL NewGrid (soilSatRZ, mask, isd)
! CALL NewGrid (soilSatTZ, mask, isd)
!
!
! !allocate and set soil moisture
! CALL NewGrid (soilMoisture, mask)
! CALL NewGrid (soilMoistureRZ, mask)
! CALL NewGrid (soilMoistureTZ, mask)
!
! DO i = 1, mask % idim
! DO j = 1, mask % jdim
! SELECT CASE ( balanceId % mat (i,j) )
! CASE(LAKE)
! soilSat % mat(i,j) = 1.
! soilSatRZ % mat(i,j) = 1.
! soilSatTZ % mat(i,j) = 1.
! soilMoisture % mat(i,j) = 1.
! CASE DEFAULT
! soilMoisture % mat(i,j) = thetar % mat(i,j) + &
! soilSat % mat(i,j) * &
! (thetas % mat(i,j) - &
! thetar % mat(i,j) )
!
! !lake cells are saturated by definition
!
! END SELECT
! END DO
! END DO
!
! !same initial soil mositure for root and transmission zones
! soilMoistureRZ = soilMoisture
! soilMoistureTZ = soilMoisture
!
! ELSE !hot start
! !soil moisture
! !TODO HOT START FOR ROOT AND TRANSMISSION ZONES
! CALL GridByIni (iniDB, soilMoisture, section = 'initial-saturation')
! IF ( .NOT. CRSisEqual (mask = mask, grid = soilMoisture, &
! checkCells = .TRUE.) ) THEN
! CALL Catch ('error', 'SoilBalance ', &
! 'wrong spatial reference in soil-moisture' )
! END IF
!
! !compute soil relative saturation
! CALL NewGrid (soilSat, mask)
!
! DO i = 1, mask % idim
! DO j = 1, mask % jdim
! SELECT CASE ( balanceId % mat (i,j) )
! !lake cells are saturated by definition
! CASE(LAKE)
! soilSat % mat(i,j) = 1.
! soilMoisture % mat(i,j) = thetas % mat(i,j)
! CASE DEFAULT
! soilSat % mat(i,j) = ( soilMoisture % mat(i,j) - &
! thetar % mat(i,j)) / &
! ( thetas % mat(i,j) - &
! thetar % mat(i,j) )
! END SELECT
! END DO
! END DO
!
!
! ! effective soil retention capacity of SCS-CN method [mm]
! IF (infiltrationModel == SCS_CN ) THEN
! IF (SectionIsPresent('soil-retention', iniDB)) THEN
! CALL GridByIni (iniDB, sEff, section = 'soil-retention')
! IF ( .NOT. CRSisEqual (mask = mask, grid = sEff, &
! checkCells = .TRUE.) ) THEN
! CALL Catch ('error', 'SoilBalance', &
! 'wrong spatial reference in soil retention capacity sEff' )
! END IF
! ELSE
! CALL Catch ('error', 'SoilWaterBalance: ', &
! 'missing soil-retention section in configuration file' )
! END IF
! END IF
!
!
!
! !cumulative infiltration
! IF (infiltrationModel == PHILIPEQ .OR. &
! infiltrationModel == GREEN_AMPT ) THEN
! IF (SectionIsPresent('cumulative-infiltration', iniDB)) THEN
! CALL GridByIni (iniDB, cuminf, section = 'cumulative-infiltration')
! IF ( .NOT. CRSisEqual (mask = mask, grid = cuminf, &
! checkCells = .TRUE.) ) THEN
! CALL Catch ('error', 'SoilBalance', &
! 'wrong spatial reference in cumulative infiltration' )
! END IF
! ELSE
! CALL Catch ('error', 'SoilWaterBalance: ', &
! 'missing cumulative-infiltration section in configuration file' )
! END IF
! END IF
!
!
! !precipitation status
! IF (SectionIsPresent('precipitation-status', iniDB)) THEN
! CALL GridByIni (iniDB, rainFlag, section = 'precipitation-status')
! IF ( .NOT. CRSisEqual (mask = mask, grid = rainFlag, &
! checkCells = .TRUE.) ) THEN
! CALL Catch ('error', 'SoilBalance', &
! 'wrong spatial reference in precipitation status rainFlag' )
! END IF
! ELSE
! CALL Catch ('error', 'SoilBalance: ', &
! 'missing precipitation-status section in configuration file' )
! END IF
!
!
!
! !interstorm duration
! IF (SectionIsPresent('interstorm-duration', iniDB)) THEN
! CALL GridByIni (iniDB, interstormDuration, section = 'interstorm-duration')
! IF ( .NOT. CRSisEqual (mask = mask, grid = interstormDuration, &
! checkCells = .TRUE.) ) THEN
! CALL Catch ('error', 'SoilBalance', &
! 'wrong spatial reference in interstorm duration' )
! END IF
! ELSE
! CALL Catch ('error', 'SoilBalance: ', &
! 'missing interstorm-duration section in configuration file' )
! END IF
!
! END IF !hot start
!ELSE
! CALL Catch ('error', 'SoilBalance: ', &
! 'missing initial-saturation section in configuration file' )
!END IF
RETURN
END SUBROUTINE SetInitialCondition
!==============================================================================
!| Description:
! set wetland for soilwater balance
SUBROUTINE SetWetland &
!
(iniDB, inifile)
IMPLICIT NONE
! Arguments with intent(in):
TYPE (IniList), INTENT(IN) :: iniDB
CHARACTER (LEN = *), INTENT(IN) :: inifile
!Local declaration:
TYPE (Table) :: wetlandTable
INTEGER (KIND = short) :: k
!------------end of declaration------------------------------------------------
IF (SectionIsPresent('wetland', iniDB)) THEN
CALL GridByIni (iniDB, wetland, section = 'wetland')
!read table of flooded period
CALL TableNew ( inifile, wetlandTable )
!allocate arrays
ALLOCATE ( wetCode ( TableGetNrows(wetlandTable) ) )
ALLOCATE ( wetBegin ( TableGetNrows(wetlandTable) ) )
ALLOCATE ( wetEnd ( TableGetNrows(wetlandTable) ) )
!fill in arrays
DO k = 1, TableGetNrows(wetlandTable)
wetCode (k) = StringToShort ( wetlandTable % col(1) % row(k) )
wetBegin (k) = wetlandTable % col(2) % row(k)
wetEnd (k) = wetlandTable % col(3) % row(k)
END DO
END IF
RETURN
END SUBROUTINE SetWetland
!==============================================================================
!| Description:
! compute percolation and capilalry rise
SUBROUTINE PercolationAndCaprise &
!
(id, i, j, rain, vadose, pet, soilDepthCell, percolationcellRZ, &
percolationcellTZ, runoffcell)
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: id !!soil balance id
INTEGER (KIND = short), INTENT(IN) :: i,j
REAL (KIND = float), INTENT(IN) :: rain
TYPE (grid_real), INTENT(IN) :: vadose !!vadose zone depth
TYPE (grid_real), INTENT(IN) :: pet !!potential evapotranspiration
!Arguments with intent(out):
REAL (KIND = double), INTENT(OUT) :: soilDepthCell
REAL (KIND = float), INTENT(OUT) :: percolationcellRZ
REAL (KIND = float), INTENT(OUT) :: percolationcellTZ
REAL (KIND = double), INTENT(OUT) :: runoffcell
!Local declaration:
REAL (KIND = float) :: smAdjustedRZ !! used to prevent soil moisture going to zero
REAL (KIND = float) :: smAdjustedTZ !! used to prevent soil moisture going to zero
REAL (KIND = float) :: conductivityCellRZ !!partial saturation soil hydraulic
!conductivity of current cell [m/s]
REAL (KIND = float) :: conductivityCellTZ !!partial saturation soil hydraulic
!conductivity of current cell [m/s]
!conductivity of current cell [m/s]
REAL (KIND = float) :: psiCell !!matric potential of current cell [m]
REAL (KIND = float) :: meanHydCond !!mean hydraulic conductivity used to
!compute capillary rise [m/s]
REAL (KIND = float) :: dsdt
!------------end of declaration------------------------------------------------
!calculate unsaturated hydraulic conductivity [m/s]
!root zone
IF (soilMoistureRZ % mat(i,j) <= thetar % mat(i,j)) THEN
smAdjustedRZ = thetar % mat(i,j) + 0.001
ELSE
smAdjustedRZ = soilMoistureRZ % mat(i,j)
END IF
conductivityCellRZ = UnsHydCond (ksat = ksat % mat(i,j), &
theta = smAdjustedRZ, &
thetas = thetas % mat(i,j), &
thetar = thetar % mat(i,j), &
psdi = psdi % mat(i,j) )
!transmission zone
IF (soilMoistureTZ % mat(i,j) <= thetar % mat(i,j)) THEN
smAdjustedTZ = thetar % mat(i,j) + 0.001
ELSE
smAdjustedTZ = soilMoistureTZ % mat(i,j)
END IF
conductivityCellTZ = UnsHydCond (ksat = ksat % mat(i,j), &
theta = smAdjustedTZ, &
thetas = thetas % mat(i,j), &
thetar = thetar % mat(i,j), &
psdi = psdi % mat(i,j) )
IF (id == LANDPLAIN ) THEN
!set local soil depth
!soilDepthCell = MIN (soildepth % mat(i,j), vadose % mat(i,j) )
soilDepthCell = soildepth % mat(i,j)
!compute root zone soil suction (m)
psiCell = Psi (psic = psic % mat(i,j), theta = smAdjustedRZ, &
thetas = thetas % mat(i,j), &
thetar = thetar % mat(i,j), &
psdi = psdi % mat(i,j) )
!Compute harmonic mean among saturated conductivity
!at the interface with groundwater table and unsaturated
!conductivity of vadose zone
meanHydCond = 2. * conductivityCellRZ * ksat % mat(i,j) / &
(conductivityCellRZ + Ksat % mat(i,j))
!compute capillary rise and percolation
IF ( vadose % mat (i,j) < soilDepthRZ % mat (i,j) ) THEN
! water table depth lies within the root zone or above ground
saturatedByGroundwater = .TRUE.
ELSE
capRise % mat (i,j) = meanHydCond * psiCell / vadose % mat (i,j)
dsdt = ( soilMoistureRZ % mat(i,j) - thetar % mat(i,j) ) * &
soilDepthRZ % mat(i,j) / dtSoilBalance
percolationcellRZ = MIN ( conductivityCellRZ, dsdt)
dsdt = ( soilMoistureTZ % mat(i,j) - thetar % mat(i,j) ) * &
soilDepthTZ % mat(i,j) / dtSoilBalance
percolationcellTZ = MIN ( conductivityCellTZ, dsdt) * &
percolationFactor % mat (i,j)
saturatedByGroundwater = .FALSE.
END IF
ELSE !slope or channel cell
soilDepthCell = soilDepth % mat(i,j)
capRise % mat(i,j) = 0.
dsdt = ( soilMoistureRZ % mat(i,j) - thetar % mat(i,j) ) * &
soilDepthRZ % mat(i,j) / dtSoilBalance
percolationcellRZ = MIN ( conductivityCellRZ, dsdt)
dsdt = ( soilMoistureTZ % mat(i,j) - thetar % mat(i,j) ) * &
soilDepthTZ % mat(i,j) / dtSoilBalance
percolationcellTZ = MIN ( ksat % mat(i,j), dsdt) * &
percolationFactor % mat (i,j)
END IF
RETURN
END SUBROUTINE PercolationAndCaprise
!==============================================================================
!| Description:
! update soil moisture
SUBROUTINE UpdateSoilMoisture &
!
(id, i, j, dt, soilDepthCell, percolationcellRZ, percolationcellTZ, &
runoffcell, Qin, Qout )
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: id !!soil balance id
INTEGER (KIND = short), INTENT(IN) :: i,j
INTEGER (KIND = short), INTENT(IN) :: dt
REAL (KIND = double), INTENT(IN) :: soilDepthCell
REAL (KIND = float), INTENT(IN) :: Qin
REAL (KIND = float), INTENT(IN) :: Qout
!Arguments with intent(inout):
REAL (KIND = double), INTENT(INOUT) :: runoffcell
REAL (KIND = float), INTENT(INOUT) :: percolationcellRZ
REAL (KIND = float), INTENT(INOUT) :: percolationcellTZ
!Local declaration:
REAL (KIND = double) :: smPrevRZ !! root zone soil moisture of the previous time step
REAL (KIND = double) :: smPrevTZ !! transmission zone soil moisture of the previous time step
REAL (KIND = float) :: check
REAL (KIND = double) :: dsRZ !!root zone soil mositure excess rate (m/s)
REAL (KIND = double) :: dsTZ !!transmission zone soil mositure excess rate (m/s)
REAL (KIND = float) :: sdRZ !!root zone soil depth (m)
REAL (KIND = float) :: sdTZ !!transmission zone soil depth (m)
REAL (KIND = double) :: subIn, subOut !!subsurface fluxes
REAL (KIND = double) :: area !!cell area (m2)
!------------end of declaration------------------------------------------------
IF ( id == LANDPLAIN .AND. saturatedByGroundwater ) THEN
! water table depth lies within the root zone or above ground
percolationcellRZ = 0.
percolationcellTZ = 0.
infilt % mat(i,j) = 0.
et % mat(i,j) = pet % mat(i,j)
capRise % mat (i,j) = et % mat(i,j)
runoffcell = rainBalance % mat (i, j)
soilMoistureRZ % mat(i,j) = thetas % mat (i,j)
soilMoistureTZ % mat(i,j) = thetas % mat (i,j)
soilMoisture % mat(i,j) = thetas % mat (i,j)
balanceError % mat (i,j) = 0.
RETURN
END IF
IF ( id == LAKE ) THEN
soilMoistureRZ % mat(i,j) = thetas % mat (i,j)
soilMoistureTZ % mat(i,j) = thetas % mat (i,j)
soilMoisture % mat(i,j) = thetas % mat (i,j)
balanceError % mat (i,j) = 0.
deltaSoilMoisture % mat (i,j) = 0.
RETURN
END IF
smPrevRZ = soilMoistureRZ % mat(i,j)
smPrevTZ = soilMoistureTZ % mat(i,j)
sdRZ = soilDepthRZ % mat (i,j)
sdTZ = soilDepthTZ % mat (i,j)
!IF ( soilDepthCell < soilDepthRZ % mat(i,j) ) THEN
! sdRZ = soilDepthCell
! sdTZ = 0.
!ELSE
! sdRZ = soilDepthRZ % mat(i,j)
! sdTZ = MAX (0.1, ( soilDepthCell - sdRZ ) )
!END IF
IF ( id == HILLSLOPE .OR. id == CHANNEL ) THEN
area = CellArea (soilMoisture, i, j)
subIn = Qin / area
subOut = Qout / area
ELSE IF ( id == LANDPLAIN ) THEN
subIn = 0.
subOut = 0.
END IF
IF (sdTZ > 0.) THEN
soilMoistureTZ % mat(i,j) = smPrevTZ + &
( percolationcellRZ - &
percolationcellTZ + &
subIn - subOut ) / &
sdTZ * dt
IF (soilMoistureTZ % mat(i,j) < thetar % mat(i,j)) THEN
soilMoistureTZ % mat(i,j) = thetar % mat(i,j)
END IF
IF ( soilMoistureTZ % mat(i,j) > thetas % mat(i,j) ) THEN !saturation excess
dsTZ = ( soilMoistureTZ % mat(i,j) - thetas % mat(i,j) ) * sdTZ / dt
soilMoistureTZ % mat(i,j) = thetas % mat(i,j)
ELSE
dsTZ = 0.
END IF
END IF
IF (sdRZ > 0.) THEN
soilMoistureRZ % mat(i,j) = smPrevRZ + ( infilt % mat(i,j) - percolationcellRZ - &
et % mat(i,j) + capRise % mat(i,j) + dsTZ ) / sdRZ * dt
END IF
!particular cases
!case 1: soil is too dry, soil moisture goes below residual value
IF (soilMoistureRZ % mat(i,j) < thetar % mat(i,j) ) THEN
!compute soil moisture deficit (negative) converted to flux (m/s)
dsRZ = ( soilMoistureRZ % mat(i,j) - thetar % mat(i,j) ) * soilDepthCell / dt
!adjust evapotranspiration
et % mat(i,j) = et % mat(i,j) + dsRZ
!check for negative et
IF (et % mat(i,j) < 0. ) et % mat(i,j) = 0.
!limit soil moisture
soilMoistureRZ % mat(i,j) = thetar % mat(i,j)
!case 2: soil is too wet, soil moisture goes above saturated value
ELSE IF ( soilMoistureRZ % mat(i,j) > thetas % mat(i,j) ) THEN
!compute soil moisture excess converted to flux (m/s)
dsRZ = ( soilMoistureRZ % mat(i,j) - thetas % mat(i,j) ) * sdRZ / dt
!adjust runoff for saturation excess runoff and infiltration rate
runoffcell = runoffcell + dsRZ
infilt % mat(i,j) = infilt % mat(i,j) - dsRZ
!limit soil moisture to saturation value
soilMoistureRZ % mat(i,j) = thetas % mat(i,j)
!check if infiltration is negative after correction
!adjust capillary rise if present (plain cell)
IF (infilt % mat(i,j) < 0.) THEN
dsRZ = - infilt % mat(i,j)
infilt % mat(i,j) = 0.
IF (id == LANDPLAIN ) THEN
capRise % mat(i,j) = capRise % mat(i,j) - dsRZ
END IF
END IF
END IF
!balance error
check = infilt % mat(i,j) + &
capRise % mat(i,j) - &
percolationcellTZ - &
et % mat(i,j) + &
subIn - subOut - &
(soilMoistureRZ % mat(i,j) - smPrevRZ) * sdRZ / dt - &
(soilMoistureTZ % mat(i,j) - smPrevTZ) * sdTZ / dt
balanceError % mat (i,j) = check * dt * 1000.
!soil mositure variation
deltaSoilMoisture % mat (i,j) = (soilMoistureRZ % mat(i,j) - smPrevRZ) * sdRZ + &
(soilMoistureTZ % mat(i,j) - smPrevTZ) * sdTZ
!update relative saturation and mean soil moisture
soilSatRZ % mat(i,j) = ( soilMoistureRZ % mat(i,j) - thetar % mat(i,j) ) / &
( thetas % mat(i,j) - thetar % mat(i,j) )
soilSatTZ % mat(i,j) = ( soilMoistureTZ % mat(i,j) - thetar % mat(i,j) ) / &
( thetas % mat(i,j) - thetar % mat(i,j) )
soilSat % mat(i,j) = ( soilSatRZ % mat(i,j) * sdRZ + &
soilSatTZ % mat(i,j) * sdTZ ) / (sdRZ + sdTZ)
IF ( sdRZ + sdTZ > 0. ) THEN
soilMoisture % mat(i,j) = ( soilMoistureRZ % mat(i,j) * sdRZ + &
soilMoistureTZ % mat(i,j) * sdTZ ) / (sdRZ + sdTZ)
ELSE
soilMoisture % mat(i,j) = thetas % mat (i,j)
END IF
RETURN
END SUBROUTINE UpdateSoilMoisture
END MODULE SoilBalance