diff --git a/build/source/engine/groundwatr.f90 b/build/source/engine/groundwatr.f90 index 0e16b27ae0796c48894cac41884a0f2571da5921..6c9c91744ed1a6f2e045ca27f8908dc27b5d2ae0 100755 --- a/build/source/engine/groundwatr.f90 +++ b/build/source/engine/groundwatr.f90 @@ -28,8 +28,8 @@ USE multiconst,only:iden_water ! density of water (kg m-3) ! derived types to define the data structures USE data_types,only:& - var_d, & ! data vector (dp) - var_dlength ! data vector with variable length dimension (dp) + var_d, & ! data vector (rkind) + var_dlength ! data vector with variable length dimension (rkind) ! named variables defining elements in the data structures USE var_lookup,only:iLookATTR ! named variables for structure elements @@ -40,496 +40,484 @@ USE var_lookup,only:iLookPARAM ! named variables for structure elements ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & - qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization - bigBucket, & ! a big bucket (lumped aquifer model) - noExplicit ! no explicit groundwater parameterization + qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization + bigBucket, & ! a big bucket (lumped aquifer model) + noExplicit ! no explicit groundwater parameterization ! privacy implicit none ! constant parameters -real(dp),parameter :: valueMissing=-9999._dp ! missing value parameter -real(dp),parameter :: verySmall=epsilon(1.0_dp) ! a very small number (used to avoid divide by zero) -real(dp),parameter :: dx=1.e-8_dp ! finite difference increment +real(rkind),parameter :: valueMissing=-9999._rkind ! missing value parameter +real(rkind),parameter :: verySmall=epsilon(1.0_rkind) ! a very small number (used to avoid divide by zero) +real(rkind),parameter :: dx=1.e-8_rkind ! finite difference increment private public::groundwatr contains - ! ************************************************************************************************ - ! public subroutine groundwatr: compute the groundwater sink term in Richards' equation - ! ************************************************************************************************ - ! - ! Method - ! ------ - ! - ! Here we assume that water avaialble for shallow groundwater flow includes is all water above - ! "field capacity" below the depth zCrit, where zCrit is defined as the lowest point in the soil - ! profile where the volumetric liquid water content is less than field capacity. - ! - ! We further assume that transmssivity (m2 s-1) for each layer is defined asuming that the water - ! available for saturated flow is located at the bottom of the soil profile. Specifically: - ! trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL - ! trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) - ! where zActive(iLayer) is the effective water table thickness for all layers up to and including - ! the current layer (working from the bottom to the top). - ! - ! The outflow from each layer is then (m3 s-1) - ! mLayerOutflow(iLayer) = trSoil(iLayer)*tan_slope*contourLength - ! where contourLength is the width of a hillslope (m) parallel to a stream - ! - ! ************************************************************************************************ - subroutine groundwatr(& - - ! input: model control - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - getSatDepth, & ! intent(in): logical flag to compute index of the lowest saturated layer - - ! input: state and diagnostic variables - mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - mLayerMatricHeadLiq, & ! intent(in): liquid water matric potential (m) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) - - ! input/output: data structures - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(in): model diagnostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - - ! output: baseflow - ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) - mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) - dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) - - ! output: error control - err,message) ! intent(out): error control - ! --------------------------------------------------------------------------------------- - ! utility modules - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head - USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: getSatDepth ! logical flag to compute index of the lowest saturated layer - ! input: state and diagnostic variables - real(dp),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) - real(dp),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) - real(dp),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) - real(dp),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - ! input/output: data structures - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - ! output: baseflow - integer(i4b),intent(inout) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) - real(dp),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) - real(dp),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - ! general local variables - integer(i4b) :: iLayer ! index of soil layer - real(dp),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) - ! local variables to compute the numerical Jacobian - logical(lgt),parameter :: doNumericalJacobian=.false. ! flag to compute the numerical Jacobian - real(dp),dimension(nSoil) :: mLayerMatricHeadPerturbed ! perturbed matric head (m) - real(dp),dimension(nSoil) :: mLayerVolFracLiqPerturbed ! perturbed volumetric fraction of liquid water (-) - real(dp),dimension(nSoil) :: mLayerBaseflowPerturbed ! perturbed baseflow (m s-1) - real(dp),dimension(nSoil,nSoil) :: nJac ! numerical Jacobian (s-1) - ! *************************************************************************************** - ! *************************************************************************************** - ! initialize error control - err=0; message='groundwatr/' - ! --------------------------------------------------------------------------------------- - ! --------------------------------------------------------------------------------------- - ! associate variables in data structures - associate(& - - ! input: baseflow parameters - fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp] residual volumetric water content (-) - - ! input: van Genuchten soil parametrers - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) - - ! output: diagnostic variables - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) - - ) ! end association to variables in data structures - - ! ************************************************************************************************ - ! (1) compute the "active" portion of the soil profile - ! ************************************************************************************************ - - ! get index of the lowest saturated layer - if(getSatDepth)then ! NOTE: only compute for the first flux call - ixSaturation = nSoil+1 ! unsaturated profile when ixSaturation>nSoil - do iLayer=nSoil,1,-1 ! start at the lowest soil layer and work upwards to the top layer - if(mLayerVolFracLiq(iLayer) > fieldCapacity)then; ixSaturation = iLayer ! index of saturated layer -- keeps getting over-written as move upwards - else; exit; end if ! (only consider saturated layer at the bottom of the soil profile) - end do ! (looping through soil layers) - end if - - ! check for an early return (no layers are "active") - if(ixSaturation > nSoil)then - scalarExfiltration = 0._dp ! exfiltration from the soil profile (m s-1) - mLayerColumnOutflow(:) = 0._dp ! column outflow from each soil layer (m3 s-1) - mLayerBaseflow(:) = 0._dp ! baseflow from each soil layer (m s-1) - dBaseflow_dMatric(:,:) = 0._dp ! derivative in baseflow w.r.t. matric head (s-1) - return - end if ! if some layers are saturated - - ! ************************************************************************************************ - ! (2) compute the baseflow flux and its derivative w.r.t volumetric liquid water content - ! ************************************************************************************************ - - ! use private subroutine to compute baseflow (for multiple calls for numerical Jacobian) - call computeBaseflow(& - ! input: control and state variables - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - .true., & ! intent(in): .true. if analytical derivatives are desired - ixSaturation, & ! intent(in): index of upper-most "saturated" layer - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) - ! input/output: data structures - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives - mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) - dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) - - ! use the chain rule to compute the baseflow derivative w.r.t. matric head (s-1) - do iLayer=1,nSoil - dBaseflow_dMatric(1:iLayer,iLayer) = dBaseflow_dVolLiq(1:iLayer,iLayer)*mLayerdTheta_dPsi(iLayer) - if(iLayer<nSoil) dBaseflow_dMatric(iLayer+1:nSoil,iLayer) = 0._dp - end do - - ! ************************************************************************************************ - ! (4) compute the numerical Jacobian - ! ************************************************************************************************ - if(doNumericalJacobian)then - - ! first, print the analytical Jacobian - print*, '** analytical Jacobian:' - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,nSoil) - do iLayer=1,nSoil; write(*,'(i4,1x,100(e12.5,1x))') iLayer, dBaseflow_dMatric(1:nSoil,iLayer); end do - - ! get a copy of the state vector to perturb - mLayerMatricHeadPerturbed(:) = mLayerMatricHeadLiq(:) - mLayerVolFracLiqPerturbed(:) = mLayerVolFracLiq(:) - - ! loop through the state variables - do iLayer=1,nSoil - - ! perturb state vector - mLayerMatricHeadPerturbed(iLayer) = mLayerMatricHeadPerturbed(iLayer) + dx - - ! compute the columetruc liquid water content - mLayerVolFracLiqPerturbed(iLayer) = volFracLiq(mLayerMatricHeadPerturbed(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) - - ! compute baseflow flux - ! NOTE: This is an optional second call to computeBaseflow that is invoked when computing numerical derivatives. - ! Since the purpose here is to compute the numerical derivatives, we do not need to compute analytical derivatives also. - ! Hence, analytical derivatives are not desired - call computeBaseflow(& - ! input: control and state variables - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - .false., & ! intent(in): .true. if analytical derivatives are desired - ixSaturation, & ! intent(in): index of upper-most "saturated" layer - mLayerVolFracLiqPerturbed, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) - ! input/output: data structures - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives - mLayerBaseflowPerturbed, & ! intent(out): baseflow flux in each soil layer (m s-1) - dBaseflow_dVolLiq) ! intent(out): ** NOT USED ** derivative in baseflow w.r.t. volumetric liquid water content (s-1) - - ! compute the numerical Jacobian - nJac(:,iLayer) = (mLayerBaseflowPerturbed(:) - mLayerBaseflow(:))/dx ! compute the Jacobian - - ! set the state back to the input value - mLayerMatricHeadPerturbed(iLayer) = mLayerMatricHeadLiq(iLayer) - mLayerVolFracLiqPerturbed(iLayer) = mLayerVolFracLiq(iLayer) - - end do ! looping through state variables - - ! print the numerical Jacobian - print*, '** numerical Jacobian:' - write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,nSoil) - do iLayer=1,nSoil; write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(1:nSoil,iLayer); end do - !pause 'testing Jacobian' - - end if ! if desire to compute the Jacobian - - ! end association to variables in data structures - end associate - - end subroutine groundwatr - - - ! *********************************************************************************************************************** - ! * private subroutine computeBaseflow: private subroutine so can be used to test the numerical jacobian - ! *********************************************************************************************************************** - subroutine computeBaseflow(& - ! input: control and state variables - nSnow, & ! intent(in): number of snow layers - nSoil, & ! intent(in): number of soil layers - nLayers, & ! intent(in): total number of layers - derivDesired, & ! intent(in): .true. if derivatives are desired - ixSaturation, & ! intent(in): index of upper-most "saturated" layer - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) - ! input/output: data structures - attr_data, & ! intent(in): spatial attributes - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - flux_data, & ! intent(inout): model fluxes for a local HRU - ! output: fluxes and derivatives - mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) - dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) - implicit none - ! --------------------------------------------------------------------------------------- - ! * dummy variables - ! --------------------------------------------------------------------------------------- - ! input: control and state variables - integer(i4b),intent(in) :: nSnow ! number of snow layers - integer(i4b),intent(in) :: nSoil ! number of soil layers - integer(i4b),intent(in) :: nLayers ! total number of layers - logical(lgt),intent(in) :: derivDesired ! .true. if derivatives are desired - integer(i4b),intent(in) :: ixSaturation ! index of upper-most "saturated" layer - real(dp),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) - real(dp),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) - ! input/output: data structures - type(var_d),intent(in) :: attr_data ! spatial attributes - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU - ! output: baseflow - real(dp),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) - real(dp),intent(out) :: dBaseflow_dVolLiq(:,:) ! derivative in baseflow w.r.t. matric head (s-1) - ! --------------------------------------------------------------------------------------- - ! * local variables - ! --------------------------------------------------------------------------------------- - ! general local variables - integer(i4b) :: iLayer,jLayer ! index of model layer - ! local variables for the exfiltration - real(dp) :: totalColumnInflow ! total column inflow (m s-1) - real(dp) :: totalColumnOutflow ! total column outflow (m s-1) - real(dp) :: availStorage ! available storage (m) - real(dp),parameter :: xMinEval=0.002_dp ! minimum value to evaluate the exfiltration function (m) - real(dp),parameter :: xCenter=0.001_dp ! center of the exfiltration function (m) - real(dp),parameter :: xWidth=0.0001_dp ! width of the exfiltration function (m) - real(dp) :: expF,logF ! logistic smoothing function (-) - ! local variables for the lateral flux among soil columns - real(dp) :: activePorosity ! "active" porosity associated with storage above a threshold (-) - real(dp) :: drainableWater ! drainable water in eaxch layer (m) - real(dp) :: tran0 ! maximum transmissivity (m2 s-1) - real(dp),dimension(nSoil) :: zActive ! water table thickness associated with storage below and including the given layer (m) - real(dp),dimension(nSoil) :: trTotal ! total transmissivity associated with total water table depth zActive (m2 s-1) - real(dp),dimension(nSoil) :: trSoil ! transmissivity of water in a given layer (m2 s-1) - ! local variables for the derivatives - real(dp) :: qbTotal ! total baseflow (m s-1) - real(dp) :: length2area ! ratio of hillslope width to hillslope area (m m-2) - real(dp),dimension(nSoil) :: depth2capacity ! ratio of layer depth to total subsurface storage capacity (-) - real(dp),dimension(nSoil) :: dXdS ! change in dimensionless flux w.r.t. change in dimensionless storage (-) - real(dp),dimension(nSoil) :: dLogFunc_dLiq ! derivative in the logistic function w.r.t. volumetric liquid water content (-) - real(dp),dimension(nSoil) :: dExfiltrate_dVolLiq ! derivative in exfiltration w.r.t. volumetric liquid water content (-) - ! local variables for testing (debugging) - logical(lgt),parameter :: printFlag=.false. ! flag for printing (debugging) - real(dp) :: xDepth,xTran,xFlow ! temporary variables (depth, transmissivity, flow) - ! --------------------------------------------------------------------------------------- - ! * association to data in structures - ! --------------------------------------------------------------------------------------- - associate(& - - ! input: coordinate variables - soilDepth => prog_data%var(iLookPROG%iLayerHeight)%dat(nLayers), & ! intent(in): [dp] total soil depth (m) - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers),& ! intent(in): [dp(:)] depth of each soil layer (m) - - ! input: diagnostic variables - surfaceHydCond => flux_data%var(iLookFLUX%mLayerSatHydCondMP)%dat(1), & ! intent(in): [dp] saturated hydraulic conductivity at the surface (m s-1) - mLayerColumnInflow => flux_data%var(iLookFLUX%mLayerColumnInflow)%dat, & ! intent(in): [dp(:)] inflow into each soil layer (m3/s) - - ! input: local attributes - HRUarea => attr_data%var(iLookATTR%HRUarea), & ! intent(in): [dp] HRU area (m2) - tan_slope => attr_data%var(iLookATTR%tan_slope), & ! intent(in): [dp] tan water table slope, taken as tan local ground surface slope (-) - contourLength => attr_data%var(iLookATTR%contourLength), & ! intent(in): [dp] length of contour at downslope edge of HRU (m) - - ! input: baseflow parameters - zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): [dp] TOPMODEL exponent (-) - kAnisotropic => mpar_data%var(iLookPARAM%kAnisotropic)%dat(1), & ! intent(in): [dp] anisotropy factor for lateral hydraulic conductivity (- - fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) - - ! output: diagnostic variables - scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) - mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) - - ) ! end association to variables in data structures - ! *********************************************************************************************************************** - ! *********************************************************************************************************************** - ! start routine here - - ! *********************************************************************************************************************** - ! (1) compute the baseflow flux in each soil layer - ! *********************************************************************************************************************** - - ! compute the maximum transmissivity - ! NOTE: this can be done as a pre-processing step - tran0 = kAnisotropic*surfaceHydCond*soilDepth/zScale_TOPMODEL ! maximum transmissivity (m2 s-1) - - ! compute the water table thickness (m) and transmissivity in each layer (m2 s-1) - do iLayer=nSoil,ixSaturation,-1 ! loop through "active" soil layers, from lowest to highest - ! define drainable water in each layer (m) - activePorosity = theta_sat(iLayer) - fieldCapacity ! "active" porosity (-) - drainableWater = mLayerDepth(iLayer)*(max(0._dp,mLayerVolFracLiq(iLayer) - fieldCapacity))/activePorosity - ! compute layer transmissivity - if(iLayer==nSoil)then - zActive(iLayer) = drainableWater ! water table thickness associated with storage in a given layer (m) - trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL ! total transmissivity for total depth zActive (m2 s-1) - trSoil(iLayer) = trTotal(iLayer) ! transmissivity of water in a given layer (m2 s-1) - else - zActive(iLayer) = zActive(iLayer+1) + drainableWater - trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL - trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) - end if - !write(*,'(a,1x,i4,1x,10(f20.15,1x))') 'iLayer, mLayerMatricHeadLiq(iLayer), mLayerVolFracLiq(iLayer), zActive(iLayer), trTotal(iLayer), trSoil(iLayer) = ', & - ! iLayer, mLayerMatricHeadLiq(iLayer), mLayerVolFracLiq(iLayer), zActive(iLayer), trTotal(iLayer), trSoil(iLayer) - end do ! looping through soil layers - - ! set un-used portions of the vectors to zero - if(ixSaturation>1)then - zActive(1:ixSaturation-1) = 0._dp - trTotal(1:ixSaturation-1) = 0._dp - trSoil(1:ixSaturation-1) = 0._dp - end if - - ! compute the outflow from each layer (m3 s-1) - mLayerColumnOutflow(1:nSoil) = trSoil(1:nSoil)*tan_slope*contourLength - - ! compute total column inflow and total column outflow (m s-1) - totalColumnInflow = sum(mLayerColumnInflow(1:nSoil))/HRUarea - totalColumnOutflow = sum(mLayerColumnOutflow(1:nSoil))/HRUarea - - ! compute the available storage (m) - availStorage = sum(mLayerDepth(1:nSoil)*(theta_sat - (mLayerVolFracLiq(1:nSoil)+mLayerVolFracIce(1:nSoil))) ) - - ! compute the smoothing function (-) - if(availStorage < xMinEval)then - ! (compute the logistic function) - expF = exp((availStorage - xCenter)/xWidth) - logF = 1._dp / (1._dp + expF) - ! (compute the derivative in the logistic function w.r.t. volumetric liquid water content in each soil layer) - dLogFunc_dLiq(1:nSoil) = mLayerDepth(1:nSoil)*(expF/xWidth)/(1._dp + expF)**2._dp - else - logF = 0._dp - dLogFunc_dLiq(:) = 0._dp - end if - - ! compute the exfiltartion (m s-1) - if(totalColumnInflow > totalColumnOutflow .and. logF > tiny(1._dp))then - scalarExfiltration = logF*(totalColumnInflow - totalColumnOutflow) ! m s-1 - !write(*,'(a,1x,10(f30.20,1x))') 'scalarExfiltration = ', scalarExfiltration - else - scalarExfiltration = 0._dp - end if - - ! check - !write(*,'(a,1x,10(f30.20,1x))') 'zActive(1), soilDepth, availStorage, logF, scalarExfiltration = ', & - ! zActive(1), soilDepth, availStorage, logF, scalarExfiltration - !if(scalarExfiltration > tiny(1.0_dp)) pause 'exfiltrating' - - ! compute the baseflow in each layer (m s-1) - mLayerBaseflow(1:nSoil) = (mLayerColumnOutflow(1:nSoil) - mLayerColumnInflow(1:nSoil))/HRUarea - - ! compute the total baseflow - qbTotal = sum(mLayerBaseflow) - - ! add exfiltration to the baseflow flux at the top layer - mLayerBaseflow(1) = mLayerBaseflow(1) + scalarExfiltration - mLayerColumnOutflow(1) = mLayerColumnOutflow(1) + scalarExfiltration*HRUarea - - ! test - if(printFlag)then - xDepth = sum(mLayerDepth(ixSaturation:nSoil)*(mLayerVolFracLiq(ixSaturation:nSoil) - fieldCapacity))/sum(theta_sat(ixSaturation:nSoil) - fieldCapacity) ! "effective" water table thickness (m) - xTran = tran0*(xDepth/soilDepth)**zScale_TOPMODEL ! transmissivity for the entire aquifer (m2 s-1) - xFlow = xTran*tan_slope*contourLength/HRUarea ! total column outflow (m s-1) - print*, 'ixSaturation = ', ixSaturation - write(*,'(a,1x,5(f30.20,1x))') 'tran0, soilDepth = ', tran0, soilDepth - write(*,'(a,1x,5(f30.20,1x))') 'surfaceHydCond, zScale_TOPMODEL = ', surfaceHydCond, zScale_TOPMODEL - write(*,'(a,1x,5(f30.20,1x))') 'xDepth, zActive(ixSaturation) = ', xDepth, zActive(ixSaturation) - write(*,'(a,1x,5(f30.20,1x))') 'xTran, trTotal(ixSaturation) = ', xTran, trTotal(ixSaturation) - write(*,'(a,1x,5(f30.20,1x))') 'xFlow, totalColumnOutflow = ', xFlow, sum(mLayerColumnOutflow(:))/HRUarea - !pause 'check groundwater' - end if - - ! *********************************************************************************************************************** - ! (2) compute the derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) - ! *********************************************************************************************************************** - - ! initialize the derivative matrix - dBaseflow_dVolLiq(:,:) = 0._dp - - ! check if derivatives are actually required - if(.not.derivDesired) return - - ! compute ratio of hillslope width to hillslope area (m m-2) - length2area = tan_slope*contourLength/HRUarea - - ! compute the ratio of layer depth to maximum water holding capacity (-) - depth2capacity(1:nSoil) = mLayerDepth(1:nSoil)/sum( (theta_sat(1:nSoil) - fieldCapacity)*mLayerDepth(1:nSoil) ) - - ! compute the change in dimensionless flux w.r.t. change in dimensionless storage (-) - dXdS(1:nSoil) = zScale_TOPMODEL*(zActive(1:nSoil)/SoilDepth)**(zScale_TOPMODEL - 1._dp) - - ! loop through soil layers - do iLayer=1,nSoil - ! compute diagonal terms (s-1) - dBaseflow_dVolLiq(iLayer,iLayer) = tran0*dXdS(iLayer)*depth2capacity(iLayer)*length2area - ! compute off-diagonal terms - do jLayer=iLayer+1,nSoil ! (only dependent on layers below) - dBaseflow_dVolLiq(iLayer,jLayer) = tran0*(dXdS(iLayer) - dXdS(iLayer+1))*depth2capacity(jLayer)*length2area - end do ! looping through soil layers - end do ! looping through soil layers - - ! compute the derivative in the exfiltration flux w.r.t. volumetric liquid water content (m s-1) - if(qbTotal < 0._dp)then - do iLayer=1,nSoil - dExfiltrate_dVolLiq(iLayer) = dBaseflow_dVolLiq(iLayer,iLayer)*logF + dLogFunc_dLiq(iLayer)*qbTotal - end do ! looping through soil layers - dBaseflow_dVolLiq(1,1:nSoil) = dBaseflow_dVolLiq(1,1:nSoil) - dExfiltrate_dVolLiq(1:nSoil) - end if - - ! end association to data in structures - end associate - - end subroutine computeBaseflow - +! ************************************************************************************************ +! public subroutine groundwatr: compute the groundwater sink term in Richards' equation +! ************************************************************************************************ +! +! Method +! ------ +! +! Here we assume that water avaialble for shallow groundwater flow includes is all water above +! "field capacity" below the depth zCrit, where zCrit is defined as the lowest point in the soil +! profile where the volumetric liquid water content is less than field capacity. +! +! We further assume that transmssivity (m2 s-1) for each layer is defined asuming that the water +! available for saturated flow is located at the bottom of the soil profile. Specifically: +! trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL +! trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) +! where zActive(iLayer) is the effective water table thickness for all layers up to and including +! the current layer (working from the bottom to the top). +! +! The outflow from each layer is then (m3 s-1) +! mLayerOutflow(iLayer) = trSoil(iLayer)*tan_slope*contourLength +! where contourLength is the width of a hillslope (m) parallel to a stream +! +! ************************************************************************************************ +subroutine groundwatr(& + + ! input: model control + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + getSatDepth, & ! intent(in): logical flag to compute index of the lowest saturated layer + + ! input: state and diagnostic variables + mLayerdTheta_dPsi, & ! intent(in): derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + mLayerMatricHeadLiq, & ! intent(in): liquid water matric potential (m) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water (-) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice (-) + + ! input/output: data structures + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + + ! output: baseflow + ixSaturation, & ! intent(inout) index of lowest saturated layer (NOTE: only computed on the first iteration) + mLayerBaseflow, & ! intent(out): baseflow from each soil layer (m s-1) + dBaseflow_dMatric, & ! intent(out): derivative in baseflow w.r.t. matric head (s-1) + + ! output: error control + err,message) ! intent(out): error control + ! --------------------------------------------------------------------------------------- + ! utility modules + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water as a function of matric head + USE soil_utils_module,only:hydCond_psi ! compute hydraulic conductivity as a function of matric head + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: getSatDepth ! logical flag to compute index of the lowest saturated layer + ! input: state and diagnostic variables + real(rkind),intent(in) :: mLayerdTheta_dPsi(:) ! derivative in the soil water characteristic w.r.t. matric head in each layer (m-1) + real(rkind),intent(in) :: mLayerMatricHeadLiq(:) ! matric head in each layer at the current iteration (m) + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) + ! input/output: data structures + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! output: baseflow + integer(i4b),intent(inout) :: ixSaturation ! index of lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) + real(rkind),intent(out) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + ! general local variables + integer(i4b) :: iLayer ! index of soil layer + real(rkind),dimension(nSoil,nSoil) :: dBaseflow_dVolLiq ! derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) + ! local variables to compute the numerical Jacobian + logical(lgt),parameter :: doNumericalJacobian=.false. ! flag to compute the numerical Jacobian + real(rkind),dimension(nSoil) :: mLayerMatricHeadPerturbed ! perturbed matric head (m) + real(rkind),dimension(nSoil) :: mLayerVolFracLiqPerturbed ! perturbed volumetric fraction of liquid water (-) + real(rkind),dimension(nSoil) :: mLayerBaseflowPerturbed ! perturbed baseflow (m s-1) + real(rkind),dimension(nSoil,nSoil) :: nJac ! numerical Jacobian (s-1) + ! *************************************************************************************** + ! *************************************************************************************** + ! initialize error control + err=0; message='groundwatr/' + ! --------------------------------------------------------------------------------------- + ! --------------------------------------------------------------------------------------- + ! associate variables in data structures + associate(& + + ! input: baseflow parameters + fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat, & ! intent(in): [dp] residual volumetric water content (-) + + ! input: van Genuchten soil parametrers + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat, & ! intent(in): [dp] van Genutchen "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat, & ! intent(in): [dp] van Genutchen "n" parameter (-) + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat, & ! intent(in): [dp] van Genutchen "m" parameter (-) + + ! output: diagnostic variables + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) + + ) ! end association to variables in data structures + + ! ************************************************************************************************ + ! (1) compute the "active" portion of the soil profile + ! ************************************************************************************************ + + ! get index of the lowest saturated layer + if(getSatDepth)then ! NOTE: only compute for the first flux call + ixSaturation = nSoil+1 ! unsaturated profile when ixSaturation>nSoil + do iLayer=nSoil,1,-1 ! start at the lowest soil layer and work upwards to the top layer + if(mLayerVolFracLiq(iLayer) > fieldCapacity)then; ixSaturation = iLayer ! index of saturated layer -- keeps getting over-written as move upwards + else; exit; end if ! (only consider saturated layer at the bottom of the soil profile) + end do ! (looping through soil layers) + end if + + ! check for an early return (no layers are "active") + if(ixSaturation > nSoil)then + scalarExfiltration = 0._rkind ! exfiltration from the soil profile (m s-1) + mLayerColumnOutflow(:) = 0._rkind ! column outflow from each soil layer (m3 s-1) + mLayerBaseflow(:) = 0._rkind ! baseflow from each soil layer (m s-1) + dBaseflow_dMatric(:,:) = 0._rkind ! derivative in baseflow w.r.t. matric head (s-1) + return + end if ! if some layers are saturated + + ! ************************************************************************************************ + ! (2) compute the baseflow flux and its derivative w.r.t volumetric liquid water content + ! ************************************************************************************************ + + ! use private subroutine to compute baseflow (for multiple calls for numerical Jacobian) + call computeBaseflow(& + ! input: control and state variables + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + .true., & ! intent(in): .true. if analytical derivatives are desired + ixSaturation, & ! intent(in): index of upper-most "saturated" layer + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) + ! input/output: data structures + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives + mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) + dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) + + ! use the chain rule to compute the baseflow derivative w.r.t. matric head (s-1) + do iLayer=1,nSoil + dBaseflow_dMatric(1:iLayer,iLayer) = dBaseflow_dVolLiq(1:iLayer,iLayer)*mLayerdTheta_dPsi(iLayer) + if(iLayer<nSoil) dBaseflow_dMatric(iLayer+1:nSoil,iLayer) = 0._rkind + end do + + ! ************************************************************************************************ + ! (4) compute the numerical Jacobian + ! ************************************************************************************************ + if(doNumericalJacobian)then + + ! first, print the analytical Jacobian + print*, '** analytical Jacobian:' + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,nSoil) + do iLayer=1,nSoil; write(*,'(i4,1x,100(e12.5,1x))') iLayer, dBaseflow_dMatric(1:nSoil,iLayer); end do + + ! get a copy of the state vector to perturb + mLayerMatricHeadPerturbed(:) = mLayerMatricHeadLiq(:) + mLayerVolFracLiqPerturbed(:) = mLayerVolFracLiq(:) + + ! loop through the state variables + do iLayer=1,nSoil + + ! perturb state vector + mLayerMatricHeadPerturbed(iLayer) = mLayerMatricHeadPerturbed(iLayer) + dx + + ! compute the columetruc liquid water content + mLayerVolFracLiqPerturbed(iLayer) = volFracLiq(mLayerMatricHeadPerturbed(iLayer),vGn_alpha(iLayer),theta_res(iLayer),theta_sat(iLayer),vGn_n(iLayer),vGn_m(iLayer)) + + ! compute baseflow flux + ! NOTE: This is an optional second call to computeBaseflow that is invoked when computing numerical derivatives. + ! Since the purpose here is to compute the numerical derivatives, we do not need to compute analytical derivatives also. + ! Hence, analytical derivatives are not desired + call computeBaseflow(& + ! input: control and state variables + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + .false., & ! intent(in): .true. if analytical derivatives are desired + ixSaturation, & ! intent(in): index of upper-most "saturated" layer + mLayerVolFracLiqPerturbed, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) + ! input/output: data structures + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives + mLayerBaseflowPerturbed, & ! intent(out): baseflow flux in each soil layer (m s-1) + dBaseflow_dVolLiq) ! intent(out): ** NOT USED ** derivative in baseflow w.r.t. volumetric liquid water content (s-1) + + ! compute the numerical Jacobian + nJac(:,iLayer) = (mLayerBaseflowPerturbed(:) - mLayerBaseflow(:))/dx ! compute the Jacobian + + ! set the state back to the input value + mLayerMatricHeadPerturbed(iLayer) = mLayerMatricHeadLiq(iLayer) + mLayerVolFracLiqPerturbed(iLayer) = mLayerVolFracLiq(iLayer) + + end do ! looping through state variables + + ! print the numerical Jacobian + print*, '** numerical Jacobian:' + write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=1,nSoil) + do iLayer=1,nSoil; write(*,'(i4,1x,100(e12.5,1x))') iLayer, nJac(1:nSoil,iLayer); end do + !pause 'testing Jacobian' + + end if ! if desire to compute the Jacobian + + ! end association to variables in data structures + end associate + +end subroutine groundwatr + + +! *********************************************************************************************************************** +! * private subroutine computeBaseflow: private subroutine so can be used to test the numerical jacobian +! *********************************************************************************************************************** +subroutine computeBaseflow(& + ! input: control and state variables + nSnow, & ! intent(in): number of snow layers + nSoil, & ! intent(in): number of soil layers + nLayers, & ! intent(in): total number of layers + derivDesired, & ! intent(in): .true. if derivatives are desired + ixSaturation, & ! intent(in): index of upper-most "saturated" layer + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water in each soil layer (-) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice in each soil layer (-) + ! input/output: data structures + attr_data, & ! intent(in): spatial attributes + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + flux_data, & ! intent(inout): model fluxes for a local HRU + ! output: fluxes and derivatives + mLayerBaseflow, & ! intent(out): baseflow flux in each soil layer (m s-1) + dBaseflow_dVolLiq) ! intent(out): derivative in baseflow w.r.t. volumetric liquid water content (s-1) + implicit none + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: control and state variables + integer(i4b),intent(in) :: nSnow ! number of snow layers + integer(i4b),intent(in) :: nSoil ! number of soil layers + integer(i4b),intent(in) :: nLayers ! total number of layers + logical(lgt),intent(in) :: derivDesired ! .true. if derivatives are desired + integer(i4b),intent(in) :: ixSaturation ! index of upper-most "saturated" layer + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! volumetric fraction of ice (-) + ! input/output: data structures + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + ! output: baseflow + real(rkind),intent(out) :: mLayerBaseflow(:) ! baseflow from each soil layer (m s-1) + real(rkind),intent(out) :: dBaseflow_dVolLiq(:,:) ! derivative in baseflow w.r.t. matric head (s-1) + ! --------------------------------------------------------------------------------------- + ! * local variables + ! --------------------------------------------------------------------------------------- + ! general local variables + integer(i4b) :: iLayer,jLayer ! index of model layer + ! local variables for the exfiltration + real(rkind) :: totalColumnInflow ! total column inflow (m s-1) + real(rkind) :: totalColumnOutflow ! total column outflow (m s-1) + real(rkind) :: availStorage ! available storage (m) + real(rkind),parameter :: xMinEval=0.002_rkind ! minimum value to evaluate the exfiltration function (m) + real(rkind),parameter :: xCenter=0.001_rkind ! center of the exfiltration function (m) + real(rkind),parameter :: xWidth=0.0001_rkind ! width of the exfiltration function (m) + real(rkind) :: expF,logF ! logistic smoothing function (-) + ! local variables for the lateral flux among soil columns + real(rkind) :: activePorosity ! "active" porosity associated with storage above a threshold (-) + real(rkind) :: drainableWater ! drainable water in eaxch layer (m) + real(rkind) :: tran0 ! maximum transmissivity (m2 s-1) + real(rkind),dimension(nSoil) :: zActive ! water table thickness associated with storage below and including the given layer (m) + real(rkind),dimension(nSoil) :: trTotal ! total transmissivity associated with total water table depth zActive (m2 s-1) + real(rkind),dimension(nSoil) :: trSoil ! transmissivity of water in a given layer (m2 s-1) + ! local variables for the derivatives + real(rkind) :: qbTotal ! total baseflow (m s-1) + real(rkind) :: length2area ! ratio of hillslope width to hillslope area (m m-2) + real(rkind),dimension(nSoil) :: depth2capacity ! ratio of layer depth to total subsurface storage capacity (-) + real(rkind),dimension(nSoil) :: dXdS ! change in dimensionless flux w.r.t. change in dimensionless storage (-) + real(rkind),dimension(nSoil) :: dLogFunc_dLiq ! derivative in the logistic function w.r.t. volumetric liquid water content (-) + real(rkind),dimension(nSoil) :: dExfiltrate_dVolLiq ! derivative in exfiltration w.r.t. volumetric liquid water content (-) + ! local variables for testing (debugging) + logical(lgt),parameter :: printFlag=.false. ! flag for printing (debugging) + real(rkind) :: xDepth,xTran,xFlow ! temporary variables (depth, transmissivity, flow) + ! --------------------------------------------------------------------------------------- + ! * association to data in structures + ! --------------------------------------------------------------------------------------- + associate(& + + ! input: coordinate variables + soilDepth => prog_data%var(iLookPROG%iLayerHeight)%dat(nLayers), & ! intent(in): [dp] total soil depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat(nSnow+1:nLayers),& ! intent(in): [dp(:)] depth of each soil layer (m) + + ! input: diagnostic variables + surfaceHydCond => flux_data%var(iLookFLUX%mLayerSatHydCondMP)%dat(1), & ! intent(in): [dp] saturated hydraulic conductivity at the surface (m s-1) + mLayerColumnInflow => flux_data%var(iLookFLUX%mLayerColumnInflow)%dat, & ! intent(in): [dp(:)] inflow into each soil layer (m3/s) + + ! input: local attributes + HRUarea => attr_data%var(iLookATTR%HRUarea), & ! intent(in): [dp] HRU area (m2) + tan_slope => attr_data%var(iLookATTR%tan_slope), & ! intent(in): [dp] tan water table slope, taken as tan local ground surface slope (-) + contourLength => attr_data%var(iLookATTR%contourLength), & ! intent(in): [dp] length of contour at downslope edge of HRU (m) + + ! input: baseflow parameters + zScale_TOPMODEL => mpar_data%var(iLookPARAM%zScale_TOPMODEL)%dat(1), & ! intent(in): [dp] TOPMODEL exponent (-) + kAnisotropic => mpar_data%var(iLookPARAM%kAnisotropic)%dat(1), & ! intent(in): [dp] anisotropy factor for lateral hydraulic conductivity (- + fieldCapacity => mpar_data%var(iLookPARAM%fieldCapacity)%dat(1), & ! intent(in): [dp] field capacity (-) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): [dp(:)] soil porosity (-) + + ! output: diagnostic variables + scalarExfiltration => flux_data%var(iLookFLUX%scalarExfiltration)%dat(1), & ! intent(out):[dp] exfiltration from the soil profile (m s-1) + mLayerColumnOutflow => flux_data%var(iLookFLUX%mLayerColumnOutflow)%dat & ! intent(out):[dp(:)] column outflow from each soil layer (m3 s-1) + + ) ! end association to variables in data structures + + ! *********************************************************************************************************************** + ! (1) compute the baseflow flux in each soil layer + ! *********************************************************************************************************************** + + ! compute the maximum transmissivity + ! NOTE: this can be done as a pre-processing step + tran0 = kAnisotropic*surfaceHydCond*soilDepth/zScale_TOPMODEL ! maximum transmissivity (m2 s-1) + + ! compute the water table thickness (m) and transmissivity in each layer (m2 s-1) + do iLayer=nSoil,ixSaturation,-1 ! loop through "active" soil layers, from lowest to highest + ! define drainable water in each layer (m) + activePorosity = theta_sat(iLayer) - fieldCapacity ! "active" porosity (-) + drainableWater = mLayerDepth(iLayer)*(max(0._rkind,mLayerVolFracLiq(iLayer) - fieldCapacity))/activePorosity + ! compute layer transmissivity + if(iLayer==nSoil)then + zActive(iLayer) = drainableWater ! water table thickness associated with storage in a given layer (m) + trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL ! total transmissivity for total depth zActive (m2 s-1) + trSoil(iLayer) = trTotal(iLayer) ! transmissivity of water in a given layer (m2 s-1) + else + zActive(iLayer) = zActive(iLayer+1) + drainableWater + trTotal(iLayer) = tran0*(zActive(iLayer)/soilDepth)**zScale_TOPMODEL + trSoil(iLayer) = trTotal(iLayer) - trTotal(iLayer+1) + end if + end do ! looping through soil layers + + ! set un-used portions of the vectors to zero + if(ixSaturation>1)then + zActive(1:ixSaturation-1) = 0._rkind + trTotal(1:ixSaturation-1) = 0._rkind + trSoil(1:ixSaturation-1) = 0._rkind + end if + + ! compute the outflow from each layer (m3 s-1) + mLayerColumnOutflow(1:nSoil) = trSoil(1:nSoil)*tan_slope*contourLength + + ! compute total column inflow and total column outflow (m s-1) + totalColumnInflow = sum(mLayerColumnInflow(1:nSoil))/HRUarea + totalColumnOutflow = sum(mLayerColumnOutflow(1:nSoil))/HRUarea + + ! compute the available storage (m) + availStorage = sum(mLayerDepth(1:nSoil)*(theta_sat - (mLayerVolFracLiq(1:nSoil)+mLayerVolFracIce(1:nSoil))) ) + + ! compute the smoothing function (-) + if(availStorage < xMinEval)then + ! (compute the logistic function) + expF = exp((availStorage - xCenter)/xWidth) + logF = 1._rkind / (1._rkind + expF) + ! (compute the derivative in the logistic function w.r.t. volumetric liquid water content in each soil layer) + dLogFunc_dLiq(1:nSoil) = mLayerDepth(1:nSoil)*(expF/xWidth)/(1._rkind + expF)**2._rkind + else + logF = 0._rkind + dLogFunc_dLiq(:) = 0._rkind + end if + + ! compute the exfiltartion (m s-1) + if(totalColumnInflow > totalColumnOutflow .and. logF > tiny(1._rkind))then + scalarExfiltration = logF*(totalColumnInflow - totalColumnOutflow) ! m s-1 + else + scalarExfiltration = 0._rkind + end if + + ! compute the baseflow in each layer (m s-1) + mLayerBaseflow(1:nSoil) = (mLayerColumnOutflow(1:nSoil) - mLayerColumnInflow(1:nSoil))/HRUarea + + ! compute the total baseflow + qbTotal = sum(mLayerBaseflow) + + ! add exfiltration to the baseflow flux at the top layer + mLayerBaseflow(1) = mLayerBaseflow(1) + scalarExfiltration + mLayerColumnOutflow(1) = mLayerColumnOutflow(1) + scalarExfiltration*HRUarea + + ! test + if(printFlag)then + xDepth = sum(mLayerDepth(ixSaturation:nSoil)*(mLayerVolFracLiq(ixSaturation:nSoil) - fieldCapacity))/sum(theta_sat(ixSaturation:nSoil) - fieldCapacity) ! "effective" water table thickness (m) + xTran = tran0*(xDepth/soilDepth)**zScale_TOPMODEL ! transmissivity for the entire aquifer (m2 s-1) + xFlow = xTran*tan_slope*contourLength/HRUarea ! total column outflow (m s-1) + print*, 'ixSaturation = ', ixSaturation + write(*,'(a,1x,5(f30.20,1x))') 'tran0, soilDepth = ', tran0, soilDepth + write(*,'(a,1x,5(f30.20,1x))') 'surfaceHydCond, zScale_TOPMODEL = ', surfaceHydCond, zScale_TOPMODEL + write(*,'(a,1x,5(f30.20,1x))') 'xDepth, zActive(ixSaturation) = ', xDepth, zActive(ixSaturation) + write(*,'(a,1x,5(f30.20,1x))') 'xTran, trTotal(ixSaturation) = ', xTran, trTotal(ixSaturation) + write(*,'(a,1x,5(f30.20,1x))') 'xFlow, totalColumnOutflow = ', xFlow, sum(mLayerColumnOutflow(:))/HRUarea + !pause 'check groundwater' + end if + + ! *********************************************************************************************************************** + ! (2) compute the derivative in the baseflow flux w.r.t. volumetric liquid water content (m s-1) + ! *********************************************************************************************************************** + + ! initialize the derivative matrix + dBaseflow_dVolLiq(:,:) = 0._rkind + + ! check if derivatives are actually required + if(.not.derivDesired) return + + ! compute ratio of hillslope width to hillslope area (m m-2) + length2area = tan_slope*contourLength/HRUarea + + ! compute the ratio of layer depth to maximum water holding capacity (-) + depth2capacity(1:nSoil) = mLayerDepth(1:nSoil)/sum( (theta_sat(1:nSoil) - fieldCapacity)*mLayerDepth(1:nSoil) ) + + ! compute the change in dimensionless flux w.r.t. change in dimensionless storage (-) + dXdS(1:nSoil) = zScale_TOPMODEL*(zActive(1:nSoil)/SoilDepth)**(zScale_TOPMODEL - 1._rkind) + + ! loop through soil layers + do iLayer=1,nSoil + ! compute diagonal terms (s-1) + dBaseflow_dVolLiq(iLayer,iLayer) = tran0*dXdS(iLayer)*depth2capacity(iLayer)*length2area + ! compute off-diagonal terms + do jLayer=iLayer+1,nSoil ! (only dependent on layers below) + dBaseflow_dVolLiq(iLayer,jLayer) = tran0*(dXdS(iLayer) - dXdS(iLayer+1))*depth2capacity(jLayer)*length2area + end do ! looping through soil layers + end do ! looping through soil layers + + ! compute the derivative in the exfiltration flux w.r.t. volumetric liquid water content (m s-1) + if(qbTotal < 0._rkind)then + do iLayer=1,nSoil + dExfiltrate_dVolLiq(iLayer) = dBaseflow_dVolLiq(iLayer,iLayer)*logF + dLogFunc_dLiq(iLayer)*qbTotal + end do ! looping through soil layers + dBaseflow_dVolLiq(1,1:nSoil) = dBaseflow_dVolLiq(1,1:nSoil) - dExfiltrate_dVolLiq(1:nSoil) + end if + + ! end association to data in structures + end associate + +end subroutine computeBaseflow end module groundwatr_module diff --git a/build/source/engine/snowLiqFlx.f90 b/build/source/engine/snowLiqFlx.f90 index 48566b9ccfd840778a30f1254914beb9b4adaf47..e31612523722a9a7622570594ea15b933c0c73ac 100644 --- a/build/source/engine/snowLiqFlx.f90 +++ b/build/source/engine/snowLiqFlx.f90 @@ -20,92 +20,92 @@ module snowLiqFlx_module - ! access modules - USE nrtype ! numerical recipes data types - USE multiconst,only:iden_ice,iden_water ! intrinsic density of ice and water (kg m-3) - - ! access missing values - USE globalData,only:integerMissing ! missing integer - USE globalData,only:realMissing ! missing real number - - ! named variables - USE var_lookup,only:iLookINDEX ! named variables for structure elements - USE var_lookup,only:iLookPARAM ! named variables for structure elements - USE var_lookup,only:iLookPROG ! named variables for structure elements - USE var_lookup,only:iLookDIAG ! named variables for structure elements - - ! data types - USE data_types,only:var_d ! x%var(:) (rkind) - USE data_types,only:var_dlength ! x%var(:)%dat (rkind) - USE data_types,only:var_ilength ! x%var(:)%dat (i4b) - - ! privacy +! access modules +USE nrtype ! numerical recipes data types +USE multiconst,only:iden_ice,iden_water ! intrinsic density of ice and water (kg m-3) + +! access missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! named variables +USE var_lookup,only:iLookINDEX ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDIAG ! named variables for structure elements + +! data types +USE data_types,only:var_d ! x%var(:) (rkind) +USE data_types,only:var_dlength ! x%var(:)%dat (rkind) +USE data_types,only:var_ilength ! x%var(:)%dat (i4b) + +! privacy +implicit none +private +public::snowLiqFlx +public::snowLiqFlxSundials +contains + + +! ************************************************************************************************ +! public subroutine snowLiqFlx: compute liquid water flux through the snowpack +! ************************************************************************************************ +subroutine snowLiqFlx(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + ! input: model state vector + mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + indx_data, & ! intent(in): model indices + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: fluxes and derivatives + iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err,message) ! intent(out): error control implicit none - private - public::snowLiqFlx - public::snowLiqFlxSundials - contains - - - ! ************************************************************************************************ - ! public subroutine snowLiqFlx: compute liquid water flux through the snowpack - ! ************************************************************************************************ - subroutine snowLiqFlx(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,message) ! intent(out): error control - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - logical(lgt),intent(in) :: firstFluxCall ! the first flux call - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: forcing for the snow domain - real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) - ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - type(var_ilength),intent(in) :: indx_data ! model indices - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes and derivatives - real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! local variables - integer(i4b) :: i ! search index for scalar solution - integer(i4b) :: iLayer ! layer index - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: multResid ! multiplier for the residual water content (-) - real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) - real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) - real(rkind) :: availCap ! available storage capacity [0,1] (-) - real(rkind) :: relSaturn ! relative saturation [0,1] (-) - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! make association of local variables with information in the data structures - associate(& + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + logical(lgt),intent(in) :: firstFluxCall ! the first flux call + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: forcing for the snow domain + real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) + ! input: model state vector + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + ! output: fluxes and derivatives + real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) + real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! local variables + integer(i4b) :: i ! search index for scalar solution + integer(i4b) :: iLayer ! layer index + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(rkind) :: multResid ! multiplier for the residual water content (-) + real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) + real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) + real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) + real(rkind) :: availCap ! available storage capacity [0,1] (-) + real(rkind) :: relSaturn ! relative saturation [0,1] (-) + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! make association of local variables with information in the data structures + associate(& ! input: layer indices ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain @@ -117,151 +117,140 @@ module snowLiqFlx_module ! input/output: diagnostic variables -- only computed for the first iteration mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='snowLiqFlx/' - - ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & - size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then - err=20; message=trim(message)//'size mismatch of input/output vectors'; return - end if - - ! check the meltwater exponent is >=1 - if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if - - ! get the indices for the snow+soil layers - ixTop = integerMissing - if(scalarSolution)then - ! WARNING: Previously this was implemented as: - ! ixLayerDesired = pack(ixLayerState, ixSnowOnlyHyd/=integerMissing) - ! ixTop = ixLayerDesired(1) - ! ixBot = ixLayerDesired(1) - ! This implementation can result in a segfault when using JRDN layering. - ! The segfault occurs when trying to access `mw_exp` in: - ! iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - ! Debugging found that the `pack` statement caused `mw_exp` to no longer be accessible. - ! We have not been able to determine the underlying reason for this segfault. - do i=1,size(ixSnowOnlyHyd) - if(ixSnowOnlyHyd(i) /= integerMissing)then - ixTop=ixLayerState(i) - ixBot=ixTop - exit ! break out of loop once found - endif - end do - if(ixTop == integerMissing)then - err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return + ) ! association of local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='snowLiqFlx/' + + ! check that the input vectors match nSnow + if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & + size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then + err=20; message=trim(message)//'size mismatch of input/output vectors'; return end if - else - ixTop = 1 - ixBot = nSnow - endif - - ! define the liquid flux at the upper boundary (m s-1) - iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacobSundials_module - - ! compute properties fixed over the time step - if(firstFluxCall)then - ! loop through snow layers - do iLayer=1,nSnow - ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) - ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) - ! compute the residual volumetric liquid water content (-) - mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid - end do ! (looping through snow layers) - end if ! (if the first flux call) - - ! compute fluxes - do iLayer=ixTop,ixBot ! (loop through snow layers) - ! check that flow occurs - if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then - ! compute the relative saturation (-) - availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity - relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation - iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems - ! ** allow liquid water to pass through under very high ice density - iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. - end if - else ! flow does not occur - iLayerLiqFluxSnow(iLayer) = 0._rkind - iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind - endif ! storage above residual content - end do ! loop through snow layers - - ! end association of local variables with information in the data structures - end associate - - end subroutine snowLiqFlx - - - - ! ************************************************************************************************ - ! public subroutine snowLiqFlxSundials: compute liquid water flux through the snowpack - ! ************************************************************************************************ - subroutine snowLiqFlxSundials(& - ! input: model control - nSnow, & ! intent(in): number of snow layers - firstFluxCall, & ! intent(in): the first flux call - scalarSolution, & ! intent(in): flag to indicate the scalar solution - ! input: forcing for the snow domain - scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) - scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) - ! input: model state vector - mLayerVolFracIce, & ! intent(in) - mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - indx_data, & ! intent(in): model indices - mpar_data, & ! intent(in): model parameters - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: fluxes and derivatives - iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) - iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - err,message) ! intent(out): error control - implicit none - ! input: model control - integer(i4b),intent(in) :: nSnow ! number of snow layers - logical(lgt),intent(in) :: firstFluxCall ! the first flux call - logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution - ! input: forcing for the snow domain - real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) - real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) - ! input: model state vector - real(rkind),intent(in) :: mLayerVolFracIce(:) - real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) - ! input-output: data structures - type(var_ilength),intent(in) :: indx_data ! model indices - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - ! output: fluxes and derivatives - real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) - real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! local variables - integer(i4b) :: i ! search index for scalar solution - integer(i4b) :: iLayer ! layer index - integer(i4b) :: ixTop ! top layer in subroutine call - integer(i4b) :: ixBot ! bottom layer in subroutine call - real(rkind) :: multResid ! multiplier for the residual water content (-) - real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) - real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) - real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) - real(rkind) :: availCap ! available storage capacity [0,1] (-) - real(rkind) :: relSaturn ! relative saturation [0,1] (-) - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! make association of local variables with information in the data structures - associate(& + + ! check the meltwater exponent is >=1 + if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if + + ! get the indices for the snow+soil layers + ixTop = integerMissing + if(scalarSolution)then + do i=1,size(ixSnowOnlyHyd) + if(ixSnowOnlyHyd(i) /= integerMissing)then + ixTop=ixLayerState(i) + ixBot=ixTop + exit ! break out of loop once found + endif + end do + if(ixTop == integerMissing)then + err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return + end if + else + ixTop = 1 + ixBot = nSnow + endif + + ! define the liquid flux at the upper boundary (m s-1) + iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water + iLayerLiqFluxSnowDeriv(0) = 0._rkind !computed inside computJacobSundials_module + + ! compute properties fixed over the time step + if(firstFluxCall)then + ! loop through snow layers + do iLayer=1,nSnow + ! compute the reduction in liquid water holding capacity at high snow density (-) + multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) + ! compute the pore space (-) + mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) + ! compute the residual volumetric liquid water content (-) + mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid + end do ! (looping through snow layers) + end if ! (if the first flux call) + + ! compute fluxes + do iLayer=ixTop,ixBot ! (loop through snow layers) + ! check that flow occurs + if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then + ! compute the relative saturation (-) + availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity + relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation + iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp + iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) + if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems + ! ** allow liquid water to pass through under very high ice density + iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. + end if + else ! flow does not occur + iLayerLiqFluxSnow(iLayer) = 0._rkind + iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind + endif ! storage above residual content + end do ! loop through snow layers + + ! end association of local variables with information in the data structures + end associate + +end subroutine snowLiqFlx + +! ************************************************************************************************ +! public subroutine snowLiqFlxSundials: compute liquid water flux through the snowpack +! ************************************************************************************************ +subroutine snowLiqFlxSundials(& + ! input: model control + nSnow, & ! intent(in): number of snow layers + firstFluxCall, & ! intent(in): the first flux call + scalarSolution, & ! intent(in): flag to indicate the scalar solution + ! input: forcing for the snow domain + scalarThroughfallRain, & ! intent(in): rain that reaches the snow surface without ever touching vegetation (kg m-2 s-1) + scalarCanopyLiqDrainage, & ! intent(in): liquid drainage from the vegetation canopy (kg m-2 s-1) + ! input: model state vector + mLayerVolFracIce, & ! intent(in) + mLayerVolFracLiqTrial, & ! intent(in): trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + indx_data, & ! intent(in): model indices + mpar_data, & ! intent(in): model parameters + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: fluxes and derivatives + iLayerLiqFluxSnow, & ! intent(inout): vertical liquid water flux at layer interfaces (m s-1) + iLayerLiqFluxSnowDeriv, & ! intent(inout): derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + err,message) ! intent(out): error control + implicit none + ! input: model control + integer(i4b),intent(in) :: nSnow ! number of snow layers + logical(lgt),intent(in) :: firstFluxCall ! the first flux call + logical(lgt),intent(in) :: scalarSolution ! flag to denote if implementing the scalar solution + ! input: forcing for the snow domain + real(rkind),intent(in) :: scalarThroughfallRain ! computed throughfall rate (kg m-2 s-1) + real(rkind),intent(in) :: scalarCanopyLiqDrainage ! computed drainage of liquid water (kg m-2 s-1) + ! input: model state vector + real(rkind),intent(in) :: mLayerVolFracIce(:) + real(rkind),intent(in) :: mLayerVolFracLiqTrial(:) ! trial value of volumetric fraction of liquid water at the current iteration (-) + ! input-output: data structures + type(var_ilength),intent(in) :: indx_data ! model indices + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + ! output: fluxes and derivatives + real(rkind),intent(inout) :: iLayerLiqFluxSnow(0:) ! vertical liquid water flux at layer interfaces (m s-1) + real(rkind),intent(inout) :: iLayerLiqFluxSnowDeriv(0:) ! derivative in vertical liquid water flux at layer interfaces (m s-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! local variables + integer(i4b) :: i ! search index for scalar solution + integer(i4b) :: iLayer ! layer index + integer(i4b) :: ixTop ! top layer in subroutine call + integer(i4b) :: ixBot ! bottom layer in subroutine call + real(rkind) :: multResid ! multiplier for the residual water content (-) + real(rkind),parameter :: residThrs=550._rkind ! ice density threshold to reduce residual liquid water content (kg m-3) + real(rkind),parameter :: residScal=10._rkind ! scaling factor for residual liquid water content reduction factor (kg m-3) + real(rkind),parameter :: maxVolIceContent=0.7_rkind ! maximum volumetric ice content to store water (-) + real(rkind) :: availCap ! available storage capacity [0,1] (-) + real(rkind) :: relSaturn ! relative saturation [0,1] (-) + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! make association of local variables with information in the data structures + associate(& ! input: layer indices ixLayerState => indx_data%var(iLookINDEX%ixLayerState)%dat, & ! intent(in): list of indices for all model layers ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat, & ! intent(in): index in the state subset for hydrology state variables in the snow domain @@ -272,88 +261,75 @@ module snowLiqFlx_module ! input/output: diagnostic variables -- only computed for the first iteration mLayerPoreSpace => diag_data%var(iLookDIAG%mLayerPoreSpace)%dat, & ! intent(inout): pore space in each snow layer (-) mLayerThetaResid => diag_data%var(iLookDIAG%mLayerThetaResid)%dat & ! intent(inout): esidual volumetric liquid water content in each snow layer (-) - ) ! association of local variables with information in the data structures - ! ------------------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='snowLiqFlxSundials/' - - ! check that the input vectors match nSnow - if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & - size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then - err=20; message=trim(message)//'size mismatch of input/output vectors'; return - end if - - ! check the meltwater exponent is >=1 - if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if - - ! get the indices for the snow+soil layers - ixTop = integerMissing - if(scalarSolution)then - ! WARNING: Previously this was implemented as: - ! ixLayerDesired = pack(ixLayerState, ixSnowOnlyHyd/=integerMissing) - ! ixTop = ixLayerDesired(1) - ! ixBot = ixLayerDesired(1) - ! This implementation can result in a segfault when using JRDN layering. - ! The segfault occurs when trying to access `mw_exp` in: - ! iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - ! Debugging found that the `pack` statement caused `mw_exp` to no longer be accessible. - ! We have not been able to determine the underlying reason for this segfault. - do i=1,size(ixSnowOnlyHyd) - if(ixSnowOnlyHyd(i) /= integerMissing)then - ixTop=ixLayerState(i) - ixBot=ixTop - exit ! break out of loop once found - endif - end do - if(ixTop == integerMissing)then - err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return + ) ! association of local variables with information in the data structures + ! ------------------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='snowLiqFlxSundials/' + + ! check that the input vectors match nSnow + if(size(mLayerVolFracLiqTrial)/=nSnow .or. size(mLayerVolFracIce)/=nSnow .or. & + size(iLayerLiqFluxSnow)/=nSnow+1 .or. size(iLayerLiqFluxSnowDeriv)/=nSnow+1) then + err=20; message=trim(message)//'size mismatch of input/output vectors'; return end if - else - ixTop = 1 - ixBot = nSnow - endif - - ! define the liquid flux at the upper boundary (m s-1) - iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water - iLayerLiqFluxSnowDeriv(0) = 0._rkind - - ! compute properties fixed over the time step - if(firstFluxCall)then - ! loop through snow layers - do iLayer=1,nSnow - ! compute the reduction in liquid water holding capacity at high snow density (-) - multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) - ! compute the pore space (-) - mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) - ! compute the residual volumetric liquid water content (-) - mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid - end do ! (looping through snow layers) - end if ! (if the first flux call) - - ! compute fluxes - do iLayer=ixTop,ixBot ! (loop through snow layers) - ! check that flow occurs - if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then - ! compute the relative saturation (-) - availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity - relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation - iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp - iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) - if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems - ! ** allow liquid water to pass through under very high ice density - iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. - end if - else ! flow does not occur - iLayerLiqFluxSnow(iLayer) = 0._rkind - iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind - endif ! storage above residual content - end do ! loop through snow layers - - ! end association of local variables with information in the data structures - end associate - - end subroutine snowLiqFlxSundials - - - end module snowLiqFlx_module - \ No newline at end of file + + ! check the meltwater exponent is >=1 + if(mw_exp<1._rkind)then; err=20; message=trim(message)//'meltwater exponent < 1'; return; end if + + ! get the indices for the snow+soil layers + ixTop = integerMissing + if(scalarSolution)then + do i=1,size(ixSnowOnlyHyd) + if(ixSnowOnlyHyd(i) /= integerMissing)then + ixTop=ixLayerState(i) + ixBot=ixTop + exit ! break out of loop once found + endif + end do + if(ixTop == integerMissing)then + err=20; message=trim(message)//'Unable to identify snow layer for scalar solution!'; return + end if + else + ixTop = 1 + ixBot = nSnow + endif + + ! define the liquid flux at the upper boundary (m s-1) + iLayerLiqFluxSnow(0) = (scalarThroughfallRain + scalarCanopyLiqDrainage)/iden_water + iLayerLiqFluxSnowDeriv(0) = 0._rkind + + ! compute properties fixed over the time step + if(firstFluxCall)then + ! loop through snow layers + do iLayer=1,nSnow + ! compute the reduction in liquid water holding capacity at high snow density (-) + multResid = 1._rkind / ( 1._rkind + exp( (mLayerVolFracIce(iLayer)*iden_ice - residThrs) / residScal) ) + ! compute the pore space (-) + mLayerPoreSpace(iLayer) = 1._rkind - mLayerVolFracIce(iLayer) + ! compute the residual volumetric liquid water content (-) + mLayerThetaResid(iLayer) = Fcapil*mLayerPoreSpace(iLayer) * multResid + end do ! (looping through snow layers) + end if ! (if the first flux call) + + ! compute fluxes + do iLayer=ixTop,ixBot ! (loop through snow layers) + ! check that flow occurs + if(mLayerVolFracLiqTrial(iLayer) > mLayerThetaResid(iLayer))then + ! compute the relative saturation (-) + availCap = mLayerPoreSpace(iLayer) - mLayerThetaResid(iLayer) ! available capacity + relSaturn = (mLayerVolFracLiqTrial(iLayer) - mLayerThetaResid(iLayer)) / availCap ! relative saturation + iLayerLiqFluxSnow(iLayer) = k_snow*relSaturn**mw_exp + iLayerLiqFluxSnowDeriv(iLayer) = ( (k_snow*mw_exp)/availCap ) * relSaturn**(mw_exp - 1._rkind) + if(mLayerVolFracIce(iLayer) > maxVolIceContent)then ! NOTE: use start-of-step ice content, to avoid convergence problems + ! ** allow liquid water to pass through under very high ice density + iLayerLiqFluxSnow(iLayer) = iLayerLiqFluxSnow(iLayer) + iLayerLiqFluxSnow(iLayer-1) !NOTE: derivative may need to be updated in future. + end if + else ! flow does not occur + iLayerLiqFluxSnow(iLayer) = 0._rkind + iLayerLiqFluxSnowDeriv(iLayer) = 0._rkind + endif ! storage above residual content + end do ! loop through snow layers + ! end association of local variables with information in the data structures + end associate + +end subroutine snowLiqFlxSundials +end module snowLiqFlx_module