!! Simulate quasi 3D groundwater flux and river-groundwater interaction
!|author: Giovanni Ravazzani
! license: GPL
!
!### History
!
! current version 1.1 - 12th February 2023
!
! | version | date | comment |
! |----------|-------------|----------|
! | 1.0 | 14/Sep/2011 | Original code |
! | 1.0 | 22/Feb/2023 | Code rewritten |
!
!### License
! license: GNU GPL
!
!### Module Description
!Simulate quasi 3D groundwater flux and river-groundwater interaction
!
!### References
!
! Ravazzani, G., Rametta, D., & Mancini, M.. (2011). Macroscopic cellular
! automata for groundwater modelling: a first approach. Environmental
! modelling & software, 26(5), 634–643.
!
! Ravazzani, G., Curti, D., Gattinoni, P., Della Valentina, S.,
! Fiorucci, A., & Rosso, R.. (2016). Assessing groundwater
! contribution to streamflow of a large alpine river with heat
! tracer methods and hydrological modelling. River research
! and applications, 32(5), 871-884.
!
! Ravazzani, G., Barbero, S., Salandin, A., Senatore, A.,
! & Mancini, M.. (2015). An integrated hydrological model
! for assessing climate change impacts on water resources
! of the upper po river basin. Water resources management,
! 29 (4), 1193-1215.
!
MODULE Groundwater
! Modules used:
USE DataTypeSizes, ONLY : &
! Imported Type Definitions:
short, float
USE DomainProperties, ONLY : &
!imported variables:
mask
USE GridLib, ONLY : &
!imported Type definitions:
grid_real, grid_integer, &
!imported routines:
NewGrid, GridDestroy, &
!Imported parameters:
NET_CDF
USE GridOperations, ONLY : &
!Imported routines:
GridByIni, CellArea, &
!Imported operators:
ASSIGNMENT (=)
USE IniLib, ONLY : &
!Imported definitions:
IniList, &
!Imported routines:
IniOpen, IniClose, &
SectionIsPresent, KeyIsPresent, &
SubSectionIsPresent, &
IniReadReal, IniReadInt
USE LogLib, ONLY: &
!Imported routines:
Catch
USE StringManipulation, ONLY : &
!Imported routines
ToString
USE Chronos, ONLY : &
!Imported definitions:
DateTime , &
!Imported variables:
timeString, &
!Imported operations:
OPERATOR (+), &
OPERATOR (==), &
ASSIGNMENT (=)
USE ObservationalNetworks, ONLY : &
!Imported routines:
ReadMetadata, CopyNetwork, &
WriteMetadata, DestroyNetwork, &
AssignDataFromGrid, WriteData, &
!Imported defined variable:
ObservationalNetwork
USE Utilities, ONLY : &
!Imported routines:
GetUnit
USE GridStatistics, ONLY : &
!imported routines:
GetMean
USE TableLib, ONLY: &
!imported definitions:
Table, &
!Imported routines:
TableNew, TableGetValue
IMPLICIT NONE
! Global (i.e. public) Declarations:
! Global TYPE Definitions:
TYPE Aquifer
TYPE(grid_real) :: top
TYPE(grid_real) :: bottom
TYPE(grid_real) :: Ks !hydraulic conductivity (m/s)
TYPE(grid_real) :: sy !specific yield
TYPE(grid_real) :: KsAquitard !hydraulic conductivity of the aquitard
TYPE(grid_integer):: domainBC !set domain and boundary conditions type
TYPE(grid_real) :: valueBC ! value to be used for boundary condition
TYPE(grid_real) :: head0 !head at time t-1
TYPE(grid_real) :: head1 !head at time t
END TYPE Aquifer
TYPE GroundwaterBasin
INTEGER (KIND = short) :: nAquifers !!number of aquifers in the basin
TYPE (Aquifer), POINTER :: aquifer (:)
END TYPE GroundwaterBasin
!Global routines:
PUBLIC :: GroundwaterInit
PUBLIC :: GroundwaterUpdate
PUBLIC :: GroundwaterPointInit
PUBLIC :: GroundwaterOutputInit
PUBLIC :: GroundwaterOutput
PUBLIC :: GroundwaterRiverUpdate
!Global variables:
TYPE (GroundwaterBasin) :: basin
INTEGER (KIND = short) :: dtGroundwater
TYPE (grid_real) :: vadoseDepth
TYPE (grid_real) :: groundwaterToRiver !!discharge from groundwater to river (m3/s)
TYPE (grid_real) :: riverToGroundwater !!discharge from river to groundwater (m3/s)
LOGICAL :: riverGroundwaterInteract
!Local (private) parameters:
INTEGER (KIND = short), PARAMETER, PRIVATE :: BC_DIRICHLET = 2 ! head boundary condition
INTEGER (KIND = short), PARAMETER, PRIVATE :: BC_NEUMANN = 3 ! flux boundary condition
INTEGER (KIND = short), PARAMETER, PRIVATE :: ACTIVE_CELL = 1 ! active cell within the groundwater domain
!local variables
TYPE (grid_real), PRIVATE :: transmissivity !!local transmissivity (m2/s)
TYPE (grid_real), PRIVATE :: fluxUpward !!flux from lower aquitard (m/s)
TYPE (grid_real), PRIVATE :: fluxDownward !!flux from upper aquitard (m/s)
TYPE (grid_real), PRIVATE :: riverDem !!reference digital elevation model to compute
!!river water surface elevation (m asl)
TYPE (grid_real), PRIVATE :: streambedThickness !! streambed thickness (m)
TYPE (grid_real), PRIVATE :: streambedConductivity !! streambed conductivity (m/s)
TYPE (grid_integer), PRIVATE :: riverGroundwaterID !!id of river reaches that interact with groundwater
TYPE (Table), PRIVATE :: riverGroundwaterParameters
TYPE (ObservationalNetwork),PRIVATE :: sites
INTEGER (KIND = short), ALLOCATABLE, PRIVATE :: fileUnitPointGW (:)
INTEGER (KIND = short), PRIVATE :: fileUnitOutGW
TYPE (DateTime), PRIVATE :: timePointExport
REAL (KIND = float), PRIVATE :: volumeRecharge !! recharge volume (m3)
REAL (KIND = float), PRIVATE :: volumeResidual !! balance error (m3)
REAL (KIND = float), PRIVATE :: volumeNeumann !!volume from Neumann boundary condition (m3)
REAL (KIND = float), PRIVATE :: volumeDirichlet !!volume from Dirichlet boundary condition (m3)
REAL (KIND = float), PRIVATE :: volumeChange !! change of stored water volume in groundwater (m3)
REAL (KIND = float), PRIVATE :: volumeRiverToGroundwater !! volume from river to groundwater (m3)
REAL (KIND = float), PRIVATE :: volumeGroundwaterToRiver !! volume from groundwater to river (m3)
!local (private) routines:
PRIVATE :: GroundwaterParameterUpdate
PRIVATE :: GroundwaterPointExport
PRIVATE :: GroundwaterRiverInit
!=======
CONTAINS
!=======
! Define procedures contained in this module.
!==============================================================================
!| Description:
! Initialize groundwater
SUBROUTINE GroundwaterInit &
!
(inifile)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: inifile !!stores configuration information
!local declarations:
TYPE (IniList) :: iniDB
INTEGER (KIND = short) :: k , i, j
CHARACTER (LEN = 100) :: string
REAL (KIND = float) :: scalar
!-----------------------------------end of declarations------------------------
!open and read configuration file
CALL IniOpen (inifile, iniDB)
!read the number of aquifers
IF ( KeyIsPresent ('aquifers', iniDB ) ) THEN
basin % naquifers = IniReadInt ( 'aquifers', iniDB )
ELSE
CALL Catch ('error', 'Groundwater', &
'aquifers not found in configuration file' )
END IF
!allocate aquifers
ALLOCATE ( basin % aquifer ( basin % naquifers ) )
!load parameters
DO k = 1, basin % naquifers
string = 'aquifer_' // ToString (k)
IF (SectionIsPresent(TRIM(string), iniDB)) THEN
!load domain and boundary condition id
IF (SubSectionisPresent (subsection = 'boundary-condition-id', &
section = TRIM(string), iniDB = iniDB) ) THEN
CALL GridByIni ( ini = iniDB, &
grid = basin % aquifer (k) % domainBC, &
section = TRIM(string), &
subsection = 'boundary-condition-id')
ELSE
CALL Catch ('error', 'Groundwater', &
'missing boundary-condition-id in configuration file: ', &
argument = TRIM(string) )
END IF
!load boundary condition value
IF (SubSectionisPresent (subsection = 'boundary-condition-value', &
section = TRIM(string), iniDB = iniDB) ) THEN
IF (KeyIsPresent ('scalar', iniDB, TRIM(string), &
'boundary-condition-value' ) ) THEN
scalar = IniReadReal ('scalar', iniDB, &
TRIM(string), 'boundary-condition-value' )
CALL NewGrid (basin % aquifer (k) % valueBC, &
basin % aquifer (k) % domainBC, scalar)
ELSE
CALL GridByIni ( ini = iniDB, &
grid = basin % aquifer (k) % valueBC, &
section = TRIM(string), &
subsection = 'boundary-condition-value')
END IF
ELSE
CALL Catch ('error', 'Groundwater', &
'missing boundary-condition-value in configuration file: ', &
argument = TRIM(string) )
END IF
!load initial head
IF (SubSectionisPresent (subsection = 'initial-head', &
section = TRIM(string), iniDB = iniDB) ) THEN
IF (KeyIsPresent ('scalar', iniDB, TRIM(string), &
'initial-head' ) ) THEN
scalar = IniReadReal ('scalar', iniDB, &
TRIM(string), 'initial-head' )
CALL NewGrid (basin % aquifer (k) % head0, &
basin % aquifer (k) % domainBC, scalar)
ELSE
CALL GridByIni ( ini = iniDB, &
grid = basin % aquifer (k) % head0, &
section = TRIM(string), &
subsection = 'initial-head')
END IF
CALL NewGrid (basin % aquifer (k) % head1, &
basin % aquifer (k) % domainBC, 0.)
basin % aquifer (k) % head1 = basin % aquifer (k) % head0
!head boundary condition overlay
DO i = 1, basin % aquifer (k) % domainBC % idim
DO j = 1, basin % aquifer (k) % domainBC % jdim
IF ( basin % aquifer (k) % domainBC % mat (i,j) == &
BC_DIRICHLET ) THEN
basin % aquifer (k) % head0 % mat (i,j) = &
basin % aquifer (k) % valueBC % mat (i,j)
basin % aquifer (k) % head1 % mat (i,j) = &
basin % aquifer (k) % valueBC % mat (i,j)
END IF
END DO
END DO
ELSE
CALL Catch ('error', 'Groundwater', &
'missing initial-condition-head in configuration file: ', &
argument = TRIM(string) )
END IF
!load top elevation
IF (SubSectionisPresent (subsection = 'top-elevation', &
section = TRIM(string), iniDB = iniDB) ) THEN
IF (KeyIsPresent ('scalar', iniDB, TRIM(string), &
'top-elevation' ) ) THEN
scalar = IniReadReal ('scalar', iniDB, &
TRIM(string), 'top-elevation' )
CALL NewGrid (basin % aquifer (k) % top, &
basin % aquifer (k) % domainBC, scalar)
ELSE
CALL GridByIni ( ini = iniDB, &
grid = basin % aquifer (k) % top, &
section = TRIM(string), &
subsection = 'top-elevation')
END IF
ELSE
CALL Catch ('error', 'Groundwater', &
'missing top-elevation in configuration file: ', &
argument = TRIM(string) )
END IF
!load bottom elevation
IF (SubSectionisPresent (subsection = 'bottom-elevation', &
section = TRIM(string), iniDB = iniDB) ) THEN
IF (KeyIsPresent ('scalar', iniDB, TRIM(string), &
'bottom-elevation' ) ) THEN
scalar = IniReadReal ('scalar', iniDB, &
TRIM(string), 'bottom-elevation' )
CALL NewGrid (basin % aquifer (k) % bottom, &
basin % aquifer (k) % domainBC, scalar)
ELSE
CALL GridByIni ( ini = iniDB, &
grid = basin % aquifer (k) % bottom, &
section = TRIM(string), &
subsection = 'bottom-elevation')
END IF
ELSE
CALL Catch ('error', 'Groundwater', &
'missing bottom-elevation in configuration file: ', &
argument = TRIM(string) )
END IF
!load hydraulic conductivity
IF (SubSectionisPresent (subsection = 'hydraulic-conductivity', &
section = TRIM(string), iniDB = iniDB) ) THEN
IF (KeyIsPresent ('scalar', iniDB, TRIM(string), &
'hydraulic-conductivity' ) ) THEN
scalar = IniReadReal ('scalar', iniDB, &
TRIM(string), 'hydraulic-conductivity' )
CALL NewGrid (basin % aquifer (k) % Ks, &
basin % aquifer (k) % domainBC, scalar)
ELSE
CALL GridByIni ( ini = iniDB, &
grid = basin % aquifer (k) % Ks, &
section = TRIM(string), &
subsection = 'hydraulic-conductivity')
END IF
ELSE
CALL Catch ('error', 'Groundwater', &
'missing hydraulic-conductivity in configuration file: ', &
argument = TRIM(string) )
END IF
!load specific yield
IF (SubSectionisPresent (subsection = 'specific-yield', &
section = TRIM(string), iniDB = iniDB) ) THEN
IF (KeyIsPresent ('scalar', iniDB, TRIM(string), &
'specific-yield' ) ) THEN
scalar = IniReadReal ('scalar', iniDB, &
TRIM(string), 'specific-yield' )
CALL NewGrid (basin % aquifer (k) % sy, &
basin % aquifer (k) % domainBC, scalar)
ELSE
CALL GridByIni ( ini = iniDB, &
grid = basin % aquifer (k) % sy, &
section = TRIM(string), &
subsection = 'specific-yield')
END IF
ELSE
CALL Catch ('error', 'Groundwater', &
'missing specific-yield in configuration file: ', &
argument = TRIM(string) )
END IF
! aquitard hydraulic conductivity
IF ( k < basin % naquifers ) THEN
IF (SubSectionisPresent (subsection = 'aquitard-conductivity', &
section = TRIM(string), iniDB = iniDB) ) THEN
IF (KeyIsPresent ('scalar', iniDB, TRIM(string), &
'aquitard-conductivity' ) ) THEN
scalar = IniReadReal ('scalar', iniDB, &
TRIM(string), 'aquitard-conductivity' )
CALL NewGrid (basin % aquifer (k) % KsAquitard, &
basin % aquifer (k) % domainBC, scalar)
ELSE
CALL GridByIni ( ini = iniDB, &
grid = basin % aquifer (k) % KsAquitard, &
section = TRIM(string), &
subsection = 'aquitard-conductivity')
END IF
ELSE
CALL Catch ('error', 'Groundwater', &
'missing aquitard-conductivity in configuration file: ', &
argument = TRIM(string) )
END IF
END IF
ELSE
CALL Catch ('error', 'Groundwater', &
'missing section in configuration file: ', &
argument = TRIM(string) )
END IF
END DO
!allocate variables
CALL NewGrid ( transmissivity, basin % aquifer (1) % domainBC, 0. )
CALL NewGrid ( fluxUpward, basin % aquifer (1) % domainBC, 0. )
CALL NewGrid ( fluxDownward, basin % aquifer (1) % domainBC, 0. )
CALL NewGrid ( vadoseDepth, basin % aquifer (1) % domainBC, 0. )
!check river-groundwater interaction
IF ( SectionIsPresent ( 'river-groundwater', iniDB) ) THEN
riverGroundwaterInteract = .TRUE.
CALL GroundwaterRiverInit ( inifile )
ELSE
riverGroundwaterInteract = .FALSE.
END IF
!destroy iniDB
CALL IniClose (IniDB)
END SUBROUTINE GroundwaterInit
!==============================================================================
!| Description:
! update groundwater head with Darcy law and mass conservation
! in two dimensions, with a macrocopic cellular automata approach
!```
! +-----+
! | N |
! +-----+-----+-----+
! | W | C | E |
! +-----+-----+-----+
! | S |
! +-----+
!```
! References:
! Ravazzani, G., Rametta, D., & Mancini, M.. (2011). Macroscopic cellular
! automata for groundwater modelling: a first approach. Environmental
! modelling & software, 26(5), 634–643.
!
SUBROUTINE GroundwaterUpdate &
!
( time, hillslopeFlux, percolation, capflux )
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(IN) :: time
TYPE (grid_real), INTENT(IN) :: hillslopeFlux !!flux from hillslope (m3/s)
TYPE (grid_real), INTENT(IN) :: percolation !! depp infiltration from soil balance (m/s)
TYPE (grid_real), INTENT(IN) :: capflux !! capillary flux from soil balance (m/s)
!local declarations:
INTEGER (KIND = short) :: i, j, k
REAL (KIND = float) :: rechargeQ !!vertical recharge (m3/s)
REAL (KIND = float) :: transNC !!mean transmissivity North-Centre (m2/s)
REAL (KIND = float) :: transEC !!mean transmissivity East-Centre (m2/s)
REAL (KIND = float) :: transSC !!mean transmissivity South-Centre (m2/s)
REAL (KIND = float) :: transWC !!mean transmissivity West-Centre (m2/s)
REAL (KIND = float) :: fluxNC !!flux North-Centre (m3/s)
REAL (KIND = float) :: fluxEC !!flux East-Centre (m3/s)
REAL (KIND = float) :: fluxSC !!flux South-Centre (m3/s)
REAL (KIND = float) :: fluxWC !!flux West-Centre (m3/s)
REAL (KIND = float) :: fluxNESW !! total flux from the 4 directions (m3/s)
REAL (KIND = float) :: areaOfCell !! cell area (m2)
REAL (KIND = float) :: rToG !!river to groundwater discharge (m3/s)
REAL (KIND = float) :: gToR !!groundwater to river discharge (m3/s)
!-------------------------------------end of declarations----------------------
!reset counter variables
volumeNeumann = 0.
volumeDirichlet = 0.
volumeRecharge = 0.
volumeChange = 0.
volumeResidual = 0.
!update boundary condition
CALL GroundwaterParameterUpdate (time)
!save head1 into head0
DO k = 1, basin % nAquifers
basin % aquifer (k) % head0 = basin % aquifer (k) % head1
END DO
!update flux from hillslope of first (surface) aquifer
DO i = 1, basin % aquifer (1) % domainBC % idim
DO j = 1, basin % aquifer (1) % domainBC % jdim
IF ( basin % aquifer (1) % domainBC % mat (i,j) == &
BC_NEUMANN ) THEN
basin % aquifer (1) % valueBC % mat (i,j) = &
hillslopeFlux % mat (i,j)
END IF
END DO
END DO
!update aquifers head
DO k = 1, basin % nAquifers
!compute local transmissivity
DO i = 1, basin % aquifer (k) % domainBC % idim
DO j = 1, basin % aquifer (k) % domainBC % jdim
IF ( basin % aquifer (k) % domainBC % mat(i,j) /= &
basin % aquifer (k) % domainBC % nodata ) THEN
!transmissivity: for confined aquifer = ks * aquifer depth
transmissivity % mat (i,j) = MIN ( &
basin % aquifer (k) % Ks % mat (i,j) * &
( basin % aquifer (k) % head0 % mat (i,j) - &
basin % aquifer (k) % bottom % mat (i,j) ) , &
basin % aquifer (k) % Ks % mat (i,j) * &
( basin % aquifer (k) % top % mat (i,j) - &
basin % aquifer (k) % bottom % mat (i,j) ) &
)
IF ( transmissivity % mat (i,j) <= 0. ) THEN
transmissivity % mat (i,j) = 0.01
END IF
END IF
END DO
END DO
!update head1
DO i = 1, basin % aquifer (k) % domainBC % idim
DO j = 1, basin % aquifer (k) % domainBC % jdim
IF ( basin % aquifer (k) % domainBC % mat(i,j) == &
ACTIVE_CELL ) THEN
!harmonic mean transmissivity
transNC = 2.0 * transmissivity % mat(i-1,j) * &
transmissivity % mat(i,j) / &
( transmissivity %mat(i-1,j) + &
transmissivity %mat(i,j) )
transSC = 2.0 * transmissivity % mat(i+1,j) * &
transmissivity % mat(i,j) / &
( transmissivity % mat(i+1,j) + &
transmissivity % mat(i,j) )
transEC = 2.0 * transmissivity % mat(i,j+1) * &
transmissivity % mat(i,j) / &
( transmissivity % mat(i,j+1) + &
transmissivity % mat(i,j) )
transWC = 2.0 * transmissivity % mat(i,j-1) * &
transmissivity % mat(i,j) / &
( transmissivity % mat(i,j-1) + &
transmissivity % mat(i,j) )
!flux from North
IF ( basin % aquifer (k) % domainBC % mat(i-1,j) == &
BC_NEUMANN ) THEN
fluxNC = basin % aquifer (k) % valueBC % mat(i-1,j)
volumeNeumann = volumeNeumann + fluxNC * dtGroundwater
ELSE
fluxNC = transNC * &
( basin % aquifer (k) % head0 % mat (i-1,j) - &
basin % aquifer (k) % head0 % mat (i,j) )
IF ( basin % aquifer (k) % domainBC % mat(i-1,j) == &
BC_DIRICHLET ) THEN
volumeDirichlet = volumeDirichlet + &
fluxNC * dtGroundwater
END IF
END IF
!flux from East
IF ( basin % aquifer (k) % domainBC % mat(i,j+1) == &
BC_NEUMANN ) THEN
fluxEC = basin % aquifer (k) % valueBC % mat(i,j+1)
volumeNeumann = volumeNeumann + fluxEC * dtGroundwater
ELSE
fluxEC = transEC * &
( basin % aquifer (k) % head0 % mat (i,j+1) - &
basin % aquifer (k) % head0 % mat (i,j) )
IF ( basin % aquifer (k) % domainBC % mat(i,j+1) == &
BC_DIRICHLET ) THEN
volumeDirichlet = volumeDirichlet + &
fluxEC * dtGroundwater
END IF
END IF
!flux from South
IF ( basin % aquifer (k) % domainBC % mat(i+1,j) == &
BC_NEUMANN ) THEN
fluxSC = basin % aquifer (k) % valueBC % mat(i+1,j)
volumeNeumann = volumeNeumann + fluxSC * dtGroundwater
ELSE
fluxSC = transSC * &
( basin % aquifer (k) % head0 % mat (i+1,j) - &
basin % aquifer (k) % head0 % mat (i,j) )
IF ( basin % aquifer (k) % domainBC % mat(i+1,j) == &
BC_DIRICHLET ) THEN
volumeDirichlet = volumeDirichlet + &
fluxSC * dtGroundwater
END IF
END IF
!flux from West
IF ( basin % aquifer (k) % domainBC % mat(i,j-1) == &
BC_NEUMANN ) THEN
fluxWC = basin % aquifer (k) % valueBC % mat(i,j-1)
volumeNeumann = volumeNeumann + fluxWC * dtGroundwater
ELSE
fluxWC = transWC * &
( basin % aquifer (k) % head0 % mat (i,j-1) - &
basin % aquifer (k) % head0 % mat (i,j) )
IF ( basin % aquifer (k) % domainBC % mat(i,j-1) == &
BC_DIRICHLET ) THEN
volumeDirichlet = volumeDirichlet + &
fluxWC * dtGroundwater
END IF
END IF
!total flux
fluxNESW = fluxNC + fluxEC + fluxSC + fluxWC
!area of the cell
areaOfCell = CellArea (transmissivity, i, j)
!vertical recharge
IF ( ALLOCATED (percolation % mat ) ) THEN
IF ( k == 1 .AND. percolation % mat (i,j) /= &
percolation % nodata ) THEN
rechargeQ = ( percolation % mat (i,j) - &
capflux % mat (i,j) ) * areaOfCell
volumeRecharge = volumeRecharge + &
rechargeQ * dtGroundwater
ELSE
rechargeQ = 0.
END IF
ELSE
rechargeQ = 0.
END IF
!upward flux from lower aquifer
IF (k /= basin % nAquifers ) THEN
fluxUpward % mat(i,j) = &
basin % aquifer (k) % ksAquitard % mat(i,j) * &
( ( basin % aquifer (k+1) % head0 % mat(i,j) - &
basin % aquifer (k) % head0 % mat(i,j) ) / &
( basin % aquifer (k) % bottom % mat(i,j) - &
basin % aquifer (k+1) % top % mat(i,j) ) )
ELSE
fluxUpward % mat (i,j) = 0.0
END IF
!update head1
IF ( k == 1) THEN
fluxDownward % mat (i,j) = 0.
IF ( riverGroundwaterInteract ) THEN
rToG = riverToGroundwater % mat (i,j)
gToR = - groundwaterToRiver % mat (i,j)
ELSE
rToG = 0.
gToR = 0.
END IF
ELSE
rToG = 0.
gToR = 0.
END IF
basin % aquifer (k) % head1 % mat(i,j) = &
basin % aquifer (k) % head0 % mat(i,j) + &
( fluxNESW + rechargeQ + rToG + gToR ) * &
dtGroundwater / areaOfCell / &
basin % aquifer (k) % sy % mat(i,j) + &
( fluxUpward % mat(i,j) + fluxDownward % mat(i,j) ) * &
dtGroundwater / basin % aquifer (k) % sy % mat(i,j)
IF ( ISNAN ( basin % aquifer (k) % head1 % mat(i,j) ) ) THEN
basin % aquifer (k) % head1 % mat(i,j) = &
basin % aquifer (k) % head0 % mat(i,j)
volumeChange = 0.
ELSE
!volume change
volumeChange = volumeChange + &
( basin % aquifer (k) % head1 % mat(i,j) - &
basin % aquifer (k) % head0 % mat(i,j) ) * &
basin % aquifer (k) % sy % mat(i,j) * areaOfCell
END IF
!swap fluxUpward and fluxDownward
fluxDownward % mat (i,j) = - fluxUpward % mat (i,j)
!update vadose zone depth
IF ( k == 1) THEN
vadoseDepth % mat (i,j) = &
basin % aquifer (k) % top % mat(i,j) - &
basin % aquifer (k) % head1 % mat(i,j)
END IF
END IF
END DO
END DO
END DO
!compute balance error
volumeResidual = volumeRecharge + volumeDirichlet + &
volumeNeumann + volumeRiverToGroundwater - &
volumeGroundwaterToRiver - volumeChange
!write point data
IF ( time == timePointExport ) THEN
CALL GroundwaterPointExport ( time )
END IF
RETURN
END SUBROUTINE GroundwaterUpdate
!==============================================================================
!| Description:
! update boundary condition map that change in time
SUBROUTINE GroundwaterParameterUpdate &
!
(time)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT (IN) :: time
!local declarations:
CHARACTER (LEN = 300) :: filename
CHARACTER (LEN = 300) :: varname
INTEGER (KIND = short) :: k, i, j
!------------------------------end of declarations-----------------------------
!boundary condition
DO k = 1, basin % naquifers
IF ( time == basin % aquifer (k) % valueBC % next_time ) THEN
!destroy current grid
filename = basin % aquifer (k) % valueBC % file_name
varname = basin % aquifer (k) % valueBC % var_name
CALL GridDestroy (basin % aquifer (k) % valueBC )
!read new grid in netcdf file
CALL NewGrid (basin % aquifer (k) % valueBC, TRIM(filename), NET_CDF, &
variable = TRIM(varname), time = time)
!head boundary condition overlay
DO i = 1, basin % aquifer (k) % domainBC % idim
DO j = 1, basin % aquifer (k) % domainBC % jdim
IF ( basin % aquifer (k) % domainBC % mat (i,j) == &
BC_DIRICHLET ) THEN
basin % aquifer (k) % head0 % mat (i,j) = &
basin % aquifer (k) % valueBC % mat (i,j)
basin % aquifer (k) % head1 % mat (i,j) = &
basin % aquifer (k) % valueBC % mat (i,j)
END IF
END DO
END DO
END IF
END DO
RETURN
END SUBROUTINE GroundwaterParameterUpdate
!==============================================================================
!| Description:
! Initialize export of point site data
SUBROUTINE GroundwaterPointInit &
!
( 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
INTEGER (KIND = short) :: k
!-------------------------end of declarations----------------------------------
timePointExport = time
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', 'Groundwater', 'out point file does not exist')
END IF
!Read metadata
CALL ReadMetadata (fileunit, sites)
!check dt
IF (.NOT. ( MOD ( sites % timeIncrement, dtGroundwater ) == 0 ) ) THEN
CALL Catch ('error', 'Groundwater', 'dt out sites must be multiple of dtGroundwater ')
END IF
CLOSE ( fileunit )
sites % description = 'groundwater head data exported from FEST'
sites % unit = 'm'
sites % offsetZ = 0.
!allocate file unit
ALLOCATE ( fileUnitPointGW ( basin % nAquifers ) )
!open and initialize files
DO k = 1, basin % nAquifers
fileUnitPointGW (k) = GetUnit ()
OPEN ( unit = fileUnitPointGW (k), &
file = TRIM(path_out) // 'point_aquifer_' // TRIM(ToString (k)) // '.fts' )
CALL WriteMetadata ( network = sites, fileunit = fileUnitPointGW (k) )
CALL WriteData (sites, fileUnitPointGW (k), .TRUE.)
END DO
RETURN
END SUBROUTINE GroundwaterPointInit
!==============================================================================
!| Description:
! Export of point site data
SUBROUTINE GroundwaterPointExport &
!
( time )
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT (IN) :: time
!local declarations:
INTEGER (KIND = short) :: k
!-------------------------end of declarations----------------------------------
!set current time
sites % time = time
DO k = 1, basin % nAquifers
!populate data
CALL AssignDataFromGrid ( basin % aquifer (k) % head1, sites )
!write data
CALL WriteData ( sites, fileUnitPointGW (k) )
END DO
timePointExport = timePointExport + sites % timeIncrement
RETURN
END SUBROUTINE GroundwaterPointExport
!==============================================================================
!| Description:
! Initialize files for exporting output results
SUBROUTINE GroundwaterOutputInit &
!
( path_out )
IMPLICIT NONE
!Arguments with intent (in):
CHARACTER (LEN = *), INTENT(IN) :: path_out !!path of output folder
!local declarations
REAL (KIND = float) :: area !! (km2)
INTEGER (KIND = short) :: i, j
INTEGER (KIND = short) :: nvars
!----------------------------------end of declarations-------------------------
!compute groundwater extent area
area = 0.
DO i = 1, basin % aquifer (1) % domainBC % idim
DO j = 1, basin % aquifer (1) % domainBC % jdim
IF (basin % aquifer (1) % domainBC % mat (i,j) == ACTIVE_CELL ) THEN
area = area + CellArea ( basin % aquifer (1) % domainBC, i, j )
END IF
END DO
END DO
area = area / 1000000. !conversion m2 -> km2
!number of variables to write in output file
nvars = basin % nAquifers + 5
IF ( riverGroundwaterInteract ) THEN
nvars = nvars + 2
END IF
!open and initialize file
fileUnitOutGW = GetUnit ()
OPEN ( unit = fileUnitOutGW, file = TRIM(path_out) // 'aquifer.out' )
WRITE (fileUnitOutGW,'(a)') 'Output variables of groundwater simulation'
WRITE (fileUnitOutGW,fmt = '(a, F12.2)') 'extent area (km2): ', area
WRITE (fileUnitOutGW,fmt = '(a, I3)') 'number of variables: ', nvars
WRITE (fileUnitOutGW,*)
WRITE (fileUnitOutGW,*)
WRITE (fileUnitOutGW,'(a)') 'data'
WRITE (fileUnitOutGW,'(a)', advance='no') 'DateTime '
DO i = 1, basin % nAquifers
WRITE (fileUnitOutGW,'(a)', advance='no') 'head_aquifer_' // &
TRIM(ToString(i)) // '_m'
END DO
IF ( riverGroundwaterInteract ) THEN
WRITE (fileUnitOutGW,'(a)') ' recharge_m3 volume_neumann_m3 &
volume_dirichlet_m3 volume_change_m3 residual_m3 &
river_to_groundwater_m3 groundwater_to_river_m3 '
ELSE
WRITE (fileUnitOutGW,'(a)') ' recharge_m3 volume_neumann_m3 &
volume_dirichlet_m3 volume_change_m3 residual_m3 '
END IF
RETURN
END SUBROUTINE GroundwaterOutputInit
!==============================================================================
!! Description:
!! Write results on output file
SUBROUTINE GroundwaterOutput &
!
( time )
IMPLICIT NONE
!Arguments with intent (in):
TYPE (DateTime), INTENT(IN) :: time !! current time
!local variables:
INTEGER (KIND = short) :: k
REAL (KIND = float) :: meanHead
!----------------------------end of declarations-------------------------------
timeString = time
WRITE (fileUnitOutGW,'(a)',advance='no') timeString
DO k = 1, basin % nAquifers
meanHead = GetMean ( basin % aquifer (k) % head1, &
maskInteger = basin % aquifer (k) % domainBC )
WRITE (fileUnitOutGW,'(6x,E12.5)',advance='no') meanHead
END DO
IF ( riverGroundwaterInteract ) THEN
WRITE (fileUnitOutGW,'(7(5x,E12.5))') volumeRecharge, volumeNeumann, &
volumeDirichlet, volumeChange, volumeResidual, &
volumeRiverToGroundwater, volumeGroundwaterToRiver
ELSE
WRITE (fileUnitOutGW,'(5(5x,E12.5))') volumeRecharge, volumeNeumann, &
volumeDirichlet, volumeChange, volumeResidual
END IF
RETURN
END SUBROUTINE GroundwaterOutput
!==============================================================================
!| Description:
! Configure river-groundwater interaction
SUBROUTINE GroundwaterRiverInit &
!
( inifile )
IMPLICIT NONE
!Arguments with intent (in):
CHARACTER (LEN = *), INTENT(IN) :: inifile !! configuration file
!local variables:
TYPE (IniList) :: iniDB
INTEGER (KIND = short) :: i,j
!----------------------------end of declarations-------------------------------
!open and read configuration file
CALL IniOpen ( inifile, iniDB )
!load river id
IF (SubSectionisPresent (subsection = 'river-id', &
section = 'river-groundwater', iniDB = iniDB) ) THEN
CALL GridByIni ( ini = iniDB, &
grid = riverGroundwaterID, &
section = 'river-groundwater', &
subsection = 'river-id')
ELSE
CALL Catch ('error', 'Groundwater', &
'missing river-id in configuration file' )
END IF
!load river dem
IF (SubSectionisPresent (subsection = 'river-dem', &
section = 'river-groundwater', iniDB = iniDB) ) THEN
CALL GridByIni ( ini = iniDB, &
grid = riverDem, &
section = 'river-groundwater', &
subsection = 'river-dem')
ELSE
CALL Catch ('error', 'Groundwater', &
'missing river-dem in configuration file' )
END IF
!exchange parameters
CALL TableNew ( file = inifile, tab = riverGroundwaterParameters, &
id = 'river-groundwater')
!allocate exchange flux maps
CALL NewGrid ( riverToGroundwater, riverGroundwaterID, 0. )
CALL NewGrid ( groundwaterToRiver, riverGroundwaterID, 0. )
!allocate streambed parameter maps
CALL NewGrid ( streambedThickness, riverGroundwaterID, 0. )
CALL NewGrid ( streambedConductivity, riverGroundwaterID, 0. )
!populate streambed parameter maps
DO i = 1, riverGroundwaterID % idim
DO j = 1, riverGroundwaterID % jdim
IF ( riverGroundwaterID % mat (i,j) /= &
riverGroundwaterID % nodata ) THEN
CALL TableGetValue ( &
valueIn = REAL(riverGroundwaterID % mat (i,j)),&
tab = riverGroundwaterParameters, &
keyIn = 'id', keyOut ='streambed-conductivity', &
match = 'exact', valueOut = streambedConductivity % mat (i,j) )
CALL TableGetValue ( &
valueIn = REAL(riverGroundwaterID % mat (i,j)),&
tab = riverGroundwaterParameters, &
keyIn = 'id', keyOut ='streambed-thickness', &
match = 'exact', valueOut = streambedThickness % mat (i,j) )
END IF
END DO
END DO
!free db
CALL IniClose ( iniDB )
RETURN
END SUBROUTINE GroundwaterRiverInit
!==============================================================================
!| Description:
! Update river-groundwater exchange fluxes
SUBROUTINE GroundwaterRiverUpdate &
!
( waterDepth, topWidth )
IMPLICIT NONE
!Arguments with intent (in):
TYPE (grid_real), INTENT(IN) :: waterDepth !!river water depth (m)
TYPE (grid_real), INTENT(IN) :: topWidth !!river top width (m)
!local variables:
INTEGER (KIND = short) :: i, j
REAL (KIND = float) :: riverWSE !river water surface elevation (m asl)
!TYPE (grid_real) :: waterTable !!aquifer water table (m)
REAL (KIND = float) :: waterTable
REAL (KIND = float) :: area
!-----------------------------end of declarations------------------------------
volumeRiverToGroundwater = 0.
volumeGroundwaterToRiver = 0.
!waterTable = basin % aquifer (1) % head1
DO i = 1, riverGroundwaterID % idim
DO j = 1, riverGroundwaterID % jdim
IF ( riverGroundwaterID % mat (i,j) /= &
riverGroundwaterID % nodata ) THEN
waterTable = basin % aquifer (1) % head1 % mat (i,j)
riverWSE = waterDepth % mat (i,j) + riverDem % mat (i,j)
area = CellArea (riverGroundwaterID, i, j)
IF ( waterTable > riverWSE ) THEN
groundwaterToRiver % mat (i,j) = &
( waterTable - riverWSE ) * & !head difference
streambedConductivity % mat (i,j) / & !conductivity
streambedThickness % mat (i,j) * & !thickness
topWidth % mat (i,j) * & !river width
area ** 0.5 !cell size
riverToGroundwater % mat (i,j) = 0.
ELSE IF ( waterTable < riverWSE .AND. &
waterTable > riverDem % mat (i,j) ) THEN
! when waterdepth > 10 cm, compute river discharge toward groundwater
IF ( waterDepth % mat (i,j) > 0.10 ) THEN
riverToGroundwater % mat(i,j) = &
( riverWSE - waterTable ) * & !head difference
streambedConductivity % mat (i,j) / & !conductivity
streambedThickness % mat (i,j) * & !thickness
topWidth % mat (i,j) * & !river width
area ** 0.5 !cell size
groundwaterToRiver % mat (i,j) = 0.
ELSE
riverToGroundwater % mat(i,j) = 0.
END IF
ELSE IF ( waterTable < riverDem % mat (i,j) ) THEN
! when waterdepth > 10 cm, compute river discharge toward groundwater
IF ( waterDepth % mat (i,j) > 0.10 ) THEN
riverToGroundwater % mat(i,j) = &
waterDepth % mat (i,j) * & !head
streambedConductivity % mat (i,j) / & !conductivity
streambedThickness % mat (i,j) * & !thickness
topWidth % mat (i,j) * & !river width
area ** 0.5 !cell size
groundwaterToRiver % mat (i,j) = 0.
ELSE
riverToGroundwater % mat(i,j) = 0.
END IF
END IF
volumeRiverToGroundwater = volumeRiverToGroundwater + &
riverToGroundwater % mat (i,j) * &
dtGroundwater
volumeGroundwaterToRiver = volumeGroundwaterToRiver + &
groundwaterToRiver % mat (i,j) * &
dtGroundwater
END IF
END DO
END DO
RETURN
END SUBROUTINE GroundwaterRiverUpdate
END MODULE Groundwater