From a21930ac2d0bda58d1b60f07622b6fea8e5ebbea Mon Sep 17 00:00:00 2001 From: Kyle <kyle.c.klenk@gmail.com> Date: Fri, 9 Sep 2022 20:02:43 +0000 Subject: [PATCH] compiles after several additions --- build/makefile_sundials | 17 +- .../source/engine/sundials/computHeatCap.f90 | 965 ++++---- .../engine/sundials/computThermConduct.f90 | 563 ++--- .../engine/sundials/summaSolveSundialsIDA.f90 | 2 +- .../engine/sundials/systemSolvSundials.f90 | 2 +- build/source/engine/sundials/tol4IDA.f90 | 2 + build/source/engine/sundials/type4IDA.f90 | 2 +- .../engine/sundials/updatStateSundials.f90 | 492 ++--- .../engine/sundials/updateVarsSundials.f90 | 1888 +++++++--------- .../engine/sundials/varSubstepSundials.f90 | 1938 ++++++++--------- 10 files changed, 2733 insertions(+), 3138 deletions(-) diff --git a/build/makefile_sundials b/build/makefile_sundials index 6775654..583ee6d 100644 --- a/build/makefile_sundials +++ b/build/makefile_sundials @@ -104,7 +104,7 @@ SUMMA_UTILMS= \ mDecisions.f90 \ snow_utils.f90 \ soil_utils.f90 \ - sundials/soil_utilsSundials.f90 \ + sundials/soil_utilsAddSundials.f90 \ updatState.f90 \ sundials/updatStateSundials.f90 \ matrixOper.f90 @@ -138,15 +138,11 @@ SUMMA_SOLVER= \ sundials/computEnthalpy.f90 \ sundials/computHeatCap.f90 \ sundials/computThermConduct.f90 \ - sundials/computResidDAE.f90 \ - sundials/eval8DAE.f90 \ - sundials/evalDAE4IDA.f90 \ - sundials/computJacDAE.f90 \ - sundials/eval8DAE.f90 \ - sundials/eval8JacDAE.f90 \ - sundials/evalJac4IDA.f90 \ + sundials/computResidSundials.f90 \ + sundials/eval8summaSundials.f90 \ + sundials/computJacobSundials.f90 \ sundials/computSnowDepth.f90 \ - sundials/solveByIDA.f90 \ + sundials/summaSolveSundialsIDA.f90 \ sundials/systemSolvSundials.f90 \ varSubstep.f90 \ sundials/varSubstepSundials.f90 \ @@ -221,11 +217,10 @@ NOAHMP = $(patsubst %, $(NOAHMP_DIR)/%, $(SUMMA_NOAHMP)) SUMMA_MODRUN = \ indexState.f90 \ getVectorz.f90 \ - sundials/varExtrSundials.f90 \ + sundials/getVectorzAddSundials.f90 \ sundials/t2enthalpy.f90 \ updateVars.f90 \ sundials/updateVarsSundials.f90 \ - sundials/updateVars4JacDAE.f90 \ var_derive.f90 \ read_forcingActors.f90 \ access_forcing.f90\ diff --git a/build/source/engine/sundials/computHeatCap.f90 b/build/source/engine/sundials/computHeatCap.f90 index 87b7bd5..53997c8 100644 --- a/build/source/engine/sundials/computHeatCap.f90 +++ b/build/source/engine/sundials/computHeatCap.f90 @@ -20,487 +20,492 @@ module computHeatCap_module -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! named variables defining elements in the data structures -USE var_lookup,only:iLookPARAM,iLookDIAG,iLookINDEX ! named variables for structure elements - -! physical constants -USE multiconst,only:& - Tfreeze, & ! freezing point of water (K) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of 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) -! 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 -USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer - -! missing values -USE globalData,only:integerMissing ! missing integer - -! named variables that define the layer type -USE globalData,only:iname_snow ! snow -USE globalData,only:iname_soil ! soil - - -! privacy -implicit none -private -public::computHeatCap -public::computStatMult -public::computHeatCapAnalytic -public::computCm - -contains - - - ! ********************************************************************************************************** - ! public subroutine computHeatCap: compute diagnostic energy variables (heat capacity) - ! ********************************************************************************************************** - subroutine computHeatCap(& - ! input: control variables - nLayers, & ! intent(in): number of layers (soil+snow) - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! input: state variables - scalarCanopyIce, & ! intent(in) - scalarCanopyLiquid, & ! intent(in) - scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) - scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) - scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) - scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerTempTrial, & ! intent(in): trial temperature - mLayerTempPrev, & ! intent(in): previous temperature - mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil - mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil - ! output - heatCapVeg, & - mLayerHeatCap, & ! intent(out): heat capacity for snow and soil - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: control variables - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - ! input: - integer(i4b),intent(in) :: nLayers - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature - real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) - real(rkind),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - real(rkind),intent(in) :: mLayerTempTrial(:) - real(rkind),intent(in) :: mLayerTempPrev(:) - real(rkind),intent(in) :: mLayerEnthalpyTrial(:) - real(rkind),intent(in) :: mLayerEnthalpyPrev(:) - ! output: - real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of model layer - real(rkind) :: delT - real(rkind) :: delEnt - integer(i4b) :: iSoil ! index of soil layer - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) - ) ! end associate statemen - ! initialize error control - err=0; message="computHeatCap/" - - ! initialize the soil layer - iSoil=integerMissing - - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - delT = scalarCanopyTempTrial - scalarCanopyTempPrev - if(abs(delT) <= 1e-14_rkind)then + ! data types + USE nrtype + + ! derived types to define the data structures + USE data_types,only:& + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (rkind) + + ! named variables defining elements in the data structures + USE var_lookup,only:iLookPARAM,iLookDIAG,iLookINDEX ! named variables for structure elements + + ! physical constants + USE multiconst,only:& + Tfreeze, & ! freezing point of water (K) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of 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) + ! 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 + USE globalData,only:iname_watAquifer ! named variable defining the water storage in the aquifer + + ! missing values + USE globalData,only:integerMissing ! missing integer + + ! named variables that define the layer type + USE globalData,only:iname_snow ! snow + USE globalData,only:iname_soil ! soil + + + ! privacy + implicit none + private + public::computHeatCap + public::computStatMult + public::computHeatCapAnalytic + public::computCm + + contains + + + ! ********************************************************************************************************** + ! public subroutine computHeatCap: compute diagnostic energy variables (heat capacity) + ! ********************************************************************************************************** + subroutine computHeatCap(& + ! input: control variables + nLayers, & ! intent(in): number of layers (soil+snow) + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + diag_data, & ! intent(in): model diagnostic variables for a local HRU + ! input: state variables + scalarCanopyIce, & ! intent(in) + scalarCanopyLiquid, & ! intent(in) + scalarCanopyTempTrial, & ! intent(in): trial value of canopy temperature (K) + scalarCanopyTempPrev, & ! intent(in): previous value of canopy temperature (K) + scalarCanopyEnthalpyTrial, & ! intent(in): trial enthalpy of the vegetation canopy (J m-3) + scalarCanopyEnthalpyPrev, & ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerTempTrial, & ! intent(in): trial temperature + mLayerTempPrev, & ! intent(in): previous temperature + mLayerEnthalpyTrial, & ! intent(in): trial enthalpy for snow and soil + mLayerEnthalpyPrev, & ! intent(in): previous enthalpy for snow and soil + ! output + heatCapVeg, & + mLayerHeatCap, & ! intent(out): heat capacity for snow and soil + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: control variables + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! input: + integer(i4b),intent(in) :: nLayers + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature + real(rkind),intent(in) :: scalarCanopyEnthalpyTrial ! trial enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyEnthalpyPrev ! intent(in): previous enthalpy of the vegetation canopy (J m-3) + real(rkind),intent(in) :: scalarCanopyTempPrev ! Previous value of canopy temperature + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + real(rkind),intent(in) :: mLayerTempTrial(:) + real(rkind),intent(in) :: mLayerTempPrev(:) + real(rkind),intent(in) :: mLayerEnthalpyTrial(:) + real(rkind),intent(in) :: mLayerEnthalpyPrev(:) + ! output: + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + integer(i4b) :: iLayer ! index of model layer + real(rkind) :: delT + real(rkind) :: delEnt + integer(i4b) :: iSoil ! index of soil layer + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + ) ! end associate statemen + ! initialize error control + err=0; message="computHeatCap/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + delT = scalarCanopyTempTrial - scalarCanopyTempPrev + if(abs(delT) <= 1e-14_rkind)then heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - else - delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev - heatCapVeg = delEnt / delT + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + else + delEnt = scalarCanopyEnthalpyTrial - scalarCanopyEnthalpyPrev + heatCapVeg = delEnt / delT + end if end if - end if - - ! loop through layers - do iLayer=1,nLayers - delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) - if(abs(delT) <= 1e-14_rkind)then - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - else - delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) - mLayerHeatCap(iLayer) = delEnt / delT + + ! loop through layers + do iLayer=1,nLayers + delT = mLayerTempTrial(iLayer) - mLayerTempPrev(iLayer) + if(abs(delT) <= 1e-14_rkind)then + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_Ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + case(iname_snow) + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + end select + else + delEnt = mLayerEnthalpyTrial(iLayer) - mLayerEnthalpyPrev(iLayer) + mLayerHeatCap(iLayer) = delEnt / delT + endif + end do ! looping through layers + + end associate + + end subroutine computHeatCap + + ! ********************************************************************************************************** + ! public subroutine computStatMult: get scale factors + ! ********************************************************************************************************** + subroutine computStatMult(& + heatCapVeg, & + mLayerHeatCap, & + ! input: data structures + diag_data, & ! intent(in): model diagnostic variables for a local HRU + indx_data, & ! intent(in): indices defining model states and layers + ! output + sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------- + USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) + USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector + ! -------------------------------------------------------------------------------------------------------------------------------- + ! input: data structures + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU + type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + ! output: state vectors + real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + ! -------------------------------------------------------------------------------------------------------------------------------- + ! state subsets + integer(i4b) :: iLayer ! index of layer within the snow+soil domain + integer(i4b) :: ixStateSubset ! index within the state subset + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + ! make association with variables in the data structures + associate(& + ! model diagnostic variables + canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) + volHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in) : [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) + ! indices defining specific model states + ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable + ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable + ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) + ! vector of energy and hydrology indices for the snow and soil domains + ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain + ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain + nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain + nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain + ! type of model state variabless + ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables + ! 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 + ) ! end association with variables in the data structures + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message='computStatMult/' + + ! ----- + ! * define components of derivative matrices that are constant over a time step (substep)... + ! ------------------------------------------------------------------------------------------ + + ! define the multiplier for the state vector for residual calculations (vegetation canopy) + ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) + + where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) + where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) + where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side + where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side + + + ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) + if(nSnowSoilNrg>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector + sMul(ixStateSubset) = mLayerHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier + end do ! looping through non-missing energy state variables in the snow+soil domain endif - end do ! looping through layers - - end associate - - end subroutine computHeatCap - - ! ********************************************************************************************************** - ! public subroutine computStatMult: get scale factors - ! ********************************************************************************************************** - subroutine computStatMult(& - heatCapVeg, & - mLayerHeatCap, & - ! input: data structures - diag_data, & ! intent(in): model diagnostic variables for a local HRU - indx_data, & ! intent(in): indices defining model states and layers - ! output - sMul, & ! intent(out): multiplier for state vector (used in the residual calculations) - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------- - USE nr_utility_module,only:arth ! get a sequence of numbers arth(start, incr, count) - USE f2008funcs_module,only:findIndex ! finds the index of the first value within a vector - ! -------------------------------------------------------------------------------------------------------------------------------- - ! input: data structures - real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) - type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU - type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers - ! output: state vectors - real(qp),intent(out) :: sMul(:) ! NOTE: qp ! multiplier for state vector (used in the residual calculations) - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - ! -------------------------------------------------------------------------------------------------------------------------------- - ! state subsets - integer(i4b) :: iLayer ! index of layer within the snow+soil domain - integer(i4b) :: ixStateSubset ! index within the state subset - ! -------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------- - ! make association with variables in the data structures - associate(& - ! model diagnostic variables - canopyDepth => diag_data%var(iLookDIAG%scalarCanopyDepth)%dat(1) ,& ! intent(in): [dp] canopy depth (m) - volHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1),& ! intent(in) : [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) - ! indices defining specific model states - ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy air space energy state variable - ixVegNrg => indx_data%var(iLookINDEX%ixVegNrg)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy energy state variable - ixVegHyd => indx_data%var(iLookINDEX%ixVegHyd)%dat ,& ! intent(in) : [i4b(:)] [length=1] index of canopy hydrology state variable (mass) - ! vector of energy and hydrology indices for the snow and soil domains - ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for energy state variables in the snow+soil domain - ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in) : [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain - nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in) : [i4b] number of energy state variables in the snow+soil domain - nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in) : [i4b] number of hydrology state variables in the snow+soil domain - ! type of model state variabless - ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in) : [i4b(:)] [state subset] type of desired model state variables - ! 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 - ) ! end association with variables in the data structures - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message='computStatMult/' - - ! ----- - ! * define components of derivative matrices that are constant over a time step (substep)... - ! ------------------------------------------------------------------------------------------ - - ! define the multiplier for the state vector for residual calculations (vegetation canopy) - ! NOTE: Use the "where" statement to generalize to multiple canopy layers (currently one canopy layer) - - where(ixStateType_subset==iname_nrgCanair) sMul = Cp_air*iden_air ! volumetric heat capacity of air (J m-3 K-1) - where(ixStateType_subset==iname_nrgCanopy) sMul = heatCapVeg ! volumetric heat capacity of the vegetation (J m-3 K-1) - where(ixStateType_subset==iname_watCanopy) sMul = 1._rkind ! nothing else on the left hand side - where(ixStateType_subset==iname_liqCanopy) sMul = 1._rkind ! nothing else on the left hand side - - - ! define the energy multiplier for the state vector for residual calculations (snow-soil domain) - if(nSnowSoilNrg>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilNrg(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilNrg(iLayer) ! index within the state vector - sMul(ixStateSubset) = mLayerHeatCap(iLayer) ! transfer volumetric heat capacity to the state multiplier - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! define the hydrology multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) - if(nSnowSoilHyd>0)then - do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) - ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector - sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) - end do ! looping through non-missing energy state variables in the snow+soil domain - endif - - ! define the scaling factor and diagonal elements for the aquifer - where(ixStateType_subset==iname_watAquifer) sMul = 1._rkind - - ! ------------------------------------------------------------------------------------------ - ! ------------------------------------------------------------------------------------------ - - end associate - ! end association to variables in the data structure where vector length does not change - end subroutine computStatMult - - ! ********************************************************************************************************** - ! public subroutine computHeatCapAnalytic: compute diagnostic energy variables (heat capacity) - ! ********************************************************************************************************** - subroutine computHeatCapAnalytic(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - canopyDepth, & ! intent(in): canopy depth (m) - ! input: state variables - scalarCanopyIce, & ! intent(in) - scalarCanopyLiquid, & ! intent(in) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! output - heatCapVeg, & - mLayerHeatCap, & - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - ! output: error control - real(qp),intent(out) :: heatCapVeg - real(qp),intent(out) :: mLayerHeatCap(:) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computHeatCapAnalytic/" - - ! initialize the soil layer - iSoil=integerMissing - - ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) - if(computeVegFlux)then - heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component - Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component - Cp_ice*scalarCanopyIce/canopyDepth ! ice component - end if - - ! loop through layers - do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! ***** - ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... - ! ******************************************************************* - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component - case(iname_snow) - mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component - iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return - end select - - end do ! looping through layers - !pause - - ! end association to variables in the data structure - end associate - - end subroutine computHeatCapAnalytic - - ! ********************************************************************************************************** - ! public subroutine computCm - ! ********************************************************************************************************** - subroutine computCm(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - ! input: state variables - scalarCanopyTemp, & ! intent(in) - mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - mLayerMatricHead, & ! intent(in) - ! input data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - ! output - scalarCanopyCm, & ! intent(out): Cm for vegetation - mLayerCm, & ! intent(out): Cm for soil and snow - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) - real(rkind),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - ! output: error control - real(qp),intent(out) :: scalarCanopyCm - real(qp),intent(out) :: mLayerCm(:) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: g1 - real(rkind) :: g2 - real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) - - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) -snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computCm/" - - ! initialize the soil layer - iSoil=integerMissing - - ! compute Cm of vegetation - ! Note that scalarCanopyCm/iden_water is computed - if(computeVegFlux)then - g2 = scalarCanopyTemp - Tfreeze - g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) - if(scalarCanopyTemp < Tfreeze)then - scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) - else - scalarCanopyCm = Cp_water * g2 - end if - end if - - ! loop through layers - do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! ***** - ! * compute Cm of of each layer - ! ******************************************************************* - select case(layerType(iLayer)) - ! * soil - case(iname_soil) - g2 = mLayerTemp(iLayer) - Tfreeze - Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) - if( mLayerTemp(iLayer) < Tcrit)then - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * g2 - else - mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * g2 - end if - - case(iname_snow) - g2 = mLayerTemp(iLayer) - Tfreeze + + ! define the hydrology multiplier and diagonal elements for the state vector for residual calculations (snow-soil domain) + if(nSnowSoilHyd>0)then + do concurrent (iLayer=1:nLayers,ixSnowSoilHyd(iLayer)/=integerMissing) ! (loop through non-missing energy state variables in the snow+soil domain) + ixStateSubset = ixSnowSoilHyd(iLayer) ! index within the state vector + sMul(ixStateSubset) = 1._rkind ! state multiplier = 1 (nothing else on the left-hand-side) + end do ! looping through non-missing energy state variables in the snow+soil domain + endif + + ! define the scaling factor and diagonal elements for the aquifer + where(ixStateType_subset==iname_watAquifer) sMul = 1._rkind + + ! ------------------------------------------------------------------------------------------ + ! ------------------------------------------------------------------------------------------ + + end associate + ! end association to variables in the data structure where vector length does not change + end subroutine computStatMult + + ! ********************************************************************************************************** + ! public subroutine computHeatCapAnalytic: compute diagnostic energy variables (heat capacity) + ! ********************************************************************************************************** + subroutine computHeatCapAnalytic(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIce, & ! intent(in) + scalarCanopyLiquid, & ! intent(in) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + heatCapVeg, & + mLayerHeatCap, & + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: error control + real(qp),intent(out) :: heatCapVeg + real(qp),intent(out) :: mLayerHeatCap(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat & ! intent(in): soil porosity (-) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computHeatCapAnalytic/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute the bulk volumetric heat capacity of vegetation (J m-3 K-1) + if(computeVegFlux)then + heatCapVeg = specificHeatVeg*maxMassVegetation/canopyDepth + & ! vegetation component + Cp_water*scalarCanopyLiquid/canopyDepth + & ! liquid water component + Cp_ice*scalarCanopyIce/canopyDepth ! ice component + end if + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! ***** + ! * compute the volumetric heat capacity of each layer (J m-3 K-1)... + ! ******************************************************************* + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + mLayerHeatCap(iLayer) = iden_soil(iSoil) * Cp_soil * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) )! air component + case(iname_snow) + mLayerHeatCap(iLayer) = iden_ice * Cp_ice * mLayerVolFracIce(iLayer) + & ! ice component + iden_water * Cp_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + iden_air * Cp_air * ( 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) ) ! air component + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute olumetric heat capacity'; return + end select + + end do ! looping through layers + !pause + + ! end association to variables in the data structure + end associate + + end subroutine computHeatCapAnalytic + + ! ********************************************************************************************************** + ! public subroutine computCm + ! ********************************************************************************************************** + subroutine computCm(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + ! input: state variables + scalarCanopyTemp, & ! intent(in) + mLayerTemp, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + mLayerMatricHead, & ! intent(in) + ! input data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + ! output + scalarCanopyCm, & ! intent(out): Cm for vegetation + mLayerCm, & ! intent(out): Cm for soil and snow + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE soil_utils_module,only:crit_soilT ! compute critical temperature below which ice exists + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: scalarCanopyTemp ! value of canopy ice content (kg m-2) + real(rkind),intent(in) :: mLayerTemp(:) ! vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerMatricHead(:) ! vector of total water matric potential (m) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + ! output: error control + real(qp),intent(out) :: scalarCanopyCm + real(qp),intent(out) :: mLayerCm(:) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: g1 + real(rkind) :: g2 + real(rkind) :: Tcrit ! temperature where all water is unfrozen (K) + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + snowfrz_scale => mpar_data%var(iLookPARAM%snowfrz_scale)%dat(1) & ! intent(in): [dp] scaling parameter for the snow freezing curve (K-1) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computCm/" + + ! initialize the soil layer + iSoil=integerMissing + + ! compute Cm of vegetation + ! Note that scalarCanopyCm/iden_water is computed + if(computeVegFlux)then + g2 = scalarCanopyTemp - Tfreeze g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) - mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & - + (iden_water * Cp_water - iden_air * Cp_air) * g1 - - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return - end select - - end do ! looping through layers - !pause - - ! end association to variables in the data structure - end associate - - end subroutine computCm - - -end module computHeatCap_module + if(scalarCanopyTemp < Tfreeze)then + scalarCanopyCm = Cp_water * g1 + Cp_ice * (g2 - g1) + else + scalarCanopyCm = Cp_water * g2 + end if + end if + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! ***** + ! * compute Cm of of each layer + ! ******************************************************************* + select case(layerType(iLayer)) + ! * soil + case(iname_soil) + g2 = mLayerTemp(iLayer) - Tfreeze + Tcrit = crit_soilT( mLayerMatricHead(iSoil) ) + if( mLayerTemp(iLayer) < Tcrit)then + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air) * g2 + else + mLayerCm(iLayer) = (iden_water * Cp_water - iden_air * Cp_air) * g2 + end if + + case(iname_snow) + g2 = mLayerTemp(iLayer) - Tfreeze + g1 = (1._rkind/snowfrz_scale) * atan(snowfrz_scale * g2) + mLayerCm(iLayer) = (iden_ice * Cp_ice - iden_air * Cp_air * iden_water/iden_ice) * ( g2 - g1 ) & + + (iden_water * Cp_water - iden_air * Cp_air) * g1 + + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute Cm'; return + end select + + end do ! looping through layers + !pause + + ! end association to variables in the data structure + end associate + + end subroutine computCm + + + end module computHeatCap_module + \ No newline at end of file diff --git a/build/source/engine/sundials/computThermConduct.f90 b/build/source/engine/sundials/computThermConduct.f90 index 2a9c646..dc5ab72 100644 --- a/build/source/engine/sundials/computThermConduct.f90 +++ b/build/source/engine/sundials/computThermConduct.f90 @@ -1,285 +1,288 @@ module computThermConduct_module -! data types -USE nrtype - -! derived types to define the data structures -USE data_types,only:& - var_d, & ! data vector (rkind) - var_ilength, & ! data vector with variable length dimension (i4b) - var_dlength ! data vector with variable length dimension (rkind) - -! named variables defining elements in the data structures -USE var_lookup,only:iLookPARAM,iLookPROG,iLookDIAG,iLookINDEX ! named variables for structure elements -USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure - -! physical constants -USE multiconst,only:& - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of 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) - ! thermal conductivity - lambda_air, & ! thermal conductivity of air (J s-1 m-1) - lambda_ice, & ! thermal conductivity of ice (J s-1 m-1) - lambda_water ! thermal conductivity of water (J s-1 m-1) - -! missing values -USE globalData,only:integerMissing ! missing integer -USE globalData,only:realMissing ! missing real number - -! named variables that define the layer type -USE globalData,only:iname_snow ! snow -USE globalData,only:iname_soil ! soil - -! provide access to named variables for thermal conductivity of soil -USE globalData,only:model_decisions ! model decision structure - -! decisions for thermal conductivity of soil -USE mDecisions_module,only:Smirnova2000 ! option for temporally constant thermal conductivity - -! decisions for thermal conductivity of soil -USE mDecisions_module,only: funcSoilWet, & ! function of soil wetness - mixConstit, & ! mixture of constituents - hanssonVZJ ! test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 - -! privacy -implicit none -private -public::computThermConduct - -! algorithmic parameters -real(rkind),parameter :: valueMissing=-9999._rkind ! missing value, used when diagnostic or state variables are undefined -real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers -real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero -real(rkind),parameter :: dx=1.e-6_rkind ! finite difference increment -contains - - - ! ********************************************************************************************************** - ! public subroutine computThermConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) - ! ********************************************************************************************************** - subroutine computThermConduct(& - ! input: control variables - computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux - ! input: state variables - scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) - scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) - mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) - mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) - ! input/output: data structures - mpar_data, & ! intent(in): model parameters - indx_data, & ! intent(in): model layer indices - prog_data, & ! intent(in): model prognostic variables for a local HRU - diag_data, & ! intent(inout): model diagnostic variables for a local HRU - ! output: error control - err,message) ! intent(out): error control - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! provide access to external subroutines - USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow - ! -------------------------------------------------------------------------------------------------------------------------------------- - ! input: model control - logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux - real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) - real(rkind),intent(in) :: scalarCanopyLiquid - real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) - real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) - ! input/output: data structures - type(var_dlength),intent(in) :: mpar_data ! model parameters - type(var_ilength),intent(in) :: indx_data ! model layer indices - type(var_dlength),intent(in) :: prog_data ! model prognostic variables for a local HRU - type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU - ! output: error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! -------------------------------------------------------------------------------------------------------------------------------- - ! local variables - character(LEN=256) :: cmessage ! error message of downwind routine - integer(i4b) :: iLayer ! index of model layer - integer(i4b) :: iSoil ! index of soil layer - real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) - real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) - real(rkind) :: zdn ! height difference between interface and lower value (m) - real(rkind) :: zdp ! height difference between interface and upper value (m) - real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) - real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) - real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) - real(rkind) :: lambda_wet ! thermal conductivity of the wet material - real(rkind) :: relativeSat ! relative saturation (-) - real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium - real(rkind) :: den ! denominator in the thermal conductivity calculations - ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 - real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) - real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) - real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) - ! -------------------------------------------------------------------------------------------------------------------------------- - ! associate variables in data structure - associate(& - ! input: model decisions - ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow - ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil - ! input: coordinate variables - nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers - nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): number of soil layers - nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers - layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) - mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) - iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) - ! input: heat capacity and thermal conductivity - specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) - maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) - fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) - ! input: depth varying soil parameters - iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) - thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) - theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) - frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) - frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) - frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) - ! output: diagnostic variables - mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) - iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) - mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) - ) ! end associate statement - ! -------------------------------------------------------------------------------------------------------------------------------- - ! initialize error control - err=0; message="computThermConduct/" - - ! initialize the soil layer - iSoil=integerMissing - - ! loop through layers - do iLayer=1,nLayers - - ! get the soil layer - if(iLayer>nSnow) iSoil = iLayer-nSnow - - ! compute the thermal conductivity of dry and wet soils (W m-1) - ! NOTE: this is actually constant over the simulation, and included here for clarity - if(ixThCondSoil == funcSoilWet .and. layerType(iLayer)==iname_soil)then - bulkden_soil = iden_soil(iSoil)*( 1._rkind - theta_sat(iSoil) ) - lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iSoil) - 0.947_rkind*bulkden_soil) - lambda_wetsoil = (8.80_rkind*frac_sand(iSoil) + 2.92_rkind*frac_clay(iSoil)) / (frac_sand(iSoil) + frac_clay(iSoil)) - end if - - ! ***** - ! * compute the volumetric fraction of air in each layer... - ! ********************************************************* - select case(layerType(iLayer)) - case(iname_soil); mLayerVolFracAir(iLayer) = theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) - case(iname_snow); mLayerVolFracAir(iLayer) = 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return - end select - - ! ***** - ! * compute the thermal conductivity of snow and soil at the mid-point of each layer... - ! ************************************************************************************* - select case(layerType(iLayer)) - - ! ***** soil - case(iname_soil) - - ! select option for thermal conductivity of soil - select case(ixThCondSoil) - - ! ** function of soil wetness - case(funcSoilWet) - - ! compute the thermal conductivity of the wet material (W m-1) - lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iSoil) ) * lambda_water**theta_sat(iSoil) * lambda_ice**(theta_sat(iSoil) - mLayerVolFracLiq(iLayer)) - relativeSat = (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer))/theta_sat(iSoil) ! relative saturation - ! compute the Kersten number (-) - if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 - kerstenNum = log10(relativeSat) + 1._rkind + ! data types + USE nrtype + + ! derived types to define the data structures + USE data_types,only:& + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (rkind) + + ! named variables defining elements in the data structures + USE var_lookup,only:iLookPARAM,iLookPROG,iLookDIAG,iLookINDEX ! named variables for structure elements + USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure + + ! physical constants + USE multiconst,only:& + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of 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) + ! thermal conductivity + lambda_air, & ! thermal conductivity of air (J s-1 m-1) + lambda_ice, & ! thermal conductivity of ice (J s-1 m-1) + lambda_water ! thermal conductivity of water (J s-1 m-1) + + ! missing values + USE globalData,only:integerMissing ! missing integer + USE globalData,only:realMissing ! missing real number + + ! named variables that define the layer type + USE globalData,only:iname_snow ! snow + USE globalData,only:iname_soil ! soil + + ! provide access to named variables for thermal conductivity of soil + USE globalData,only:model_decisions ! model decision structure + + ! decisions for thermal conductivity of soil + USE mDecisions_module,only:Smirnova2000 ! option for temporally constant thermal conductivity + + ! decisions for thermal conductivity of soil + USE mDecisions_module,only: funcSoilWet, & ! function of soil wetness + mixConstit, & ! mixture of constituents + hanssonVZJ ! test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + + ! privacy + implicit none + private + public::computThermConduct + + ! algorithmic parameters + real(rkind),parameter :: valueMissing=-9999._rkind ! missing value, used when diagnostic or state variables are undefined + real(rkind),parameter :: verySmall=1.e-6_rkind ! used as an additive constant to check if substantial difference among real numbers + real(rkind),parameter :: mpe=1.e-6_rkind ! prevents overflow error if division by zero + real(rkind),parameter :: dx=1.e-6_rkind ! finite difference increment + contains + + + ! ********************************************************************************************************** + ! public subroutine computThermConduct: compute diagnostic energy variables (thermal conductivity and heat capacity) + ! ********************************************************************************************************** + subroutine computThermConduct(& + ! input: control variables + computeVegFlux, & ! intent(in): flag to denote if computing the vegetation flux + canopyDepth, & ! intent(in): canopy depth (m) + ! input: state variables + scalarCanopyIce, & ! intent(in): canopy ice content (kg m-2) + scalarCanopyLiquid, & ! intent(in): canopy liquid water content (kg m-2) + mLayerVolFracIce, & ! intent(in): volumetric fraction of ice at the start of the sub-step (-) + mLayerVolFracLiq, & ! intent(in): volumetric fraction of liquid water at the start of the sub-step (-) + ! input/output: data structures + mpar_data, & ! intent(in): model parameters + indx_data, & ! intent(in): model layer indices + prog_data, & ! intent(in): model prognostic variables for a local HRU + diag_data, & ! intent(inout): model diagnostic variables for a local HRU + ! output: error control + err,message) ! intent(out): error control + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! provide access to external subroutines + USE snow_utils_module,only:tcond_snow ! compute thermal conductivity of snow + ! -------------------------------------------------------------------------------------------------------------------------------------- + ! input: model control + logical(lgt),intent(in) :: computeVegFlux ! logical flag to denote if computing the vegetation flux + real(rkind),intent(in) :: canopyDepth ! depth of the vegetation canopy (m) + real(rkind),intent(in) :: scalarCanopyIce ! trial value of canopy ice content (kg m-2) + real(rkind),intent(in) :: scalarCanopyLiquid + real(rkind),intent(in) :: mLayerVolFracLiq(:) ! trial vector of volumetric liquid water content (-) + real(rkind),intent(in) :: mLayerVolFracIce(:) ! trial vector of volumetric ice water content (-) + ! input/output: data structures + type(var_dlength),intent(in) :: mpar_data ! model parameters + type(var_ilength),intent(in) :: indx_data ! model layer indices + type(var_dlength),intent(in) :: prog_data ! model prognostic variables for a local HRU + type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU + ! output: error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! -------------------------------------------------------------------------------------------------------------------------------- + ! local variables + character(LEN=256) :: cmessage ! error message of downwind routine + integer(i4b) :: iLayer ! index of model layer + integer(i4b) :: iSoil ! index of soil layer + real(rkind) :: TCn ! thermal conductivity below the layer interface (W m-1 K-1) + real(rkind) :: TCp ! thermal conductivity above the layer interface (W m-1 K-1) + real(rkind) :: zdn ! height difference between interface and lower value (m) + real(rkind) :: zdp ! height difference between interface and upper value (m) + real(rkind) :: bulkden_soil ! bulk density of soil (kg m-3) + real(rkind) :: lambda_drysoil ! thermal conductivity of dry soil (W m-1) + real(rkind) :: lambda_wetsoil ! thermal conductivity of wet soil (W m-1) + real(rkind) :: lambda_wet ! thermal conductivity of the wet material + real(rkind) :: relativeSat ! relative saturation (-) + real(rkind) :: kerstenNum ! the Kersten number (-), defining weight applied to conductivity of the wet medium + real(rkind) :: den ! denominator in the thermal conductivity calculations + ! local variables to reproduce the thermal conductivity of Hansson et al. VZJ 2005 + real(rkind),parameter :: c1=0.55_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c2=0.8_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c3=3.07_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: c4=0.13_rkind ! optimized parameter from Hansson et al. VZJ 2005 (W m-1 K-1) + real(rkind),parameter :: c5=4._rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f1=13.05_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind),parameter :: f2=1.06_rkind ! optimized parameter from Hansson et al. VZJ 2005 (-) + real(rkind) :: fArg,xArg ! temporary variables (see Hansson et al. VZJ 2005 for details) + ! -------------------------------------------------------------------------------------------------------------------------------- + ! associate variables in data structure + associate(& + ! input: model decisions + ixThCondSnow => model_decisions(iLookDECISIONS%thCondSnow)%iDecision, & ! intent(in): choice of method for thermal conductivity of snow + ixThCondSoil => model_decisions(iLookDECISIONS%thCondSoil)%iDecision, & ! intent(in): choice of method for thermal conductivity of soil + ! input: coordinate variables + nSnow => indx_data%var(iLookINDEX%nSnow)%dat(1), & ! intent(in): number of snow layers + nSoil => indx_data%var(iLookINDEX%nSoil)%dat(1), & ! intent(in): number of soil layers + nLayers => indx_data%var(iLookINDEX%nLayers)%dat(1), & ! intent(in): total number of layers + layerType => indx_data%var(iLookINDEX%layerType)%dat, & ! intent(in): layer type (iname_soil or iname_snow) + mLayerHeight => prog_data%var(iLookPROG%mLayerHeight)%dat, & ! intent(in): height at the mid-point of each layer (m) + iLayerHeight => prog_data%var(iLookPROG%iLayerHeight)%dat, & ! intent(in): height at the interface of each layer (m) + ! input: heat capacity and thermal conductivity + specificHeatVeg => mpar_data%var(iLookPARAM%specificHeatVeg)%dat(1), & ! intent(in): specific heat of vegetation (J kg-1 K-1) + maxMassVegetation => mpar_data%var(iLookPARAM%maxMassVegetation)%dat(1), & ! intent(in): maximum mass of vegetation (kg m-2) + fixedThermalCond_snow => mpar_data%var(iLookPARAM%fixedThermalCond_snow)%dat(1), & ! intent(in): temporally constant thermal conductivity of snow (W m-1 K-1) + ! input: depth varying soil parameters + iden_soil => mpar_data%var(iLookPARAM%soil_dens_intr)%dat, & ! intent(in): intrinsic density of soil (kg m-3) + thCond_soil => mpar_data%var(iLookPARAM%thCond_soil)%dat, & ! intent(in): thermal conductivity of soil (W m-1 K-1) + theta_sat => mpar_data%var(iLookPARAM%theta_sat)%dat, & ! intent(in): soil porosity (-) + frac_sand => mpar_data%var(iLookPARAM%frac_sand)%dat, & ! intent(in): fraction of sand (-) + frac_silt => mpar_data%var(iLookPARAM%frac_silt)%dat, & ! intent(in): fraction of silt (-) + frac_clay => mpar_data%var(iLookPARAM%frac_clay)%dat, & ! intent(in): fraction of clay (-) + ! output: diagnostic variables + mLayerThermalC => diag_data%var(iLookDIAG%mLayerThermalC)%dat, & ! intent(out): thermal conductivity at the mid-point of each layer (W m-1 K-1) + iLayerThermalC => diag_data%var(iLookDIAG%iLayerThermalC)%dat, & ! intent(out): thermal conductivity at the interface of each layer (W m-1 K-1) + mLayerVolFracAir => diag_data%var(iLookDIAG%mLayerVolFracAir)%dat & ! intent(out): volumetric fraction of air in each layer (-) + ) ! end associate statement + ! -------------------------------------------------------------------------------------------------------------------------------- + ! initialize error control + err=0; message="computThermConduct/" + + ! initialize the soil layer + iSoil=integerMissing + + ! loop through layers + do iLayer=1,nLayers + + ! get the soil layer + if(iLayer>nSnow) iSoil = iLayer-nSnow + + ! compute the thermal conductivity of dry and wet soils (W m-1) + ! NOTE: this is actually constant over the simulation, and included here for clarity + if(ixThCondSoil == funcSoilWet .and. layerType(iLayer)==iname_soil)then + bulkden_soil = iden_soil(iSoil)*( 1._rkind - theta_sat(iSoil) ) + lambda_drysoil = (0.135_rkind*bulkden_soil + 64.7_rkind) / (iden_soil(iSoil) - 0.947_rkind*bulkden_soil) + lambda_wetsoil = (8.80_rkind*frac_sand(iSoil) + 2.92_rkind*frac_clay(iSoil)) / (frac_sand(iSoil) + frac_clay(iSoil)) + end if + + ! ***** + ! * compute the volumetric fraction of air in each layer... + ! ********************************************************* + select case(layerType(iLayer)) + case(iname_soil); mLayerVolFracAir(iLayer) = theta_sat(iSoil) - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) + case(iname_snow); mLayerVolFracAir(iLayer) = 1._rkind - (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer)) + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute volumetric fraction of air'; return + end select + + ! ***** + ! * compute the thermal conductivity of snow and soil at the mid-point of each layer... + ! ************************************************************************************* + select case(layerType(iLayer)) + + ! ***** soil + case(iname_soil) + + ! select option for thermal conductivity of soil + select case(ixThCondSoil) + + ! ** function of soil wetness + case(funcSoilWet) + + ! compute the thermal conductivity of the wet material (W m-1) + lambda_wet = lambda_wetsoil**( 1._rkind - theta_sat(iSoil) ) * lambda_water**theta_sat(iSoil) * lambda_ice**(theta_sat(iSoil) - mLayerVolFracLiq(iLayer)) + relativeSat = (mLayerVolFracIce(iLayer) + mLayerVolFracLiq(iLayer))/theta_sat(iSoil) ! relative saturation + ! compute the Kersten number (-) + if(relativeSat > 0.1_rkind)then ! log10(0.1) = -1 + kerstenNum = log10(relativeSat) + 1._rkind + else + kerstenNum = 0._rkind ! dry thermal conductivity + endif + ! ...and, compute the thermal conductivity + mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil + + ! ** mixture of constituents + case(mixConstit) + mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component + lambda_ice * mLayerVolFracIce(iLayer) + & ! ice component + lambda_water * mLayerVolFracLiq(iLayer) + & ! liquid water component + lambda_air * mLayerVolFracAir(iLayer) ! air component + + ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 + case(hanssonVZJ) + fArg = 1._rkind + f1*mLayerVolFracIce(iLayer)**f2 + xArg = mLayerVolFracLiq(iLayer) + fArg*mLayerVolFracIce(iLayer) + mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) + + ! ** check + case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return + + end select ! option for the thermal conductivity of soil + + ! ***** snow + case(iname_snow) + ! temporally constant thermal conductivity + if(ixThCondSnow==Smirnova2000)then + mLayerThermalC(iLayer) = fixedThermalCond_snow + ! thermal conductivity as a function of snow density + else + call tcond_snow(mLayerVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) + mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) + err,cmessage) ! output: error control + if(err/=0)then; message=trim(message)//trim(cmessage); return; end if + endif + + ! * error check + case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return + + end select + !print*, 'iLayer, mLayerThermalC(iLayer) = ', iLayer, mLayerThermalC(iLayer) + + end do ! looping through layers + !pause + + ! ***** + ! * compute the thermal conductivity of snow at the interface of each layer... + ! **************************************************************************** + do iLayer=1,nLayers-1 ! (loop through layers) + ! get temporary variables + TCn = mLayerThermalC(iLayer) ! thermal conductivity below the layer interface (W m-1 K-1) + TCp = mLayerThermalC(iLayer+1) ! thermal conductivity above the layer interface (W m-1 K-1) + zdn = iLayerHeight(iLayer) - mLayerHeight(iLayer) ! height difference between interface and lower value (m) + zdp = mLayerHeight(iLayer+1) - iLayerHeight(iLayer) ! height difference between interface and upper value (m) + den = TCn*zdp + TCp*zdn ! denominator + ! compute thermal conductivity + if(TCn+TCp > epsilon(TCn))then + iLayerThermalC(iLayer) = (TCn*TCp*(zdn + zdp)) / den else - kerstenNum = 0._rkind ! dry thermal conductivity + iLayerThermalC(iLayer) = (TCn*zdn + TCp*zdp) / (zdn + zdp) endif - ! ...and, compute the thermal conductivity - mLayerThermalC(iLayer) = kerstenNum*lambda_wet + (1._rkind - kerstenNum)*lambda_drysoil - - ! ** mixture of constituents - case(mixConstit) - mLayerThermalC(iLayer) = thCond_soil(iSoil) * ( 1._rkind - theta_sat(iSoil) ) + & ! soil component - lambda_ice * mLayerVolFracIce(iLayer) + & ! ice component - lambda_water * mLayerVolFracLiq(iLayer) + & ! liquid water component - lambda_air * mLayerVolFracAir(iLayer) ! air component - - ! ** test case for the mizoguchi lab experiment, Hansson et al. VZJ 2004 - case(hanssonVZJ) - fArg = 1._rkind + f1*mLayerVolFracIce(iLayer)**f2 - xArg = mLayerVolFracLiq(iLayer) + fArg*mLayerVolFracIce(iLayer) - mLayerThermalC(iLayer) = c1 + c2*xArg + (c1 - c4)*exp(-(c3*xArg)**c5) - - ! ** check - case default; err=20; message=trim(message)//'unable to identify option for thermal conductivity of soil'; return - - end select ! option for the thermal conductivity of soil - - ! ***** snow - case(iname_snow) - ! temporally constant thermal conductivity - if(ixThCondSnow==Smirnova2000)then - mLayerThermalC(iLayer) = fixedThermalCond_snow - ! thermal conductivity as a function of snow density - else - call tcond_snow(mLayerVolFracIce(iLayer)*iden_ice, & ! input: snow density (kg m-3) - mLayerThermalC(iLayer), & ! output: thermal conductivity (W m-1 K-1) - err,cmessage) ! output: error control - if(err/=0)then; message=trim(message)//trim(cmessage); return; end if - endif - - ! * error check - case default; err=20; message=trim(message)//'unable to identify type of layer (snow or soil) to compute thermal conductivity'; return - - end select - !print*, 'iLayer, mLayerThermalC(iLayer) = ', iLayer, mLayerThermalC(iLayer) - - end do ! looping through layers - !pause - - ! ***** - ! * compute the thermal conductivity of snow at the interface of each layer... - ! **************************************************************************** - do iLayer=1,nLayers-1 ! (loop through layers) - ! get temporary variables - TCn = mLayerThermalC(iLayer) ! thermal conductivity below the layer interface (W m-1 K-1) - TCp = mLayerThermalC(iLayer+1) ! thermal conductivity above the layer interface (W m-1 K-1) - zdn = iLayerHeight(iLayer) - mLayerHeight(iLayer) ! height difference between interface and lower value (m) - zdp = mLayerHeight(iLayer+1) - iLayerHeight(iLayer) ! height difference between interface and upper value (m) - den = TCn*zdp + TCp*zdn ! denominator - ! compute thermal conductivity - if(TCn+TCp > epsilon(TCn))then - iLayerThermalC(iLayer) = (TCn*TCp*(zdn + zdp)) / den - else - iLayerThermalC(iLayer) = (TCn*zdn + TCp*zdp) / (zdn + zdp) - endif - !write(*,'(a,1x,i4,1x,10(f9.3,1x))') 'iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) = ', iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) - end do ! looping through layers - - ! special case of hansson - if(ixThCondSoil==hanssonVZJ)then - iLayerThermalC(0) = 28._rkind*(0.5_rkind*(iLayerHeight(1) - iLayerHeight(0))) - else - iLayerThermalC(0) = mLayerThermalC(1) - end if - - ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer - iLayerThermalC(nLayers) = mLayerThermalC(nLayers) - - ! end association to variables in the data structure - end associate - - end subroutine computThermConduct - - -end module computThermConduct_module + !write(*,'(a,1x,i4,1x,10(f9.3,1x))') 'iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) = ', iLayer, TCn, TCp, zdn, zdp, iLayerThermalC(iLayer) + end do ! looping through layers + + ! special case of hansson + if(ixThCondSoil==hanssonVZJ)then + iLayerThermalC(0) = 28._rkind*(0.5_rkind*(iLayerHeight(1) - iLayerHeight(0))) + else + iLayerThermalC(0) = mLayerThermalC(1) + end if + + ! assume the thermal conductivity at the domain boundaries is equal to the thermal conductivity of the layer + iLayerThermalC(nLayers) = mLayerThermalC(nLayers) + + ! end association to variables in the data structure + end associate + + end subroutine computThermConduct + + + end module computThermConduct_module + \ No newline at end of file diff --git a/build/source/engine/sundials/summaSolveSundialsIDA.f90 b/build/source/engine/sundials/summaSolveSundialsIDA.f90 index 7f2fbb1..48cfe32 100644 --- a/build/source/engine/sundials/summaSolveSundialsIDA.f90 +++ b/build/source/engine/sundials/summaSolveSundialsIDA.f90 @@ -142,7 +142,7 @@ module summaSolveSundialsIDA_module USE fsundials_nvector_mod ! Fortran interface to generic N_Vector USE fsundials_linearsolver_mod ! Fortran interface to generic SUNLinearSolver USE fsundials_nonlinearsolver_mod ! Fortran interface to generic SUNNonlinearSolver - USE allocspace_module,only:allocLocal ! allocate local data structures + USE allocspace4chm_module,only:allocLocal ! allocate local data structures USE eval8summaSundials_module,only:eval8summa4IDA ! DAE/ODE functions USE computJacobSundials_module,only:computJacob4IDA ! system Jacobian USE tol4IDA_module,only:computWeight4IDA ! weight required for tolerances diff --git a/build/source/engine/sundials/systemSolvSundials.f90 b/build/source/engine/sundials/systemSolvSundials.f90 index a941c72..308f741 100644 --- a/build/source/engine/sundials/systemSolvSundials.f90 +++ b/build/source/engine/sundials/systemSolvSundials.f90 @@ -127,7 +127,7 @@ module systemSolvSundials_module err,message) ! intent(out): error code and error message ! --------------------------------------------------------------------------------------- ! structure allocations - USE allocspace_module,only:allocLocal ! allocate local data structures + USE allocspace4chm_module,only:allocLocal ! allocate local data structures ! simulation of fluxes and residuals given a trial state vector USE eval8summaSundials_module,only:eval8summaSundials ! simulation of fluxes and residuals given a trial state vector USE getVectorz_module,only:getScaling ! get the scaling vectors diff --git a/build/source/engine/sundials/tol4IDA.f90 b/build/source/engine/sundials/tol4IDA.f90 index 91080ba..4674c31 100644 --- a/build/source/engine/sundials/tol4IDA.f90 +++ b/build/source/engine/sundials/tol4IDA.f90 @@ -134,6 +134,7 @@ contains 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 + mpar_data, & ! intent(in) ! output absTol, & ! intent(out): model state vector relTol, & @@ -144,6 +145,7 @@ contains type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers + type(var_dlength),intent(in) :: mpar_data ! model parameters ! output real(rkind),intent(out) :: absTol(:) ! model state vector (mixed units) real(rkind),intent(out) :: relTol(:) ! model state vector (mixed units) diff --git a/build/source/engine/sundials/type4IDA.f90 b/build/source/engine/sundials/type4IDA.f90 index 842ef39..e0988f3 100644 --- a/build/source/engine/sundials/type4IDA.f90 +++ b/build/source/engine/sundials/type4IDA.f90 @@ -22,7 +22,7 @@ implicit none integer(i4b) :: nSnow ! number of snow layers integer(i4b) :: nSoil ! number of soil layers integer(i4b) :: nLayers ! total number of layers - integer(kind=8) :: nState ! total number of state variables + integer :: nState ! total number of state variables integer(i4b) :: ixMatrix ! form of matrix (dense or banded) logical(lgt) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt) :: firstFluxCall diff --git a/build/source/engine/sundials/updatStateSundials.f90 b/build/source/engine/sundials/updatStateSundials.f90 index c02a1b0..fdd53e4 100644 --- a/build/source/engine/sundials/updatStateSundials.f90 +++ b/build/source/engine/sundials/updatStateSundials.f90 @@ -1,318 +1,180 @@ module updatStateSundials_module -USE nrtype -! physical constants -USE multiconst,only:& - Tfreeze, & ! freezing point of pure water (K) - iden_air, & ! intrinsic density of air (kg m-3) - iden_ice, & ! intrinsic density of ice (kg m-3) - iden_water, & ! intrinsic density of water (kg m-3) - gravity, & ! gravitational acceleteration (m s-2) - LH_fus ! latent heat of fusion (J kg-1) -implicit none -private -public::updateSnowSundials -public::updateSoilSundials -public::updateSoilSundials2 -public::updateVegSundials - -real(rkind),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) - -contains - - - ! ************************************************************************************************************* - ! public subroutine updateVegSundials: compute phase change impacts on volumetric liquid water and ice - ! Input: Theta * canopyDepth * iden_water - ! Outputs: VolFracLiq * canopyDepth * iden_water and VolFracIce * canopyDepth * iden_ice - ! ************************************************************************************************************* - subroutine updateVegSundials(& - ! input - Temp ,& ! intent(in): temperature (K) - Theta ,& ! intent(in): volume fraction of total water (-) - snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) - TempPrime ,& ! intent(in): temperature (K) - ThetaPrime ,& ! intent(in): volume fraction of total water (-) - ! output - VolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - VolFracIce ,& ! intent(out): volumetric fraction of ice (-) - VolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) - VolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) - fLiq ,& ! intent(out): fraction of liquid water (-) - err,message) ! intent(out): error control - ! utility routines - USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - implicit none - ! input variables - real(rkind),intent(in) :: Temp ! temperature (K) - real(rkind),intent(in) :: Theta ! volume fraction of total water (-) - real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - real(rkind),intent(in) :: TempPrime ! temperature (K) - real(rkind),intent(in) :: ThetaPrime ! volume fraction of total water (-) - ! output variables - real(rkind),intent(out) :: VolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: VolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: VolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: VolFracIcePrime ! volumetric fraction of ice (-) - real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) - ! error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! initialize error control - err=0; message="updateVegSundials/" - - ! compute the volumetric fraction of liquid water and ice (-) - fLiq = fracliquid(Temp,snowfrz_scale) - VolFracLiq = fLiq*Theta - VolFracIce = (1._rkind - fLiq)*Theta - VolFracLiqPrime = fLiq * ThetaPrime + dFracLiq_dTk(Temp,snowfrz_scale) * Theta * TempPrime - VolFracIcePrime = ( ThetaPrime - VolFracLiqPrime ) - - - end subroutine updateVegSundials - - + USE nrtype + ! physical constants + USE multiconst,only:& + Tfreeze, & ! freezing point of pure water (K) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water, & ! intrinsic density of water (kg m-3) + gravity, & ! gravitational acceleteration (m s-2) + LH_fus ! latent heat of fusion (J kg-1) + implicit none + private + public::updateSnowSundials + public::updateSoilSundials + + real(rkind),parameter :: verySmall=1e-14_rkind ! a very small number (used to avoid divide by zero) + + contains + + ! ************************************************************************************************************* - ! public subroutine updateSnowSundials: compute phase change impacts on volumetric liquid water and ice - ! ************************************************************************************************************* - subroutine updateSnowSundials(& - ! input - mLayerTemp ,& ! intent(in): temperature (K) - mLayerTheta ,& ! intent(in): volume fraction of total water (-) - snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) - mLayerTempPrime ,& ! intent(in): temperature (K) - mLayerThetaPrime ,& ! intent(in): volume fraction of total water (-) - ! output - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) - fLiq ,& ! intent(out): fraction of liquid water (-) - err,message) ! intent(out): error control - ! utility routines - USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water - USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) - implicit none - ! input variables - real(rkind),intent(in) :: mLayerTemp ! temperature (K) - real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) - real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) - real(rkind),intent(in) :: mLayerTempPrime ! temperature (K) - real(rkind),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) - ! output variables - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) - real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) - ! error control - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! initialize error control - err=0; message="updateSnowSundials/" - - ! compute the volumetric fraction of liquid water and ice (-) - fLiq = fracliquid(mLayerTemp,snowfrz_scale) - mLayerVolFracLiq = fLiq*mLayerTheta - mLayerVolFracIce = (1._rkind - fLiq)*mLayerTheta*(iden_water/iden_ice) - mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime - mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) - - - end subroutine updateSnowSundials - - ! ************************************************************************************************************* - ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice - ! uses mLayerMatricHeadPrev and mLayerVolFracWatPrev to get dt_cur, or use dt_cur as it can change here - ! ************************************************************************************************************* - subroutine updateSoilSundials(& - ! input - dt_cur, & - mLayerTemp ,& ! intent(in): temperature (K) - mLayerMatricHead ,& ! intent(in): total water matric potential (m) - mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) - mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) - mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) - mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) - vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter - vGn_n ,& ! intent(in): van Genutchen "n" parameter - theta_sat ,& ! intent(in): soil porosity (-) - theta_res ,& ! intent(in): soil residual volumetric water content (-) - vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) - ! output - mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water time derivative (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water time derivative (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice time derivative (-) - err,message) ! intent(out): error control - ! utility routines - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content - USE soil_utils_module,only:dTheta_dPsi - implicit none - ! input variables - real(rkind),intent(in) :: dt_cur - real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) - real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) - real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) - real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) - ! output variables - real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! define local variables - real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - real(rkind) :: dt_inv ! inverse of timestep - ! initialize error control - err=0; message="updateSoilSundials/" - - ! compute fractional **volume** of total water (liquid plus ice) - mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - ! mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then - dt_inv = 1._rkind/ dt_cur !WHY NOT JUST USE THIS - else - dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) - endif - mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv - - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if - - ! compute the critical soil temperature where all water is unfrozen (K) - ! (eq 17 in Dall'Amico 2011) - TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - - ! *** compute volumetric fraction of liquid water for partially frozen soil - if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice - ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind - endif - - ! *** compute volumetric fraction of liquid water for unfrozen soil - else !( mLayerTemp >= TcSoil, all water is unfrozen ) - mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracLiqPrime = mLayerVolFracWatPrime - mLayerVolFracIcePrime = 0._rkind - - end if ! (check if soil is partially frozen) - - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime - - end subroutine updateSoilSundials - - ! ************************************************************************************************************* - ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice - ! uses mLayerMatricHeadPrime, and wants instantaneous dt, dt_cur will always be a full step size as input here - ! ************************************************************************************************************* - subroutine updateSoilSundials2(& - ! input - mLayerTemp ,& ! intent(in): temperature vector (K) - mLayerMatricHead ,& ! intent(in): total water matric potential (m) - mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) - mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) - vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter - vGn_n ,& ! intent(in): van Genutchen "n" parameter - theta_sat ,& ! intent(in): soil porosity (-) - theta_res ,& ! intent(in): soil residual volumetric water content (-) - vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) - ! output - mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) - mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water (-) - mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) - mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) - err,message) ! intent(out): error control - ! utility routines - USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head - USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content - USE soil_utils_module,only:dTheta_dPsi - implicit none - ! input variables - real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) - real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) - real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) - real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) - real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter - real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter - real(rkind),intent(in) :: theta_sat ! soil porosity (-) - real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) - real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) - ! output variables - real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) - real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) - real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) - real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water time derivative (-) - real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water time derivative (-) - real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice time derivative (-) - integer(i4b),intent(out) :: err ! error code - character(*),intent(out) :: message ! error message - ! define local variables - real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) - real(rkind) :: xConst ! constant in the freezing curve function (m K-1) - real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) - ! initialize error control - err=0; message="updateSoilSundials2/" - - ! compute fractional **volume** of total water (liquid plus ice) - mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime - - if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if - - ! compute the critical soil temperature where all water is unfrozen (K) - ! (eq 17 in Dall'Amico 2011) - TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) - - ! *** compute volumetric fraction of liquid water for partially frozen soil - if( mLayerTemp < TcSoil )then ! (check if soil temperature is less than the critical temperature) - ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice - ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations - xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) - mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution - mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) - if(mLayerPsiLiq<0._rkind)then - mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime - else - mLayerVolFracLiqPrime = 0._rkind - endif - - ! *** compute volumetric fraction of liquid water for unfrozen soil - else !( mLayerTemp >= TcSoil, all water is unfrozen ) - mLayerVolFracLiq = mLayerVolFracWat - mLayerVolFracLiqPrime = mLayerVolFracWatPrime - - end if ! (check if soil is partially frozen) - - ! - volumetric ice content (-) - mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq - mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime - - end subroutine updateSoilSundials2 -end module updatStateSundials_module + ! public subroutine updateSnowSundials: compute phase change impacts on volumetric liquid water and ice + ! ************************************************************************************************************* + subroutine updateSnowSundials(& + ! input + mLayerTemp ,& ! intent(in): temperature (K) + mLayerTheta ,& ! intent(in): volume fraction of total water (-) + snowfrz_scale ,& ! intent(in): scaling parameter for the snow freezing curve (K-1) + mLayerTempPrime ,& ! intent(in): temperature (K) + mLayerThetaPrime ,& ! intent(in): volume fraction of total water (-) + ! output + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice (-) + fLiq ,& ! intent(out): fraction of liquid water (-) + err,message) ! intent(out): error control + ! utility routines + USE snow_utils_module,only:fracliquid ! compute volumetric fraction of liquid water + USE snow_utils_module,only:dFracLiq_dTk ! differentiate the freezing curve w.r.t. temperature (snow) + implicit none + ! input variables + real(rkind),intent(in) :: mLayerTemp ! temperature (K) + real(rkind),intent(in) :: mLayerTheta ! volume fraction of total water (-) + real(rkind),intent(in) :: snowfrz_scale ! scaling parameter for the snow freezing curve (K-1) + real(rkind),intent(in) :: mLayerTempPrime ! temperature (K) + real(rkind),intent(in) :: mLayerThetaPrime ! volume fraction of total water (-) + ! output variables + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + real(rkind),intent(out) :: fLiq ! fraction of liquid water (-) + ! error control + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! initialize error control + err=0; message="updateSnowSundials/" + + ! compute the volumetric fraction of liquid water and ice (-) + fLiq = fracliquid(mLayerTemp,snowfrz_scale) + mLayerVolFracLiq = fLiq*mLayerTheta + mLayerVolFracIce = (1._rkind - fLiq)*mLayerTheta*(iden_water/iden_ice) + mLayerVolFracLiqPrime = fLiq * mLayerThetaPrime + dFracLiq_dTk(mLayerTemp,snowfrz_scale) * mLayerTheta * mLayerTempPrime + mLayerVolFracIcePrime = ( mLayerThetaPrime - mLayerVolFracLiqPrime ) * (iden_water/iden_ice) + + + end subroutine updateSnowSundials + + ! *********************************************************************************************************************************** + ! public subroutine updateSoilSundials: compute phase change impacts on matric head and volumetric liquid water and ice (veg or soil) + ! *********************************************************************************************************************************** + subroutine updateSoilSundials(& + ! input + dt ,& ! intent(in): time step + insideIDA ,& ! intent(in): flag if inside Sundials solver + mLayerTemp ,& ! intent(in): temperature (K) + mLayerMatricHead ,& ! intent(in): total water matric potential (m) + mLayerMatricHeadPrev ,& ! intent(in): total water matric potential previous time step (m) + mLayerVolFracWatPrev ,& ! intent(in): volumetric fraction of total water previous time step (-) + mLayerTempPrime ,& ! intent(in): temperature time derivative (K/s) + mLayerMatricHeadPrime, & ! intent(in): total water matric potential time derivative (m/s) + vGn_alpha ,& ! intent(in): van Genutchen "alpha" parameter + vGn_n ,& ! intent(in): van Genutchen "n" parameter + theta_sat ,& ! intent(in): soil porosity (-) + theta_res ,& ! intent(in): soil residual volumetric water content (-) + vGn_m ,& ! intent(in): van Genutchen "m" parameter (-) + ! output + mLayerVolFracWat ,& ! intent(out): volumetric fraction of total water (-) + mLayerVolFracLiq ,& ! intent(out): volumetric fraction of liquid water (-) + mLayerVolFracIce ,& ! intent(out): volumetric fraction of ice (-) + mLayerVolFracWatPrime ,& ! intent(out): volumetric fraction of total water time derivative (-) + mLayerVolFracLiqPrime ,& ! intent(out): volumetric fraction of liquid water time derivative (-) + mLayerVolFracIcePrime ,& ! intent(out): volumetric fraction of ice time derivative (-) + err,message) ! intent(out): error control + ! utility routines + USE soil_utils_module,only:volFracLiq ! compute volumetric fraction of liquid water based on matric head + USE soil_utils_module,only:matricHead ! compute the matric head based on volumetric liquid water content + USE soil_utils_module,only:dTheta_dPsi + implicit none + ! input variables + real(rkind),intent(in) :: dt + logical(lgt),intent(in) :: insideIDA ! flag if inside Sundials solver + real(rkind),intent(in) :: mLayerTemp ! estimate of temperature (K) + real(rkind),intent(in) :: mLayerMatricHead ! matric head (m) + real(rkind),intent(in) :: mLayerMatricHeadPrev ! matric head previous time step (m) + real(rkind),intent(in) :: mLayerVolFracWatPrev ! volumetric fraction of total waterprevious time step (m) + real(rkind),intent(in) :: mLayerTempPrime ! temperature time derivative (K/s) + real(rkind),intent(in) :: mLayerMatricHeadPrime ! matric head time derivative (m/s) + real(rkind),intent(in) :: vGn_alpha ! van Genutchen "alpha" parameter + real(rkind),intent(in) :: vGn_n ! van Genutchen "n" parameter + real(rkind),intent(in) :: theta_sat ! soil porosity (-) + real(rkind),intent(in) :: theta_res ! soil residual volumetric water content (-) + real(rkind),intent(in) :: vGn_m ! van Genutchen "m" parameter (-) + ! output variables + real(rkind),intent(out) :: mLayerVolFracWat ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiq ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIce ! volumetric fraction of ice (-) + real(rkind),intent(out) :: mLayerVolFracWatPrime ! fractional volume of total water (-) + real(rkind),intent(out) :: mLayerVolFracLiqPrime ! volumetric fraction of liquid water (-) + real(rkind),intent(out) :: mLayerVolFracIcePrime ! volumetric fraction of ice (-) + integer(i4b),intent(out) :: err ! error code + character(*),intent(out) :: message ! error message + ! define local variables + real(rkind) :: TcSoil ! critical soil temperature when all water is unfrozen (K) + real(rkind) :: xConst ! constant in the freezing curve function (m K-1) + real(rkind) :: mLayerPsiLiq ! liquid water matric potential (m) + real(rkind) :: dt_inv ! inverse of timestep + ! initialize error control + err=0; message="updateSoilSundials/" + + ! compute fractional **volume** of total water (liquid plus ice) + mLayerVolFracWat = volFracLiq(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + if (.not.insideIDA)then ! calculate dt current, or use dt current as it can change here + if( abs(mLayerMatricHead - mLayerMatricHeadPrev) < verySmall )then !this difference is set as 0 inside varSubstep + dt_inv = 1._rkind/dt + else + dt_inv = mLayerMatricHeadPrime / (mLayerMatricHead - mLayerMatricHeadPrev) ! + endif + mLayerVolFracWatPrime = (mLayerVolFracWat - mLayerVolFracWatPrev)*dt_inv + else ! inside Sundials: instantaneous derivative will always be a full step size as input here + mLayerVolFracWatPrime = dTheta_dPsi(mLayerMatricHead,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * mLayerMatricHeadPrime + endif + + if(mLayerVolFracWat > theta_sat)then; err=20; message=trim(message)//'volume of liquid and ice exceeds porosity'; return; end if + + ! compute the critical soil temperature where all water is unfrozen (K) + ! (eq 17 in Dall'Amico 2011) + TcSoil = Tfreeze + min(mLayerMatricHead,0._rkind)*gravity*Tfreeze/LH_fus ! (NOTE: J = kg m2 s-2, so LH_fus is in units of m2 s-2) + + ! *** compute volumetric fraction of liquid water for partially frozen soil + if(mLayerTemp < TcSoil)then ! (check if soil temperature is less than the critical temperature) + ! NOTE: mLayerPsiLiq is the liquid water matric potential from the Clapeyron equation, used to separate the total water into liquid water and ice + ! mLayerPsiLiq is DIFFERENT from the liquid water matric potential used in the flux calculations + xConst = LH_fus/(gravity*Tfreeze) ! m K-1 (NOTE: J = kg m2 s-2) + mLayerPsiLiq = xConst*(mLayerTemp - Tfreeze) ! liquid water matric potential from the Clapeyron eqution + mLayerVolFracLiq = volFracLiq(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) + if(mLayerPsiLiq<0._rkind)then + mLayerVolFracLiqPrime = dTheta_dPsi(mLayerPsiLiq,vGn_alpha,theta_res,theta_sat,vGn_n,vGn_m) * xConst * mLayerTempPrime + else + mLayerVolFracLiqPrime = 0._rkind + endif + + ! *** compute volumetric fraction of liquid water for unfrozen soil + else !( mLayerTemp >= TcSoil, all water is unfrozen ) + mLayerVolFracLiq = mLayerVolFracWat + mLayerVolFracLiqPrime = mLayerVolFracWatPrime + mLayerVolFracIcePrime = 0._rkind + + end if ! (check if soil is partially frozen) + + ! - volumetric ice content (-) + mLayerVolFracIce = mLayerVolFracWat - mLayerVolFracLiq + mLayerVolFracIcePrime = mLayerVolFracWatPrime - mLayerVolFracLiqPrime + + end subroutine updateSoilSundials + + end module updatStateSundials_module + \ No newline at end of file diff --git a/build/source/engine/sundials/updateVarsSundials.f90 b/build/source/engine/sundials/updateVarsSundials.f90 index 3a33e4e..7d5a97f 100644 --- a/build/source/engine/sundials/updateVarsSundials.f90 +++ b/build/source/engine/sundials/updateVarsSundials.f90 @@ -18,1087 +18,831 @@ ! 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 varSubstepSundials_module +module updateVarsSundials_module - ! data types - USE nrtype - - ! access missing values - USE globalData,only:integerMissing ! missing integer - 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 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 - - ! global metadata - USE globalData,only:flux_meta ! metadata on the model fluxes - - ! 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) - 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: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 + ! data types + USE nrtype + + ! missing values + USE globalData,only:integerMissing ! missing integer + USE globalData,only:realMissing ! missing real number + + ! access the global print flag + USE globalData,only:globalPrintFlag + + ! domain types + USE globalData,only:iname_cas ! named variables for canopy air space + USE globalData,only:iname_veg ! named variables for vegetation canopy + USE globalData,only:iname_snow ! named variables for snow + USE globalData,only:iname_soil ! named variables for soil + USE globalData,only:iname_aquifer ! named variables for the aquifer + + ! named variables to describe the state variable type + USE globalData,only:iname_nrgCanair ! named variable defining the energy of the canopy air space + USE globalData,only:iname_nrgCanopy ! named variable defining the energy of the vegetation canopy + USE globalData,only:iname_watCanopy ! named variable defining the mass of total water on the vegetation canopy + USE globalData,only:iname_liqCanopy ! named variable defining the mass of liquid water on the vegetation canopy + USE globalData,only:iname_nrgLayer ! named variable defining the energy state variable for snow+soil layers + USE globalData,only:iname_watLayer ! named variable defining the total water state variable for snow+soil layers + USE globalData,only:iname_liqLayer ! named variable defining the liquid water state variable for snow+soil layers + USE globalData,only:iname_matLayer ! named variable defining the matric head state variable for soil layers + USE globalData,only:iname_lmpLayer ! named variable defining the liquid matric potential state variable for soil layers + + ! metadata for information in the data structures + USE globalData,only:indx_meta ! metadata for the variables in the index structure + + ! constants + USE multiconst,only:& + gravity, & ! acceleration of gravity (m s-2) + Tfreeze, & ! temperature at freezing (K) + Cp_air, & ! specific heat of air (J kg-1 K-1) + Cp_ice, & ! specific heat of ice (J kg-1 K-1) + Cp_water, & ! specific heat of liquid water (J kg-1 K-1) + LH_fus, & ! latent heat of fusion (J kg-1) + iden_air, & ! intrinsic density of air (kg m-3) + iden_ice, & ! intrinsic density of ice (kg m-3) + iden_water ! intrinsic density of liquid water (kg m-3) + + ! provide access to the derived types to define the data structures + USE data_types,only:& + var_i, & ! data vector (i4b) + var_d, & ! data vector (rkind) + var_ilength, & ! data vector with variable length dimension (i4b) + var_dlength ! data vector with variable length dimension (rkind) + + ! provide access to indices that define elements of the data structures + USE var_lookup,only:iLookDIAG ! named variables for structure elements + USE var_lookup,only:iLookPROG ! named variables for structure elements + USE var_lookup,only:iLookDERIV ! named variables for structure elements + USE var_lookup,only:iLookPARAM ! named variables for structure elements + USE var_lookup,only:iLookINDEX ! named variables for structure elements + + ! provide access to 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.) + + implicit none + private + public::updateVarsSundials + + contains + + ! ********************************************************************************************************** + ! public subroutine updateVarsSundials: compute diagnostic variables and derivatives for Sundials Jacobian + ! ********************************************************************************************************** + 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 + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- implicit none - private - 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 varSubstepSundials: run the model for a collection of substeps for a given state subset - ! ********************************************************************************************************** - 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 - ! --------------------------------------------------------------------------------------- - ! * 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) :: 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='varSubstepSundials/' - - ! initialize flag for the success of the substepping - failedMinimumStep=.false. - - ! initialize the length of the substep - dtSubstep = dtInit - - ! 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 - - ! 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 - - ! 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) - - ! ----- - ! * 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 + ! 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 + ! -------------------------------------------------------------------------------------------------------------------------------- + ! 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 + + ! -------------------------------------------------------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------------------------------------------------------- + + ! initialize error control + err=0; message='updateVarsSundials/' + + ! allocate space and assign values to the flag vector + allocate(computedCoupling(size(ixMapSubset2Full)),stat=err) ! .true. if computed the coupling for a given state variable + if(err/=0)then; message=trim(message)//'problem allocating computedCoupling'; return; endif + computedCoupling(:)=.false. + + ! loop through model state variables + do iState=1,size(ixMapSubset2Full) + + ! check the need for the computations + if(computedCoupling(iState)) cycle + + ! ----- + ! - compute indices... + ! -------------------- + + ! get domain type, and index of the control volume within the domain + ixFullVector = ixMapSubset2Full(iState) ! index within full state vector + ixDomainType = ixDomainType_subset(iState) ! named variables defining the domain (iname_cas, iname_veg, etc.) + ixControlIndex = ixControlVolume(ixFullVector) ! index within a given domain + + ! get the layer index + select case(ixDomainType) + case(iname_cas); cycle ! canopy air space: do nothing + case(iname_veg); iLayer = 0 + case(iname_snow); iLayer = ixControlIndex + case(iname_soil); iLayer = ixControlIndex + nSnow + case(iname_aquifer); cycle ! aquifer: do nothing + case default; err=20; message=trim(message)//'expect case to be iname_cas, iname_veg, iname_snow, iname_soil, iname_aquifer'; return + 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 + 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 + + ! 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 - - 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 - - else ! step is still OK - dtSubstep = dtSubstep*dtMultiplier - cycle subSteps - endif ! if step is less than the minimum - - endif ! if failed the substep - + bFlag = .false. + endif + ! ----- - ! * update model fluxes... + ! - compute derivatives... ! ------------------------ - - ! 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 + + ! 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 + + ! 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) + endif + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + end select ! domain type + + ! --> unfrozen: no dependence of liquid water on temperature + else + select case(ixDomainType) + case(iname_veg); dTheta_dTkCanopy = 0._rkind; 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 - - 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 + + ! ----- + ! - 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 + + ! *** 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 + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + ! check + case default; err=20; message=trim(message)//'expect case to be iname_veg, iname_snow, iname_soil'; return + + end select ! domain type + + ! final check + else + + ! do nothing (input = output) -- and check that we got here correctly + if( (isNrgState .or. isCoupled) )then + scalarVolFracLiq = realMissing + scalarVolFracIce = realMissing else - cycle substeps + message=trim(message)//'unexpected else branch' + err=20; return endif - - 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 - - ! 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 - end do - endif ! (domain splitting) - - endif ! (if the flux is desired) - end do ! (loop through fluxes) - - ! ------------------------------------------------------ - ! ------------------------------------------------------ - - ! 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 - - ! 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 - - endif - - ! ----- - ! * check mass balance... - ! ----------------------- - - ! NOTE: should not need to do this, since mass balance is checked in the solver - if(checkMassBalance)then - - ! 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 - - ! --> 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 the trial state - scalarCanopyWatTrial = canopyBalance1 - - ! set the modification flag - nrgFluxModified = .true. - + + 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 + endif + + ! update bracket + if(residual < 0._rkind)then + tempMax = min(xTemp,tempMax) 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 - 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 - 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) - - endif ! if checking a snow layer - - end do ! (looping through state variables) - - endif ! (if we removed too much water) - - ! ======================================================================================================== - - ! *** liquid water - - ! --> check if we removed too much water - if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyLiqTrial < 0._rkind)then - - if(scalarCanopyLiqTrial > -verySmall)then - scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial - scalarCanopyLiqTrial = 0._rkind - - - ! encountered an inconsistency: spit the dummy + tempMin = max(tempMin,xTemp) + end if + + ! compute iteration increment + tempInc = residual/derivative ! K + + ! check + if(globalPrintFlag)& + write(*,'(i4,1x,e20.10,1x,5(f20.10,1x),L1)') iter, residual, xTemp-Tcrit, tempInc, Tcrit, tempMin, tempMax, bFlag + + ! check convergence + if(abs(residual) < nrgConvTol .or. abs(tempInc) < tempConvTol) exit iterations + + ! add constraints for snow temperature + if(ixDomainType==iname_veg .or. ixDomainType==iname_snow)then + if(tempInc > Tcrit - xTemp) tempInc=(Tcrit - xTemp)*0.5_rkind ! simple bi-section method + endif ! if the domain is vegetation or snow + + ! deal with the discontinuity between partially frozen and unfrozen soil + if(ixDomainType==iname_soil)then + ! difference from the temperature below which ice exists + critDiff = Tcrit - xTemp + ! --> initially frozen (T < Tcrit) + if(critDiff > 0._rkind)then + if(tempInc > critDiff) tempInc = critDiff + epsT ! set iteration increment to slightly above critical temperature + ! --> initially unfrozen (T > Tcrit) else - 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) - - endif ! checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracLiqTrial) - - ! 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 - - ! 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 - + if(tempInc < critDiff) tempInc = critDiff - epsT ! set iteration increment to slightly below critical temperature + endif + endif ! if the domain is soil + + ! update the temperature trial + xTemp = xTemp + tempInc + + ! check failed convergence + if(iter==maxiter)then + message=trim(message)//'failed to converge' + err=-20; return ! negative error code = try to recover + endif + + endif ! if adjusting the temperature + + end do iterations ! iterating + + ! save temperature + select case(ixDomainType) + case(iname_veg); scalarCanopyTempTrial = xTemp + case(iname_snow, iname_soil); mLayerTempTrial(iLayer) = xTemp + end select + + ! ======================================================================================================================================= + ! ======================================================================================================================================= + ! ----- - ! * 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 + ! - compute the liquid water matric potential (and necessay derivatives)... + ! ------------------------------------------------------------------------- + + ! only for soil + if(ixDomainType==iname_soil)then + + ! 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 + + ! derivatives relating liquid water matric potential to total water matric potential and temperature + dPsiLiq_dPsi0(ixControlIndex) = 1._rkind ! exact correspondence (psiLiq=psi0) + dPsiLiq_dTemp(ixControlIndex) = 0._rkind ! no relationship between liquid water matric potential and temperature + + ! case of energy state or coupled solution + else + ! compute the liquid matric potential (and the derivatives w.r.t. total matric potential and temperature) + call 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 + + end subroutine updateVarsSundials + + + ! ********************************************************************************************************** + ! 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 + \ No newline at end of file diff --git a/build/source/engine/sundials/varSubstepSundials.f90 b/build/source/engine/sundials/varSubstepSundials.f90 index 5e5f8da..3da718e 100644 --- a/build/source/engine/sundials/varSubstepSundials.f90 +++ b/build/source/engine/sundials/varSubstepSundials.f90 @@ -20,796 +20,787 @@ module varSubstepSundials_module -! data types -USE nrtype - -! access missing values -USE globalData,only:integerMissing ! missing integer -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 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 - -! global metadata -USE globalData,only:flux_meta ! metadata on the model fluxes - -! 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) - 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: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::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 varSubstepSundials: run the model for a collection of substeps for a given state subset -! ********************************************************************************************************** -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 allocspace4chm_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 getVectorz_module,only:varExtract ! extract variables from the state vector - USE updateVarsSundials_module,only:updateVarsSundials ! update prognostic variables - USE varExtrSundials_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 + ! data types + USE nrtype + + ! access missing values + USE globalData,only:integerMissing ! missing integer + 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 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 + + ! global metadata + USE globalData,only:flux_meta ! metadata on the model fluxes + + ! 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) + 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: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 - ! --------------------------------------------------------------------------------------- - ! * 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) :: 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=.false. ! 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='varSubstepSundials/' - - ! initialize flag for the success of the substepping - failedMinimumStep=.false. - - ! initialize the length of the substep - dtSubstep = dtInit - - ! 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) - print*, message - return - endif - - ! initialize the model fluxes (some model fluxes are not computed in the iterations) - do iVar=1,size(flux_data%var) + private + 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 varSubstepSundials: run the model for a collection of substeps for a given state subset + ! ********************************************************************************************************** + 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 allocspace4chm_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 + ! --------------------------------------------------------------------------------------- + ! * 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) :: 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='varSubstepSundials/' + + ! initialize flag for the success of the substepping + failedMinimumStep=.false. + + ! initialize the length of the substep + dtSubstep = dtInit + + ! 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 + + ! 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 - - ! 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 - + end do + + ! 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) - print*, message - return - endif ! (check for errors) - + ! 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) + ! ----- ! * 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 - + ! 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) - print*, message - if(err>0) return + message=trim(message)//trim(cmessage) + if(err>0) return 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 - + + ! 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 - - else ! step is still OK - dtSubstep = dtSubstep*dtMultiplier - cycle subSteps - endif ! if step is less than the minimum - + + ! 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 + + 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' - print*, message - return + 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 + 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) - print*, message - if(err>0) return + 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 + message=trim(message)//'water balance error' + reduceCoupledStep=.true. + err=-20; return endif - + if(globalPrintFlag)& - print*, trim(cmessage)//': dt = ', dtSubstep - + 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 - cycle substeps - endif - + + ! 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 + cycle substeps + endif + 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) + 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) + 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 + ! 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 - + ! print progress if(globalPrintFlag)& - write(*,'(a,1x,3(f13.2,1x))') 'updating: dtSubstep, dtSum, dt = ', dtSubstep, dtSum, dt - + 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 - end do - endif ! (domain splitting) - - endif ! (if the flux is desired) + 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 + end do + endif ! (domain splitting) + + endif ! (if the flux is desired) end do ! (loop through fluxes) - + ! ------------------------------------------------------ ! ------------------------------------------------------ - + ! 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 - + ! check that we have completed the sub-step if(dtSum >= dt-verySmall)then - failedMinimumStep=.false. - exit subSteps + 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 + + 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 + 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' - print*, message - 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 varExtrSundials_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 (-) - ! 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) - ! 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 (-) - 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) - ! ------------------------------------------------------------------------------------------------------------------- - - ! ------------------------------------------------------------------------------------------------------------------- - ! 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) + 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... - ! ------------------ - ! extract states from the state vector - call varExtract(& - ! input - stateVecTrial, & ! 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(out): trial value of canopy air temperature (K) - scalarCanopyTempTrial, & ! intent(out): trial value of canopy temperature (K) - scalarCanopyWatTrial, & ! intent(out): trial value of canopy total water (kg m-2) - scalarCanopyLiqTrial, & ! intent(out): trial value of canopy liquid water (kg m-2) - scalarCanopyIceTrial, & ! intent(out): trial value of canopy ice content (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempTrial, & ! intent(out): trial vector of layer temperature (K) - mLayerVolFracWatTrial, & ! intent(out): trial vector of volumetric total water content (-) - mLayerVolFracLiqTrial, & ! intent(out): trial vector of volumetric liquid water content (-) - mLayerVolFracIceTrial, & ! intent(out): trial vector of volumetric ice water content (-) - mLayerMatricHeadTrial, & ! intent(out): trial vector of total water matric potential (m) - mLayerMatricHeadLiqTrial, & ! intent(out): trial vector of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStorageTrial,& ! intent(out): trial value 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) - print*, message - return - end if ! (check for errors) - - call varExtractSundials(& - ! input - stateVecPrime, & ! intent(in): derivative of model state vector (mixed units) - indx_data, & ! intent(in): indices defining model states and layers - ! output: variables for the vegetation canopy - scalarCanairTempPrime, & ! intent(out): derivative of canopy air temperature (K) - scalarCanopyTempPrime, & ! intent(out): derivative of canopy temperature (K) - scalarCanopyWatPrime, & ! intent(out): derivative of canopy total water (kg m-2) - scalarCanopyLiqPrime, & ! intent(out): derivative of canopy liquid water (kg m-2) - ! output: variables for the snow-soil domain - mLayerTempPrime, & ! intent(out): derivative of layer temperature (K) - mLayerVolFracWatPrime, & ! intent(out): derivative of volumetric total water content (-) - mLayerVolFracLiqPrime, & ! intent(out): derivative of volumetric liquid water content (-) - mLayerMatricHeadPrime, & ! intent(out): derivative of total water matric potential (m) - mLayerMatricHeadLiqPrime, & ! intent(out): derivative of liquid water matric potential (m) - ! output: variables for the aquifer - scalarAquiferStoragePrime,& ! intent(out): 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) - print*, message - return - end if ! (check for errors) - - + 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, & - doAdjustTemp, & ! intent(in): logical flag to adjust temperature to accou 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, & - mLayerMatricHeadTrial, & - 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) - print*, message - 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(& + 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 @@ -831,290 +822,283 @@ subroutine updateProgSundials(dt,nSnow,nSoil,nLayers,doAdjustTemp,computeVegFlux 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) - print*, message - return - endif - - endif - - ! ----- - ! * check mass balance... - ! ----------------------- - - ! NOTE: should not need to do this, since mass balance is checked in the solver - if(checkMassBalance)then - + if(err/=0)then; message=trim(message)//trim(cmessage); return; endif + + endif + + ! ----- + ! * check mass balance... + ! ----------------------- + + ! NOTE: should not need to do this, since mass balance is checked in the solver + if(checkMassBalance)then + ! 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 - - ! --> 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 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 + + ! 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 + + ! --> 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 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 - + 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 - + + ! ----- + ! * 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]'; print*, message; 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]'; print*, message; return - end select - + + ! 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 - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'scalarCanopyiceTrial = ', scalarCanopyIceTrial - message=trim(message)//'melted more than the available water' - print*, message - 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 - print*, 'dt = ', dt - print*, 'untappedMelt = ', untappedMelt - print*, 'untappedMelt*dt = ', untappedMelt*dt - print*, 'mLayerVolFracIceTrial = ', mLayerVolFracIceTrial - message=trim(message)//'melted more than the available water' - print*, message - err=20; return - endif ! (inconsistency) - - endif ! if checking a snow layer - - end do ! (looping through state variables) - + + ! ** + ! 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 + 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 + 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) + + endif ! if checking a snow layer + + end do ! (looping through state variables) + endif ! (if we removed too much water) - + ! ======================================================================================================== - + ! *** liquid water - + ! --> check if we removed too much water if(scalarCanopyLiqTrial < 0._rkind .or. any(mLayerVolFracLiqTrial < 0._rkind) )then - - ! ** - ! canopy within numerical precision - if(scalarCanopyLiqTrial < 0._rkind)then - - if(scalarCanopyLiqTrial > -verySmall)then - scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial - scalarCanopyLiqTrial = 0._rkind - - - ! 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' - print*, message - err=20; return - endif ! (inconsistency) - - endif ! checking the canopy - - ! ** - ! snow+soil within numerical precision - do iState=1,size(mLayerVolFracLiqTrial) - - ! 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 - - ! 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) - + + ! ** + ! canopy within numerical precision + if(scalarCanopyLiqTrial < 0._rkind)then + + if(scalarCanopyLiqTrial > -verySmall)then + scalarCanopyIceTrial = scalarCanopyIceTrial - scalarCanopyLiqTrial + scalarCanopyLiqTrial = 0._rkind + + + ! 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) + + endif ! checking the canopy + + ! ** + ! snow+soil within numerical precision + do iState=1,size(mLayerVolFracLiqTrial) + + ! 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 + + ! 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... - ! -------------------------------- + + 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 + 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 -- GitLab