From 7d434e85bc5349307f55edfd243015c3260e74b6 Mon Sep 17 00:00:00 2001 From: Kyle <kyle.c.klenk@gmail.com> Date: Tue, 27 Sep 2022 18:44:28 +0000 Subject: [PATCH] copied from summa non actors --- .../engine/sundials/soil_utilsAddSundials.f90 | 331 +++++++++--------- 1 file changed, 161 insertions(+), 170 deletions(-) diff --git a/build/source/engine/sundials/soil_utilsAddSundials.f90 b/build/source/engine/sundials/soil_utilsAddSundials.f90 index 5e4df09..9f26a4f 100644 --- a/build/source/engine/sundials/soil_utilsAddSundials.f90 +++ b/build/source/engine/sundials/soil_utilsAddSundials.f90 @@ -20,216 +20,207 @@ module soil_utilsAddSundials_module - ! data types - USE nrtype - - USE multiconst,only: gravity, & ! acceleration of gravity (m s-2) - Tfreeze, & ! temperature at freezing (K) - LH_fus, & ! latent heat of fusion (J kg-1, or m2 s-2) - R_wv ! gas constant for water vapor (J kg-1 K-1; [J = Pa m3]) - USE soil_utils_module,only:matricHead - USE soil_utils_module,only:dPsi_dTheta - USE soil_utils_module,only:volFracLiq - USE soil_utils_module,only:dTheta_dPsi - - ! privacy +! data types +USE nrtype + +USE multiconst,only: gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + LH_fus, & ! latent heat of fusion (J kg-1, or m2 s-2) + R_wv ! gas constant for water vapor (J kg-1 K-1; [J = Pa m3]) +USE soil_utils_module,only:matricHead +USE soil_utils_module,only:dPsi_dTheta +USE soil_utils_module,only:volFracLiq +USE soil_utils_module,only:dTheta_dPsi + +! privacy +implicit none +private + +! routines to make public + +public::liquidHeadSundials +public::d2Theta_dPsi2 +public::d2Theta_dTk2 + +! constant parameters +real(rkind),parameter :: verySmall=epsilon(1.0_rkind) ! a very small number (used to avoid divide by zero) +contains + + +! ****************************************************************************************************************************** +! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) +! ****************************************************************************************************************************** +subroutine liquidHeadSundials(& + ! input + matricHeadTotal ,& ! intent(in) : total water matric potential (m) + matricHeadTotalPrime ,& ! intent(in) + volFracLiq ,& ! intent(in) : volumetric fraction of liquid water (-) + volFracIce ,& ! intent(in) : volumetric fraction of ice (-) + vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m,& ! intent(in) : soil parameters + dVolTot_dPsi0 ,& ! intent(in) : derivative in the soil water characteristic (m-1) + dTheta_dT ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) + tempPrime ,& ! intent(in) + volFracLiqPrime ,& ! intent(in) + volFracIcePrime ,& ! intent(in) + ! output + matricHeadLiq ,& ! intent(out) : liquid water matric potential (m) + matricHeadLiqPrime ,& ! intent(out) + dPsiLiq_dPsi0 ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + dPsiLiq_dTemp ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. temperature (m K-1) + err,message) ! intent(out) : error control + ! computes the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) implicit none - private - - ! routines to make public - - public::liquidHeadSundials - public::d2Theta_dPsi2 - public::d2Theta_dTk2 - - ! constant parameters - real(rkind),parameter :: verySmall=epsilon(1.0_rkind) ! a very small number (used to avoid divide by zero) - contains - - - ! ****************************************************************************************************************************** - ! public subroutine: compute the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) - ! ****************************************************************************************************************************** - subroutine liquidHeadSundials(& - ! input - matricHeadTotal ,& ! intent(in) : total water matric potential (m) - matricHeadTotalPrime ,& ! intent(in) - volFracLiq ,& ! intent(in) : volumetric fraction of liquid water (-) - volFracIce ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m,& ! intent(in) : soil parameters - dVolTot_dPsi0 ,& ! intent(in) : derivative in the soil water characteristic (m-1) - dTheta_dT ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - tempPrime ,& ! intent(in) - volFracLiqPrime ,& ! intent(in) - volFracIcePrime ,& ! intent(in) - ! output - matricHeadLiq ,& ! intent(out) : liquid water matric potential (m) - matricHeadLiqPrime ,& ! intent(out) - dPsiLiq_dPsi0 ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - dPsiLiq_dTemp ,& ! intent(out) : derivative in the liquid water matric potential w.r.t. temperature (m K-1) - err,message) ! intent(out) : error control - ! computes the liquid water matric potential (and the derivatives w.r.t. total matric potential and temperature) - implicit none - ! input - real(rkind),intent(in) :: matricHeadTotal ! total water matric potential (m) - real(rkind),intent(in) :: matricHeadTotalPrime - real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) - real(rkind),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters - real(rkind),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) - real(rkind),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) - real(rkind),intent(in) :: TempPrime - real(rkind),intent(in) :: volFracLiqPrime - real(rkind),intent(in) :: volFracIcePrime - ! output - real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) - real(rkind),intent(out) :: matricHeadLiqPrime - real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) - real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! local - real(rkind) :: xNum,xDen ! temporary variables (numeratir, denominator) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) - real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) - real(rkind) :: dEffSat_dFracLiq - real(rkind) :: effSatPrime - ! ------------------------------------------------------------------------------------------------------------------------------ - ! initialize error control - err=0; message='liquidHeadSundials/' - - ! ** partially frozen soil - if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated - - + ! input + real(rkind),intent(in) :: matricHeadTotal ! total water matric potential (m) + real(rkind),intent(in) :: matricHeadTotalPrime + real(rkind),intent(in) :: volFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(in) :: volFracIce ! volumetric fraction of ice (-) + real(rkind),intent(in) :: vGn_alpha,vGn_n,theta_sat,theta_res,vGn_m ! soil parameters + real(rkind),intent(in) ,optional :: dVolTot_dPsi0 ! derivative in the soil water characteristic (m-1) + real(rkind),intent(in) ,optional :: dTheta_dT ! derivative in volumetric total water w.r.t. temperature (K-1) + real(rkind),intent(in) :: TempPrime + real(rkind),intent(in) :: volFracLiqPrime + real(rkind),intent(in) :: volFracIcePrime + ! output + real(rkind),intent(out) :: matricHeadLiq ! liquid water matric potential (m) + real(rkind),intent(out) :: matricHeadLiqPrime + real(rkind),intent(out) ,optional :: dPsiLiq_dPsi0 ! derivative in the liquid water matric potential w.r.t. the total water matric potential (-) + real(rkind),intent(out) ,optional :: dPsiLiq_dTemp ! derivative in the liquid water matric potential w.r.t. temperature (m K-1) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! local + real(rkind) :: xNum,xDen ! temporary variables (numeratir, denominator) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: dPsiLiq_dEffSat ! derivative in liquid water matric potential w.r.t. effective saturation (m) + real(rkind) :: dEffSat_dTemp ! derivative in effective saturation w.r.t. temperature (K-1) + real(rkind) :: dEffSat_dFracLiq + real(rkind) :: effSatPrime + ! ------------------------------------------------------------------------------------------------------------------------------ + ! initialize error control + err=0; message='liquidHeadSundials/' + + ! ** partially frozen soil + if(volFracIce > verySmall .and. matricHeadTotal < 0._rkind)then ! check that ice exists and that the soil is unsaturated + + ! ----- ! - compute liquid water matric potential... ! ------------------------------------------ - + ! - compute effective saturation ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) xNum = volFracLiq - theta_res xDen = theta_sat - volFracIce - theta_res effSat = xNum/xDen ! effective saturation - + ! - matric head associated with liquid water matricHeadLiq = matricHead(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) ! argument is effective saturation, so theta_res=0 and theta_sat=1 if (effSat < 1._rkind .and. effSat > 0._rkind)then effSatPrime = (volFracLiqPrime * xDen + volFracIcePrime * xNum) / xDen**2._rkind matricHeadLiqPrime = -( 1._rkind/(vGn_alpha*vGn_n*vGn_m) ) * effSat**(-1._rkind-1._rkind/vGn_m) * ( effSat**(-1._rkind/vGn_m) - 1._rkind )**(-1._rkind+1._rkind/vGn_n) * effSatPrime else - matricHeadLiqPrime = 0._rkind + matricHeadLiqPrime = 0._rkind endif - - + + ! compute derivative in liquid water matric potential w.r.t. effective saturation (m) if(present(dPsiLiq_dPsi0).or.present(dPsiLiq_dTemp))then - dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) + dPsiLiq_dEffSat = dPsi_dTheta(effSat,vGn_alpha,0._rkind,1._rkind,vGn_n,vGn_m) endif - + ! ----- ! - compute derivative in the liquid water matric potential w.r.t. the total water matric potential... ! ---------------------------------------------------------------------------------------------------- - + ! check if the derivative is desired if(present(dPsiLiq_dTemp))then - ! (check required input derivative is present) - if(.not.present(dVolTot_dPsi0))then - message=trim(message)//'dVolTot_dPsi0 argument is missing' - err=20; return - endif - - ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) - dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) - ! matricHeadLiqPrime = dPsiLiq_dPsi0 * matricHeadTotalPrime - + ! (check required input derivative is present) + if(.not.present(dVolTot_dPsi0))then + message=trim(message)//'dVolTot_dPsi0 argument is missing' + err=20; return + endif + + ! (compute derivative in the liquid water matric potential w.r.t. the total water matric potential) + dPsiLiq_dPsi0 = dVolTot_dPsi0*dPsiLiq_dEffSat*xNum/(xDen**2._rkind) + endif ! if dPsiLiq_dTemp is desired - + ! ----- ! - compute the derivative in the liquid water matric potential w.r.t. temperature... ! ----------------------------------------------------------------------------------- - + ! check if the derivative is desired if(present(dPsiLiq_dTemp))then - - ! (check required input derivative is present) - if(.not.present(dTheta_dT))then - message=trim(message)//'dTheta_dT argument is missing' - err=20; return - endif - ! (compute the derivative in the liquid water matric potential w.r.t. temperature) - dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen - dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp - ! matricHeadLiqPrime = dPsiLiq_dTemp * tempPrime - - - + + ! (check required input derivative is present) + if(.not.present(dTheta_dT))then + message=trim(message)//'dTheta_dT argument is missing' + err=20; return + endif + ! (compute the derivative in the liquid water matric potential w.r.t. temperature) + dEffSat_dTemp = -dTheta_dT*xNum/(xDen**2._rkind) + dTheta_dT/xDen + dPsiLiq_dTemp = dPsiLiq_dEffSat*dEffSat_dTemp endif ! if dPsiLiq_dTemp is desired - - ! ** unfrozen soil - else ! (no ice) + + ! ** unfrozen soil + else ! (no ice) matricHeadLiq = matricHeadTotal matricHeadLiqPrime = matricHeadTotalPrime if(present(dPsiLiq_dTemp)) dPsiLiq_dPsi0 = 1._rkind ! derivative=1 because values are identical if(present(dPsiLiq_dTemp)) dPsiLiq_dTemp = 0._rkind ! derivative=0 because no impact of temperature for unfrozen conditions - end if ! (if ice exists) - - end subroutine liquidHeadSundials - - - ! ****************************************************************************************************************************** - ! public function d2Theta_dPsi2: compute the second derivative of the soil water characteristic (m-1) - ! ****************************************************************************************************************************** - function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) - implicit none - real(rkind),intent(in) :: psi ! soil water suction (m) - real(rkind),intent(in) :: alpha ! scaling parameter (m-1) - real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: d2Theta_dPsi2 ! derivative of the soil water characteristic (m-1) - real(rkind) :: mult_fcn - real(rkind) :: mult_fcnp - if(psi<0._rkind)then + end if ! (if ice exists) + +end subroutine liquidHeadSundials + +! ****************************************************************************************************************************** +! public function d2Theta_dPsi2: compute the second derivative of the soil water characteristic (m-1) +! ****************************************************************************************************************************** +function d2Theta_dPsi2(psi,alpha,theta_res,theta_sat,n,m) + implicit none + real(rkind),intent(in) :: psi ! soil water suction (m) + real(rkind),intent(in) :: alpha ! scaling parameter (m-1) + real(rkind),intent(in) :: theta_res ! residual volumetric water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: d2Theta_dPsi2 ! derivative of the soil water characteristic (m-1) + real(rkind) :: mult_fcn + real(rkind) :: mult_fcnp + if(psi<0._rkind)then mult_fcn = (-m*n*alpha*(alpha*psi)**(n-1._rkind)) * ( 1._rkind + (psi*alpha)**n )**(-1._rkind) mult_fcnp = -m*n*alpha*(n-1._rkind)*alpha*(alpha*psi)**(n-2._rkind)*( 1._rkind + (psi*alpha)**n )**(-1._rkind) - & ( n*alpha*(alpha*psi)**(n-1._rkind)*(1._rkind + (psi*alpha)**n)**(-2._rkind) ) * ( -m*n*alpha*(alpha*psi)**(n-1._rkind) ) d2Theta_dPsi2 = mult_fcn * dTheta_dPsi(psi,alpha,theta_res,theta_sat,n,m) + & mult_fcnp * ( volFracLiq(psi,alpha,theta_res,theta_sat,n,m) - theta_res ) - else + else d2Theta_dPsi2 = 0._rkind - end if - end function d2Theta_dPsi2 - - - ! ****************************************************************************************************************************** - ! public function d2Theta_dTk2: differentiate the freezing curve w.r.t. temperature - ! ****************************************************************************************************************************** - function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) - implicit none - real(rkind),intent(in) :: Tk ! temperature (K) - real(rkind),intent(in) :: theta_res ! residual liquid water content (-) - real(rkind),intent(in) :: theta_sat ! porosity (-) - real(rkind),intent(in) :: alpha ! vGn scaling parameter (m-1) - real(rkind),intent(in) :: n ! vGn "n" parameter (-) - real(rkind),intent(in) :: m ! vGn "m" parameter (-) - real(rkind) :: d2Theta_dTk2 ! derivative of the freezing curve w.r.t. temperature (K-1) - ! local variables - real(rkind) :: kappa ! constant (m K-1) - real(rkind) :: xtemp ! alpha*kappa*(Tk-Tfreeze) -- dimensionless variable (used more than once) - ! compute kappa (m K-1) - kappa = LH_fus/(gravity*Tfreeze) ! NOTE: J = kg m2 s-2 - ! define a tempory variable that is used more than once (-) - xtemp = alpha*kappa*(Tk-Tfreeze) - ! differentiate the freezing curve w.r.t. temperature -- making use of the chain rule - d2Theta_dTk2 = (-alpha*kappa*m*n*alpha*kappa)* (theta_sat - theta_res) * ( (n-1)*xtemp**(n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 1._rkind) & + end if +end function d2Theta_dPsi2 + +! ****************************************************************************************************************************** +! public function d2Theta_dTk2: differentiate the freezing curve w.r.t. temperature +! ****************************************************************************************************************************** +function d2Theta_dTk2(Tk,theta_res,theta_sat,alpha,n,m) + implicit none + real(rkind),intent(in) :: Tk ! temperature (K) + real(rkind),intent(in) :: theta_res ! residual liquid water content (-) + real(rkind),intent(in) :: theta_sat ! porosity (-) + real(rkind),intent(in) :: alpha ! vGn scaling parameter (m-1) + real(rkind),intent(in) :: n ! vGn "n" parameter (-) + real(rkind),intent(in) :: m ! vGn "m" parameter (-) + real(rkind) :: d2Theta_dTk2 ! derivative of the freezing curve w.r.t. temperature (K-1) + ! local variables + real(rkind) :: kappa ! constant (m K-1) + real(rkind) :: xtemp ! alpha*kappa*(Tk-Tfreeze) -- dimensionless variable (used more than once) + ! compute kappa (m K-1) + kappa = LH_fus/(gravity*Tfreeze) ! NOTE: J = kg m2 s-2 + ! define a tempory variable that is used more than once (-) + xtemp = alpha*kappa*(Tk-Tfreeze) + ! differentiate the freezing curve w.r.t. temperature -- making use of the chain rule + d2Theta_dTk2 = (-alpha*kappa*m*n*alpha*kappa)* (theta_sat - theta_res) * ( (n-1)*xtemp**(n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 1._rkind) & + n*(-m-1)*xtemp**(2*n - 2._rkind) * (1._rkind + xtemp**n)**(-m - 2._rkind) ) - end function d2Theta_dTk2 - - - end module soil_utilsAddSundials_module - \ No newline at end of file +end function d2Theta_dTk2 + +end module soil_utilsAddSundials_module -- GitLab