diff --git a/build/includes/hru_actor/hru_actor_subroutine_wrappers.hpp b/build/includes/hru_actor/hru_actor_subroutine_wrappers.hpp index c625ca30cd0f9bea11b4ad86b3dea8aae5dd0099..fa1e20a93a6ba8dc443fbe4419e2aa7043e9b24f 100644 --- a/build/includes/hru_actor/hru_actor_subroutine_wrappers.hpp +++ b/build/includes/hru_actor/hru_actor_subroutine_wrappers.hpp @@ -22,6 +22,8 @@ extern "C" { void* attrStruct, void* typeStruct, void* idStruct, // primary data structures (variable length vectors) void* mparStruct, void* bparStruct, void* bvarStruct, void* dparStruct, + // lookup tables + void* lookupStruct, // local HRU data void* startTime, void* oldTime, // miscellaneous diff --git a/build/makefile_sundials b/build/makefile_sundials index fdb0785c1d581c58e6814372a0e206271c3d3acd..25b13bdadf751667eb365f8cd7853e6a7b1b60f0 100644 --- a/build/makefile_sundials +++ b/build/makefile_sundials @@ -198,9 +198,11 @@ SUMMA_NOAHMP= \ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) # Define routines for the SUMMA model runs +# sundials - t2enthalpy.f90 SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ + t2enthalpy.f90 \ updateVars.f90 \ var_derive.f90 \ read_forcingActors.f90 \ diff --git a/build/source/actors/hru_actor/hru_actor.cpp b/build/source/actors/hru_actor/hru_actor.cpp index 0a62507f66e85c3bae0febc950c3b573e9e39521..c8f0620cf73fa2a910b4fc1c95d22778d3bd1e18 100644 --- a/build/source/actors/hru_actor/hru_actor.cpp +++ b/build/source/actors/hru_actor/hru_actor.cpp @@ -198,6 +198,7 @@ void Initialize_HRU(stateful_actor<hru_state>* self) { self->state.handle_bparStruct, self->state.handle_bvarStruct, self->state.handle_dparStruct, + self->state.handle_lookupStruct, self->state.handle_startTime, self->state.handle_oldTime, &self->state.upArea, &self->state.err); diff --git a/build/source/driver/SummaActors_setup.f90 b/build/source/driver/SummaActors_setup.f90 index c2f860ee9bd07c6d3746c6d7aac7a00481034afb..daf2994a93afd8eff5196888aa22fb63f395bcfa 100755 --- a/build/source/driver/SummaActors_setup.f90 +++ b/build/source/driver/SummaActors_setup.f90 @@ -29,7 +29,8 @@ USE data_types,only:& var_i8, & ! x%var(:) (i8b) var_d, & ! x%var(:) (dp) var_ilength, & ! x%var(:)%dat (i4b) - var_dlength ! x%var(:)%dat (dp) + var_dlength, & ! x%var(:)%dat (dp) + zLookup ! access missing values USE globalData,only:integerMissing ! missing integer @@ -77,7 +78,8 @@ subroutine setupHRUParam(& handle_bparStruct, & ! basin-average parameters handle_bvarStruct, & ! basin-average variables handle_dparStruct, & ! default model parameters - ! local HRU data + handle_lookupStruct, & ! lookup tables + ! local HRU data handle_startTime, & ! start time for the model simulation handle_oldTime, & ! time for the previous model time step ! miscellaneous variables @@ -96,6 +98,7 @@ subroutine setupHRUParam(& USE pOverwrite_module,only:pOverwrite ! module to overwrite default parameter values with info from the Noah tables USE read_param4chm_module,only:read_param ! module to read model parameter sets USE ConvE2Temp_module,only:E2T_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion + USE t2enthalpy_module,only:T2E_lookup ! module to calculate a look-up table for the temperature-enthalpy conversion USE var_derive_module,only:fracFuture ! module to calculate the fraction of runoff in future time steps (time delay histogram) USE module_sf_noahmplsm,only:read_mp_veg_parameters ! module to read NOAH vegetation tables ! global data structures @@ -135,6 +138,7 @@ subroutine setupHRUParam(& type(c_ptr), intent(in), value :: handle_bparStruct ! basin-average parameters type(c_ptr), intent(in), value :: handle_bvarStruct ! basin-average variables type(c_ptr), intent(in), value :: handle_dparStruct ! default model parameters + type(c_ptr), intent(in), value :: handle_lookupStruct ! start time for the model simulation type(c_ptr), intent(in), value :: handle_startTime ! start time for the model simulation type(c_ptr), intent(in), value :: handle_oldTime ! time for the previous model time step real(c_double),intent(inout) :: upArea @@ -148,6 +152,7 @@ subroutine setupHRUParam(& type(var_d),pointer :: bparStruct ! basin-average parameters type(var_dlength),pointer :: bvarStruct ! basin-average variables type(var_d),pointer :: dparStruct ! default model parameters + type(zLookup),pointer :: lookupStruct ! default model parameters type(var_i),pointer :: startTime ! start time for the model simulation type(var_i),pointer :: oldTime ! time for the previous model time step character(len=256) :: message ! error message @@ -167,6 +172,7 @@ subroutine setupHRUParam(& call c_f_pointer(handle_bparStruct, bparStruct) call c_f_pointer(handle_bvarStruct, bvarStruct) call c_f_pointer(handle_dparStruct, dparStruct) + call c_f_pointer(handle_lookupStruct, lookupStruct) call c_f_pointer(handle_startTime, startTime) call c_f_pointer(handle_oldTime, oldTime) @@ -252,6 +258,13 @@ subroutine setupHRUParam(& return endif + ! calculate a lookup table to compute enthalpy from temperature + call T2E_lookup(gru_struc(indxGRU)%hruInfo(indxHRU)%nSoil, & ! intent(in): number of soil layers + mparStruct, & ! intent(in): parameter data structure + lookupStruct, & ! intent(inout): lookup table data structure + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + ! overwrite the vegetation height HVT(typeStruct%var(iLookTYPE%vegTypeIndex)) = mparStruct%var(iLookPARAM%heightCanopyTop)%dat(1) HVB(typeStruct%var(iLookTYPE%vegTypeIndex)) = mparStruct%var(iLookPARAM%heightCanopyBottom)%dat(1) diff --git a/build/source/engine/t2enthalpy.f90 b/build/source/engine/t2enthalpy.f90 new file mode 100644 index 0000000000000000000000000000000000000000..f773196b0f87a114446d49e52f149de00480e10d --- /dev/null +++ b/build/source/engine/t2enthalpy.f90 @@ -0,0 +1,803 @@ +! 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 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) + + ! data types + USE nrtype + USE data_types,only:var_iLength ! var(:)%dat(:) + USE data_types,only:var_dLength ! var(:)%dat(:) + USE data_types,only:zLookup ! z(:)%var(:)%lookup(:) + + ! indices within parameter structure + USE var_lookup,only:iLookPARAM ! named variables to define structure element + USE var_lookup,only:iLookINDEX ! named variables to define structure element + USE var_lookup,only:iLookLOOKUP ! named variables to define structure element + USE var_lookup,only:iLookDIAG ! named variables for structure elements + + ! data dimensions + USE var_lookup,only:maxvarLookup ! maximum number of variables in the lookup tables + + ! domain types + USE globalData,only:iname_cas ! named variables for canopy air space + USE globalData,only:iname_veg ! named variables for vegetation canopy + USE globalData,only:iname_snow ! named variables for snow + USE globalData,only:iname_soil ! named variables for soil + USE globalData,only:iname_aquifer ! named variables for the aquifer + + ! named variables to describe the state variable type + USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space + USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy + USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers + + ! missing values + USE globalData,only:integerMissing ! missing integer + USE globalData,only:realMissing ! missing real number + + ! privacy + implicit none + private + public::T2E_lookup + public::t2enthalpy + 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) + 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 + implicit none + ! declare dummy variables + integer(i4b),intent(in) :: nSoil + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(zLookup),intent(inout) :: lookup_data ! lookup tables + 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 + logical(lgt),parameter :: doTest=.false. ! flag to test + integer(i4b),parameter :: nLook=100 ! number of elements in the lookup table + integer(i4b),parameter :: nIntegr8=10000 ! number of points used in the numerical integration + real(rkind),parameter :: T_lower=260.0_rkind ! lowest temperature value where all liquid water is assumed frozen (K) + real(rkind),dimension(nLook) :: xTemp ! temporary vector + real(rkind) :: xIncr ! temporary increment + real(rkind) :: T_incr ! temperature increment + real(rkind),parameter :: T_test=272.9742_rkind ! test value for temperature (K) + real(rkind) :: E_test ! test value for enthalpy (J m-3) + integer(i4b) :: iVar ! loop through variables + integer(i4b) :: iSoil ! loop through soil layers + integer(i4b) :: iLook ! loop through lookup table + integer(i4b) :: jIntegr8 ! index for numerical integration + logical(lgt) :: check ! flag to check allocation + real(rkind) :: vGn_m ! van Genuchten "m" parameter (-) + real(rkind) :: vFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: vFracIce ! volumetric fraction of ice (-) + real(rkind) :: matricHead ! matric head (m) + ! initialize error control + err=0; message="T2E_lookup/" + + ! get the values of temperature for the lookup table + xIncr = 1._rkind/real(nLook-1, kind(rkind)) + xTemp = T_lower + (Tfreeze - T_lower)*arth(0._rkind,xIncr,nLook)**0.25_rkind ! use **0.25 to give more values near freezing + + ! ----- + ! * allocate space for the lookup table... + ! ---------------------------------------- + + ! initialize checks + check=.false. + + ! allocate space for soil layers + if(allocated(lookup_data%z))then; check=.true.; else; allocate(lookup_data%z(nSoil), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table z dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table z dimension dimension'; return; end if + + ! allocate space for the variables in the lookup table + do iSoil=1,nSoil + if(allocated(lookup_data%z(iSoil)%var))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(maxvarLookup), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table var dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table var dimension dimension'; return; end if + + ! allocate space for the values in the lookup table + do iVar=1,maxvarLookup + if(allocated(lookup_data%z(iSoil)%var(iVar)%lookup))then; check=.true.; else; allocate(lookup_data%z(iSoil)%var(iVar)%lookup(nLook), stat=err); endif + if(check) then; err=20; message=trim(message)//'lookup table value dimension was unexpectedly allocated already'; return; end if + if(err/=0)then; err=20; message=trim(message)//'problem allocating lookup table vaule dimension dimension'; return; end if + + end do ! (looping through variables) + end do ! (looping through soil layers) + + ! 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 + + ! ----- + ! * 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 + + 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(*,*) + + 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) + + ! ----- + ! - 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 + + 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) + + ! ----- + ! - 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 + + 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 + + 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