! SUMMA - Structure for Unifying Multiple Modeling Alternatives ! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington ! ! This file is part of SUMMA ! ! For more information see: http://www.ral.ucar.edu/projects/summa ! ! This program is free software: you can redistribute it and/or modify ! it under the terms of the GNU General Public License as published by ! the Free Software Foundation, either version 3 of the License, or ! (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! You should have received a copy of the GNU General Public License ! along with this program. If not, see <http://www.gnu.org/licenses/>. module opSplittin_module ! data types USE nrtype ! access the global print flag USE globalData,only:globalPrintFlag ! 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 matrix information USE globalData,only: nBands ! length of the leading dimension of the band diagonal matrix USE globalData,only: ixFullMatrix ! named variable for the full Jacobian matrix USE globalData,only: ixBandMatrix ! named variable for the band diagonal matrix USE globalData,only: iJac1 ! first layer of the Jacobian to print USE globalData,only: iJac2 ! last layer of the Jacobian to print ! 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 ! 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 ! global metadata USE globalData,only:flux_meta ! metadata on the model fluxes USE globalData,only:diag_meta ! metadata on the model diagnostic variables USE globalData,only:prog_meta ! metadata on the model prognostic variables USE globalData,only:deriv_meta ! metadata on the model derivatives USE globalData,only:flux2state_orig ! metadata on flux-to-state mapping (original state variables) USE globalData,only:flux2state_liq ! metadata on flux-to-state mapping (liquid water state variables) ! constants USE multiconst,only:& gravity, & ! acceleration of gravity (m s-2) Tfreeze, & ! temperature at freezing (K) LH_fus, & ! latent heat of fusion (J kg-1) LH_vap, & ! latent heat of vaporization (J kg-1) LH_sub, & ! latent heat of sublimation (J kg-1) Cp_air, & ! specific heat of air (J kg-1 K-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 indices that define elements of the data structures USE var_lookup,only:iLookATTR ! named variables for structure elements USE var_lookup,only:iLookTYPE ! 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:iLookFLUX ! named variables for structure elements USE var_lookup,only:iLookFORCE ! 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 USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! look up structure for variable types USE var_lookup,only:iLookVarType ! provide access to the number of flux variables USE var_lookup,only:nFlux=>maxvarFlux ! number of model flux variables ! provide access to the derived types to define the data structures USE data_types,only:& var_i, & ! data vector (i4b) var_d, & ! data vector (dp) 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 (dp) model_options ! defines the model decisions ! look-up values for the choice of groundwater representation (local-column, or single-basin) USE mDecisions_module,only: & localColumn, & ! separate groundwater representation in each local soil column singleBasin ! single groundwater store over the entire basin ! look-up values for the choice of groundwater parameterization USE mDecisions_module,only: & qbaseTopmodel, & ! TOPMODEL-ish baseflow parameterization bigBucket, & ! a big bucket (lumped aquifer model) noExplicit ! no explicit groundwater parameterization ! safety: set private unless specified otherwise implicit none private public::opSplittin ! named variables for the coupling method integer(i4b),parameter :: fullyCoupled=1 ! 1st try: fully coupled solution integer(i4b),parameter :: stateTypeSplit=2 ! 2nd try: separate solutions for each state type integer(i4b),parameter :: nCoupling=2 ! number of possible solutions ! named variables for the state variable split integer(i4b),parameter :: nrgSplit=1 ! order in sequence for the energy operation integer(i4b),parameter :: massSplit=2 ! order in sequence for the mass operation ! named variables for the domain type split integer(i4b),parameter :: vegSplit=1 ! order in sequence for the vegetation split integer(i4b),parameter :: snowSplit=2 ! order in sequence for the snow split integer(i4b),parameter :: soilSplit=3 ! order in sequence for the soil split integer(i4b),parameter :: aquiferSplit=4 ! order in sequence for the aquifer split ! named variables for the solution method integer(i4b),parameter :: vector=1 ! vector solution method integer(i4b),parameter :: scalar=2 ! scalar solution method integer(i4b),parameter :: nSolutions=2 ! number of solution methods ! named variables for the switch between states and domains integer(i4b),parameter :: fullDomain=1 ! full domain (veg+snow+soil) integer(i4b),parameter :: subDomain=2 ! sub domain (veg, snow, and soil separately) ! maximum number of possible splits integer(i4b),parameter :: nStateTypes=2 ! number of state types (energy, water) integer(i4b),parameter :: nDomains=4 ! number of domains (vegetation, snow, soil, and aquifer) ! control parameters real(dp),parameter :: valueMissing=-9999._dp ! missing value real(dp),parameter :: verySmall=1.e-12_dp ! a very small number (used to check consistency) real(dp),parameter :: veryBig=1.e+20_dp ! a very big number real(dp),parameter :: dx = 1.e-8_dp ! finite difference increment contains ! ********************************************************************************************************** ! public subroutine opSplittin: run the coupled energy-mass model for one timestep ! ! The logic of the solver is as follows: ! (1) Attempt different solutions in the following order: (a) fully coupled; (b) split by state type and by ! domain type for a given energy and mass split (vegetation, snow, and soil); and (c) scalar solution ! for a given state type and domain subset. ! (2) For a given split, compute a variable number of substeps (in varSubstep). ! ********************************************************************************************************** subroutine opSplittin(& ! input: model control nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers nState, & ! intent(in): total number of state variables dt, & ! intent(inout): time step (s) firstSubStep, & ! intent(in): flag to denote first sub-step computeVegFlux, & ! intent(in): flag to denote if computing energy flux over vegetation ! input/output: data structures 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 bvar_data, & ! intent(in): model variables for the local basin model_decisions,& ! intent(in): model decisions ! output: model control dtMultiplier, & ! intent(out): substep multiplier (-) tooMuchMelt, & ! intent(out): flag to denote that ice is insufficient to support melt stepFailure, & ! intent(out): flag to denote step failure ixCoupling, & ! intent(out): coupling method used in this iteration 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 soil_utils_module,only:matricHead ! compute the matric head based on volumetric water content USE soil_utils_module,only:liquidHead ! compute the liquid water matric potential ! population/extraction of state vectors USE indexState_module,only:indexSplit ! get state indices USE varSubstep_module,only:varSubstep ! complete substeps for a given split ! identify name of variable type (for error message) USE get_ixName_module,only:get_varTypeName ! to access type strings for error messages implicit none ! --------------------------------------------------------------------------------------- ! * dummy variables ! --------------------------------------------------------------------------------------- ! input: model control 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 integer(i4b),intent(in) :: nState ! total number of state variables real(dp),intent(inout) :: dt ! time step (seconds) logical(lgt),intent(in) :: firstSubStep ! flag to indicate if we are processing the first sub-step logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow) ! input/output: data structures 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(in) :: bvar_data ! model variables for the local basin type(model_options),intent(in) :: model_decisions(:) ! model decisions ! output: model control real(dp),intent(out) :: dtMultiplier ! substep multiplier (-) logical(lgt),intent(out) :: tooMuchMelt ! flag to denote that ice is insufficient to support melt logical(lgt),intent(out) :: stepFailure ! flag to denote step failure integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! --------------------------------------------------------------------------------------- ! * general local variables ! --------------------------------------------------------------------------------------- character(LEN=256) :: cmessage ! error message of downwind routine integer(i4b) :: minLayer ! the minimum layer used in assigning flags for flux aggregations integer(i4b) :: iOffset ! offset to account for different indices in the soil domain integer(i4b) :: iMin(1),iMax(1) ! bounds of a given vector integer(i4b) :: iLayer,jLayer ! index of model layer integer(i4b) :: iSoil ! index of soil layer integer(i4b) :: iVar ! index of variables in data structures logical(lgt) :: firstSuccess ! flag to define the first success logical(lgt) :: firstFluxCall ! flag to define the first flux call logical(lgt) :: reduceCoupledStep ! flag to define the need to reduce the length of the coupled step type(var_dlength) :: prog_temp ! temporary model prognostic variables type(var_dlength) :: diag_temp ! temporary model diagnostic variables type(var_dlength) :: flux_temp ! temporary model fluxes type(var_dlength) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(dp),dimension(nLayers) :: mLayerVolFracIceInit ! initial vector for volumetric fraction of ice (-) ! ------------------------------------------------------------------------------------------------------ ! * operator splitting ! ------------------------------------------------------------------------------------------------------ ! minimum timestep real(dp),parameter :: dtmin_coupled=1800._dp ! minimum time step for the fully coupled solution (seconds) real(dp),parameter :: dtmin_split=60._dp ! minimum time step for the fully split solution (seconds) real(dp),parameter :: dtmin_scalar=10._dp ! minimum time step for the scalar solution (seconds) real(dp) :: dt_min ! minimum time step (seconds) real(dp) :: dtInit ! initial time step (seconds) ! explicit error tolerance (depends on state type split, so defined here) real(dp),parameter :: errorTolLiqFlux=0.01_dp ! error tolerance in the explicit solution (liquid flux) real(dp),parameter :: errorTolNrgFlux=10._dp ! error tolerance in the explicit solution (energy flux) ! number of substeps taken for a given split integer(i4b) :: nSubsteps ! number of substeps taken for a given split ! named variables defining the coupling and solution method integer(i4b) :: ixCoupling ! index of coupling method (1,2) integer(i4b) :: ixSolution ! index of solution method (1,2) integer(i4b) :: ixStateThenDomain ! switch between the state and domain (1,2) integer(i4b) :: tryDomainSplit ! (0,1) - flag to try the domain split ! actual number of splits integer(i4b) :: nStateTypeSplit ! number of splits for the state type integer(i4b) :: nDomainSplit ! number of splits for the domain integer(i4b) :: nStateSplit ! number of splits for the states within a given domain ! indices for the state type and the domain split integer(i4b) :: iStateTypeSplit ! index of the state type split integer(i4b) :: iDomainSplit ! index of the domain split integer(i4b) :: iStateSplit ! index of the state split ! flux masks logical(lgt) :: neededFlux(nFlux) ! .true. if flux is needed at all logical(lgt) :: desiredFlux ! .true. if flux is desired for a given split type(var_ilength) :: fluxCount ! number of times each flux is updated (should equal nSubsteps) type(var_flagVec) :: fluxMask ! mask defining model fluxes ! state masks integer(i4b),dimension(nState) :: stateCheck ! number of times each state variable is updated (should equal 1) logical(lgt),dimension(nState) :: stateMask ! mask defining desired state variables integer(i4b) :: nSubset ! number of selected state variables for a given split ! flags logical(lgt) :: failure ! flag to denote failure of substepping logical(lgt) :: doAdjustTemp ! flag to adjust temperature after the mass split logical(lgt) :: failedMinimumStep ! flag to denote failure of substepping for a given split integer(i4b) :: ixSaturation ! index of the lowest saturated layer (NOTE: only computed on the first iteration) ! --------------------------------------------------------------------------------------- ! point to variables in the data structures ! --------------------------------------------------------------------------------------- globalVars: associate(& ! model decisions ixGroundwater => model_decisions(iLookDECISIONS%groundwatr)%iDecision ,& ! intent(in): [i4b] groundwater parameterization ixSpatialGroundwater => model_decisions(iLookDECISIONS%spatial_gw)%iDecision ,& ! intent(in): [i4b] spatial representation of groundwater (local-column or single-basin) ! domain boundary conditions airtemp => forc_data%var(iLookFORCE%airtemp) ,& ! intent(in): [dp] temperature of the upper boundary of the snow and soil domains (K) ! 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 ! indices of model state variables ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset (missing for values not in the subset) ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) 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 ixCasNrg => indx_data%var(iLookINDEX%ixCasNrg)%dat(1) ,& ! intent(in): [i4b] index of canopy air space energy state variable 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) ! numerix tracking numberStateSplit => indx_data%var(iLookINDEX%numberStateSplit )%dat(1) ,& ! intent(inout): [i4b] number of state splitting solutions (-) numberDomainSplitNrg => indx_data%var(iLookINDEX%numberDomainSplitNrg )%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for energy (-) numberDomainSplitMass => indx_data%var(iLookINDEX%numberDomainSplitMass)%dat(1) ,& ! intent(inout): [i4b] number of domain splitting solutions for mass (-) numberScalarSolutions => indx_data%var(iLookINDEX%numberScalarSolutions)%dat(1) ,& ! intent(inout): [i4b] number of scalar solutions (-) ! domain configuration 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) ! 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 soil 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 (-) ! soil parameters specificStorage => mpar_data%var(iLookPARAM%specificStorage)%dat(1) ,& ! intent(in): [dp] specific storage coefficient (m-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 (-) mLayerMeltFreeze => diag_data%var(iLookDIAG%mLayerMeltFreeze)%dat ,& ! intent(out): [dp(:)] melt-freeze in each snow and soil layer (kg m-3) ! model state variables (vegetation canopy) scalarCanairTemp => prog_data%var(iLookPROG%scalarCanairTemp)%dat(1) ,& ! intent(out): [dp] temperature of the canopy air space (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1) ,& ! intent(out): [dp] temperature of the vegetation canopy (K) scalarCanopyIce => prog_data%var(iLookPROG%scalarCanopyIce)%dat(1) ,& ! intent(out): [dp] mass of ice on the vegetation canopy (kg m-2) scalarCanopyLiq => prog_data%var(iLookPROG%scalarCanopyLiq)%dat(1) ,& ! intent(out): [dp] mass of liquid water on the vegetation canopy (kg m-2) scalarCanopyWat => prog_data%var(iLookPROG%scalarCanopyWat)%dat(1) ,& ! intent(out): [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(out): [dp(:)] temperature of each snow/soil layer (K) mLayerVolFracIce => prog_data%var(iLookPROG%mLayerVolFracIce)%dat ,& ! intent(out): [dp(:)] volumetric fraction of ice (-) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat ,& ! intent(out): [dp(:)] volumetric fraction of liquid water (-) mLayerVolFracWat => prog_data%var(iLookPROG%mLayerVolFracWat)%dat ,& ! intent(out): [dp(:)] volumetric fraction of total water (-) mLayerMatricHead => prog_data%var(iLookPROG%mLayerMatricHead)%dat ,& ! intent(out): [dp(:)] matric head (m) mLayerMatricHeadLiq => diag_data%var(iLookDIAG%mLayerMatricHeadLiq)%dat & ! intent(out): [dp(:)] matric potential of liquid water (m) ) ! --------------------------------------------------------------------------------------- ! initialize error control err=0; message="opSplittin/" ! ***** ! (0) PRELIMINARIES... ! ******************** ! ----- ! * initialize... ! --------------- ! set the global print flag globalPrintFlag=.false. if(globalPrintFlag)& print*, trim(message), dt ! initialize the first success call firstSuccess=.false. ! initialize the flags tooMuchMelt=.false. ! too much melt (merge snow layers) stepFailure=.false. ! step failure ! initialize flag for the success of the substepping failure=.false. ! initialize the flux check neededFlux(:) = .false. ! initialize the state check stateCheck(:) = 0 ! compute the total water content in the vegetation canopy scalarCanopyWat = scalarCanopyLiq + scalarCanopyIce ! kg m-2 ! save volumetric ice content at the start of the step ! NOTE: used for volumetric loss due to melt-freeze mLayerVolFracIceInit(:) = mLayerVolFracIce(:) ! compute the total water content in snow and soil ! NOTE: no ice expansion allowed for soil if(nSnow>0)& mLayerVolFracWat( 1:nSnow ) = mLayerVolFracLiq( 1:nSnow ) + mLayerVolFracIce( 1:nSnow )*(iden_ice/iden_water) mLayerVolFracWat(nSnow+1:nLayers) = mLayerVolFracLiq(nSnow+1:nLayers) + mLayerVolFracIce(nSnow+1:nLayers) ! compute the liquid water matric potential (m) ! NOTE: include ice content as part of the solid porosity - major effect of ice is to reduce the pore size; ensure that effSat=1 at saturation ! (from Zhao et al., J. Hydrol., 1997: Numerical analysis of simultaneous heat and mass transfer...) do iSoil=1,nSoil call liquidHead(mLayerMatricHead(iSoil),mLayerVolFracLiq(nSnow+iSoil),mLayerVolFracIce(nSnow+iSoil), & ! input: state variables vGn_alpha(iSoil),vGn_n(iSoil),theta_sat(iSoil),theta_res(iSoil),vGn_m(iSoil), & ! input: parameters matricHeadLiq=mLayerMatricHeadLiq(iSoil), & ! output: liquid water matric potential (m) err=err,message=cmessage) ! output: error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif end do ! looping through soil layers (computing liquid water matric potential) ! allocate space for the flux mask (used to define when fluxes are updated) call allocLocal(flux_meta(:),fluxMask,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! allocate space for the flux count (used to check that fluxes are only updated once) call allocLocal(flux_meta(:),fluxCount,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! allocate space for the temporary prognostic variable structure call allocLocal(prog_meta(:),prog_temp,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! allocate space for the temporary diagnostic variable structure call allocLocal(diag_meta(:),diag_temp,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! allocate space for the temporary flux variable structure call allocLocal(flux_meta(:),flux_temp,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; endif ! allocate space for the derivative structure call allocLocal(deriv_meta(:),deriv_data,nSnow,nSoil,err,cmessage) if(err/=0)then; err=20; message=trim(message)//trim(cmessage); return; end if ! intialize the flux conter do iVar=1,size(flux_meta) ! loop through fluxes fluxCount%var(iVar)%dat(:) = 0 end do ! initialize the model fluxes do iVar=1,size(flux_meta) ! loop through fluxes if(flux2state_orig(iVar)%state1==integerMissing .and. flux2state_orig(iVar)%state2==integerMissing) cycle ! flux does not depend on state (e.g., input) if(flux2state_orig(iVar)%state1==iname_watCanopy .and. .not.computeVegFlux) cycle ! use input fluxes in cases where there is no canopy flux_data%var(iVar)%dat(:) = 0._dp end do ! initialize derivatives do iVar=1,size(deriv_meta) deriv_data%var(iVar)%dat(:) = 0._dp end do ! ========================================================================================================================================== ! ========================================================================================================================================== ! ========================================================================================================================================== ! ========================================================================================================================================== ! loop through different coupling strategies coupling: do ixCoupling=1,nCoupling ! initialize the time step dtInit = min( merge(dt, dtmin_coupled, ixCoupling==fullyCoupled), dt) ! initial time step dt_min = min( merge(dtmin_coupled, dtmin_split, ixCoupling==fullyCoupled), dt) ! minimum time step ! keep track of the number of state splits if(ixCoupling/=fullyCoupled) numberStateSplit = numberStateSplit + 1 ! define the number of operator splits for the state type select case(ixCoupling) case(fullyCoupled); nStateTypeSplit=1 case(stateTypeSplit); nStateTypeSplit=nStateTypes case default; err=20; message=trim(message)//'coupling case not found'; return end select ! operator splitting option ! define if we wish to try the domain split select case(ixCoupling) case(fullyCoupled); tryDomainSplit=0 case(stateTypeSplit); tryDomainSplit=1 case default; err=20; message=trim(message)//'coupling case not found'; return end select ! operator splitting option ! state splitting loop stateTypeSplitLoop: do iStateTypeSplit=1,nStateTypeSplit !print*, 'iStateTypeSplit, nStateTypeSplit = ', iStateTypeSplit, nStateTypeSplit ! ----- ! * identify state-specific variables for a given state split... ! -------------------------------------------------------------- ! flag to adjust the temperature doAdjustTemp = (ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit) ! modify the state type names associated with the state vector if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then if(computeVegFlux)then where(ixStateType(ixHydCanopy)==iname_watCanopy) ixStateType(ixHydCanopy)=iname_liqCanopy endif where(ixStateType(ixHydLayer) ==iname_watLayer) ixStateType(ixHydLayer) =iname_liqLayer where(ixStateType(ixHydLayer) ==iname_matLayer) ixStateType(ixHydLayer) =iname_lmpLayer endif ! if modifying state variables for the mass split ! first try the state type split, then try the domain split within a given state type stateThenDomain: do ixStateThenDomain=1,1+tryDomainSplit ! 1=state type split; 2=domain split within a given state type !print*, 'start of stateThenDomain loop' ! keep track of the number of domain splits if(iStateTypeSplit==nrgSplit .and. ixStateThenDomain==subDomain) numberDomainSplitNrg = numberDomainSplitNrg + 1 if(iStateTypeSplit==massSplit .and. ixStateThenDomain==subDomain) numberDomainSplitMass = numberDomainSplitMass + 1 ! define the number of domain splits for the state type select case(ixStateThenDomain) case(fullDomain); nDomainSplit=1 case(subDomain); nDomainSplit=nDomains case default; err=20; message=trim(message)//'coupling case not found'; return end select ! check that we haven't split the domain when we are fully coupled if(ixCoupling==fullyCoupled .and. nDomainSplit==nDomains)then message=trim(message)//'cannot split domains when fully coupled' err=20; return endif ! domain splitting loop domainSplit: do iDomainSplit=1,nDomainSplit ! trial with the vector then scalar solution solution: do ixSolution=1,nSolutions ! initialize error control err=0; message="opSplittin/" ! refine the time step if(ixSolution==scalar)then dtInit = min(dtmin_split, dt) ! initial time step dt_min = min(dtmin_scalar, dt) ! minimum time step endif ! initialize the first flux call firstFluxCall=.true. ! get the number of split layers select case(ixSolution) case(vector); nStateSplit=1 case(scalar); nStateSplit=count(stateMask) case default; err=20; message=trim(message)//'unknown solution method'; return end select !print*, '*****' !print*, 'computeVegFlux = ', computeVegFlux !print*, '(ixSolution==scalar) = ', (ixSolution==scalar) !print*, 'ixCoupling, iStateTypeSplit, ixStateThenDomain, iDomainSplit, nDomainSplit: ', ixCoupling, iStateTypeSplit, ixStateThenDomain, iDomainSplit, nDomainSplit !print*, 'ixSoilOnlyHyd = ', indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ! loop through layers (NOTE: nStateSplit=1 for the vector solution, hence no looping) stateSplit: do iStateSplit=1,nStateSplit ! ----- ! * define state subsets for a given split... ! ------------------------------------------- ! get the mask for the state subset call stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& indx_data,stateMask,nSubset,err,cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! (check for errors) ! check that state variables exist if(nSubset==0) cycle domainSplit ! avoid redundant case where vector solution is of length 1 if(ixSolution==vector .and. count(stateMask)==1) cycle solution ! check !print*, 'after stateFilter: stateMask = ', stateMask !print*, 'count(stateMask) = ', count(stateMask) !if(ixSolution==scalar)then ! print*, 'iStateSplit, nStateSplit = ', iStateSplit, nStateSplit ! print*, 'start of scalar solution' ! !print*, 'PAUSE'; read(*,*) !endif ! ----- ! * assemble vectors for a given split... ! --------------------------------------- ! get indices for a given split call indexSplit(stateMask, & ! intent(in) : logical vector (.true. if state is in the subset) nSnow,nSoil,nLayers,nSubset, & ! intent(in) : number of snow and soil layers, and total number of layers indx_data, & ! intent(inout) : index data structure err,cmessage) ! intent(out) : error control if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! ----- ! * define the mask of the fluxes used... ! --------------------------------------- ! identify the type of state for the states in the subset stateSubset: associate(ixStateType_subset => indx_data%var(iLookINDEX%ixStateType_subset)%dat ,& ! intent(in): [i4b(:)] indices of state types 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) ixLayerActive => indx_data%var(iLookINDEX%ixLayerActive)%dat ,& ! intent(in): [i4b(:)] list of indices for all active layers (inactive=integerM ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ) ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) ! loop through flux variables do iVar=1,size(flux_meta) ! * identify flux mask for the fully coupled solution if(ixCoupling==fullyCoupled)then desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) fluxMask%var(iVar)%dat = desiredFlux ! * identify flux mask for the split solution else ! identify the flux mask for a given state split select case(iStateTypeSplit) case(nrgSplit); desiredFlux = any(ixStateType_subset==flux2state_orig(iVar)%state1) .or. any(ixStateType_subset==flux2state_orig(iVar)%state2) case(massSplit); desiredFlux = any(ixStateType_subset==flux2state_liq(iVar)%state1) .or. any(ixStateType_subset==flux2state_liq(iVar)%state2) case default; err=20; message=trim(message)//'unable to identify split based on state type'; return end select ! no domain splitting if(nDomains==1)then fluxMask%var(iVar)%dat = desiredFlux ! domain splitting else ! initialize to .false. fluxMask%var(iVar)%dat = .false. ! only need to proceed if the flux is desired if(desiredFlux)then ! different domain splitting operations select case(iDomainSplit) ! canopy fluxes -- (:1) gets the upper boundary(0) if it exists case(vegSplit) ! vector solution (should only be present for energy) if(ixSolution==vector)then fluxMask%var(iVar)%dat(:1) = desiredFlux if(ixStateThenDomain>1 .and. iStateTypeSplit/=nrgSplit)then message=trim(message)//'only expect a vector solution for the vegetation domain for energy' err=20; return endif ! scalar solution else fluxMask%var(iVar)%dat(:1) = desiredFlux endif ! fluxes through snow and soil case(snowSplit,soilSplit) ! loop through layers do iLayer=1,nLayers if(ixlayerActive(iLayer)/=integerMissing)then ! get the offset (ixLayerActive=1,2,3,...nLayers, and soil vectors nSnow+1, nSnow+2, ..., nLayers) iOffset = merge(nSnow, 0, flux_meta(iVar)%vartype==iLookVarType%midSoil .or. flux_meta(iVar)%vartype==iLookVarType%ifcSoil) jLayer = iLayer-iOffset ! identify the minimum layer select case(flux_meta(iVar)%vartype) case(iLookVarType%ifcToto, iLookVarType%ifcSnow, iLookVarType%ifcSoil); minLayer=merge(jLayer-1, jLayer, jLayer==1) case(iLookVarType%midToto, iLookVarType%midSnow, iLookVarType%midSoil); minLayer=jLayer case default; minLayer=integerMissing end select ! set desired layers select case(flux_meta(iVar)%vartype) case(iLookVarType%midToto,iLookVarType%ifcToto); fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux case(iLookVarType%midSnow,iLookVarType%ifcSnow); if(iLayer<=nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux case(iLookVarType%midSoil,iLookVarType%ifcSoil); if(iLayer> nSnow) fluxMask%var(iVar)%dat(minLayer:jLayer) = desiredFlux end select ! add hydrology states for scalar variables if(iStateTypeSplit==massSplit .and. flux_meta(iVar)%vartype==iLookVarType%scalarv)then select case(iDomainSplit) case(snowSplit); if(iLayer==nSnow) fluxMask%var(iVar)%dat = desiredFlux case(soilSplit); if(iLayer==nSnow+1) fluxMask%var(iVar)%dat = desiredFlux end select endif ! if hydrology split and scalar endif ! if the layer is active end do ! looping through layers ! check case default; err=20; message=trim(message)//'unable to identify split based on domain type'; return end select ! domain split endif ! if flux is desired endif ! domain splitting endif ! not fully coupled ! define if the flux is desired if(desiredFlux) neededFlux(iVar)=.true. !if(desiredFlux) print*, flux_meta(iVar)%varname, fluxMask%var(iVar)%dat ! * check if( globalPrintFlag .and. count(fluxMask%var(iVar)%dat)>0 )& print*, trim(flux_meta(iVar)%varname) end do ! (loop through fluxes) end associate stateSubset ! ******************************************************************************************************************************* ! ******************************************************************************************************************************* ! ******************************************************************************************************************************* ! ***** trial with a given solution method... ! check that we do not attempt the scalar solution for the fully coupled case if(ixCoupling==fullyCoupled .and. ixSolution==scalar)then message=trim(message)//'only apply the scalar solution to the fully split coupling strategy' err=20; return endif ! reset the flag for the first flux call if(.not.firstSuccess) firstFluxCall=.true. ! save/recover copies of prognostic variables do iVar=1,size(prog_data%var) select case(failure) case(.false.); prog_temp%var(iVar)%dat(:) = prog_data%var(iVar)%dat(:) case(.true.); prog_data%var(iVar)%dat(:) = prog_temp%var(iVar)%dat(:) end select end do ! looping through variables ! save/recover copies of diagnostic variables do iVar=1,size(diag_data%var) select case(failure) case(.false.); diag_temp%var(iVar)%dat(:) = diag_data%var(iVar)%dat(:) case(.true.); diag_data%var(iVar)%dat(:) = diag_temp%var(iVar)%dat(:) end select end do ! looping through variables ! save/recover copies of model fluxes do iVar=1,size(flux_data%var) select case(failure) case(.false.); flux_temp%var(iVar)%dat(:) = flux_data%var(iVar)%dat(:) case(.true.); flux_data%var(iVar)%dat(:) = flux_temp%var(iVar)%dat(:) end select end do ! looping through variables ! ----- ! * solve variable subset for one time step... ! -------------------------------------------- !print*, trim(message)//'before varSubstep: nSubset = ', nSubset ! keep track of the number of scalar solutions if(ixSolution==scalar) numberScalarSolutions = numberScalarSolutions + 1 ! solve variable subset for one full time step call varSubstep(& ! input: model control dt, & ! intent(inout) : time step (s) dtInit, & ! intent(in) : initial time step (seconds) dt_min, & ! intent(in) : minimum time step (seconds) nSubset, & ! intent(in) : total number of variables in the state subset 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 (ixSolution==scalar), & ! intent(in) : flag to denote computing the scalar solution iStateSplit, & ! intent(in) : index of the layer in the splitting operation fluxMask, & ! intent(in) : mask for the fluxes used in this given state subset fluxCount, & ! intent(inout) : number of times fluxes are updated (should equal nsubstep) ! input/output: data structures model_decisions, & ! intent(in) : model decisions 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: 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 for failed substeps 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 err,cmessage) ! intent(out) : error code and error message if(err/=0)then message=trim(message)//trim(cmessage) if(err>0) return endif ! (check for errors) !print*, trim(message)//'after varSubstep: scalarSnowDrainage = ', flux_data%var(iLookFLUX%scalarSnowDrainage)%dat !print*, trim(message)//'after varSubstep: iLayerLiqFluxSnow = ', flux_data%var(iLookFLUX%iLayerLiqFluxSnow)%dat !print*, trim(message)//'after varSubstep: iLayerLiqFluxSoil = ', flux_data%var(iLookFLUX%iLayerLiqFluxSoil)%dat ! check !if(ixSolution==scalar)then ! print*, 'PAUSE: check scalar'; read(*,*) !endif ! reduce coupled step if failed the minimum step for the scalar solution if(failedMinimumStep .and. ixSolution==scalar) reduceCoupledStep=.true. ! check !if(ixCoupling/=fullyCoupled)then ! print*, 'dt = ', dt ! print*, 'after varSubstep: err = ', err ! print*, 'after varSubstep: cmessage = ', trim(cmessage) ! print*, 'after varSubstep: computeVegFlux = ', computeVegFlux ! print*, 'after varSubstep: stateMask = ', stateMask ! print*, 'after varSubstep: coupling = ', (ixCoupling==fullyCoupled) ! print*, 'after varSubstep: scalar solve = ', (ixSolution==scalar) ! print*, 'iStateTypeSplit, nStateTypeSplit = ', iStateTypeSplit, nStateTypeSplit ! print*, 'iDomainSplit, nDomainSplit = ', iDomainSplit, nDomainSplit ! print*, 'nSubset = ', nSubset ! print*, 'tooMuchMelt = ', tooMuchMelt ! print*, 'reduceCoupledStep = ', reduceCoupledStep ! print*, 'failedMinimumStep = ', failedMinimumStep, merge('coupled','opSplit',ixCoupling==fullyCoupled) ! if(ixSolution==scalar)then; print*, 'PAUSE'; read(*,*); endif !endif !if(ixSolution==scalar)then ! !print*, trim(message)//'stop: checking scalar solution'; stop ! print*, trim(message)//'pause: checking scalar solution'; read(*,*) !endif !print*, 'tooMuchMelt, reduceCoupledStep = ', tooMuchMelt, reduceCoupledStep ! if too much melt (or some other need to reduce 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)then stepFailure=.true. err=0 ! recovering return endif ! define failure failure = (failedMinimumStep .or. err<0) if(.not.failure) firstSuccess=.true. ! if failed, need to reset the flux counter if(failure)then !print*, 'failure!' do iVar=1,size(flux_meta) iMin=lbound(flux_data%var(iVar)%dat) iMax=ubound(flux_data%var(iVar)%dat) do iLayer=iMin(1),iMax(1) if(fluxMask%var(iVar)%dat(iLayer)) fluxCount%var(iVar)%dat(iLayer) = fluxCount%var(iVar)%dat(iLayer) - nSubsteps end do !if(iVar==iLookFLUX%mLayerTranspire) print*, flux_meta(iVar)%varname, fluxCount%var(iVar)%dat end do endif ! try the fully split solution if failed to converge with a minimum time step in the coupled solution if(ixCoupling==fullyCoupled .and. failure) cycle coupling ! try the scalar solution if failed to converge with a minimum time step in the split solution if(ixCoupling/=fullyCoupled)then select case(ixStateThenDomain) case(fullDomain); if(failure) cycle stateThenDomain case(subDomain); if(failure) cycle solution case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select endif ! check that state variables updated where(stateMask) stateCheck = stateCheck+1 if(any(stateCheck>1))then message=trim(message)//'state variable updated more than once!' err=20; return endif ! success = exit solution if(.not.failure)then select case(ixStateThenDomain) case(fullDomain); if(iStateSplit==nStateSplit) exit stateThenDomain case(subDomain); if(iStateSplit==nStateSplit) exit solution case default; err=20; message=trim(message)//'unknown ixStateThenDomain case' end select else ! check that we did not fail for the scalar solution (last resort) if(ixSolution==scalar)then message=trim(message)//'failed the minimum step for the scalar solution' err=20; return ! check for an unexpected failure else message=trim(message)//'unexpected failure' err=20; return endif endif ! success check end do stateSplit ! solution with split layers !print*, 'after stateSplit' end do solution ! trial with the full layer solution then the split layer solution !print*, 'after solution loop' ! ***** trial with a given solution method... ! ******************************************************************************************************************************* ! ******************************************************************************************************************************* ! ******************************************************************************************************************************* end do domainSplit ! domain type splitting loop !print*, 'ixStateThenDomain = ', ixStateThenDomain !print*, 'after domain split loop' end do stateThenDomain ! switch between the state and the domain !print*, 'after stateThenDomain switch' ! ----- ! * reset state variables for the mass split... ! --------------------------------------------- ! modify the state type names associated with the state vector if(ixCoupling/=fullyCoupled .and. iStateTypeSplit==massSplit)then if(computeVegFlux)then where(ixStateType(ixHydCanopy)==iname_liqCanopy) ixStateType(ixHydCanopy)=iname_watCanopy endif where(ixStateType(ixHydLayer) ==iname_liqLayer) ixStateType(ixHydLayer) =iname_watLayer where(ixStateType(ixHydLayer) ==iname_lmpLayer) ixStateType(ixHydLayer) =iname_matLayer endif ! if modifying state variables for the mass split end do stateTypeSplitLoop ! state type splitting loop ! check !if(ixCoupling/=fullyCoupled)then ! print*, 'PAUSE: end of splitting loop'; read(*,*) !endif ! ========================================================================================================================================== ! ========================================================================================================================================== ! success = exit the coupling loop ! terminate DO loop early if fullyCoupled returns a solution, ! so that the loop does not proceed to ixCoupling = stateTypeSplit if(ixCoupling==fullyCoupled .and. .not. failure) exit coupling ! if we reach stateTypeSplit, terminating the DO loop here is cleaner ! than letting the loop complete, because in the latter case the coupling ! loop will end with ixCoupling = nCoupling+1 = 3 (a FORTRAN loop ! increments the index variable at the end of each iteration and stops ! the loop if the index > specified stop value). Variable ixCoupling is ! used for error reporting in coupled_em.f90 in the balance checks and ! we thus need to make sure ixCoupling is not incremented to be larger ! than nCoupling. if(ixCoupling==stateTypeSplit .and. .not. failure) exit coupling end do coupling ! coupling method ! check that all state variables were updated if(any(stateCheck==0))then message=trim(message)//'some state variables were not updated!' err=20; return endif ! check that the desired fluxes were computed do iVar=1,size(flux_meta) if(neededFlux(iVar) .and. any(fluxCount%var(iVar)%dat==0))then print*, 'fluxCount%var(iVar)%dat = ', fluxCount%var(iVar)%dat message=trim(message)//'flux '//trim(flux_meta(iVar)%varname)//' was not computed' err=20; return endif end do ! use step halving if unable to complete the fully coupled solution in one substep if(ixCoupling/=fullyCoupled .or. nSubsteps>1) dtMultiplier=0.5_dp ! compute the melt in each snow and soil layer if(nSnow>0) mLayerMeltFreeze( 1:nSnow ) = -(mLayerVolFracIce( 1:nSnow ) - mLayerVolFracIceInit( 1:nSnow ))*iden_ice mLayerMeltFreeze(nSnow+1:nLayers) = -(mLayerVolFracIce(nSnow+1:nLayers) - mLayerVolFracIceInit(nSnow+1:nLayers))*iden_water ! end associate statements end associate globalVars end subroutine opSplittin ! ********************************************************************************************************** ! private subroutine stateFilter: get a mask for the desired state variables ! ********************************************************************************************************** subroutine stateFilter(ixCoupling,ixSolution,ixStateThenDomain,iStateTypeSplit,iDomainSplit,iStateSplit,& indx_data,stateMask,nSubset,err,message) USE indexState_module,only:indxSubset ! get state indices implicit none ! input integer(i4b),intent(in) :: ixCoupling ! index of coupling method (1,2) integer(i4b),intent(in) :: ixSolution ! index of solution method (1,2) integer(i4b),intent(in) :: ixStateThenDomain ! switch between full domain and sub domains integer(i4b),intent(in) :: iStateTypeSplit ! index of the state type split integer(i4b),intent(in) :: iDomainSplit ! index of the domain split integer(i4b),intent(in) :: iStateSplit ! index of the layer split type(var_ilength),intent(inout) :: indx_data ! indices for a local HRU ! output logical(lgt),intent(out) :: stateMask(:) ! mask defining desired state variables integer(i4b),intent(out) :: nSubset ! number of selected state variables for a given split integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local integer(i4b),allocatable :: ixSubset(:) ! list of indices in the state subset character(len=256) :: cmessage ! error message ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ! data structures associate(& ! indices of model state variables ixStateType => indx_data%var(iLookINDEX%ixStateType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the state (ixNrgState...) 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 ixWatAquifer => indx_data%var(iLookINDEX%ixWatAquifer)%dat ,& ! intent(in): [i4b(:)] indices IN THE FULL VECTOR for water storage in the aquifer ixAllState => indx_data%var(iLookINDEX%ixAllState)%dat ,& ! intent(in): [i4b(:)] list of indices for all model state variables (1,2,3,...nState) ! 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 ) ! data structures ! -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='stateFilter/' ! identify splitting option select case(ixCoupling) ! ----- ! - fully coupled... ! ------------------ ! use all state variables case(fullyCoupled); stateMask(:) = .true. ! ----- ! - splitting by state type... ! ---------------------------- ! initial split by state type case(stateTypeSplit) ! switch between full domain and sub domains select case(ixStateThenDomain) ! split into energy and mass case(fullDomain) select case(iStateTypeSplit) case(nrgSplit); stateMask = (ixStateType==iname_nrgCanair .or. ixStateType==iname_nrgCanopy .or. ixStateType==iname_nrgLayer) case(massSplit); stateMask = (ixStateType==iname_liqCanopy .or. ixStateType==iname_liqLayer .or. ixStateType==iname_lmpLayer .or. ixStateType==iname_watAquifer) case default; err=20; message=trim(message)//'unable to identify split based on state type'; return end select ! split into vegetation, snow, and soil case(subDomain) ! define state mask stateMask=.false. ! (initialize state mask) select case(iStateTypeSplit) ! define mask for energy case(nrgSplit) select case(iDomainSplit) case(vegSplit) if(ixNrgCanair(1)/=integerMissing) stateMask(ixNrgCanair) = .true. ! energy of the canopy air space if(ixNrgCanopy(1)/=integerMissing) stateMask(ixNrgCanopy) = .true. ! energy of the vegetation canopy stateMask(ixNrgLayer(1)) = .true. ! energy of the upper-most layer in the snow+soil domain case(snowSplit); if(nSnow>1) stateMask(ixNrgLayer(2:nSnow)) = .true. ! NOTE: (2:) because the top layer in the snow+soil domain included in vegSplit case(soilSplit); stateMask(ixNrgLayer(max(2,nSnow+1):nLayers)) = .true. ! NOTE: max(2,nSnow+1) gives second layer unless more than 2 snow layers case(aquiferSplit) ! do nothing: no energy state variable for the aquifer domain case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return end select ! define mask for water case(massSplit) select case(iDomainSplit) case(vegSplit); if(ixHydCanopy(1)/=integerMissing) stateMask(ixHydCanopy) = .true. ! hydrology of the vegetation canopy case(snowSplit); stateMask(ixHydLayer(1:nSnow)) = .true. ! snow hydrology case(soilSplit); stateMask(ixHydLayer(nSnow+1:nLayers)) = .true. ! soil hydrology case(aquiferSplit); if(ixWatAquifer(1)/=integerMissing) stateMask(ixWatAquifer) = .true. ! aquifer storage case default; err=20; message=trim(message)//'unable to identify model sub-domain'; return end select ! check case default; err=20; message=trim(message)//'unable to identify the state type'; return end select ! (split based on state type) ! check case default; err=20; message=trim(message)//'unable to identify the switch between full domains and sub domains'; return end select ! (switch between full domains and sub domains) ! check case default; err=20; message=trim(message)//'unable to identify coupling method'; return end select ! (selecting solution method) !print*, 'stateMask = ', stateMask ! identify scalar solutions if(ixSolution==scalar)then ! get the subset of indices call indxSubset(ixSubset, ixAllState, stateMask, err, cmessage) if(err/=0)then; message=trim(message)//trim(cmessage); return; endif ! get the mask stateMask(:) = .false. stateMask( ixSubset(iStateSplit) ) = .true. ! check if(count(stateMask)/=1)then message=trim(message)//'expect size=1 (scalar)' err=20; return endif endif ! get the number of selected state variables nSubset = count(stateMask) ! end associations end associate end subroutine stateFilter end module opSplittin_module