!! 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