! SUMMA - Structure for Unifying Multiple Modeling Alternatives ! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! ! For more information see: http://www.ral.ucar.edu/projects/summa ! ! This program is free software: you can redistribute it and/or modify ! it under the terms of the GNU General Public License as published by ! the Free Software Foundation, either version 3 of the License, or ! (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! You should have received a copy of the GNU General Public License ! along with this program. If not, see <http://www.gnu.org/licenses/>. module convE2Temp_module ! data types USE nrtype USE data_types,only:var_dlength ! data vector with variable length dimension (dp): x%var(:)%dat(:) ! constants USE multiconst, only: Tfreeze, & ! freezing point of water (K) Cp_soil,Cp_water,Cp_ice,& ! specific heat of soil, water and ice (J kg-1 K-1) LH_fus ! latent heat of fusion (J kg-1) ! indices within parameter structure USE var_lookup,only:iLookPARAM ! named variables to define structure element ! privacy implicit none private public::E2T_lookup public::E2T_nosoil public::temp2ethpy ! define the look-up table used to compute temperature based on enthalpy integer(i4b),parameter :: nlook=10001 ! number of elements in the lookup table real(dp),dimension(nlook),public :: E_lookup ! enthalpy values (J kg-1) real(dp),dimension(nlook),public :: T_lookup ! temperature values (K) contains ! ************************************************************************************************************************ ! public subroutine E2T_lookup: define a look-up table to compute specific enthalpy based on temperature, assuming no soil ! ************************************************************************************************************************ subroutine E2T_lookup(mpar_data,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 implicit none ! declare dummy variables type(var_dlength),intent(in) :: mpar_data ! model parameters 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 real(dp),parameter :: T_start=260.0_dp ! start temperature value where all liquid water is assumed frozen (K) real(dp) :: T_incr,E_incr ! temperature/enthalpy increments real(dp),dimension(nlook) :: Tk ! initial temperature vector real(dp),dimension(nlook) :: Ey ! initial enthalpy vector real(dp),parameter :: waterWght=1._dp ! weight applied to total water (kg m-3) --- cancels out real(dp),dimension(nlook) :: T2deriv ! 2nd derivatives of the interpolating function at tabulated points integer(i4b) :: ilook ! loop through lookup table ! initialize error control err=0; message="E2T_lookup/" ! associate associate( snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) ) ! define initial temperature vector T_incr = (Tfreeze - T_start) / real(nlook-1, kind(dp)) ! temperature increment Tk = arth(T_start,T_incr,nlook) ! ***** compute specific enthalpy (NOTE: J m-3 --> J kg-1) ***** do ilook=1,nlook Ey(ilook) = temp2ethpy(Tk(ilook),waterWght,snowfrz_scale)/waterWght ! (J m-3 --> J kg-1) end do ! define the final enthalpy vector E_incr = (-Ey(1)) / real(nlook-1, kind(dp)) ! enthalpy increment E_lookup = arth(Ey(1),E_incr,nlook) ! use cubic spline interpolation to obtain temperature values at the desired values of enthalpy call spline(Ey,Tk,1.e30_dp,1.e30_dp,T2deriv,err,cmessage) ! get the second derivatives if(err/=0) then; message=trim(message)//trim(cmessage); return; end if do ilook=1,nlook call splint(Ey,Tk,T2deriv,E_lookup(ilook),T_lookup(ilook),err,cmessage) if(err/=0) then; message=trim(message)//trim(cmessage); return; end if !write(*,'(i6,1x,2(f20.4,1x))') ilook, E_lookup(ilook), T_lookup(ilook) end do end associate end subroutine E2T_lookup ! ************************************************************************************************************************ ! public subroutine E2T_nosoil: compute temperature based on specific enthalpy -- appropriate when no dry mass, as in snow ! ************************************************************************************************************************ subroutine E2T_nosoil(Ey,BulkDenWater,fc_param,Tk,err,message) ! compute temperature based on enthalpy -- appropriate when no dry mass, as in snow implicit none ! declare dummy variables real(dp),intent(in) :: Ey ! total enthalpy (J m-3) real(dp),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(dp),intent(in) :: fc_param ! freezing curve parameter (K-1) real(dp),intent(out) :: Tk ! initial temperature guess / final temperature value (K) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! declare local variables real(dp),parameter :: dx=1.d-8 ! finite difference increment (J kg-1) real(dp),parameter :: atol=1.d-12 ! convergence criteria (J kg-1) real(dp) :: E_spec ! specific enthalpy (J kg-1) real(dp) :: E_incr ! enthalpy increment integer(i4b) :: niter=15 ! maximum number of iterations integer(i4b) :: iter ! iteration index integer(i4b) :: i0 ! position in lookup table real(dp) :: Tg0,Tg1 ! trial temperatures (K) real(dp) :: Ht0,Ht1 ! specific enthalpy, based on the trial temperatures (J kg-1) real(dp) :: f0,f1 ! function evaluations (difference between enthalpy guesses) real(dp) :: dh ! enthalpy derivative real(dp) :: dT ! temperature increment ! initialize error control err=0; message="E2T_nosoil/" ! convert input of total enthalpy (J m-3) to total specific enthalpy (J kg-1) E_spec = Ey/BulkDenWater ! (NOTE: no soil) !write(*,'(a,1x,10(e20.10,1x))') 'E_spec, E_lookup(1)', E_spec, E_lookup(1) ! ***** get initial guess and derivative assuming all water is frozen if(E_spec<E_lookup(1))then ! process cases below the limit of the look-up tab;e ! get temperature guess Tg0 = (E_spec - E_lookup(1))/Cp_ice + T_lookup(1) Tg1 = Tg0+dx ! compute enthalpy Ht0 = temp2ethpy(Tg0,1._dp,fc_param) Ht1 = temp2ethpy(Tg1,1._dp,fc_param) ! compute function evaluations f0 = Ht0 - E_spec f1 = Ht1 - E_spec ! ***** get initial guess and derivative from the look-up table else ! get enthalpy increment E_incr = E_lookup(2) - E_lookup(1) ! get position in lookup table i0 = ceiling( (E_spec - E_lookup(1)) / E_incr, kind(i4b) ) ! check found the appropriate value in the look-up table if(E_spec < E_lookup(i0) .or. E_spec > E_lookup(i0+1) .or. & i0 < 1 .or. i0+1 > nlook)then err=10; message=trim(message)//'problem finding appropriate value in lookup table'; return end if ! get temperature guess Tg0 = T_lookup(i0) Tg1 = T_lookup(i0+1) ! compute function evaluations f0 = E_lookup(i0) - E_spec f1 = E_lookup(i0+1) - E_spec end if ! compute initial derivative dh = (f1 - f0) / (Tg1 - Tg0) ! compute initial change in T dT = -f0/dh !write(*,'(a,1x,f12.5,1x,10(e20.10,1x))') 'Tg1, f0, f1, dh, dT = ', Tg1, f0, f1, dh, dT ! exit if already close enough if(abs(dT)<atol)then Tk = Tg0+dT return end if ! **** iterate a little do iter=1,niter ! comute new value of Tg Tg1 = Tg0+dT ! get new function evaluation Ht1 = temp2ethpy(Tg1,1._dp,fc_param) f1 = Ht1 - E_spec ! compute derivative if dT dh = (f1 - f0)/dT ! compute change in T dT = -f1/dh ! print progress !write(*,'(a,1x,i4,1x,f12.5,1x,10(e20.10,1x))') 'iter, Tg1, Ht1, f1, dh, dT = ', iter, Tg1, Ht1, f1, dh, dT ! exit if converged if(abs(dT)<atol)then Tk = Tg1+dT return end if ! get ready for next iteration -- save old function evaluation and temperature f0 = f1 Tg0 = Tg1 ! and check for convergence if(iter==niter)then; err=20; message=trim(message)//"failedToConverge"; return; end if end do ! (iteration loop) end subroutine E2T_nosoil ! ************************************************************************************************************************ ! public function temp2ethpy: compute total enthalpy based on temperature and mass (J m-3) ! ************************************************************************************************************************ function temp2ethpy(Tk,BulkDenWater,fc_param) ! used to compute enthalpy based on temperature and total mass in layer (snow or soil) ! NOTE: enthalpy is a relative value, defined as zero at Tfreeze where all water is liquid implicit none ! declare dummy variables real(dp),intent(in) :: Tk ! layer temperature (K) real(dp),intent(in) :: BulkDenWater ! bulk density of water (kg m-3) real(dp),intent(in) :: fc_param ! freezing curve parameter (K-1) real(dp) :: temp2ethpy ! return value of the function, total specific enthalpy (J m-3) ! declare local variables real(dp) :: frac_liq ! fraction of liquid water real(dp) :: enthTempWater ! temperature component of specific enthalpy for total water (liquid and ice) (J kg-1) real(dp) :: enthMass ! mass component of specific enthalpy (J kg-1) ! NOTE: this function assumes the freezing curve for snow ... it needs modification to use vanGenuchten functions for soil ! compute the fraction of liquid water in the given layer frac_liq = 1._dp / ( 1._dp + ( fc_param*( Tfreeze - min(Tk,Tfreeze) ) )**2._dp ) ! compute the temperature component of enthalpy for the soil constituent (J kg-1) !enthTempSoil = Cp_soil*(Tk - Tfreeze) ! compute the temperature component of enthalpy for total water (J kg-1) ! NOTE: negative enthalpy means require energy to bring to Tfreeze if(Tk< Tfreeze) enthTempWater = Cp_ice*(Tk - Tfreeze) - (Cp_water - Cp_ice)*(atan(fc_param*(Tfreeze - Tk))/fc_param) if(Tk>=Tfreeze) enthTempWater = Cp_water*(Tk - Tfreeze) ! compute the mass component of enthalpy -- energy required to melt ice (J kg-1) ! NOTE: negative enthalpy means require energy to bring to Tfreeze enthMass = -LH_fus*(1._dp - frac_liq) ! finally, compute the total enthalpy (J m-3) ! NOTE: this is the case for snow (no soil).. function needs modification to use vanGenuchten functions for soil temp2ethpy = BulkDenWater*(enthTempWater + enthMass) !+ BulkDenSoil*enthTempSoil end function temp2ethpy end module ConvE2Temp_module