From 40cf8ac1177d9488d0fbe7d969a43e91ed55cafc Mon Sep 17 00:00:00 2001 From: Kyle <kyle.c.klenk@gmail.com> Date: Wed, 28 Sep 2022 17:59:19 +0000 Subject: [PATCH] copied from summa --- build/source/engine/eval8summa.f90 | 2 +- build/source/engine/updateVars.f90 | 1322 ++++++++++++++-------------- 2 files changed, 657 insertions(+), 667 deletions(-) diff --git a/build/source/engine/eval8summa.f90 b/build/source/engine/eval8summa.f90 index e6882cd..3067ecc 100755 --- a/build/source/engine/eval8summa.f90 +++ b/build/source/engine/eval8summa.f90 @@ -343,7 +343,7 @@ subroutine eval8summa(& if(stateVecTrial( ixSnowSoilHyd(iLayer) ) < xMin .or. stateVecTrial( ixSnowSoilHyd(iLayer) ) > xMax) message=trim(message)//'layer water outside bounds,' if(.not.feasible) write(*,'(a,1x,i4,1x,L1,1x,10(f20.10,1x))') 'iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax = ', iLayer, feasible, stateVecTrial( ixSnowSoilHyd(iLayer) ), xMin, xMax - endif ! if water states + endif ! if water states end do ! loop through non-missing hydrology state variables in the snow+soil domain diff --git a/build/source/engine/updateVars.f90 b/build/source/engine/updateVars.f90 index eadc29a..a796c8d 100755 --- a/build/source/engine/updateVars.f90 +++ b/build/source/engine/updateVars.f90 @@ -20,677 +20,670 @@ module updateVars_module - ! data types - USE nrtype - - ! missing values - USE globalData,only:integerMissing ! missing integer - USE globalData,only:realMissing ! missing real number - - ! access the global print flag - USE globalData,only:globalPrintFlag - - ! 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_watCanopy ! named variable defining the mass of total water on the vegetation canopy - USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy - USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers - USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers - USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers - USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers - USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers - - ! metadata for information in the data structures - USE globalData,only:indx_meta ! metadata for the variables in the index structure - - ! constants - USE multiconst,only:& - gravity, & ! acceleration of gravity (m s-2) - Tfreeze, & ! temperature at freezing (K) - Cp_air, & ! specific heat of air (J kg-1 K-1) - Cp_ice, & ! specific heat of ice (J kg-1 K-1) - Cp_water, & ! specific heat of liquid water (J kg-1 K-1) - LH_fus, & ! latent heat of fusion (J kg-1) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water ! intrinsic density of liquid water (kg m-3) - - ! provide access to the derived types to define the data structures - USE data_types,only:& - var_i, & ! data vector (i4b) - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - zLookup, & ! data vector with variable length dimension (rkind) - var_dlength ! data vector with variable length dimension (rkind) - - ! provide access to indices that define elements of the data structures - USE var_lookup,only:iLookDIAG ! named variables for structure elements - USE var_lookup,only:iLookPROG ! named variables for structure elements - USE var_lookup,only:iLookDERIV ! named variables for structure elements - USE var_lookup,only:iLookPARAM ! named variables for structure elements - USE var_lookup,only:iLookINDEX ! named variables for structure elements - - ! provide access to the routines to calculate enthalpy - USE t2enthalpy_module,only:t2enthalpy - - ! provide access to routines to update states - USE updatState_module,only:updateSnow ! update snow states - USE updatState_module,only:updateSoil ! update soil states - - ! provide access to functions for the constitutive functions and derivatives - USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) - USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) - USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential - - ! IEEE check - USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) - - implicit none - private - public::updateVars - - contains - - ! ********************************************************************************************************** - ! public subroutine updateVars: compute diagnostic variables and derivatives - ! ********************************************************************************************************** - subroutine updateVars(& - ! input - do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - lookup_data, & ! intent(in): lookup tables for a local HRU - mpar_data, & ! intent(in): model parameters for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - implicit none - ! input - logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze - type(zLookup), intent(in) :: lookup_data ! lookup tables for a local HRU - type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU - type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables - ! output: variables for the vegetation canopy - real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) - real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) - real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) - real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! general local variables - integer(i4b) :: iState ! index of model state variable - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - 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 - integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector - logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume - logical(lgt) :: isNrgState ! .true. if a given variable is an energy state - logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable - real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) - real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) - real(rkind) :: xTemp ! temporary temperature (K) - real(rkind) :: fLiq ! fraction of liquid water (-) - real(rkind) :: effSat ! effective saturation (-) - real(rkind) :: avPore ! available pore space (-) - character(len=256) :: cMessage ! error message of downwind routine - logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing - ! iterative solution for temperature - real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind) :: residual ! residual in the energy equation (J m-3) - real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) - real(rkind) :: tempInc ! iteration increment (K) - integer(i4b) :: iter ! iteration index - integer(i4b) :: niter ! number of iterations - integer(i4b),parameter :: maxiter=100 ! maximum number of iterations - real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) - real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) - real(rkind) :: critDiff ! temperature difference from critical (K) - real(rkind) :: tempMin ! minimum bracket for temperature (K) - real(rkind) :: tempMax ! maximum bracket for temperature (K) - logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section - real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - 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 - mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) - ! indices defining model states and layers - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) - ! indices in the full vector for specific domains - ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain - ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain - ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain - ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain - ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain - ! 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) - ! depth-varying model parameters - vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) - vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) - vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) - theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) - ! model diagnostic variables (heat capacity) - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) - scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) - mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) - ! model diagnostic variables (fraction of liquid water) - scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) - mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) - ! model states for the vegetation canopy - scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) - scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) - scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) - ! model state variable vectors for the snow-soil layers - mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) - mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) - mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) - mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) - ! model diagnostic variables from a previous solution - scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) - scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) - mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) - mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) - ! derivatives - dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential - dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) - dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature - mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature - dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature - ! derivatives inside solver for Jacobian only - dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential - dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content - dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature - dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature - mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature - scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) & ! intent(out): [dp ] timestep change in canopy temperature +! data types +USE nrtype + +! missing values +USE globalData,only:integerMissing ! missing integer +USE globalData,only:realMissing ! missing real number + +! access the global print flag +USE globalData,only:globalPrintFlag + +! 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_watCanopy ! named variable defining the mass of total water on the vegetation canopy +USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy +USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers +USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers +USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers +USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers +USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + +! metadata for information in the data structures +USE globalData,only:indx_meta ! metadata for the variables in the index structure + +! constants +USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_water, & ! specific heat of liquid water (J kg-1 K-1) + LH_fus, & ! latent heat of fusion (J kg-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + +! provide access to the derived types to define the data structures +USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + zLookup, & ! data vector with variable length dimension (rkind) + var_dlength ! data vector with variable length dimension (rkind) + +! provide access to indices that define elements of the data structures +USE var_lookup,only:iLookDIAG ! named variables for structure elements +USE var_lookup,only:iLookPROG ! named variables for structure elements +USE var_lookup,only:iLookDERIV ! named variables for structure elements +USE var_lookup,only:iLookPARAM ! named variables for structure elements +USE var_lookup,only:iLookINDEX ! named variables for structure elements + +! provide access to the routines to calculate enthalpy +USE t2enthalpy_module,only:t2enthalpy + +! provide access to routines to update states +USE updatState_module,only:updateSnow ! update snow states +USE updatState_module,only:updateSoil ! update soil states + +! provide access to functions for the constitutive functions and derivatives +USE snow_utils_module,only:fracliquid ! compute the fraction of liquid water (snow) +USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) +USE soil_utils_module,only:dTheta_dTk ! differentiate the freezing curve w.r.t. temperature (soil) +USE soil_utils_module,only:dTheta_dPsi ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:dPsi_dTheta ! derivative in the soil water characteristic (soil) +USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content +USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water +USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists +USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential + +! IEEE check +USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) + +implicit none +private +public::updateVars + +contains + +! ********************************************************************************************************** +! public subroutine updateVars: compute diagnostic variables and derivatives +! ********************************************************************************************************** +subroutine updateVars(& + ! input + do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + lookup_data, & ! intent(in): lookup tables for a local HRU + mpar_data, & ! intent(in): model parameters for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + scalarCanopyTempTrial, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatTrial, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqTrial, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIceTrial, & ! intent(inout): trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + mLayerTempTrial, & ! intent(inout): trial vector of layer temperature (K) + mLayerVolFracWatTrial, & ! intent(inout): trial vector of volumetric total water content (-) + mLayerVolFracLiqTrial, & ! intent(inout): trial vector of volumetric liquid water content (-) + mLayerVolFracIceTrial, & ! intent(inout): trial vector of volumetric ice water content (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + implicit none + ! input + logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze + type(zLookup), intent(in) :: lookup_data ! lookup tables for a local HRU + type(var_dlength),intent(in) :: mpar_data ! model parameters for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! diagnostic variables for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + ! output: variables for the vegetation canopy + real(rkind),intent(inout) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) + real(rkind),intent(inout) :: scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + real(rkind),intent(inout) :: scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + ! output: variables for the snow-soil domain + real(rkind),intent(inout) :: mLayerTempTrial(:) ! trial vector of layer temperature (K) + real(rkind),intent(inout) :: mLayerVolFracWatTrial(:) ! trial vector of volumetric total water content (-) + real(rkind),intent(inout) :: mLayerVolFracLiqTrial(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(inout) :: mLayerVolFracIceTrial(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(inout) :: mLayerMatricHeadTrial(:) ! trial vector of total water matric potential (m) + real(rkind),intent(inout) :: mLayerMatricHeadLiqTrial(:) ! trial vector of liquid water matric potential (m) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! general local variables + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + 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 + integer(i4b) :: ixOther,ixOtherLocal ! index of the coupled state variable within the (full, local) vector + logical(lgt) :: isCoupled ! .true. if a given variable shared another state variable in the same control volume + logical(lgt) :: isNrgState ! .true. if a given variable is an energy state + logical(lgt),allocatable :: computedCoupling(:) ! .true. if computed the coupling for a given state variable + real(rkind) :: scalarVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind) :: scalarVolFracIce ! volumetric fraction of ice (-) + real(rkind) :: Tcrit ! critical soil temperature below which ice exists (K) + real(rkind) :: xTemp ! temporary temperature (K) + real(rkind) :: fLiq ! fraction of liquid water (-) + real(rkind) :: effSat ! effective saturation (-) + real(rkind) :: avPore ! available pore space (-) + character(len=256) :: cMessage ! error message of downwind routine + logical(lgt),parameter :: printFlag=.false. ! flag to turn on printing + ! iterative solution for temperature + real(rkind) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind) :: residual ! residual in the energy equation (J m-3) + real(rkind) :: derivative ! derivative in the energy equation (J m-3 K-1) + real(rkind) :: tempInc ! iteration increment (K) + integer(i4b) :: iter ! iteration index + integer(i4b) :: niter ! number of iterations + integer(i4b),parameter :: maxiter=100 ! maximum number of iterations + real(rkind),parameter :: nrgConvTol=1.e-4_rkind ! convergence tolerance for energy (J m-3) + real(rkind),parameter :: tempConvTol=1.e-6_rkind ! convergence tolerance for temperature (K) + real(rkind) :: critDiff ! temperature difference from critical (K) + real(rkind) :: tempMin ! minimum bracket for temperature (K) + real(rkind) :: tempMax ! maximum bracket for temperature (K) + logical(lgt) :: bFlag ! flag to denote that iteration increment was constrained using bi-section + real(rkind),parameter :: epsT=1.e-7_rkind ! small interval above/below critical temperature (K) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + 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 + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! indices defining model states and layers + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in): [i4b] index of canopy hydrology state variable (mass) + ! indices in the full vector for specific domains + ixNrgCanair => indx_data%var(iLookINDEX%ixNrgCanair)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in canopy air space domain + ixNrgCanopy => indx_data%var(iLookINDEX%ixNrgCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the canopy domain + ixHydCanopy => indx_data%var(iLookINDEX%ixHydCanopy)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the canopy domain + ixNrgLayer => indx_data%var(iLookINDEX%ixNrgLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for energy states in the snow+soil domain + ixHydLayer => indx_data%var(iLookINDEX%ixHydLayer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for hydrology states in the snow+soil domain + ! 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) + ! depth-varying model parameters + vGn_m => diag_data%var(iLookDIAG%scalarVGn_m)%dat ,& ! intent(in): [dp(:)] van Genutchen "m" parameter (-) + vGn_n => mpar_data%var(iLookPARAM%vGn_n)%dat ,& ! intent(in): [dp(:)] van Genutchen "n" parameter (-) + vGn_alpha => mpar_data%var(iLookPARAM%vGn_alpha)%dat ,& ! intent(in): [dp(:)] van Genutchen "alpha" parameter (m-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat ,& ! intent(in): [dp(:)] soil porosity (-) + theta_res => mpar_data%var(iLookPARAM%theta_res)%dat ,& ! intent(in): [dp(:)] soil residual volumetric water content (-) + ! model diagnostic variables (heat capacity) + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp ] canopy depth (m) + scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in): [dp ] volumetric heat capacity of the vegetation (J m-3 K-1) + mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] volumetric heat capacity in each layer (J m-3 K-1) + ! model diagnostic variables (fraction of liquid water) + scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(out): [dp] fraction of liquid water on vegetation (-) + mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(out): [dp(:)] fraction of liquid water in each snow layer (-) + ! model states for the vegetation canopy + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(in): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(in): [dp] temperature of the vegetation canopy (K) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(in): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variable vectors for the snow-soil layers + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(in): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(in): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(in): [dp(:)] total water matric potential (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(in): [dp(:)] liquid water matric potential (m) + ! model diagnostic variables from a previous solution + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(in): [dp(:)] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(in): [dp(:)] mass of ice on the vegetation canopy (kg m-2) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(in): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(in): [dp(:)] volumetric fraction of ice (-) + ! derivatives + dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in total water content w.r.t. total water matric potential + dPsiLiq_dPsi0 => deriv_data%var(iLookDERIV%dPsiLiq_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in liquid water matric pot w.r.t. the total water matric pot (-) + dPsiLiq_dTemp => deriv_data%var(iLookDERIV%dPsiLiq_dTemp )%dat ,& ! intent(out): [dp(:)] derivative in the liquid water matric potential w.r.t. temperature + mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk)%dat ,& ! intent(out): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature + dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy)%dat(1) ,& ! intent(out): [dp] derivative of volumetric liquid water content w.r.t. temperature + ! derivatives inside solver for Jacobian only + dVolHtCapBulk_dPsi0 => deriv_data%var(iLookDERIV%dVolHtCapBulk_dPsi0 )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. matric potential + dVolHtCapBulk_dTheta => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTheta )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dCanWat => deriv_data%var(iLookDERIV%dVolHtCapBulk_dCanWat)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. volumetric water content + dVolHtCapBulk_dTk => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTk )%dat ,& ! intent(out): [dp(:)] derivative in bulk heat capacity w.r.t. temperature + dVolHtCapBulk_dTkCanopy => deriv_data%var(iLookDERIV%dVolHtCapBulk_dTkCanopy)%dat(1) ,& ! intent(out): [dp ] derivative in bulk heat capacity w.r.t. temperature + mLayerdTemp_dt => deriv_data%var(iLookDERIV%mLayerdTemp_dt )%dat ,& ! intent(out): [dp(:)] timestep change in layer temperature + scalarCanopydTemp_dt => deriv_data%var(iLookDERIV%scalarCanopydTemp_dt )%dat(1) & ! intent(out): [dp ] timestep change in canopy temperature ) ! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - - ! initialize error control - err=0; message='updateVars/' - - ! allocate space and assign values to the flag vector - allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable - if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif - computedCoupling(:)=.false. - - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) - + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='updateVars/' + + ! allocate space and assign values to the flag vector + allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif + computedCoupling(:)=.false. + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + ! check the need for the computations if(computedCoupling(iState)) cycle - + ! ----- ! - 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 - + ! get the layer index select case(ixDomainType) - case(iname_cas); cycle ! canopy air space: do nothing - case(iname_veg); iLayer = 0 - case(iname_snow); iLayer = ixControlIndex - case(iname_soil); iLayer = ixControlIndex + nSnow - 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 + case(iname_cas); cycle ! canopy air space: do nothing + case(iname_veg); iLayer = 0 + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + 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 index of the other (energy or mass) state variable within the full state vector select case(ixDomainType) - case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) - case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + case(iname_veg) ; ixOther = merge(ixHydCanopy(1), ixNrgCanopy(1), ixStateType(ixFullVector)==iname_nrgCanopy) + case(iname_snow, iname_soil); ixOther = merge(ixHydLayer(iLayer),ixNrgLayer(iLayer),ixStateType(ixFullVector)==iname_nrgLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select - + ! get the index in the local state vector ixOtherLocal = ixMapFull2Subset(ixOther) ! ixOtherLocal could equal integerMissing if(ixOtherLocal/=integerMissing) computedCoupling(ixOtherLocal)=.true. - + ! check if we have a coupled solution isCoupled = (ixOtherLocal/=integerMissing) - + ! check if we are an energy state isNrgState = (ixStateType(ixFullVector)==iname_nrgCanopy .or. ixStateType(ixFullVector)==iname_nrgLayer) - + if(printFlag)then - print*, 'iState = ', iState, size(ixMapSubset2Full) - print*, 'ixFullVector = ', ixFullVector - print*, 'ixDomainType = ', ixDomainType - print*, 'ixControlIndex = ', ixControlIndex - print*, 'ixOther = ', ixOther - print*, 'ixOtherLocal = ', ixOtherLocal - print*, 'do_adjustTemp = ', do_adjustTemp - print*, 'isCoupled = ', isCoupled - print*, 'isNrgState = ', isNrgState + print*, 'iState = ', iState, size(ixMapSubset2Full) + print*, 'ixFullVector = ', ixFullVector + print*, 'ixDomainType = ', ixDomainType + print*, 'ixControlIndex = ', ixControlIndex + print*, 'ixOther = ', ixOther + print*, 'ixOtherLocal = ', ixOtherLocal + print*, 'do_adjustTemp = ', do_adjustTemp + print*, 'isCoupled = ', isCoupled + print*, 'isNrgState = ', isNrgState endif - - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - + ! update hydrology state variables for the uncoupled solution if(.not.isNrgState .and. .not.isCoupled)then - - ! update the total water from volumetric liquid water - if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then - select case(ixDomainType) - case(iname_veg); scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial - case(iname_snow); mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water - case(iname_soil); mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return - end select - endif - - ! update the total water and the total water matric potential - if(ixDomainType==iname_soil)then - select case( ixStateType(ixFullVector) ) - ! --> update the total water from the liquid water matric potential - case(iname_lmpLayer) - effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation - avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space - mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) - ! --> update the total water from the total water matric potential - case(iname_matLayer) - mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) - case(iname_liqLayer, iname_watLayer) - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return - end select - endif ! if in the soil domain - + + ! update the total water from volumetric liquid water + if(ixStateType(ixFullVector)==iname_liqCanopy .or. ixStateType(ixFullVector)==iname_liqLayer)then + select case(ixDomainType) + case(iname_veg); scalarCanopyWatTrial = scalarCanopyLiqTrial + scalarCanopyIceTrial + case(iname_snow); mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water + case(iname_soil); mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return + end select + endif + + ! update the total water and the total water matric potential + if(ixDomainType==iname_soil)then + select case( ixStateType(ixFullVector) ) + ! --> update the total water from the liquid water matric potential + case(iname_lmpLayer) + effSat = volFracLiq(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex)) ! effective saturation + avPore = theta_sat(ixControlIndex) - mLayerVolFracIceTrial(iLayer) - theta_res(ixControlIndex) ! available pore space + mLayerVolFracLiqTrial(iLayer) = effSat*avPore + theta_res(ixControlIndex) + mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + !write(*,'(a,1x,i4,1x,3(f20.10,1x))') 'mLayerVolFracLiqTrial(iLayer) 1 = ', iLayer, mLayerVolFracLiqTrial(iLayer), mLayerVolFracIceTrial(iLayer), mLayerVolFracWatTrial(iLayer) + ! --> update the total water from the total water matric potential + case(iname_matLayer) + mLayerVolFracWatTrial(iLayer) = volFracLiq(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + ! --> update the total water matric potential (assume already have mLayerVolFracWatTrial given block above) + case(iname_liqLayer, iname_watLayer) + mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + case default; err=20; message=trim(message)//'expect iname_lmpLayer, iname_matLayer, iname_liqLayer, or iname_watLayer'; return + end select + endif ! if in the soil domain + endif ! if hydrology state variable or uncoupled solution - + ! compute the critical soil temperature below which ice exists select case(ixDomainType) - case(iname_veg, iname_snow); Tcrit = Tfreeze - case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + case(iname_veg, iname_snow); Tcrit = Tfreeze + case(iname_soil); Tcrit = crit_soilT( mLayerMatricHeadTrial(ixControlIndex) ) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select - + ! initialize temperature select case(ixDomainType) - case(iname_veg); xTemp = scalarCanopyTempTrial - case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + case(iname_veg); xTemp = scalarCanopyTempTrial + case(iname_snow, iname_soil); xTemp = mLayerTempTrial(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return end select - + ! define brackets for the root ! NOTE: start with an enormous range; updated quickly in the iterations tempMin = xTemp - 10._rkind tempMax = xTemp + 10._rkind - + ! get iterations (set to maximum iterations if adjusting the temperature) niter = merge(maxiter, 1, do_adjustTemp) - + ! iterate iterations: do iter=1,niter - - ! restrict temperature - if(xTemp <= tempMin .or. xTemp >= tempMax)then - xTemp = 0.5_rkind*(tempMin + tempMax) ! new value - bFlag = .true. - else - bFlag = .false. - endif - - ! ----- - ! - compute derivatives... - ! ------------------------ - - ! compute temperature time derivatives - select case(ixDomainType) - case(iname_veg); scalarCanopydTemp_dt = xTemp - scalarCanopyTemp - case(iname_snow, iname_soil); mLayerdTemp_dt(iLayer) = xTemp - mLayerTemp(iLayer) - end select - - ! compute the derivative in total water content w.r.t. total water matric potential (m-1) - ! NOTE 1: valid for frozen and unfrozen conditions - ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi - select case(ixDomainType) - case(iname_veg) - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - case(iname_snow) - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air - case(iname_soil) - select case( ixStateType(ixFullVector) ) - case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore - case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - end select - ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 - dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use - if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) - end select - - ! compute the derivative in liquid water content w.r.t. temperature - ! --> partially frozen: dependence of liquid water on temperature - if(xTemp<Tcrit)then - select case(ixDomainType) - case(iname_veg) - dTheta_dTkCanopy = dFracLiq_dTk(xTemp,snowfrz_scale)*scalarCanopyWat/(iden_water*canopyDepth) - dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy !same as snow but there is no derivative in air - case(iname_snow) - mLayerdTheta_dTk(iLayer) = dFracLiq_dTk(xTemp,snowfrz_scale)*mLayerVolFracWatTrial(iLayer) - dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - case(iname_soil) - mLayerdTheta_dTk(iLayer) = dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type - - ! --> unfrozen: no dependence of liquid water on temperature - else - select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type - endif - - ! ----- - ! - update volumetric fraction of liquid water and ice... - ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... - ! ----------------------------------------------------------------------------------------------- - - ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) - if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then - - ! compute the fraction of snow - select case(ixDomainType) - case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) - case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) - case(iname_soil) ! do nothing - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type - - ! ----- - ! - update volumetric fraction of liquid water and ice... - ! => case of energy state or coupled solution (or adjusting the temperature)... - ! -------------------------------------------------------------------------------- - - ! case of energy state OR coupled solution (or adjusting the temperature) - elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then - - ! identify domain type - select case(ixDomainType) - - ! *** vegetation canopy - case(iname_veg) - - ! compute volumetric fraction of liquid water and ice - call updateSnow(xTemp, & ! intent(in) : temperature (K) - scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - scalarVolFracLiq, & ! intent(out) : trial volumetric fraction of liquid water (-) - scalarVolFracIce, & ! intent(out) : trial volumetric fraction if ice (-) - scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! compute mass of water on the canopy - ! NOTE: possibilities for speed-up here - scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial !(kg m-2), scalarVolFracLiq*iden_water*canopyDepth - scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth - - ! *** snow layers - case(iname_snow) - - ! compute volumetric fraction of liquid water and ice - call updateSnow(xTemp, & ! intent(in) : temperature (K) - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! *** soil layers - case(iname_soil) - - ! compute volumetric fraction of liquid water and ice - call updateSoil(xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - err,cmessage) ! intent(out) : error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - ! check - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - - end select ! domain type - - ! final check - else - - ! do nothing (input = output) -- and check that we got here correctly - if( (isNrgState .or. isCoupled) )then - scalarVolFracLiq = realMissing - scalarVolFracIce = realMissing + + ! restrict temperature + if(xTemp <= tempMin .or. xTemp >= tempMax)then + xTemp = 0.5_rkind*(tempMin + tempMax) ! new value + bFlag = .true. else - message=trim(message)//'unexpected else branch' - err=20; return + bFlag = .false. endif - - endif ! if energy state or solution is coupled - - ! ----- - ! - update temperatures... - ! ------------------------ - - ! check the need to adjust temperature - if(do_adjustTemp)then - - ! get the melt energy - meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) - - ! compute the residual and the derivative + + ! ----- + ! - compute derivatives... + ! ------------------------ + + ! compute temperature time derivatives select case(ixDomainType) - - ! * vegetation - case(iname_veg) - call xTempSolve(& - ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) - volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) - ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice - ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) - - ! * snow and soil - case(iname_snow, iname_soil) - call xTempSolve(& - ! constant over iterations - meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) - volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) - ! trial values - xTemp = xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice - ! residual and derivative - residual = residual ,& ! intent(out) : residual (J m-3) - derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) - - ! * check - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - - end select ! domain type - - ! check validity of residual - if( ieee_is_nan(residual) )then - message=trim(message)//'residual is not valid' - err=20; return - endif - - ! update bracket - if(residual < 0._rkind)then - tempMax = min(xTemp,tempMax) + case(iname_veg); scalarCanopydTemp_dt = xTemp - scalarCanopyTemp + case(iname_snow, iname_soil); mLayerdTemp_dt(iLayer) = xTemp - mLayerTemp(iLayer) + end select + + ! compute the derivative in total water content w.r.t. total water matric potential (m-1) + ! NOTE 1: valid for frozen and unfrozen conditions + ! NOTE 2: for case "iname_lmpLayer", dVolTot_dPsi0 = dVolLiq_dPsi + select case(ixDomainType) + case(iname_veg) + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) + case(iname_snow) + fLiq = fracLiquid(xTemp,snowfrz_scale) + dVolHtCapBulk_dTheta(iLayer) = iden_water * ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq ) + iden_air * ( ( fLiq-1._rkind )*iden_water/iden_ice - fLiq ) * Cp_air + case(iname_soil) + select case( ixStateType(ixFullVector) ) + case(iname_lmpLayer); dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadLiqTrial(ixControlIndex),vGn_alpha(ixControlIndex),0._rkind,1._rkind,vGn_n(ixControlIndex),vGn_m(ixControlIndex))*avPore + case default; dVolTot_dPsi0(ixControlIndex) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + end select + ! dVolHtCapBulk_dPsi0 uses the derivative in water retention curve above critical temp w.r.t.state variable dVolTot_dPsi0 + dVolHtCapBulk_dTheta(iLayer) = realMissing ! do not use + if(xTemp< Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_ice * Cp_ice - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + if(xTemp>=Tcrit) dVolHtCapBulk_dPsi0(ixControlIndex) = (iden_water * Cp_water - iden_air * Cp_air) * dVolTot_dPsi0(ixControlIndex) + end select + + ! compute the derivative in liquid water content w.r.t. temperature + ! --> partially frozen: dependence of liquid water on temperature + if(xTemp<Tcrit)then + select case(ixDomainType) + case(iname_veg) + dTheta_dTkCanopy = dFracLiq_dTk(xTemp,snowfrz_scale)*scalarCanopyWat/(iden_water*canopyDepth) + dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy !same as snow but there is no derivative in air + case(iname_snow) + mLayerdTheta_dTk(iLayer) = dFracLiq_dTk(xTemp,snowfrz_scale)*mLayerVolFracWatTrial(iLayer) + dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) + case(iname_soil) + mLayerdTheta_dTk(iLayer) = dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) + dVolHtCapBulk_dTk(iLayer) = (-iden_ice * Cp_ice + iden_water * Cp_water) * mLayerdTheta_dTk(iLayer) + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + + ! --> unfrozen: no dependence of liquid water on temperature else - tempMin = max(tempMin,xTemp) - end if - - ! compute iteration increment - tempInc = residual/derivative ! K - - ! check - if(globalPrintFlag)& - write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag - - ! check convergence - if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations - - ! add constraints for snow temperature - if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then - if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method - endif ! if the domain is vegetation or snow - - ! deal with the discontinuity between partially frozen and unfrozen soil - if(ixDomainType==iname_soil)then - ! difference from the temperature below which ice exists - critDiff = Tcrit - xTemp - ! --> initially frozen (T < Tcrit) - if(critDiff > 0._rkind)then - if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature - ! --> initially unfrozen (T > Tcrit) - else - if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature - endif - endif ! if the domain is soil - - ! update the temperature trial - xTemp = xTemp + tempInc - - ! check failed convergence - if(iter==maxiter)then - message=trim(message)//'failed to converge' - err=-20; return ! negative error code = try to recover + select case(ixDomainType) + case(iname_veg); dTheta_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind + case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type endif - - endif ! if adjusting the temperature - + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of hydrology state uncoupled with energy (and when not adjusting the temperature)... + ! ----------------------------------------------------------------------------------------------- + + ! case of hydrology state uncoupled with energy (and when not adjusting the temperature) + if(.not.do_adjustTemp .and. .not.isNrgState .and. .not.isCoupled)then + + ! compute the fraction of snow + select case(ixDomainType) + case(iname_veg); scalarFracLiqVeg = fracliquid(xTemp,snowfrz_scale) + case(iname_snow); mLayerFracLiqSnow(iLayer) = fracliquid(xTemp,snowfrz_scale) + case(iname_soil) ! do nothing + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + + ! ----- + ! - update volumetric fraction of liquid water and ice... + ! => case of energy state or coupled solution (or adjusting the temperature)... + ! -------------------------------------------------------------------------------- + + ! case of energy state OR coupled solution (or adjusting the temperature) + elseif(do_adjustTemp .or. ( (isNrgState .or. isCoupled) ) )then + + ! identify domain type + select case(ixDomainType) + + ! *** vegetation canopy + case(iname_veg) + + ! compute volumetric fraction of liquid water and ice + call updateSnow(xTemp, & ! intent(in) : temperature (K) + scalarCanopyWatTrial/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + scalarVolFracLiq, & ! intent(out) : trial volumetric fraction of liquid water (-) + scalarVolFracIce, & ! intent(out) : trial volumetric fraction if ice (-) + scalarFracLiqVeg, & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! compute mass of water on the canopy + ! NOTE: possibilities for speed-up here + scalarCanopyLiqTrial = scalarFracLiqVeg *scalarCanopyWatTrial !(kg m-2), scalarVolFracLiq*iden_water*canopyDepth + scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth + + ! *** snow layers + case(iname_snow) + + ! compute volumetric fraction of liquid water and ice + call updateSnow(xTemp, & ! intent(in) : temperature (K) + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + snowfrz_scale, & ! intent(in) : scaling parameter for the snow freezing curve (K-1) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + mLayerFracLiqSnow(iLayer), & ! intent(out) : fraction of liquid water (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! *** soil layers + case(iname_soil) + + ! compute volumetric fraction of liquid water and ice + call updateSoil(xTemp, & ! intent(in) : temperature (K) + mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) + vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters + mLayerVolFracWatTrial(iLayer), & ! intent(in) : mass state variable = trial volumetric fraction of water (-) + mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) + mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) + err,cmessage) ! intent(out) : error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! final check + else + + ! do nothing (input = output) -- and check that we got here correctly + if( (isNrgState .or. isCoupled) )then + scalarVolFracLiq = realMissing + scalarVolFracIce = realMissing + else + message=trim(message)//'unexpected else branch' + err=20; return + endif + + endif ! if energy state or solution is coupled + + ! ----- + ! - update temperatures... + ! ------------------------ + + ! check the need to adjust temperature + if(do_adjustTemp)then + + ! get the melt energy + meltNrg = merge(LH_fus*iden_ice, LH_fus*iden_water, ixDomainType==iname_snow) + + ! compute the residual and the derivative + select case(ixDomainType) + + ! * vegetation + case(iname_veg) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = scalarBulkVolHeatCapVeg ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = scalarCanopyTemp ,& ! intent(in) : initial temperature (K) + volFracIceInit = scalarCanopyIce/(iden_water*canopyDepth),& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = dTheta_dTkCanopy ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = scalarVolFracIce ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * snow and soil + case(iname_snow, iname_soil) + call xTempSolve(& + ! constant over iterations + meltNrg = meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap = mLayerVolHtCapBulk(iLayer) ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit = mLayerTemp(iLayer) ,& ! intent(in) : initial temperature (K) + volFracIceInit = mLayerVolFracIce(iLayer) ,& ! intent(in) : initial volumetric fraction of ice (-) + ! trial values + xTemp = xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT = mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial = mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : trial value for volumetric fraction of ice + ! residual and derivative + residual = residual ,& ! intent(out) : residual (J m-3) + derivative = derivative ) ! intent(out) : derivative (J m-3 K-1) + + ! * check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! check validity of residual + if( ieee_is_nan(residual) )then + message=trim(message)//'residual is not valid' + err=20; return + endif + + ! update bracket + if(residual < 0._rkind)then + tempMax = min(xTemp,tempMax) + else + tempMin = max(tempMin,xTemp) + end if + + ! compute iteration increment + tempInc = residual/derivative ! K + + ! check + if(globalPrintFlag)& + write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag + + ! check convergence + if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations + + ! add constraints for snow temperature + if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then + if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method + endif ! if the domain is vegetation or snow + + ! deal with the discontinuity between partially frozen and unfrozen soil + if(ixDomainType==iname_soil)then + ! difference from the temperature below which ice exists + critDiff = Tcrit - xTemp + ! --> initially frozen (T < Tcrit) + if(critDiff > 0._rkind)then + if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature + ! --> initially unfrozen (T > Tcrit) + else + if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature + endif + endif ! if the domain is soil + + ! update the temperature trial + xTemp = xTemp + tempInc + + ! check failed convergence + if(iter==maxiter)then + message=trim(message)//'failed to converge' + err=-20; return ! negative error code = try to recover + endif + + endif ! if adjusting the temperature + end do iterations ! iterating - + ! save temperature select case(ixDomainType) - case(iname_veg); scalarCanopyTempTrial = xTemp - case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp + case(iname_veg); scalarCanopyTempTrial = xTemp + case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp end select - + ! ======================================================================================================================================= ! ======================================================================================================================================= - + ! ----- ! - compute the liquid water matric potential (and necessay derivatives)... ! ------------------------------------------------------------------------- - + ! only for soil if(ixDomainType==iname_soil)then - - ! check liquid water (include tolerance) - if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex)+epsT )then - message=trim(message)//'liquid water greater than porosity' - print*,'---------------' - print*,'porosity(theta_sat)=', theta_sat(ixControlIndex) - print*,'liq water =',mLayerVolFracLiqTrial(iLayer) - print*,'layer =',iLayer - print*,'---------------' - err=20; return - endif - - ! case of hydrology state uncoupled with energy - if(.not.isNrgState .and. .not.isCoupled)then - - ! derivatives relating liquid water matric potential to total water matric potential and temperature - dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) - dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature - - ! case of energy state or coupled solution - else - - ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) - call liquidHead(& + + ! check liquid water (include tolerance) + if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex)+epsT )then + message=trim(message)//'liquid water greater than porosity' + print*,'---------------' + print*,'porosity(theta_sat)=', theta_sat(ixControlIndex) + print*,'liq water =',mLayerVolFracLiqTrial(iLayer) + print*,'layer =',iLayer + print*,'---------------' + err=20; return + endif + + ! case of hydrology state uncoupled with energy + if(.not.isNrgState .and. .not.isCoupled)then + + ! derivatives relating liquid water matric potential to total water matric potential and temperature + dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) + dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature + + ! case of energy state or coupled solution + else + + ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) + call liquidHead(& ! input mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) @@ -703,57 +696,54 @@ module updateVars_module dPsiLiq_dPsi0(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. the total water matric potential (-) dPsiLiq_dTemp(ixControlIndex) ,& ! intent(out): derivative in the liquid water matric potential w.r.t. temperature (m K-1) err,cmessage) ! intent(out): error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; endif - - endif ! switch between hydrology and energy state - + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + endif ! switch between hydrology and energy state + endif ! if domain is soil - - end do ! looping through state variables - - ! deallocate space - deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable - if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif - - ! end association to the variables in the data structures - end associate - - end subroutine updateVars - - - ! ********************************************************************************************************** - ! private subroutine xTempSolve: compute residual and derivative for temperature - ! ********************************************************************************************************** - subroutine xTempSolve(& - ! input: constant over iterations - meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) - heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) - tempInit ,& ! intent(in) : initial temperature (K) - volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) - ! input-output: trial values - xTemp ,& ! intent(inout) : trial value of temperature - dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) - volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice - ! output: residual and derivative - residual ,& ! intent(out) : residual (J m-3) - derivative ) ! intent(out) : derivative (J m-3 K-1) - implicit none - ! input: constant over iterations - real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) - real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) - real(rkind),intent(in) :: tempInit ! initial temperature (K) - real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) - ! input-output: trial values - real(rkind),intent(inout) :: xTemp ! trial value for temperature - real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) - real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) - ! output: residual and derivative - real(rkind),intent(out) :: residual ! residual (J m-3) - real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) - ! subroutine starts here - residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 - derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 - end subroutine xTempSolve - - end module updateVars_module - \ No newline at end of file + + end do ! looping through state variables + + deallocate(computedCoupling,stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem deallocating computedCoupling'; return; endif + + end associate + + end subroutine updateVars + + +! ********************************************************************************************************** +! private subroutine xTempSolve: compute residual and derivative for temperature +! ********************************************************************************************************** +subroutine xTempSolve(& + ! input: constant over iterations + meltNrg ,& ! intent(in) : energy for melt+freeze (J m-3) + heatCap ,& ! intent(in) : volumetric heat capacity (J m-3 K-1) + tempInit ,& ! intent(in) : initial temperature (K) + volFracIceInit ,& ! intent(in) : initial volumetric fraction of ice (-) + ! input-output: trial values + xTemp ,& ! intent(inout) : trial value of temperature + dLiq_dT ,& ! intent(in) : derivative in liquid water content w.r.t. temperature (K-1) + volFracIceTrial ,& ! intent(in) : trial value for volumetric fraction of ice + ! output: residual and derivative + residual ,& ! intent(out) : residual (J m-3) + derivative ) ! intent(out) : derivative (J m-3 K-1) + implicit none + ! input: constant over iterations + real(rkind),intent(in) :: meltNrg ! energy for melt+freeze (J m-3) + real(rkind),intent(in) :: heatCap ! volumetric heat capacity (J m-3 K-1) + real(rkind),intent(in) :: tempInit ! initial temperature (K) + real(rkind),intent(in) :: volFracIceInit ! initial volumetric fraction of ice (-) + ! input-output: trial values + real(rkind),intent(inout) :: xTemp ! trial value for temperature + real(rkind),intent(in) :: dLiq_dT ! derivative in liquid water content w.r.t. temperature (K-1) + real(rkind),intent(in) :: volFracIceTrial ! trial value for the volumetric fraction of ice (-) + ! output: residual and derivative + real(rkind),intent(out) :: residual ! residual (J m-3) + real(rkind),intent(out) :: derivative ! derivative (J m-3 K-1) + ! subroutine starts here + residual = -heatCap*(xTemp - tempInit) + meltNrg*(volFracIceTrial - volFracIceInit) ! J m-3 + derivative = heatCap + LH_fus*iden_water*dLiq_dT ! J m-3 K-1 +end subroutine xTempSolve + +end module updateVars_module -- GitLab