diff --git a/build/source/engine/sundials/updateVarsSundials.f90 b/build/source/engine/sundials/updateVarsSundials.f90 index 0f54d15b1ef86edb8253a05526a000e70610e498..3a33e4e4ce3688cc276edc2a8dd158ff6851784e 100644 --- a/build/source/engine/sundials/updateVarsSundials.f90 +++ b/build/source/engine/sundials/updateVarsSundials.f90 @@ -18,831 +18,1087 @@ ! 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 updateVarsSundials_module +module varSubstepSundials_module ! data types USE nrtype - ! missing values + ! access missing values USE globalData,only:integerMissing ! missing integer - USE globalData,only:realMissing ! missing real number + USE globalData,only:realMissing ! missing double precision number + USE globalData,only:quadMissing ! missing quadruple precision 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_cas ! named variables for the canopy air space + USE globalData,only:iname_veg ! named variables for vegetation 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) + ! global metadata + USE globalData,only:flux_meta ! metadata on the model fluxes - ! provide access to the derived types to define the data structures + ! derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) var_d, & ! data vector (rkind) + var_flagVec, & ! data vector with variable length dimension (i4b) var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) + var_dlength, & ! data vector with variable length dimension (rkind) + zLookup, & ! data vector with variable length dimension (rkind) + model_options ! defines the model decisions ! 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 routines to update states - USE updatStateSundials_module,only:updateSnowSundials ! update snow states - USE updatStateSundials_module,only:updateSoilSundials ! 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_utilsAddSundials_module,only:liquidHeadSundials ! compute the liquid water matric potential - USE soil_utilsAddSundials_module,only:d2Theta_dPsi2 - USE soil_utilsAddSundials_module,only:d2Theta_dTk2 - - ! IEEE checks - USE, intrinsic :: ieee_arithmetic ! check values (NaN, etc.) + USE var_lookup,only:iLookFLUX ! named variables for structure elements + USE var_lookup,only:iLookPROG ! named variables for structure elements + USE var_lookup,only:iLookDIAG ! 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 + ! look up structure for variable types + USE var_lookup,only:iLookVarType + + ! constants + USE multiconst,only:& + Tfreeze, & ! freezing temperature (K) + LH_fus, & ! latent heat of fusion (J kg-1) + LH_vap, & ! latent heat of vaporization (J kg-1) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of liquid water (kg m-3) + ! specific heat + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_soil, & ! specific heat of soil (J kg-1 K-1) + Cp_water ! specific heat of liquid water (J kg-1 K-1) + + ! safety: set private unless specified otherwise implicit none private - public::updateVarsSundials + public::varSubstepSundials + + ! algorithmic parameters + real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers contains + ! ********************************************************************************************************** - ! public subroutine updateVarsSundials: compute diagnostic variables and derivatives for Sundials Jacobian + ! public subroutine varSubstepSundials: run the model for a collection of substeps for a given state subset ! ********************************************************************************************************** - subroutine updateVarsSundials(& - ! input - dt, & ! intent(in): time step - computJac, & ! intent(in): logical flag if computing Jacobian for Sundials solver - do_adjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze - 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 - mLayerVolFracWatPrev, & ! intent(in): previous vector of total water matric potential (m) - mLayerMatricHeadPrev, & ! intent(in): previous vector of volumetric total water content (-) - 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) - scalarCanopyTempPrime, & ! intent(inout): trial value of time derivative canopy temperature (K) - scalarCanopyWatPrime, & ! intent(inout): trial value of time derivative canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(inout): trial value of time derivative canopy liquid water (kg m-2) - scalarCanopyIcePrime, & ! intent(inout): trial value of time derivative 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) - mLayerTempPrime, & ! intent(inout): trial vector of time derivative layer temperature (K) - mLayerVolFracWatPrime, & ! intent(inout): trial vector of time derivative volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(inout): trial vector of time derivative volumetric liquid water content (-) - mLayerVolFracIcePrime, & ! intent(inout): trial vector of time derivative volumetric ice water content (-) - mLayerMatricHeadPrime, & ! intent(inout): trial vector of time derivative total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(inout): trial vector of time derivative liquid water matric potential (m) - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- + subroutine varSubstepSundials(& + ! input: model control + dt, & ! intent(in) : time step (s) + dtInit, & ! intent(in) : initial time step (seconds) + dt_min, & ! intent(in) : minimum time step (seconds) + nState, & ! intent(in) : total number of state variables + doAdjustTemp, & ! intent(in) : flag to indicate if we adjust the temperature + firstSubStep, & ! intent(in) : flag to denote first sub-step + firstFluxCall, & ! intent(inout) : flag to indicate if we are processing the first flux call + computeVegFlux, & ! intent(in) : flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in) : flag to denote implementing the scalar solution + iStateSplit, & ! intent(in) : index of the state in the splitting operation + fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset + fluxCount, & ! intent(inout) : number of times that fluxes are updated (should equal nSubsteps) + ! input/output: data structures + model_decisions, & ! intent(in) : model decisions + lookup_data, & ! intent(in) : lookup tables + type_data, & ! intent(in) : type of vegetation and soil + attr_data, & ! intent(in) : spatial attributes + forc_data, & ! intent(in) : model forcing data + mpar_data, & ! intent(in) : model parameters + indx_data, & ! intent(inout) : index data + prog_data, & ! intent(inout) : model prognostic variables for a local HRU + diag_data, & ! intent(inout) : model diagnostic variables for a local HRU + flux_data, & ! intent(inout) : model fluxes for a local HRU + deriv_data, & ! intent(inout) : derivatives in model fluxes w.r.t. relevant state variables + bvar_data, & ! intent(in) : model variables for the local basin + ! output: model control + ixSaturation, & ! intent(inout) : index of the lowest saturated layer (NOTE: only computed on the first iteration) + dtMultiplier, & ! intent(out) : substep multiplier (-) + nSubsteps, & ! intent(out) : number of substeps taken for a given split + failedMinimumStep, & ! intent(out) : flag to denote success of substepping for a given split + reduceCoupledStep, & ! intent(out) : flag to denote need to reduce the length of the coupled step + tooMuchMelt, & ! intent(out) : flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out) + err,message) ! intent(out) : error code and error message + ! --------------------------------------------------------------------------------------- + ! structure allocations + USE allocspace_module,only:allocLocal ! allocate local data structures + ! simulation of fluxes and residuals given a trial state vector + USE systemSolv_module,only:systemSolv ! solve the system of equations for one time step + USE getVectorz_module,only:popStateVec ! populate the state vector + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE getVectorzAddSundials_module,only:varExtractSundials + ! identify name of variable type (for error message) + USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages + USE systemSolvSundials_module,only:systemSolvSundials implicit none - ! input - real(rkind) ,intent(in) :: dt ! time step - logical(lgt) ,intent(in) :: computJac ! flag if computing Jacobian for Sundials solver - logical(lgt) ,intent(in) :: do_adjustTemp ! flag to adjust temperature to account for the energy used in melt+freeze - 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 - real(rkind),intent(in) :: mLayerVolFracWatPrev(:) ! previous vector of total water matric potential (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev(:) ! previous vector of volumetric total water content (-) - 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) - real(rkind),intent(inout) :: scalarCanopyTempPrime ! trial value of time derivative canopy temperature (K) - real(rkind),intent(inout) :: scalarCanopyWatPrime ! trial value of time derivative canopy total water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyLiqPrime ! trial value of time derivative canopy liquid water (kg m-2) - real(rkind),intent(inout) :: scalarCanopyIcePrime ! trial value of time derivative 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) - real(rkind),intent(inout) :: mLayerTempPrime(:) ! trial vector of time derivative layer temperature (K) - real(rkind),intent(inout) :: mLayerVolFracWatPrime(:) ! trial vector of time derivative volumetric total water content (-) - real(rkind),intent(inout) :: mLayerVolFracLiqPrime(:) ! trial vector of time derivative volumetric liquid water content (-) - real(rkind),intent(inout) :: mLayerVolFracIcePrime(:) ! trial vector of time derivative volumetric ice water content (-) - real(rkind),intent(inout) :: mLayerMatricHeadPrime(:) ! trial vector of time derivative total water matric potential (m) - real(rkind),intent(inout) :: mLayerMatricHeadLiqPrime(:) ! trial vector of time derivative liquid water matric potential (m) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- + ! --------------------------------------------------------------------------------------- + ! * dummy variables + ! --------------------------------------------------------------------------------------- + ! input: model control + real(rkind),intent(in) :: dt ! time step (seconds) + real(rkind),intent(in) :: dtInit ! initial time step (seconds) + real(rkind),intent(in) :: dt_min ! minimum time step (seconds) + integer(i4b),intent(in) :: nState ! total number of state variables + logical(lgt),intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step + logical(lgt),intent(inout) :: firstFluxCall ! flag to define the first flux call + logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) + logical(lgt),intent(in) :: scalarSolution ! flag to denote implementing the scalar solution + integer(i4b),intent(in) :: iStateSplit ! index of the state in the splitting operation + type(var_flagVec),intent(in) :: fluxMask ! flags to denote if the flux is calculated in the given state subset + type(var_ilength),intent(inout) :: fluxCount ! number of times that the flux is updated (should equal nSubsteps) + ! input/output: data structures + type(model_options),intent(in) :: model_decisions(:) ! model decisions + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_i),intent(in) :: type_data ! type of vegetation and soil + type(var_d),intent(in) :: attr_data ! spatial attributes + type(var_d),intent(in) :: forc_data ! model forcing data + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: 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) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables + type(var_dlength),intent(in) :: bvar_data ! model variables for the local basin + ! output: model control + integer(i4b),intent(inout) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) + real(rkind),intent(out) :: dtMultiplier ! substep multiplier (-) + integer(i4b),intent(out) :: nSubsteps ! number of substeps taken for a given split + logical(lgt),intent(out) :: failedMinimumStep ! flag to denote success of substepping for a given split + logical(lgt),intent(out) :: reduceCoupledStep ! flag to denote need to reduce the length of the coupled step + logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt + real(qp),intent(out) :: dt_out + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! --------------------------------------------------------------------------------------- + ! * general local variables + ! --------------------------------------------------------------------------------------- + ! error control + character(LEN=256) :: cmessage ! error message of downwind routine ! 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) :: scalarVolFracLiqPrime ! time derivative volumetric fraction of liquid water (-) - real(rkind) :: scalarVolFracIcePrime ! time derivative 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 - dFracLiqSnow_dTk => deriv_data%var(iLookDERIV%dFracLiqSnow_dTk )%dat ,& ! intent(out): [dp(:)] derivative in fraction of liquid snow w.r.t. temperature - dFracLiqVeg_dTkCanopy => deriv_data%var(iLookDERIV%dFracLiqVeg_dTkCanopy )%dat(1) ,& ! intent(out): [dp ] derivative in fraction of (throughfall + drainage) 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 - d2VolTot_d2Psi0 => deriv_data%var(iLookDERIV%d2VolTot_d2Psi0 )%dat ,& ! intent(out): [dp(:)] second derivative in total water content w.r.t. total water matric potential - mLayerd2Theta_dTk2 => deriv_data%var(iLookDERIV%mLayerd2Theta_dTk2 )%dat ,& ! intent(out): [dp(:)] second derivative of volumetric liquid water content w.r.t. temperature - d2Theta_dTkCanopy2 => deriv_data%var(iLookDERIV%d2Theta_dTkCanopy2 )%dat(1) & ! intent(out): [dp ] second derivative of volumetric liquid water content w.r.t. temperature - ) ! association with variables in the data structures - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- + integer(i4b) :: iVar ! index of variables in data structures + integer(i4b) :: iSoil ! index of soil layers + integer(i4b) :: ixLayer ! index in a given domain + integer(i4b), dimension(1) :: ixMin,ixMax ! bounds of a given flux vector + ! time stepping + real(rkind) :: dtSum ! sum of time from successful steps (seconds) + real(rkind) :: dt_wght ! weight given to a given flux calculation + real(rkind) :: dtSubstep ! length of a substep (s) + ! adaptive sub-stepping for the explicit solution + logical(lgt) :: failedSubstep ! flag to denote success of substepping for a given split + real(rkind),parameter :: safety=0.85_rkind ! safety factor in adaptive sub-stepping + real(rkind),parameter :: reduceMin=0.1_rkind ! mimimum factor that time step is reduced + real(rkind),parameter :: increaseMax=4.0_rkind ! maximum factor that time step is increased + ! adaptive sub-stepping for the implicit solution + integer(i4b),parameter :: n_inc=5 ! minimum number of iterations to increase time step + integer(i4b),parameter :: n_dec=15 ! maximum number of iterations to decrease time step + real(rkind),parameter :: F_inc = 1.25_rkind ! factor used to increase time step + real(rkind),parameter :: F_dec = 0.90_rkind ! factor used to decrease time step + ! state and flux vectors + real(rkind) :: untappedMelt(nState) ! un-tapped melt energy (J m-3 s-1) + real(rkind) :: stateVecInit(nState) ! initial state vector (mixed units) + real(rkind) :: stateVecTrial(nState) ! trial state vector (mixed units) + real(rkind) :: stateVecPrime(nState) ! trial state vector (mixed units) + type(var_dlength) :: flux_temp ! temporary model fluxes + ! flags + logical(lgt) :: firstSplitOper ! flag to indicate if we are processing the first flux call in a splitting operation + logical(lgt) :: checkMassBalance ! flag to check the mass balance + logical(lgt) :: checkNrgBalance + logical(lgt) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + ! energy fluxes + real(rkind) :: sumCanopyEvaporation ! sum of canopy evaporation/condensation (kg m-2 s-1) + real(rkind) :: sumLatHeatCanopyEvap ! sum of latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSenHeatCanopy ! sum of sensible heat flux from the canopy to the canopy air space (W m-2) + real(rkind) :: sumSoilCompress + real(rkind),allocatable :: sumLayerCompress(:) + ! --------------------------------------------------------------------------------------- + ! point to variables in the data structures + ! --------------------------------------------------------------------------------------- + globalVars: associate(& + ! number of layers + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1) ,& ! intent(in): [i4b] number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1) ,& ! intent(in): [i4b] number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1) ,& ! intent(in): [i4b] total number of layers + nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in): [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! mapping between state vectors and control volumes + ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerMissing) + ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] mapping of full state vector to the state subset + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of control volume for different domains (veg, snow, soil) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout): [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout): [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout): [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout): [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout): [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout): [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout): [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(inout): [dp(:)] matric potential of liquid water (m) + ) ! end association with variables in the data structures + ! ********************************************************************************************************************************************************* + ! ********************************************************************************************************************************************************* + ! Procedure starts here ! initialize error control - err=0; message='updateVarsSundials/' + err=0; message='varSubstepSundials/' + + ! initialize flag for the success of the substepping + failedMinimumStep=.false. + + ! initialize the length of the substep + dtSubstep = dtInit - ! 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. + ! allocate space for the temporary model flux structure + call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) + if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif - ! loop through model state variables - do iState=1,size(ixMapSubset2Full) + ! initialize the model fluxes (some model fluxes are not computed in the iterations) + do iVar=1,size(flux_data%var) + flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + end do - ! check the need for the computations - if(computedCoupling(iState)) cycle + ! initialize the total energy fluxes (modified in updateProgSundials) + sumCanopyEvaporation = 0._rkind ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = 0._rkind ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = 0._rkind ! sensible heat flux from the canopy to the canopy air space (W m-2) + sumSoilCompress = 0._rkind ! total soil compression + allocate(sumLayerCompress(nSoil)); sumLayerCompress = 0._rkind ! soil compression by layer + + ! define the first flux call in a splitting operation + firstSplitOper = (.not.scalarSolution .or. iStateSplit==1) + + ! initialize subStep + dtSum = 0._rkind ! keep track of the portion of the time step that is completed + nSubsteps = 0 + + ! loop through substeps + ! NOTE: continuous do statement with exit clause + substeps: do + + ! initialize error control + err=0; message='varSubstepSundials/' + + !write(*,'(a,1x,3(f13.2,1x))') '***** new subStep: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt + !print*, 'scalarCanopyIce = ', prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) + !print*, 'scalarCanopyTemp = ', prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) + + ! ----- + ! * populate state vectors... + ! --------------------------- + + ! initialize state vectors + call popStateVec(& + ! input + nState, & ! intent(in): number of desired state variables + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + stateVecInit, & ! intent(out): initial model state vector (mixed units) + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! ----- - ! - 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 - 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 - 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 + ! * iterative solution... + ! ----------------------- + ! solve the system of equations for a given state subset + call systemSolvSundials(& + ! input: model control + dtSubstep, & ! intent(in): time step (s) + nState, & ! intent(in): total number of state variables + firstSubStep, & ! intent(in): flag to denote first sub-step + firstFluxCall, & ! intent(inout): flag to indicate if we are processing the first flux call + firstSplitOper, & ! intent(in): flag to indicate if we are processing the first flux call in a splitting operation + computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation + scalarSolution, & ! intent(in): flag to denote if implementing the scalar solution + ! input/output: data structures + lookup_data, & ! intent(in): lookup tables + type_data, & ! intent(in): type of vegetation and soil + attr_data, & ! intent(in): spatial attributes + forc_data, & ! intent(in): model forcing data + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(inout): index data + prog_data, & ! intent(inout): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + flux_temp, & ! intent(inout): model fluxes for a local HRU + bvar_data, & ! intent(in): model variables for the local basin + model_decisions, & ! intent(in): model decisions + stateVecInit, & ! intent(in): initial state vector + ! output: model control + deriv_data, & ! intent(inout): derivatives in model fluxes w.r.t. relevant state variables + ixSaturation, & ! intent(inout): index of the lowest saturated layer (NOTE: only computed on the first iteration) + stateVecTrial, & ! intent(out): updated state vector + stateVecPrime, & ! intent(out): updated state vector + reduceCoupledStep, & ! intent(out): flag to reduce the length of the coupled step + tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt + dt_out, & ! intent(out): time step (s) + err,cmessage) ! intent(out): error code and error message + + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return endif - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - ! ======================================================================================================================================= - - ! update hydrology state variables for the uncoupled solution - if(.not.isNrgState .and. .not.isCoupled)then - - if(.not.computJac) stop 1 ! this does not work yet? FIX - - ! 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 - scalarCanopyWatPrime = scalarCanopyLiqPrime + scalarCanopyIcePrime - case(iname_snow) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer)*iden_ice/iden_water - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer)*iden_ice/iden_water - case(iname_soil) - mLayerVolFracWatTrial(iLayer) = mLayerVolFracLiqTrial(iLayer) + mLayerVolFracIceTrial(iLayer) ! no volume expansion - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, or iname_soil'; return - end select - endif + ! set untapped melt energy to zero + untappedMelt(:) = 0._rkind + + ! if too much melt or need to reduce length of the coupled step then return + ! NOTE: need to go all the way back to coupled_em and merge snow layers, as all splitting operations need to occur with the same layer geometry + if(tooMuchMelt .or. reduceCoupledStep) return + + ! identify failure + failedSubstep = (err<0) + + ! reduce step based on failure + if(failedSubstep)then + err=0; message='varSubstepSundials/' ! recover from failed convergence + dtMultiplier = 0.5_rkind ! system failure: step halving + else + + endif ! switch between failure and success + + ! check if we failed the substep + if(failedSubstep)then + + ! check that the substep is greater than the minimum step + if(dtSubstep*dtMultiplier<dt_min)then + ! --> exit, and either (1) try another solution method; or (2) reduce coupled step + failedMinimumStep=.true. + exit subSteps - ! 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 - mLayerVolFracWatPrime(iLayer) = mLayerVolFracLiqPrime(iLayer) + mLayerVolFracIcePrime(iLayer) - mLayerMatricHeadTrial(ixControlIndex) = matricHead(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) - !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)) - mLayerVolFracWatPrime(iLayer) = dTheta_dPsi(mLayerMatricHeadTrial(ixControlIndex),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) *mLayerMatricHeadPrime(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)) - mLayerMatricHeadPrime(ixControlIndex) = dPsi_dTheta(mLayerVolFracWatTrial(iLayer),vGn_alpha(ixControlIndex),theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) * mLayerVolFracWatPrime(iLayer) - 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 - 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 - 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 ! step is still OK + dtSubstep = dtSubstep*dtMultiplier + cycle subSteps + endif ! if step is less than the minimum + + endif ! if failed the substep + + ! ----- + ! * update model fluxes... + ! ------------------------ + + ! NOTE: if we get to here then we are accepting the step + + ! NOTE: we get to here if iterations are successful + if(err/=0)then + message=trim(message)//'expect err=0 if updating fluxes' + return + endif + + ! identify the need to check the mass balance + checkMassBalance = .true. ! (.not.scalarSolution) + checkNrgBalance = .true. + + ! update prognostic variables + call updateProgSundials(dt_out,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + lookup_data,mpar_data,indx_data,flux_temp,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,cmessage) ! output: flags and error control + if(err/=0)then + message=trim(message)//trim(cmessage) + if(err>0) return + endif + + ! if water balance error then reduce the length of the coupled step + if(waterBalanceError)then + message=trim(message)//'water balance error' + reduceCoupledStep=.true. + err=-20; return + endif + + if(globalPrintFlag)& + print*, trim(cmessage)//': dt = ', dtSubstep + + ! recover from errors in prognostic update + if(err<0)then + + ! modify step + err=0 ! error recovery + dtSubstep = dtSubstep/2._rkind + + ! check minimum: fail minimum step if there is an error in the update + if(dtSubstep<dt_min)then + failedMinimumStep=.true. + exit subSteps + ! minimum OK -- try again else - bFlag = .false. + cycle substeps endif - ! ----- - ! - compute derivatives... - ! ------------------------ - - ! compute the derivative in bulk heat capacity w.r.t. total water content or water matric potential (m-1) - ! 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, dVolHtCapBulk_dPsi0 may be wrong - select case(ixDomainType) - case(iname_veg) - if(computJac)then - fLiq = fracLiquid(xTemp,snowfrz_scale) - dVolHtCapBulk_dCanWat = ( -Cp_ice*( fLiq-1._rkind ) + Cp_water*fLiq )/canopyDepth !this is iden_water/(iden_water*canopyDepth) - endif - case(iname_snow) - if(computJac)then - 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 - endif - 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 - if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(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)) - if(computJac) d2VolTot_d2Psi0(ixControlIndex) = d2Theta_dPsi2(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 - if(computJac)then - 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) - endif - end select + endif ! if errors in prognostic update + + ! get the total energy fluxes (modified in updateProgSundials) + if(nrgFluxModified .or. indx_data%var(iLookINDEX%ixVegNrg)%dat(1)/=integerMissing)then + sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_temp%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_temp%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_temp%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + else + sumCanopyEvaporation = sumCanopyEvaporation + dt_out*flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ! canopy evaporation/condensation (kg m-2 s-1) + sumLatHeatCanopyEvap = sumLatHeatCanopyEvap + dt_out*flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + sumSenHeatCanopy = sumSenHeatCanopy + dt_out*flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ! sensible heat flux from the canopy to the canopy air space (W m-2) + endif ! if energy fluxes were modified + + ! get the total soil compression + if (count(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat/=integerMissing)>0) then + ! scalar compression + if(.not.scalarSolution .or. iStateSplit==nSoil)& + sumSoilCompress = sumSoilCompress + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) ! total soil compression + ! vector compression + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + sumLayerCompress(iSoil) = sumLayerCompress(iSoil) + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) ! soil compression in layers + end do + endif - ! compute the derivative in liquid water content w.r.t. temperature - ! --> partially frozen: dependence of liquid water on temperature - ! compute the derivative in bulk heat capacity w.r.t. temperature - if(xTemp<Tcrit)then - select case(ixDomainType) - case(iname_veg) - dFracLiqVeg_dTkCanopy = dFracLiq_dTk(xTemp,snowfrz_scale) - dTheta_dTkCanopy = dFracLiqVeg_dTkCanopy * scalarCanopyWatTrial/(iden_water*canopyDepth) - if(computJac)then - fLiq = fracLiquid(xTemp,snowfrz_scale) - d2Theta_dTkCanopy2 = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqVeg_dTkCanopy - fLiq**2._rkind ) * scalarCanopyWatTrial/(iden_water*canopyDepth) - dVolHtCapBulk_dTkCanopy = iden_water * (-Cp_ice + Cp_water) * dTheta_dTkCanopy !same as snow but there is no derivative in air - endif - case(iname_snow) - dFracLiqSnow_dTk(iLayer) = dFracLiq_dTk(xTemp,snowfrz_scale) - mLayerdTheta_dTk(iLayer) = dFracLiqSnow_dTk(iLayer) * mLayerVolFracWatTrial(iLayer) - if(computJac)then - fLiq = fracLiquid(xTemp,snowfrz_scale) - mLayerd2Theta_dTk2(iLayer) = 2._rkind * snowfrz_scale**2._rkind * ( (Tfreeze - xTemp) * 2._rkind * fLiq * dFracLiqSnow_dTk(iLayer) - fLiq**2._rkind ) * mLayerVolFracWatTrial(iLayer) - dVolHtCapBulk_dTk(iLayer) = ( iden_water * (-Cp_ice + Cp_water) + iden_air * (iden_water/iden_ice - 1._rkind) * Cp_air ) * mLayerdTheta_dTk(iLayer) - endif - case(iname_soil) - dFracLiqSnow_dTk(iLayer) = 0._rkind !dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex))/ mLayerVolFracWatTrial(iLayer) - mLayerdTheta_dTk(iLayer) = dTheta_dTk(xTemp,theta_res(ixControlIndex),theta_sat(ixControlIndex),vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),vGn_m(ixControlIndex)) - if(computJac)then - mLayerd2Theta_dTk2(iLayer) = d2Theta_dTk2(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) + ! print progress + if(globalPrintFlag)& + write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt + + ! increment fluxes + dt_wght = 1._qp !dt_out/dt ! (define weight applied to each splitting operation) + do iVar=1,size(flux_meta) + if(count(fluxMask%var(iVar)%dat)>0) then + + !print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat + + ! ** no domain splitting + if(count(ixLayerActive/=integerMissing)==nLayers)then + flux_data%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) + flux_temp%var(iVar)%dat(:)*dt_wght + fluxCount%var(iVar)%dat(:) = fluxCount%var(iVar)%dat(:) + 1 + + ! ** domain splitting + else + ixMin=lbound(flux_data%var(iVar)%dat) + ixMax=ubound(flux_data%var(iVar)%dat) + do ixLayer=ixMin(1),ixMax(1) + if(fluxMask%var(iVar)%dat(ixLayer)) then + flux_data%var(iVar)%dat(ixLayer) = flux_data%var(iVar)%dat(ixLayer) + flux_temp%var(iVar)%dat(ixLayer)*dt_wght + fluxCount%var(iVar)%dat(ixLayer) = fluxCount%var(iVar)%dat(ixLayer) + 1 endif - case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return - end select ! domain type + end do + endif ! (domain splitting) - ! --> unfrozen: no dependence of liquid water on temperature - else - select case(ixDomainType) - case(iname_veg); dTheta_dTkCanopy = 0._rkind; d2Theta_dTkCanopy2 = 0._rkind; dFracLiqVeg_dTkCanopy = 0._rkind; dVolHtCapBulk_dTkCanopy = 0._rkind - case(iname_snow, iname_soil); mLayerdTheta_dTk(iLayer) = 0._rkind; mLayerd2Theta_dTk2(iLayer) = 0._rkind; dFracLiqSnow_dTk(iLayer) = 0._rkind; dVolHtCapBulk_dTk(iLayer) = 0._rkind - end select ! domain type - endif + endif ! (if the flux is desired) + end do ! (loop through fluxes) - ! ----- - ! - 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 updateSnowSundials(& - 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) - scalarCanopyTempPrime, & ! intent(in) : canopy temperature time derivative (K/s) - scalarCanopyWatPrime/(iden_water*canopyDepth),& ! intent(in) : volumetric fraction of total water time derivative (-) - scalarVolFracLiq, & ! intent(out) : trial canopy liquid water (-) - scalarVolFracIce, & ! intent(out) : trial volumetric canopy ice (-) - scalarVolFracLiqPrime, & ! intent(out) : trial volumetric canopy liquid water (-) - scalarVolFracIcePrime, & ! intent(out) : trial volumetric canopy 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 - scalarCanopyLiqPrime = scalarVolFracLiqPrime*iden_water*canopyDepth - scalarCanopyIceTrial = (1._rkind - scalarFracLiqVeg)*scalarCanopyWatTrial !(kg m-2), scalarVolFracIce* iden_ice *canopyDepth - scalarCanopyIcePrime = scalarVolFracIcePrime* iden_ice *canopyDepth - - ! *** snow layers - case(iname_snow) - - ! compute volumetric fraction of liquid water and ice - call updateSnowSundials(& - 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) - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerVolFracWatPrime(iLayer), & ! intent(in) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqTrial(iLayer), & ! intent(out) : trial volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer), & ! intent(out) : trial volumetric fraction if ice (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - 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 + ! increment the number of substeps + nSubsteps = nSubsteps+1 + + ! increment the sub-step legth + dtSum = dtSum + dtSubstep + !print*, 'dtSum, dtSubstep, dt, nSubsteps = ', dtSum, dtSubstep, dt, nSubsteps - ! *** soil layers - case(iname_soil) - - ! compute volumetric fraction of liquid water and ice - call updateSoilSundials(& - dt, & ! intent(in) : time step - computJac, & ! intent(in) : logical flag if inside Sundials solver - xTemp, & ! intent(in) : temperature (K) - mLayerMatricHeadTrial(ixControlIndex), & ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrev(ixControlIndex), & ! intent(in) : previous values, will be same as current if computJac - mLayerVolFracWatPrev(iLayer), & ! intent(in) : previous values, will be same as current if computJac - mLayerTempPrime(iLayer), & ! intent(in) : temperature time derivative (K/s) - mLayerMatricHeadPrime(ixControlIndex), & ! intent(in) : total water matric potential time derivative (m/s) - vGn_alpha(ixControlIndex), & ! intent(in) : van Genutchen "alpha" parameter - vGn_n(ixControlIndex), & ! intent(in) : van Genutchen "n" parameter - theta_sat(ixControlIndex), & ! intent(in) : soil porosity (-) - theta_res(ixControlIndex), & ! intent(in) : soil residual volumetric water content (-) - vGn_m(ixControlIndex), & ! intent(in) : van Genutchen "m" parameter (-) - 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 (-) - mLayerVolFracWatPrime(iLayer), & ! intent(out) : volumetric fraction of total water time derivative (-) - mLayerVolFracLiqPrime(iLayer), & ! intent(out) : volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime(iLayer), & ! intent(out) : volumetric fraction of ice time derivative (-) - err,cmessage) ! intent(out) : error control + ! check that we have completed the sub-step + if(dtSum >= dt-verySmall)then + failedMinimumStep=.false. + exit subSteps + endif + + ! adjust length of the sub-step (make sure that we don't exceed the step) + dtSubstep = min(dt - dtSum, max(dtSubstep*dtMultiplier, dt_min) ) + + end do substeps ! time steps for variable-dependent sub-stepping + + ! save the energy fluxes + flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) = sumCanopyEvaporation /dt_out ! canopy evaporation/condensation (kg m-2 s-1) + flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) = sumLatHeatCanopyEvap /dt_out ! latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) = sumSenHeatCanopy /dt_out ! sensible heat flux from the canopy to the canopy air space (W m-2) + + ! save the soil compression diagnostics + diag_data%var(iLookDIAG%scalarSoilCompress)%dat(1) = sumSoilCompress + do iSoil=1,nSoil + if(indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat(iSoil)/=integerMissing)& + diag_data%var(iLookDIAG%mLayerCompress)%dat(iSoil) = sumLayerCompress(iSoil) + end do + deallocate(sumLayerCompress) + + ! end associate statements + end associate globalVars + + ! update error codes + if(failedMinimumStep)then + err=-20 ! negative = recoverable error + message=trim(message)//'failed minimum step' + endif + + + end subroutine varSubstepSundials + + + ! ********************************************************************************************************** + ! private subroutine updateProgSundials: update prognostic variables + ! ********************************************************************************************************** + subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux,untappedMelt,stateVecTrial,stateVecPrime,checkMassBalance, checkNrgBalance, & ! input: model control + lookup_data,mpar_data,indx_data,flux_data,prog_data,diag_data,deriv_data, & ! input-output: data structures + waterBalanceError,nrgFluxModified,err,message) ! output: flags and error control + USE getVectorz_module,only:varExtract ! extract variables from the state vector + USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables + USE getVectorzAddSundials_module, only:varExtractSundials + USE computEnthalpy_module,only:computEnthalpy + USE t2enthalpy_module, only:t2enthalpy ! compute enthalpy + implicit none + ! model control + real(rkind) ,intent(in) :: dt ! time step (s) + integer(i4b) ,intent(in) :: nSnow ! number of snow layers + integer(i4b) ,intent(in) :: nSoil ! number of soil layers + integer(i4b) ,intent(in) :: nLayers ! total number of layers + logical(lgt) ,intent(in) :: doAdjustTemp ! flag to indicate if we adjust the temperature + logical(lgt) ,intent(in) :: computeVegFlux ! flag to compute the vegetation flux + real(rkind) ,intent(in) :: untappedMelt(:) ! un-tapped melt energy (J m-3 s-1) + real(rkind) ,intent(in) :: stateVecTrial(:) ! trial state vector (mixed units) + real(rkind) ,intent(in) :: stateVecPrime(:) ! trial state vector (mixed units) + logical(lgt) ,intent(in) :: checkMassBalance ! flag to check the mass balance + logical(lgt) ,intent(in) :: checkNrgBalance ! flag to check the energy balance + ! data structures + type(zLookup),intent(in) :: lookup_data ! lookup tables + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! indices for a local HRU + type(var_dlength),intent(inout) :: flux_data ! model fluxes for a local HRU + type(var_dlength),intent(inout) :: 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 + ! flags and error control + logical(lgt) ,intent(out) :: waterBalanceError ! flag to denote that there is a water balance error + logical(lgt) ,intent(out) :: nrgFluxModified ! flag to denote that the energy fluxes were modified + integer(i4b) ,intent(out) :: err ! error code + character(*) ,intent(out) :: message ! error message + ! ================================================================================================================== + ! general + integer(i4b) :: iState ! index of model state variable + integer(i4b) :: ixSubset ! index within the state subset + integer(i4b) :: ixFullVector ! index within full state vector + integer(i4b) :: ixControlIndex ! index within a given domain + real(rkind) :: volMelt ! volumetric melt (kg m-3) + real(rkind),parameter :: verySmall=epsilon(1._rkind)*2._rkind ! a very small number (deal with precision issues) + ! mass balance + real(rkind) :: canopyBalance0,canopyBalance1 ! canopy storage at start/end of time step + real(rkind) :: soilBalance0,soilBalance1 ! soil storage at start/end of time step + real(rkind) :: vertFlux ! change in storage due to vertical fluxes + real(rkind) :: tranSink,baseSink,compSink ! change in storage due to sink terms + real(rkind) :: liqError ! water balance error + real(rkind) :: fluxNet ! net water fluxes (kg m-2 s-1) + real(rkind) :: superflousWat ! superflous water used for evaporation (kg m-2 s-1) + real(rkind) :: superflousNrg ! superflous energy that cannot be used for evaporation (W m-2 [J m-2 s-1]) + character(LEN=256) :: cmessage ! error message of downwind routine + ! trial state variables + real(rkind) :: scalarCanairTempTrial ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempTrial ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatTrial ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempTrial ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatTrial ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadTrial ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqTrial ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStorageTrial ! trial value for storage of water in the aquifer (m) + ! diagnostic variables + real(rkind) :: scalarCanopyLiqTrial ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIceTrial ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqTrial ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIceTrial ! trial vector for volumetric fraction of ice (-) + real(rkind) :: scalarCanairEnthalpyTrial ! enthalpy of the canopy air space (J m-3) + real(rkind) :: scalarCanopyEnthalpyTrial ! enthalpy of the vegetation canopy (J m-3) + real(rkind),dimension(nLayers) :: mLayerEnthalpyTrial ! enthalpy of snow + soil (J m-3) + ! derivative of state variables + real(rkind) :: scalarCanairTempPrime ! trial value for temperature of the canopy air space (K) + real(rkind) :: scalarCanopyTempPrime ! trial value for temperature of the vegetation canopy (K) + real(rkind) :: scalarCanopyWatPrime ! trial value for liquid water storage in the canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerTempPrime ! trial vector for temperature of layers in the snow and soil domains (K) + real(rkind),dimension(nLayers) :: mLayerVolFracWatPrime ! trial vector for volumetric fraction of total water (-) + real(rkind),dimension(nSoil) :: mLayerMatricHeadPrime ! trial vector for total water matric potential (m) + real(rkind),dimension(nSoil) :: mLayerMatricHeadLiqPrime ! trial vector for liquid water matric potential (m) + real(rkind) :: scalarAquiferStoragePrime ! trial value for storage of water in the aquifer (m) + ! derivative of diagnostic variables + real(rkind) :: scalarCanopyLiqPrime ! trial value for mass of liquid water on the vegetation canopy (kg m-2) + real(rkind) :: scalarCanopyIcePrime ! trial value for mass of ice on the vegetation canopy (kg m-2) + real(rkind),dimension(nLayers) :: mLayerVolFracLiqPrime ! trial vector for volumetric fraction of liquid water (-) + real(rkind),dimension(nLayers) :: mLayerVolFracIcePrime ! trial vector for volumetric fraction of ice (-) + ! ------------------------------------------------------------------------------------------------------------------- + + ! ------------------------------------------------------------------------------------------------------------------- + ! point to flux variables in the data structure + associate(& + ! get indices for mass balance + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat(1) ,& ! intent(in) : [i4b] index of canopy hydrology state variable (mass) + ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the soil domain + ! get indices for the un-tapped melt + ixNrgOnly => indx_data%var(iLookINDEX%ixNrgOnly)%dat ,& ! intent(in) : [i4b(:)] list of indices for all energy states + ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in) : [i4b(:)] indices defining the domain of the state (iname_veg, iname_snow, iname_soil) + ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in) : [i4b(:)] index of the control volume for different domains (veg, snow, soil) + ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in) : [i4b(:)] [state subset] list of indices of the full state vector in the state subset + ! water fluxes + scalarRainfall => flux_data%var(iLookFLUX%scalarRainfall)%dat(1) ,& ! intent(in) : [dp] rainfall rate (kg m-2 s-1) + scalarThroughfallRain => flux_data%var(iLookFLUX%scalarThroughfallRain)%dat(1) ,& ! intent(in) : [dp] rain reaches ground without touching the canopy (kg m-2 s-1) + scalarCanopyEvaporation => flux_data%var(iLookFLUX%scalarCanopyEvaporation)%dat(1) ,& ! intent(in) : [dp] canopy evaporation/condensation (kg m-2 s-1) + scalarCanopyTranspiration => flux_data%var(iLookFLUX%scalarCanopyTranspiration)%dat(1) ,& ! intent(in) : [dp] canopy transpiration (kg m-2 s-1) + scalarCanopyLiqDrainage => flux_data%var(iLookFLUX%scalarCanopyLiqDrainage)%dat(1) ,& ! intent(in) : [dp] drainage liquid water from vegetation canopy (kg m-2 s-1) + iLayerLiqFluxSoil => flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ,& ! intent(in) : [dp(0:)] vertical liquid water flux at soil layer interfaces (-) + iLayerNrgFlux => flux_data%var(iLookFLUX%iLayerNrgFlux)%dat ,& ! intent(in) : + mLayerNrgFlux => flux_data%var(iLookFLUX%mLayerNrgFlux)%dat ,& ! intent(out): [dp] net energy flux for each layer within the snow+soil domain (J m-3 s-1) + mLayerTranspire => flux_data%var(iLookFLUX%mLayerTranspire)%dat ,& ! intent(in) : [dp(:)] transpiration loss from each soil layer (m s-1) + mLayerBaseflow => flux_data%var(iLookFLUX%mLayerBaseflow)%dat ,& ! intent(in) : [dp(:)] baseflow from each soil layer (m s-1) + mLayerCompress => diag_data%var(iLookDIAG%mLayerCompress)%dat ,& ! intent(in) : [dp(:)] change in storage associated with compression of the soil matrix (-) + scalarCanopySublimation => flux_data%var(iLookFLUX%scalarCanopySublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the vegetation canopy (kg m-2 s-1) + scalarSnowSublimation => flux_data%var(iLookFLUX%scalarSnowSublimation)%dat(1) ,& ! intent(in) : [dp] sublimation of ice from the snow surface (kg m-2 s-1) + ! energy fluxes + scalarLatHeatCanopyEvap => flux_data%var(iLookFLUX%scalarLatHeatCanopyEvap)%dat(1) ,& ! intent(in) : [dp] latent heat flux for evaporation from the canopy to the canopy air space (W m-2) + scalarSenHeatCanopy => flux_data%var(iLookFLUX%scalarSenHeatCanopy)%dat(1) ,& ! intent(in) : [dp] sensible heat flux from the canopy to the canopy air space (W m-2) + ! domain depth + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in) : [dp ] canopy depth (m) + mLayerDepth => prog_data%var(iLookPROG%mLayerDepth)%dat ,& ! intent(in) : [dp(:)] depth of each layer in the snow-soil sub-domain (m) + ! model state variables (vegetation canopy) + scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the canopy air space (K) + scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(inout) : [dp] temperature of the vegetation canopy (K) + scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(inout) : [dp] mass of ice on the vegetation canopy (kg m-2) + scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(inout) : [dp] mass of liquid water on the vegetation canopy (kg m-2) + scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(inout) : [dp] mass of total water on the vegetation canopy (kg m-2) + ! model state variables (snow and soil domains) + mLayerTemp => prog_data%var(iLookPROG%mLayerTemp)%dat ,& ! intent(inout) : [dp(:)] temperature of each snow/soil layer (K) + mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of ice (-) + mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of liquid water (-) + mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(inout) : [dp(:)] volumetric fraction of total water (-) + mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(inout) : [dp(:)] matric head (m) + mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat ,& ! intent(inout) : [dp(:)] matric potential of liquid water (m) + ! enthalpy + scalarCanairEnthalpy => diag_data%var(iLookDIAG%scalarCanairEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpy => diag_data%var(iLookDIAG%scalarCanopyEnthalpy)%dat(1) ,& ! intent(inout): [dp] enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpy => diag_data%var(iLookDIAG%mLayerEnthalpy)%dat ,& ! intent(inout): [dp(:)] enthalpy of the snow+soil layers (J m-3) + ! model state variables (aquifer) + scalarAquiferStorage => prog_data%var(iLookPROG%scalarAquiferStorage)%dat(1) ,& ! intent(inout) : [dp(:)] storage of water in the aquifer (m) + ! error tolerance + absConvTol_liquid => mpar_data%var(iLookPARAM%absConvTol_liquid)%dat(1) & ! intent(in) : [dp] absolute convergence tolerance for vol frac liq water (-) + ) ! associating flux variables in the data structure + ! ------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='updateProgSundials/' + + ! initialize water balancmLayerVolFracWatTriale error + waterBalanceError=.false. + + ! get storage at the start of the step + canopyBalance0 = merge(scalarCanopyWat, realMissing, computeVegFlux) + soilBalance0 = sum( (mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + + ! ----- + ! * update states... + ! ------------------ + ! these will need to be initialized as they do not have updated prognostic structures in Sundials + ! should all be set to previous values if splits, but for now operator splitting is not hooked up + scalarCanairTempPrime = realMissing + scalarCanopyTempPrime = realMissing + scalarCanopyWatPrime = realMissing + scalarCanopyLiqPrime = realMissing + scalarCanopyIcePrime = realMissing + mLayerTempPrime = realMissing + mLayerVolFracWatPrime = realMissing + mLayerVolFracLiqPrime = realMissing + mLayerVolFracIcePrime = realMissing + mLayerMatricHeadPrime = realMissing + mLayerMatricHeadLiqPrime = realMissing + scalarAquiferStoragePrime= realMissing + ! set to previous value from prognostic structure, correct because outside Sundials + scalarCanairTempTrial = scalarCanairTemp + scalarCanopyTempTrial = scalarCanopyTemp + scalarCanopyWatTrial = scalarCanopyWat + scalarCanopyLiqTrial = scalarCanopyLiq + scalarCanopyIceTrial = scalarCanopyIce + mLayerTempTrial = mLayerTemp + mLayerVolFracWatTrial = mLayerVolFracWat + mLayerVolFracLiqTrial = mLayerVolFracLiq + mLayerVolFracIceTrial = mLayerVolFracIce + mLayerMatricHeadTrial = mLayerMatricHead + mLayerMatricHeadLiqTrial = mLayerMatricHeadLiq + scalarAquiferStorageTrial= scalarAquiferStorage + + ! extract variables from the model state vector + call varExtractSundials(& + ! input + stateVecTrial, & ! intent(in): model state vector (mixed units) + stateVecPrime, & ! intent(in): model state vector (mixed units) + diag_data, & ! intent(in): model diagnostic variables for a local HRU + prog_data, & ! intent(in): model prognostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output: variables for the vegetation canopy + scalarCanairTempTrial, & ! intent(inout): trial value of canopy air temperature (K) + 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) + scalarCanairTempPrime, & ! intent(inout): derivative of canopy air temperature (K) + scalarCanopyTempPrime, & ! intent(inout): derivative of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): derivative of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): derivative of canopy liquid water (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 (-) + mLayerMatricHeadTrial, & ! intent(inout): trial vector of total water matric potential (m) + mLayerMatricHeadLiqTrial, & ! intent(inout): trial vector of liquid water matric potential (m) + mLayerTempPrime, & ! intent(inout): derivative of layer temperature (K) + mLayerVolFracWatPrime, & ! intent(inout): derivative of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): derivative of volumetric liquid water content (-) + mLayerMatricHeadPrime, & ! intent(inout): derivative of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): derivative of liquid water matric potential (m) + ! output: variables for the aquifer + scalarAquiferStorageTrial,& ! intent(inout): trial value of storage of water in the aquifer (m) + scalarAquiferStoragePrime,& ! intent(inout): derivative of storage of water in the aquifer (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + + ! update diagnostic variables + call updateVarsSundials(& + ! input + dt, & + .false., & ! intent(in): logical flag if computing Jacobian for Sundials solver + doAdjustTemp, & ! intent(in): logical flag to adjust temperature to account for the energy used in melt+freeze + 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 + mLayerVolFracWatTrial, & ! intent(in): use current vector for prev vector of volumetric total water content (-) + mLayerMatricHeadTrial, & ! intent(in): use current vector for prev vector of total water matric potential (m) + 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) + scalarCanopyTempPrime, & ! intent(inout): trial value of canopy temperature (K) + scalarCanopyWatPrime, & ! intent(inout): trial value of canopy total water (kg m-2) + scalarCanopyLiqPrime, & ! intent(inout): trial value of canopy liquid water (kg m-2) + scalarCanopyIcePrime, & ! 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) + mLayerTempPrime, & ! + mLayerVolFracWatPrime, & ! intent(inout): Prime vector of volumetric total water content (-) + mLayerVolFracLiqPrime, & ! intent(inout): Prime vector of volumetric liquid water content (-) + mLayerVolFracIcePrime, & ! + mLayerMatricHeadPrime, & ! intent(inout): Prime vector of total water matric potential (m) + mLayerMatricHeadLiqPrime, & ! intent(inout): Prime vector of liquid water matric potential (m) + ! output: error control + err,cmessage) ! intent(out): error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! (check for errors) + + ! ---- + ! * check energy balance + !------------------------ + ! NOTE: for now, we just compute enthalpy + if(checkNrgBalance)then + ! compute enthalpy at t_{n+1} + call 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): trial vector of volumetric fraction of ice (-) + ! output: enthalpy + scalarCanairEnthalpyTrial, & ! intent(out): enthalpy of the canopy air space (J m-3) + scalarCanopyEnthalpyTrial, & ! intent(out): enthalpy of the vegetation canopy (J m-3) + mLayerEnthalpyTrial, & ! intent(out): enthalpy of each snow+soil layer (J m-3) + ! output: error control + 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 + endif - end select ! domain type + ! ----- + ! * check mass balance... + ! ----------------------- - ! final check - else + ! NOTE: should not need to do this, since mass balance is checked in the solver + if(checkMassBalance)then - ! 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 + ! check mass balance for the canopy + if(ixVegHyd/=integerMissing)then + + ! handle cases where fluxes empty the canopy + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + if(-fluxNet*dt > canopyBalance0)then + + ! --> first add water + canopyBalance1 = canopyBalance0 + (scalarRainfall - scalarThroughfallRain)*dt + + ! --> next, remove canopy evaporation -- put the unsatisfied evap into sensible heat + canopyBalance1 = canopyBalance1 + scalarCanopyEvaporation*dt + if(canopyBalance1 < 0._rkind)then + ! * get superfluous water and energy + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + superflousNrg = superflousWat*LH_vap ! W m-2 (J m-2 s-1) + ! * update fluxes and states + canopyBalance1 = 0._rkind + scalarCanopyEvaporation = scalarCanopyEvaporation + superflousWat + scalarLatHeatCanopyEvap = scalarLatHeatCanopyEvap + superflousNrg + scalarSenHeatCanopy = scalarSenHeatCanopy - superflousNrg endif - endif ! if energy state or solution is coupled - - ! ----- - ! ------------------------ - - ! check the need to adjust temperature (will always be false if inside solver) - ! can be true if inside varSubstepSundials, outside solver, but currently will not work so turn off - if(do_adjustTemp .and. computJac)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 + ! --> next, remove canopy drainage + canopyBalance1 = canopyBalance1 - scalarCanopyLiqDrainage*dt + if(canopyBalance1 < 0._rkind)then + superflousWat = -canopyBalance1/dt ! kg m-2 s-1 + canopyBalance1 = 0._rkind + scalarCanopyLiqDrainage = scalarCanopyLiqDrainage + superflousWat endif - ! update bracket - if(residual < 0._rkind)then - tempMax = min(xTemp,tempMax) + ! update the trial state + scalarCanopyWatTrial = canopyBalance1 + + ! set the modification flag + nrgFluxModified = .true. + + else + canopyBalance1 = canopyBalance0 + fluxNet*dt + nrgFluxModified = .false. + endif ! cases where fluxes empty the canopy + + ! check the mass balance + fluxNet = scalarRainfall + scalarCanopyEvaporation - scalarThroughfallRain - scalarCanopyLiqDrainage + liqError = (canopyBalance0 + fluxNet*dt) - scalarCanopyWatTrial + if(abs(liqError) > absConvTol_liquid*10._rkind*iden_water)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyWatTrial = ', scalarCanopyWatTrial + !write(*,'(a,1x,f20.10)') 'canopyBalance0 = ', canopyBalance0 + !write(*,'(a,1x,f20.10)') 'canopyBalance1 = ', canopyBalance1 + !write(*,'(a,1x,f20.10)') 'scalarRainfall*dt = ', scalarRainfall*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyLiqDrainage*dt = ', scalarCanopyLiqDrainage*dt + !write(*,'(a,1x,f20.10)') 'scalarCanopyEvaporation*dt = ', scalarCanopyEvaporation*dt + !write(*,'(a,1x,f20.10)') 'scalarThroughfallRain*dt = ', scalarThroughfallRain*dt + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if veg canopy + + ! check mass balance for soil + ! NOTE: fatal errors, though possible to recover using negative error codes + if(count(ixSoilOnlyHyd/=integerMissing)==nSoil)then + soilBalance1 = sum( (mLayerVolFracLiqTrial(nSnow+1:nLayers) + mLayerVolFracIceTrial(nSnow+1:nLayers) )*mLayerDepth(nSnow+1:nLayers) ) + vertFlux = -(iLayerLiqFluxSoil(nSoil) - iLayerLiqFluxSoil(0))*dt ! m s-1 --> m + tranSink = sum(mLayerTranspire)*dt ! m s-1 --> m + baseSink = sum(mLayerBaseflow)*dt ! m s-1 --> m + compSink = sum(mLayerCompress(1:nSoil) * mLayerDepth(nSnow+1:nLayers) ) ! dimensionless --> m + liqError = soilBalance1 - (soilBalance0 + vertFlux + tranSink - baseSink - compSink) + if(abs(liqError) > absConvTol_liquid*10._rkind)then ! *10 because of precision issues + !write(*,'(a,1x,f20.10)') 'dt = ', dt + !write(*,'(a,1x,f20.10)') 'soilBalance0 = ', soilBalance0 + !write(*,'(a,1x,f20.10)') 'soilBalance1 = ', soilBalance1 + !write(*,'(a,1x,f20.10)') 'vertFlux = ', vertFlux + !write(*,'(a,1x,f20.10)') 'tranSink = ', tranSink + !write(*,'(a,1x,f20.10)') 'baseSink = ', baseSink + !write(*,'(a,1x,f20.10)') 'compSink = ', compSink + !write(*,'(a,1x,f20.10)') 'liqError = ', liqError + !write(*,'(a,1x,f20.10)') 'absConvTol_liquid = ', absConvTol_liquid + waterBalanceError = .true. + return + endif ! if there is a water balance error + endif ! if hydrology states exist in the soil domain + + endif ! if checking the mass balance + + ! ----- + ! * remove untapped melt energy... + ! -------------------------------- + + ! only work with energy state variables + if(size(ixNrgOnly)>0)then ! energy state variables exist + + ! loop through energy state variables + do iState=1,size(ixNrgOnly) + + ! get index of the control volume within the domain + ixSubset = ixNrgOnly(iState) ! index within the state subset + ixFullVector = ixMapSubset2Full(ixSubset) ! index within full state vector + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! compute volumetric melt (kg m-3) + volMelt = dt*untappedMelt(ixSubset)/LH_fus ! (kg m-3) + + ! update ice content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyIceTrial = scalarCanopyIceTrial - volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracIceTrial(ixControlIndex) = mLayerVolFracIceTrial(ixControlIndex) - volMelt/iden_ice ! (-) + case(iname_soil); mLayerVolFracIceTrial(ixControlIndex+nSnow) = mLayerVolFracIceTrial(ixControlIndex+nSnow) - volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + ! update liquid water content + select case( ixDomainType(ixFullVector) ) + case(iname_cas); cycle ! do nothing, since there is no snow stored in the canopy air space + case(iname_veg); scalarCanopyLiqTrial = scalarCanopyLiqTrial + volMelt*canopyDepth ! (kg m-2) + case(iname_snow); mLayerVolFracLiqTrial(ixControlIndex) = mLayerVolFracLiqTrial(ixControlIndex) + volMelt/iden_water ! (-) + case(iname_soil); mLayerVolFracLiqTrial(ixControlIndex+nSnow) = mLayerVolFracLiqTrial(ixControlIndex+nSnow) + volMelt/iden_water ! (-) + case default; err=20; message=trim(message)//'unable to identify domain type [remove untapped melt energy]'; return + end select + + end do ! looping through energy variables + + ! ======================================================================================================== + + ! *** ice + + ! --> check if we removed too much water + if(scalarCanopyIceTrial < 0._rkind .or. any(mLayerVolFracIceTrial < 0._rkind) )then + + ! ** + ! canopy within numerical precision + if(scalarCanopyIceTrial < 0._rkind)then + + if(scalarCanopyIceTrial > -verySmall)then + scalarCanopyLiqTrial = scalarCanopyLiqTrial - scalarCanopyIceTrial + scalarCanopyIceTrial = 0._rkind + + ! encountered an inconsistency: spit the dummy 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) + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! if checking the canopy + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracIceTrial) + + ! snow layer within numerical precision + if(mLayerVolFracIceTrial(iState) < 0._rkind)then + + if(mLayerVolFracIceTrial(iState) > -verySmall)then + mLayerVolFracLiqTrial(iState) = mLayerVolFracLiqTrial(iState) - mLayerVolFracIceTrial(iState) + mLayerVolFracIceTrial(iState) = 0._rkind + + ! encountered an inconsistency: spit the dummy else - if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature - endif - endif ! if the domain is soil + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial + message=trim(message)//'melted more than the available water' + err=20; return + endif ! (inconsistency) - ! update the temperature trial - xTemp = xTemp + tempInc + endif ! if checking a snow layer - ! check failed convergence - if(iter==maxiter)then - message=trim(message)//'failed to converge' - err=-20; return ! negative error code = try to recover - endif + end do ! (looping through state variables) - endif ! if adjusting the temperature + endif ! (if we removed too much water) - end do iterations ! iterating + ! ======================================================================================================== - ! save temperature - select case(ixDomainType) - case(iname_veg); scalarCanopyTempTrial = xTemp - case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp - end select + ! *** liquid water - ! ======================================================================================================================================= - ! ======================================================================================================================================= + ! --> check if we removed too much water + if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then - ! ----- - ! - compute the liquid water matric potential (and necessay derivatives)... - ! ------------------------------------------------------------------------- + ! ** + ! canopy within numerical precision + if(scalarCanopyLiqTrial < 0._rkind)then - ! only for soil - if(ixDomainType==iname_soil)then + if(scalarCanopyLiqTrial > -verySmall)then + scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial + scalarCanopyLiqTrial = 0._rkind - ! check liquid water - if(mLayerVolFracLiqTrial(iLayer) > theta_sat(ixControlIndex) )then - message=trim(message)//'liquid water greater than porosity' - err=20; return - endif - ! case of hydrology state uncoupled with energy - if(.not.isNrgState .and. .not.isCoupled)then + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'scalarCanopyLiqTrial = ', scalarCanopyLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) - ! 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 + endif ! checking the canopy - ! 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 liquidHeadSundials(& - ! input - mLayerMatricHeadTrial(ixControlIndex) ,& ! intent(in) : total water matric potential (m) - mLayerMatricHeadPrime(ixControlIndex) ,& ! - mLayerVolFracLiqTrial(iLayer) ,& ! intent(in) : volumetric fraction of liquid water (-) - mLayerVolFracIceTrial(iLayer) ,& ! intent(in) : volumetric fraction of ice (-) - vGn_alpha(ixControlIndex),vGn_n(ixControlIndex),theta_sat(ixControlIndex),theta_res(ixControlIndex),vGn_m(ixControlIndex), & ! intent(in) : soil parameters - dVolTot_dPsi0(ixControlIndex) ,& ! intent(in) : derivative in the soil water characteristic (m-1) - mLayerdTheta_dTk(iLayer) ,& ! intent(in) : derivative in volumetric total water w.r.t. temperature (K-1) - mLayerTempPrime(ixControlIndex) ,& - mLayerVolFracLiqPrime(iLayer) ,& - mLayerVolFracIcePrime(iLayer) ,& - ! output - mLayerMatricHeadLiqTrial(ixControlIndex) ,& ! intent(out): liquid water matric potential (m) - mLayerMatricHeadLiqPrime(ixControlIndex) ,& ! - 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 - - 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 + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracLiqTrial) - end subroutine updateVarsSundials + ! snow layer within numerical precision + if(mLayerVolFracLiqTrial(iState) < 0._rkind)then + if(mLayerVolFracLiqTrial(iState) > -verySmall)then + mLayerVolFracIceTrial(iState) = mLayerVolFracIceTrial(iState) - mLayerVolFracLiqTrial(iState) + mLayerVolFracLiqTrial(iState) = 0._rkind - ! ********************************************************************************************************** - ! 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 updateVarsSundials_module + ! encountered an inconsistency: spit the dummy + else + print*, 'dt = ', dt + print*, 'untappedMelt = ', untappedMelt + print*, 'untappedMelt*dt = ', untappedMelt*dt + print*, 'mLayerVolFracLiqTrial = ', mLayerVolFracLiqTrial + message=trim(message)//'frozen more than the available water' + err=20; return + endif ! (inconsistency) + + endif ! checking a snow layer + + end do ! (looping through state variables) + + endif ! (if we removed too much water) + + endif ! (if energy state variables exist) + + ! ----- + ! * update enthalpy as a diagnostic variable... + ! -------------------------------- + scalarCanairEnthalpy = scalarCanairEnthalpyTrial + scalarCanopyEnthalpy = scalarCanopyEnthalpyTrial + mLayerEnthalpy = mLayerEnthalpyTrial + + ! ----- + ! * update prognostic variables... + ! -------------------------------- + ! update state variables for the vegetation canopy + scalarCanairTemp = scalarCanairTempTrial ! trial value of canopy air temperature (K) + scalarCanopyTemp = scalarCanopyTempTrial ! trial value of canopy temperature (K) + scalarCanopyWat = scalarCanopyWatTrial ! trial value of canopy total water (kg m-2) + scalarCanopyLiq = scalarCanopyLiqTrial ! trial value of canopy liquid water (kg m-2) + scalarCanopyIce = scalarCanopyIceTrial ! trial value of canopy ice content (kg m-2) + + ! update state variables for the snow+soil domain + mLayerTemp = mLayerTempTrial ! trial vector of layer temperature (K) + mLayerVolFracWat = mLayerVolFracWatTrial ! trial vector of volumetric total water content (-) + mLayerVolFracLiq = mLayerVolFracLiqTrial ! trial vector of volumetric liquid water content (-) + mLayerVolFracIce = mLayerVolFracIceTrial ! trial vector of volumetric ice water content (-) + mLayerMatricHead = mLayerMatricHeadTrial ! trial vector of matric head (m) + mLayerMatricHeadLiq = mLayerMatricHeadLiqTrial ! trial vector of matric head (m) + + ! update state variables for the aquifer + scalarAquiferStorage = scalarAquiferStorageTrial + + ! end associations to info in the data structures + end associate + + end subroutine updateProgSundials + + end module varSubstepSundials_module \ No newline at end of file