diff --git a/build/source/engine/sundials/t2enthalpy.f90 b/build/source/engine/sundials/t2enthalpy.f90 index 4d5cd5b22639740a3abc8c8e305446806d2f6832..3b26cd21b38b41dba11deef9950950d5e0041049 100644 --- a/build/source/engine/sundials/t2enthalpy.f90 +++ b/build/source/engine/sundials/t2enthalpy.f90 @@ -22,10 +22,10 @@ module t2enthalpy_module ! constants USE multiconst, only: gravity, & ! gravitational acceleration (m s-1) - Tfreeze, & ! freezing point of water (K) - Cp_soil,Cp_water,Cp_ice,Cp_air,& ! specific heat of soil, water and ice (J kg-1 K-1) - iden_water,iden_ice,iden_air,& ! intrinsic density of water and ice (kg m-3) - LH_fus ! latent heat of fusion (J kg-1) + Tfreeze, & ! freezing point of water (K) + Cp_soil,Cp_water,Cp_ice,Cp_air,& ! specific heat of soil, water and ice (J kg-1 K-1) + iden_water,iden_ice,iden_air,& ! intrinsic density of water and ice (kg m-3) + LH_fus ! latent heat of fusion (J kg-1) ! data types USE nrtype @@ -68,14 +68,14 @@ public::t2enthalpy_T ! define the look-up table used to compute temperature based on enthalpy contains - + ! ************************************************************************************************************************ ! public subroutine T2E_lookup: define a look-up table to compute enthalpy based on temperature ! ************************************************************************************************************************ subroutine T2E_lookup(nSoil, & ! intent(in): number of soil layers - mpar_data, & ! intent(in): parameter data structure - lookup_data, & ! intent(inout): lookup table data structure - err,message) + mpar_data, & ! intent(in): parameter data structure + lookup_data, & ! intent(inout): lookup table data structure + err,message) USE nr_utility_module,only:arth ! use to build vectors with regular increments USE spline_int_module,only:spline,splint ! use for cubic spline interpolation USE soil_utils_module,only:volFracLiq ! use to compute the volumetric fraction of liquid water @@ -142,662 +142,638 @@ subroutine T2E_lookup(nSoil, & ! intent(in): number of ! loop through soil layers do iSoil=1,nSoil - + ! ----- ! * make association to variables in the data structures... ! --------------------------------------------------------- - + associate(& - - ! associate model parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for freezing (K-1) - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(iSoil) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(iSoil) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(iSoil) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(iSoil) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(iSoil) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! compute vGn_m - vGn_m = 1._rkind - 1._rkind/vGn_n - + + ! associate model parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) , & ! scaling parameter for freezing (K-1) + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(iSoil) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(iSoil) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(iSoil) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(iSoil) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(iSoil) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(iSoil)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(iSoil)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(iSoil)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! compute vGn_m + vGn_m = 1._rkind - 1._rkind/vGn_n + ! ----- ! * populate the lookup table... ! ------------------------------ - + ! initialize temperature and enthalpy Tk(nLook) = Tfreeze Ey(nLook) = 0._rkind - + ! loop through lookup table do iLook=(nLook-1),1,-1 - - ! update temperature and enthalpy - Tk(iLook) = Tk(iLook+1) - Ey(iLook) = Ey(iLook+1) - - ! get the temperature increment for the numerical integration - T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) - - ! numerical integration between different values of the lookup table - do jIntegr8=1,nIntegr8 - - ! update temperature - Tk(iLook) = Tk(iLook) + T_incr - - ! compute the volumetric liquid water and ice content - ! NOTE: assume saturation - matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze - vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = theta_sat - vFracLiq - - ! compute enthalpy - ! NOTE: assume intrrinsic density of ice is the intrinsic density of water - ! NOTE: kg m-3 J kg-1 K-1 K - Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*vFracIce*T_incr - - end do ! numerical integration - + + ! update temperature and enthalpy + Tk(iLook) = Tk(iLook+1) + Ey(iLook) = Ey(iLook+1) + + ! get the temperature increment for the numerical integration + T_incr = (xTemp(iLook)-xTemp(iLook+1))/real(nIntegr8, kind(rkind)) + + ! numerical integration between different values of the lookup table + do jIntegr8=1,nIntegr8 + + ! update temperature + Tk(iLook) = Tk(iLook) + T_incr + + ! compute the volumetric liquid water and ice content + ! NOTE: assume saturation + matricHead = (LH_fus/gravity)*(Tk(iLook) - Tfreeze)/Tfreeze + vFracLiq = volFracLiq(matricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = theta_sat - vFracLiq + + ! compute enthalpy + ! NOTE: assume intrrinsic density of ice is the intrinsic density of water + ! NOTE: kg m-3 J kg-1 K-1 K + Ey(iLook) = Ey(iLook) + iden_water*Cp_water*vFracLiq*T_incr + iden_water*Cp_ice*vFracIce*T_incr + + end do ! numerical integration + end do ! loop through lookup table - + ! use cubic spline interpolation to obtain enthalpy values at the desired values of temperature call spline(Tk,Ey,1.e30_rkind,1.e30_rkind,E2,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! check - !print*, 'Tk = ', Tk - !print*, 'Ey = ', Ey - !print*, 'E2 = ', E2 - + ! test if(doTest)then - - ! calculate enthalpy - call splint(Tk,Ey,E2,T_test,E_test,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! write values - print*, 'doTest = ', doTest - print*, 'T_test = ', T_test ! temperature (K) - print*, 'E_test = ', E_test ! enthalpy (J m-3) - print*, 'theta_sat = ', theta_sat ! soil porosity (-) - print*, 'theta_res = ', theta_res ! volumetric residual water content (-) - print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) - print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) - print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' - read(*,*) - + + ! calculate enthalpy + call splint(Tk,Ey,E2,T_test,E_test,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! write values + print*, 'doTest = ', doTest + print*, 'T_test = ', T_test ! temperature (K) + print*, 'E_test = ', E_test ! enthalpy (J m-3) + print*, 'theta_sat = ', theta_sat ! soil porosity (-) + print*, 'theta_res = ', theta_res ! volumetric residual water content (-) + print*, 'vGn_alpha = ', vGn_alpha ! van Genuchten "alpha" parameter (m-1) + print*, 'vGn_n = ', vGn_n ! van Genuchten "n" parameter (-) + print*, trim(message)//'PAUSE: Set doTest=.false. to complete simulations' + read(*,*) + endif ! if testing - - ! end asssociation to variables in the data structures - end associate - - end do ! (looping through soil layers) - end subroutine T2E_lookup - - - ! ************************************************************************************************************************ - ! public subroutine t2enthalpy: compute enthalpy from temperature and total water content - ! ************************************************************************************************************************ - subroutine t2enthalpy(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation - implicit none - ! delare dummy variables - ! ------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) - ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: psiLiq ! matric head of liquid water (m) - real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: vFracIce ! volumetric fraction of ice (-) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(rkind) :: enthWater ! enthalpy of total water (J m-3) - real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) - real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: diffT - real(rkind) :: integral - real(rkind) :: enthIce - real(rkind) :: enthVeg - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="t2enthalpy/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - + + ! end asssociation to variables in the data structures + end associate + + end do ! (looping through soil layers) +end subroutine T2E_lookup + + +! ************************************************************************************************************************ +! public subroutine t2enthalpy: compute enthalpy from temperature and total water content +! ************************************************************************************************************************ +subroutine t2enthalpy(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: state variables for the vegetation canopy + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + ! output: enthalpy + real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: psiLiq ! matric head of liquid water (m) + real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(rkind) :: enthWater ! enthalpy of total water (J m-3) + real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) + real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + real(rkind) :: diffT + real(rkind) :: integral + real(rkind) :: enthIce + real(rkind) :: enthVeg + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="t2enthalpy/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + ! ----- ! - compute indices... ! -------------------- - + ! get domain type, and index of the control volume within the domain ixFullVector = ixMapSubset2Full(iState) ! index within full state vector ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - + ! check an energy state if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then - - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! identify domain - select case(ixDomainType) - - ! ----- - ! - canopy air space... - ! --------------------- - case(iname_cas) - scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) - ! ----- - ! - vegetation canopy... - ! ----------------------- - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - if(diffT>=0._rkind)then - enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._rkind - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth - end if - - scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - enthPhase - - ! end association - end associate vegVars - - ! ----- - ! - snow... - ! --------- - case(iname_snow) - - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._rkind)then - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._rkind - enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) - end if - - mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - enthPhase - - ! end association - end associate snowVars - - ! ----- - ! - soil... - ! --------- - case(iname_soil) - - ! make association to variables in the data structures... - soilVars: associate(& - - ! associate model parameters - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - - - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer) > Tcrit)then - enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also - enthPhase = 0._rkind - - ! *** compute enthalpy of water for frozen conditions - else - ! calculate enthalpy at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate enthalpy at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) - enthWater = enthMix + enthLiq - - ! *** compute the enthalpy associated with phase change - psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) - vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = vFracWat - vFracLiq - enthPhase = iden_water*LH_fus*vFracIce - - endif ! (if frozen conditions) - - ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) - - ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) - - ! *** compute the total enthalpy (J m-3) - mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - enthPhase - - ! print*, iLayer, enthSoil, enthWater, enthAir, enthPhase - - end associate soilVars - - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + + ! ----- + ! - canopy air space... + ! --------------------- + case(iname_cas) + scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) + ! ----- + ! - vegetation canopy... + ! ----------------------- + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = scalarCanopyTempTrial - Tfreeze + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + if(diffT>=0._rkind)then + enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth + enthIce = 0._rkind + enthPhase = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth + end if + + scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - enthPhase + + ! end association + end associate vegVars + + ! ----- + ! - snow... + ! --------- + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = mLayerTempTrial(iLayer) - Tfreeze + if(diffT>=0._rkind)then + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT + enthIce = 0._rkind + enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT + enthPhase = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) + end if + + mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - enthPhase + + ! end association + end associate snowVars + + ! ----- + ! - soil... + ! --------- + case(iname_soil) + + ! make association to variables in the data structures... + soilVars: associate(& + + ! associate model parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTempTrial(iLayer) > Tcrit)then + enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also + enthPhase = 0._rkind + + ! *** compute enthalpy of water for frozen conditions + else + ! calculate enthalpy at the temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate enthalpy at the critical temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate the enthalpy of water + enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix + enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) + enthWater = enthMix + enthLiq + + ! *** compute the enthalpy associated with phase change + psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) + vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = vFracWat - vFracLiq + enthPhase = iden_water*LH_fus*vFracIce + + endif ! (if frozen conditions) + + ! *** compute the enthalpy of soil + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the enthalpy of air + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the total enthalpy (J m-3) + mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - enthPhase + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + end if ! if an energy layer - end do ! looping through state variables - - end associate generalVars - - end subroutine t2enthalpy - - - ! ************************************************************************************************************************ - ! public subroutine t2enthalpy_T: compute enthalpy from temperature and total water content - ! ************************************************************************************************************************ - subroutine t2enthalpy_T(& - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - mpar_data, & ! intent(in): parameter data structure - indx_data, & ! intent(in): model indices - lookup_data, & ! intent(in): lookup table data structure - ! input: state variables for the vegetation canopy - scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) - scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) - mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) - mLayerVolFracIceTrial, & ! intent(in) - ! output: enthalpy - scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) - scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) - mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) - ! output: error control - err,message) ! intent(out): error control - ! ------------------------------------------------------------------------------------------------------------------------- - ! downwind routines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE spline_int_module,only:splint ! use for cubic spline interpolation - implicit none - ! delare dummy variables - ! ------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model indices - type(zLookup),intent(in) :: lookup_data ! lookup tables - ! input: state variables for the vegetation canopy - real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! input: variables for the snow-soil domain - real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) - ! output: enthalpy - real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) - real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! ------------------------------------------------------------------------------------------------------------------------- - ! declare local variables - character(len=128) :: cmessage ! error message in downwind routine - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: ixFullVector ! index within full state vector - integer(i4b) :: ixDomainType ! name of a given model domain - integer(i4b) :: ixControlIndex ! index within a given model domain - real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - real(rkind) :: psiLiq ! matric head of liquid water (m) - real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) - real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: vFracIce ! volumetric fraction of ice (-) - real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) - real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) - real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) - real(rkind) :: enthWater ! enthalpy of total water (J m-3) - real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) - real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) - real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) - real(rkind) :: enthAir ! enthalpy of air (J m-3) - real(rkind) :: diffT - real(rkind) :: integral - real(rkind) :: enthIce - real(rkind) :: enthVeg - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - generalVars: associate(& - ! number of model layers, and layer type - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers - ! mapping between the full state vector and the state subset - ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector - ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset - ! type of domain, type of state variable, and index of control volume within domain - ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables - ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) - ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) - ! snow parameters - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message="t2enthalpy_T/" - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - + end do ! looping through state variables + + end associate generalVars + +end subroutine t2enthalpy + + +! ************************************************************************************************************************ +! public subroutine t2enthalpy_T: compute enthalpy from temperature and total water content +! ************************************************************************************************************************ +subroutine t2enthalpy_T(& + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + mpar_data, & ! intent(in): parameter data structure + indx_data, & ! intent(in): model indices + lookup_data, & ! intent(in): lookup table data structure + ! input: state variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(in): trial value of canopy air temperature (K) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(in): trial value of canopy total water (kg m-2) + scalarCanopyIceTrial, & ! intent(in): trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + mLayerTempTrial, & ! intent(in): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(in): trial vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): trial vector of total water matric potential (m) + mLayerVolFracIceTrial, & ! intent(in) + ! output: enthalpy + scalarCanairEnthalpy, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + err,message) ! intent(out): error control + ! ------------------------------------------------------------------------------------------------------------------------- + ! downwind routines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water + USE spline_int_module,only:splint ! use for cubic spline interpolation + implicit none + ! delare dummy variables + ! ------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model indices + type(zLookup),intent(in) :: lookup_data ! lookup tables + ! input: state variables for the vegetation canopy + real(rkind),intent(in) :: scalarCanairTempTrial ! trial value of canopy air temperature (K) + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(in) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(in) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! input: variables for the snow-soil domain + real(rkind),intent(in) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(in) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(in) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(in) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric fraction of Ice (-) + ! output: enthalpy + real(rkind),intent(out) :: scalarCanairEnthalpy ! enthalpy of the canopy air space (J m-3) + real(rkind),intent(out) :: scalarCanopyEnthalpy ! enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(out) :: mLayerEnthalpy(:) ! enthalpy of each snow+soil layer (J m-3) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! ------------------------------------------------------------------------------------------------------------------------- + ! declare local variables + character(len=128) :: cmessage ! error message in downwind routine + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixDomainType ! name of a given model domain + integer(i4b) :: ixControlIndex ! index within a given model domain + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + real(rkind) :: psiLiq ! matric head of liquid water (m) + real(rkind) :: vFracWat ! volumetric fraction of total water, liquid+ice (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: enthTemp ! enthalpy at the temperature of the control volume (J m-3) + real(rkind) :: enthTcrit ! enthalpy at the critical temperature where all water is unfrozen (J m-3) + real(rkind) :: enthPhase ! enthalpy associated with phase change (J m-3) + real(rkind) :: enthWater ! enthalpy of total water (J m-3) + real(rkind) :: enthSoil ! enthalpy of soil particles (J m-3) + real(rkind) :: enthMix ! enthalpy of the mixed region, liquid+ice (J m-3) + real(rkind) :: enthLiq ! enthalpy of the liquid region (J m-3) + real(rkind) :: enthAir ! enthalpy of air (J m-3) + real(rkind) :: diffT + real(rkind) :: integral + real(rkind) :: enthIce + real(rkind) :: enthVeg + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + generalVars: associate(& + ! number of model layers, and layer type + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] total number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] total number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of snow and soil layers + ! mapping between the full state vector and the state subset + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset for each state in the full state vector + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! type of domain, type of state variable, and index of control volume within domain + ixDomainType_subset => indx_data%var(iLookINDEX%ixDomainType_subset)%dat ,& ! intent(in): [i4b(:)] [state subset] id of domain for desired model state variables + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (iname_nrgLayer...) + ! snow parameters + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message="t2enthalpy_T/" + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + ! ----- ! - compute indices... ! -------------------- - + ! get domain type, and index of the control volume within the domain ixFullVector = ixMapSubset2Full(iState) ! index within full state vector ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain - + ! check an energy state if(ixStateType(ixFullVector)==iname_nrgCanair .or. ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer)then - - ! get the layer index - select case(ixDomainType) - case(iname_cas); iLayer = integerMissing - case(iname_veg); iLayer = integerMissing - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - - ! identify domain - select case(ixDomainType) - - ! ----- - ! - canopy air space... - ! --------------------- - case(iname_cas) - scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) - ! ----- - ! - vegetation canopy... - ! ----------------------- - case(iname_veg) - ! association to necessary variables for vegetation - vegVars: associate(& - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = scalarCanopyTempTrial - Tfreeze - enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth - if(diffT>=0._rkind)then - enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth - enthIce = 0._rkind - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth - enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth - enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth - end if - - scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce - - ! end association - end associate vegVars - - ! ----- - ! - snow... - ! --------- - case(iname_snow) - - ! association to necessary variables for snow - snowVars: associate(& - snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) - - diffT = mLayerTempTrial(iLayer) - Tfreeze - if(diffT>=0._rkind)then - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT - enthIce = 0._rkind - enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT - enthPhase = 0._rkind - else - integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) - enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral - enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) - enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) - enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) - end if + + ! get the layer index + select case(ixDomainType) + case(iname_cas); iLayer = integerMissing + case(iname_veg); iLayer = integerMissing + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing (no thermodynamics in the aquifer) + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + + ! identify domain + select case(ixDomainType) + case(iname_cas) + scalarCanairEnthalpy = Cp_air * iden_air * (scalarCanairTempTrial - Tfreeze) + + case(iname_veg) + ! association to necessary variables for vegetation + vegVars: associate(& + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1), & ! intent(in): [dp] canopy depth (m) + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = scalarCanopyTempTrial - Tfreeze + enthVeg = specificHeatVeg * maxMassVegetation * diffT / canopyDepth + if(diffT>=0._rkind)then + enthLiq = Cp_water * scalarCanopyWatTrial * diffT / canopyDepth + enthIce = 0._rkind + enthPhase = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = Cp_water * scalarCanopyWatTrial * integral / canopyDepth + enthIce = Cp_ice * scalarCanopyWatTrial * ( diffT - integral ) / canopyDepth + enthPhase = LH_fus * scalarCanopyIceTrial / canopyDepth + end if - mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir - - ! end association - end associate snowVars - - ! ----- - ! - soil... - ! --------- - case(iname_soil) - - ! make association to variables in the data structures... - soilVars: associate(& - - ! associate model parameters - soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) - - ! associate values in the lookup table - Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) - Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) - E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function - - ) ! end associate statement - - ! diagnostic variables - vGn_m = 1._rkind - 1._rkind/vGn_n - Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - - - ! *** compute enthalpy of water for unfrozen conditions - if(mlayerTempTrial(iLayer) > Tcrit)then - enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also - enthPhase = 0._rkind - - ! *** compute enthalpy of water for frozen conditions - else - ! calculate enthalpy at the temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate enthalpy at the critical temperature (cubic spline interpolation) - call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) - if(err/=0) then; message=trim(message)//trim(cmessage); return; end if - - ! calculate the enthalpy of water - enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix - enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) - enthWater = enthMix + enthLiq - - ! *** compute the enthalpy associated with phase change - psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) - vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - vFracIce = vFracWat - vFracLiq - enthPhase = iden_water*LH_fus*vFracIce - - endif ! (if frozen conditions) - - ! *** compute the enthalpy of soil - enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) - - ! *** compute the enthalpy of air - enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) - - ! *** compute the total enthalpy (J m-3) - mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir - - ! print*, iLayer, enthSoil, enthWater, enthAir, enthPhase - - end associate soilVars - - ! ----- - ! - checks... - ! ----------- - case(iname_aquifer); cycle ! aquifer: do nothing - case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return - end select - + scalarCanopyEnthalpy = enthVeg + enthLiq + enthIce + + end associate vegVars + + case(iname_snow) + + ! association to necessary variables for snow + snowVars: associate(& + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) + + diffT = mLayerTempTrial(iLayer) - Tfreeze + if(diffT>=0._rkind)then + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * diffT + enthIce = 0._rkind + enthAir = iden_air * Cp_air * ( 1._rkind - mLayerVolFracWatTrial(iLayer) ) * diffT + enthPhase = 0._rkind + else + integral = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * diffT) + enthLiq = iden_water * Cp_water * mLayerVolFracWatTrial(iLayer) * integral + enthIce = iden_water * Cp_ice * mLayerVolFracWatTrial(iLayer) * ( diffT - integral ) + enthAir = iden_air * Cp_air * ( diffT - mLayerVolFracWatTrial(iLayer) * ( (iden_water/iden_ice)*(diffT-integral) + integral ) ) + enthPhase = iden_ice * LH_fus * mLayerVolFracIceTrial(iLayer) + end if + + mLayerEnthalpy(iLayer) = enthLiq + enthIce + enthAir + + end associate snowVars + + case(iname_soil) + + ! make association to variables in the data structures... + soilVars: associate(& + + ! associate model parameters + soil_dens_intr => mpar_data%var(iLookPARAM%soil_dens_intr)%dat(ixControlIndex) , & ! intrinsic soil density (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat(ixControlIndex) , & ! soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat(ixControlIndex) , & ! volumetric residual water content (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat(ixControlIndex) , & ! van Genuchten "alpha" parameter (m-1) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat(ixControlIndex) , & ! van Genuchten "n" parameter (-) + + ! associate values in the lookup table + Tk => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%temperature)%lookup , & ! temperature (K) + Ey => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%enthalpy)%lookup , & ! enthalpy (J m-3) + E2 => lookup_data%z(ixControlIndex)%var(iLookLOOKUP%deriv2)%lookup & ! second derivative of the interpolating function + + ) ! end associate statement + + ! diagnostic variables + vGn_m = 1._rkind - 1._rkind/vGn_n + Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + vFracWat = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + + + ! *** compute enthalpy of water for unfrozen conditions + if(mlayerTempTrial(iLayer) > Tcrit)then + enthWater = iden_water*Cp_water*vFracWat*(mlayerTempTrial(iLayer) - Tfreeze) ! valid for temperatures below freezing also + enthPhase = 0._rkind + + ! *** compute enthalpy of water for frozen conditions + else + ! calculate enthalpy at the temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,mlayerTempTrial(iLayer),enthTemp,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate enthalpy at the critical temperature (cubic spline interpolation) + call splint(Tk,Ey,E2,Tcrit,enthTcrit,err,cmessage) + if(err/=0) then; message=trim(message)//trim(cmessage); return; end if + + ! calculate the enthalpy of water + enthMix = enthTemp - enthTcrit ! enthalpy of the liquid+ice mix + enthLiq = iden_water*Cp_water*vFracWat*(Tcrit - Tfreeze) + enthWater = enthMix + enthLiq + + ! *** compute the enthalpy associated with phase change + psiLiq = (mLayerTempTrial(iLayer) - Tfreeze)*LH_fus/(gravity*Tfreeze) + vFracLiq = volFracLiq(psiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + vFracIce = vFracWat - vFracLiq + enthPhase = iden_water*LH_fus*vFracIce + + endif ! (if frozen conditions) + + ! *** compute the enthalpy of soil + enthSoil = soil_dens_intr*Cp_soil*(1._rkind - theta_sat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the enthalpy of air + enthAir = iden_air*Cp_air*(1._rkind - theta_sat - vFracWat)*(mlayerTempTrial(iLayer) - Tfreeze) + + ! *** compute the total enthalpy (J m-3) + mLayerEnthalpy(iLayer) = enthSoil + enthWater + enthAir + + end associate soilVars + + ! ----- + ! - checks... + ! ----------- + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + end select + end if ! if an energy layer - end do ! looping through state variables - - end associate generalVars - - end subroutine t2enthalpy_T - - end module t2enthalpy_module - \ No newline at end of file + end do ! looping through state variables + + end associate generalVars + +end subroutine t2enthalpy_T + +end module t2enthalpy_module