! 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 computJacob_module ! data types USE nrtype ! derived types to define the data structures USE data_types,only:& var_ilength, & ! data vector with variable length dimension (i4b) var_dlength ! data vector with variable length dimension (dp) ! 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:iLookINDEX ! named variables for structure elements USE var_lookup,only:iLookDERIV ! named variables for structure elements ! access the global print flag USE globalData,only:globalPrintFlag ! access missing values USE globalData,only:integerMissing ! missing integer USE globalData,only:realMissing ! missing real number ! domain types 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 ! 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 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 ! access named variables to describe the form and structure of the matrices used in the numerical solver USE globalData,only: ku ! number of super-diagonal bands USE globalData,only: kl ! number of sub-diagonal bands USE globalData,only: ixDiag ! index for the diagonal band 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 ! constants USE multiconst,only:& LH_fus, & ! latent heat of fusion (J kg-1) iden_ice, & ! intrinsic density of ice (kg m-3) iden_water ! intrinsic density of liquid water (kg m-3) implicit none ! define constants real(dp),parameter :: verySmall=tiny(1.0_dp) ! a very small number integer(i4b),parameter :: ixBandOffset=kl+ku+1 ! offset in the band Jacobian matrix private public::computJacob contains ! ********************************************************************************************************** ! public subroutine computJacob: compute the Jacobian matrix ! ********************************************************************************************************** subroutine computJacob(& ! input: model control dt, & ! intent(in): length of the time step (seconds) nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers computeVegFlux, & ! intent(in): flag to indicate if we need to compute fluxes over vegetation computeBaseflow, & ! intent(in): flag to indicate if we need to compute baseflow ixMatrix, & ! intent(in): form of the Jacobian matrix ! input: data structures indx_data, & ! intent(in): index data prog_data, & ! intent(in): model prognostic variables for a local HRU diag_data, & ! intent(in): model diagnostic variables for a local HRU deriv_data, & ! intent(in): derivatives in model fluxes w.r.t. relevant state variables dBaseflow_dMatric, & ! intent(in): derivative in baseflow w.r.t. matric head (s-1) ! input-output: Jacobian and its diagonal dMat, & ! intent(inout): diagonal of the Jacobian matrix aJac, & ! intent(out): Jacobian matrix ! output: error control err,message) ! intent(out): error code and error message ! ----------------------------------------------------------------------------------------------------------------- implicit none ! input: model control real(dp),intent(in) :: dt ! length of the time step (seconds) 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 in the snow+soil domain logical(lgt),intent(in) :: computeVegFlux ! flag to indicate if computing fluxes over vegetation logical(lgt),intent(in) :: computeBaseflow ! flag to indicate if computing baseflow integer(i4b),intent(in) :: ixMatrix ! form of the Jacobian matrix ! input: data structures type(var_ilength),intent(in) :: indx_data ! indices defining model states and layers type(var_dlength),intent(in) :: prog_data ! prognostic variables for a local HRU type(var_dlength),intent(in) :: diag_data ! diagnostic variables for a local HRU type(var_dlength),intent(in) :: deriv_data ! derivatives in model fluxes w.r.t. relevant state variables real(dp),intent(in) :: dBaseflow_dMatric(:,:) ! derivative in baseflow w.r.t. matric head (s-1) ! input-output: Jacobian and its diagonal real(dp),intent(inout) :: dMat(:) ! diagonal of the Jacobian matrix real(dp),intent(out) :: aJac(:,:) ! Jacobian matrix ! output variables integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! -------------------------------------------------------------- ! * local variables ! -------------------------------------------------------------- ! indices of model state variables integer(i4b) :: jState ! index of state within the state subset integer(i4b) :: qState ! index of cross-derivative state variable for baseflow integer(i4b) :: nrgState ! energy state variable integer(i4b) :: watState ! hydrology state variable integer(i4b) :: nState ! number of state variables ! indices of model layers integer(i4b) :: iLayer ! index of model layer integer(i4b) :: jLayer ! index of model layer within the full state vector (hydrology) integer(i4b) :: pLayer ! indices of soil layers (used for the baseflow derivatives) ! conversion factors real(dp) :: convLiq2tot ! factor to convert liquid water derivative to total water derivative ! -------------------------------------------------------------- ! associate variables from data structures associate(& ! indices of model state variables 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) ixTopNrg => indx_data%var(iLookINDEX%ixTopNrg)%dat(1) ,& ! intent(in): [i4b] index of upper-most energy state in the snow+soil subdomain ixTopHyd => indx_data%var(iLookINDEX%ixTopHyd)%dat(1) ,& ! intent(in): [i4b] index of upper-most hydrology state in the snow+soil subdomain ixAqWat => indx_data%var(iLookINDEX%ixAqWat)%dat(1) ,& ! intent(in): [i4b] index of water storage in the aquifer ! vectors of indices for specfic state types within specific sub-domains IN THE FULL STATE VECTOR 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 ! vector of energy indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing ixSnowSoilNrg => indx_data%var(iLookINDEX%ixSnowSoilNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow+soil domain ixSnowOnlyNrg => indx_data%var(iLookINDEX%ixSnowOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the snow domain ixSoilOnlyNrg => indx_data%var(iLookINDEX%ixSoilOnlyNrg)%dat ,& ! intent(in): [i4b(:)] index in the state subset for energy state variables in the soil domain ! vector of hydrology indices for the snow and soil domains ! NOTE: states not in the subset are equal to integerMissing ixSnowSoilHyd => indx_data%var(iLookINDEX%ixSnowSoilHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow+soil domain ixSnowOnlyHyd => indx_data%var(iLookINDEX%ixSnowOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the snow domain ixSoilOnlyHyd => indx_data%var(iLookINDEX%ixSoilOnlyHyd)%dat ,& ! intent(in): [i4b(:)] index in the state subset for hydrology state variables in the soil domain ! number of state variables of a specific type nSnowSoilNrg => indx_data%var(iLookINDEX%nSnowSoilNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow+soil domain nSnowOnlyNrg => indx_data%var(iLookINDEX%nSnowOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the snow domain nSoilOnlyNrg => indx_data%var(iLookINDEX%nSoilOnlyNrg )%dat(1) ,& ! intent(in): [i4b] number of energy state variables in the soil domain nSnowSoilHyd => indx_data%var(iLookINDEX%nSnowSoilHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow+soil domain nSnowOnlyHyd => indx_data%var(iLookINDEX%nSnowOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the snow domain nSoilOnlyHyd => indx_data%var(iLookINDEX%nSoilOnlyHyd )%dat(1) ,& ! intent(in): [i4b] number of hydrology variables in the soil domain ! type and index of model control volume ixHydType => indx_data%var(iLookINDEX%ixHydType)%dat ,& ! intent(in): [i4b(:)] index of the type of hydrology states in snow+soil domain ixDomainType => indx_data%var(iLookINDEX%ixDomainType)%dat ,& ! intent(in): [i4b(:)] indices defining the type of the domain (iname_veg, iname_snow, iname_soil) ixControlVolume => indx_data%var(iLookINDEX%ixControlVolume)%dat ,& ! intent(in): [i4b(:)] index of the control volume for specific model domains ! mapping between states and model layers ixMapSubset2Full => indx_data%var(iLookINDEX%ixMapSubset2Full)%dat ,& ! intent(in): [i4b(:)] list of indices in the full state vector that are in the state subset ixMapFull2Subset => indx_data%var(iLookINDEX%ixMapFull2Subset)%dat ,& ! intent(in): [i4b(:)] list of indices in the state subset in each element of the full state vector ! derivatives in net vegetation energy fluxes w.r.t. relevant state variables dCanairNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy air temperature dCanairNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. canopy temperature dCanairNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanairNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy air space flux w.r.t. ground temperature dCanopyNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy air temperature dCanopyNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. canopy temperature dCanopyNetFlux_dGroundTemp => deriv_data%var(iLookDERIV%dCanopyNetFlux_dGroundTemp )%dat(1) ,& ! intent(in): [dp] derivative in net canopy flux w.r.t. ground temperature dCanopyNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dCanopyNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net canopy fluxes w.r.t. canopy liquid water content dGroundNetFlux_dCanairTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanairTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy air temperature dGroundNetFlux_dCanopyTemp => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanopyTemp )%dat(1) ,& ! intent(in): [dp] derivative in net ground flux w.r.t. canopy temperature dGroundNetFlux_dCanLiq => deriv_data%var(iLookDERIV%dGroundNetFlux_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in net ground fluxes w.r.t. canopy liquid water content ! derivatives in evaporative fluxes w.r.t. relevant state variables dCanopyEvaporation_dTCanair => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy air temperature dCanopyEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy temperature dCanopyEvaporation_dTGround => deriv_data%var(iLookDERIV%dCanopyEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. ground temperature dCanopyEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dCanopyEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in canopy evaporation w.r.t. canopy liquid water content dGroundEvaporation_dTCanair => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanair )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy air temperature dGroundEvaporation_dTCanopy => deriv_data%var(iLookDERIV%dGroundEvaporation_dTCanopy )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy temperature dGroundEvaporation_dTGround => deriv_data%var(iLookDERIV%dGroundEvaporation_dTGround )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. ground temperature dGroundEvaporation_dCanLiq => deriv_data%var(iLookDERIV%dGroundEvaporation_dCanLiq )%dat(1) ,& ! intent(in): [dp] derivative in ground evaporation w.r.t. canopy liquid water content ! derivatives in canopy water w.r.t canopy temperature dCanLiq_dTcanopy => deriv_data%var(iLookDERIV%dCanLiq_dTcanopy )%dat(1) ,& ! intent(in): [dp] derivative of canopy liquid storage w.r.t. temperature dTheta_dTkCanopy => deriv_data%var(iLookDERIV%dTheta_dTkCanopy )%dat(1) ,& ! intent(in): [dp] derivative of volumetric liquid water content w.r.t. temperature ! derivatives in canopy liquid fluxes w.r.t. canopy water scalarCanopyLiqDeriv => deriv_data%var(iLookDERIV%scalarCanopyLiqDeriv )%dat(1) ,& ! intent(in): [dp] derivative in (throughfall + drainage) w.r.t. canopy liquid water ! derivatives in energy fluxes at the interface of snow+soil layers w.r.t. temperature in layers above and below dNrgFlux_dTempAbove => deriv_data%var(iLookDERIV%dNrgFlux_dTempAbove )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer above dNrgFlux_dTempBelow => deriv_data%var(iLookDERIV%dNrgFlux_dTempBelow )%dat ,& ! intent(in): [dp(:)] derivatives in the flux w.r.t. temperature in the layer below ! derivative in liquid water fluxes at the interface of snow layers w.r.t. volumetric liquid water content in the layer above iLayerLiqFluxSnowDeriv => deriv_data%var(iLookDERIV%iLayerLiqFluxSnowDeriv )%dat ,& ! intent(in): [dp(:)] derivative in vertical liquid water flux at layer interfaces ! derivative in liquid water fluxes for the soil domain w.r.t hydrology state variables dVolTot_dPsi0 => deriv_data%var(iLookDERIV%dVolTot_dPsi0 )%dat ,& ! intent(in): [dp(:)] derivative in total water content w.r.t. total water matric potential dq_dHydStateAbove => deriv_data%var(iLookDERIV%dq_dHydStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dHydStateBelow => deriv_data%var(iLookDERIV%dq_dHydStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below dCompress_dPsi => deriv_data%var(iLookDERIV%dCompress_dPsi )%dat ,& ! intent(in): [dp(:)] derivative in compressibility w.r.t matric head ! derivative in baseflow flux w.r.t. aquifer storage dBaseflow_dAquifer => deriv_data%var(iLookDERIV%dBaseflow_dAquifer )%dat(1) ,& ! intent(out): [dp(:)] erivative in baseflow flux w.r.t. aquifer storage (s-1) ! derivative in liquid water fluxes for the soil domain w.r.t energy state variables dq_dNrgStateAbove => deriv_data%var(iLookDERIV%dq_dNrgStateAbove )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer above dq_dNrgStateBelow => deriv_data%var(iLookDERIV%dq_dNrgStateBelow )%dat ,& ! intent(in): [dp(:)] change in flux at layer interfaces w.r.t. states in the layer below mLayerdTheta_dTk => deriv_data%var(iLookDERIV%mLayerdTheta_dTk )%dat ,& ! intent(in): [dp(:)] derivative of volumetric liquid water content w.r.t. temperature ! diagnostic variables scalarFracLiqVeg => diag_data%var(iLookDIAG%scalarFracLiqVeg)%dat(1) ,& ! intent(in): [dp] fraction of liquid water on vegetation (-) scalarBulkVolHeatCapVeg => diag_data%var(iLookDIAG%scalarBulkVolHeatCapVeg)%dat(1) ,& ! intent(in): [dp] bulk volumetric heat capacity of vegetation (J m-3 K-1) mLayerFracLiqSnow => diag_data%var(iLookDIAG%mLayerFracLiqSnow)%dat ,& ! intent(in): [dp(:)] fraction of liquid water in each snow layer (-) mLayerVolHtCapBulk => diag_data%var(iLookDIAG%mLayerVolHtCapBulk)%dat ,& ! intent(in): [dp(:)] bulk volumetric heat capacity in each snow and soil layer (J m-3 K-1) scalarSoilControl => diag_data%var(iLookDIAG%scalarSoilControl)%dat(1) ,& ! intent(in): [dp] soil control on infiltration, zero or one ! canopy and layer 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) ) ! making association with data in structures ! -------------------------------------------------------------- ! initialize error control err=0; message='computJacob/' ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 0: PRELIMINARIES (INITIALIZE JACOBIAN AND COMPUTE TIME-VARIABLE DIAGONAL TERMS) ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! get the number of state variables nState = size(dMat) ! initialize the Jacobian ! NOTE: this needs to be done every time, since Jacobian matrix is modified in the solver aJac(:,:) = 0._dp ! analytical Jacobian matrix ! compute terms in the Jacobian for vegetation (excluding fluxes) ! NOTE: energy for vegetation is computed *within* the iteration loop as it includes phase change if(ixVegNrg/=integerMissing) dMat(ixVegNrg) = scalarBulkVolHeatCapVeg + LH_fus*iden_water*dTheta_dTkCanopy ! volumetric heat capacity of the vegetation (J m-3 K-1) ! compute additional terms for the Jacobian for the snow-soil domain (excluding fluxes) ! NOTE: energy for snow+soil is computed *within* the iteration loop as it includes phase change do iLayer=1,nLayers if(ixSnowSoilNrg(iLayer)/=integerMissing) dMat(ixSnowSoilNrg(iLayer)) = mLayerVolHtCapBulk(iLayer) + LH_fus*iden_water*mLayerdTheta_dTk(iLayer) end do ! compute additional terms for the Jacobian for the soil domain (excluding fluxes) do iLayer=1,nSoil if(ixSoilOnlyHyd(iLayer)/=integerMissing) dMat(ixSoilOnlyHyd(iLayer)) = dVolTot_dPsi0(iLayer) + dCompress_dPsi(iLayer) end do ! define the form of the matrix select case(ixMatrix) ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 1: BAND MATRIX ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* case(ixBandMatrix) ! check if(size(aJac,1)/=nBands .or. size(aJac,2)/=size(dMat))then message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nBands,nState)' err=20; return end if ! ----- ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) ! * diagonal elements for the vegetation canopy (-) if(ixCasNrg/=integerMissing) aJac(ixDiag,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) if(ixVegNrg/=integerMissing) aJac(ixDiag,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) if(ixVegHyd/=integerMissing) aJac(ixDiag,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._dp ! ixVegHyd: CORRECT ! * cross-derivative terms w.r.t. canopy water if(ixVegHyd/=integerMissing)then ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixCasNrg),ixCasNrg) = -dCanopyEvaporation_dTCanair*dt ! ixCasNrg: CORRECT if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixVegNrg),ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy ! ixVegNrg: CORRECT if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegHyd,ixTopNrg),ixTopNrg) = -dCanopyEvaporation_dTGround*dt ! ixTopNrg: CORRECT ! cross-derivative terms w.r.t. canopy water (kg-1 m2) if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixVegHyd),ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) - (1._dp - scalarFracLiqVeg)*LH_fus/canopyDepth ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) endif ! cross-derivative terms between surface hydrology and the temperature of the vegetation canopy (K-1) if(ixVegNrg/=integerMissing)then if(ixTopHyd/=integerMissing) aJac(ixOffDiag(ixTopHyd,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water endif ! cross-derivative terms w.r.t. the temperature of the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixVegNrg),ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixCasNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif ! cross-derivative terms w.r.t. the temperature of the vegetation canopy (J m-3 K-1) if(ixVegNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixCasNrg),ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) if(ixTopNrg/=integerMissing) aJac(ixOffDiag(ixVegNrg,ixTopNrg),ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) endif ! cross-derivative terms w.r.t. the temperature of the surface (J m-3 K-1) if(ixTopNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) if(ixVegNrg/=integerMissing) aJac(ixOffDiag(ixTopNrg,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) endif endif ! if there is a need to compute energy fluxes within vegetation ! ----- ! * energy fluxes for the snow+soil domain... ! ------------------------------------------- if(nSnowSoilNrg>0)then do iLayer=1,nLayers ! loop through all layers in the snow+soil domain ! check if the state is in the subset if(ixSnowSoilNrg(iLayer)==integerMissing) cycle ! - define index within the state subset and the full state vector jState = ixSnowSoilNrg(iLayer) ! index within the state subset ! - diagonal elements aJac(ixDiag,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) ! - lower-diagonal elements if(iLayer > 1)then if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer-1),jState),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) endif ! - upper diagonal elements if(iLayer < nLayers)then if(ixSnowSoilNrg(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSnowSoilNrg(iLayer+1),jState),jState) = (dt/mLayerDepth(iLayer+1))*(-dNrgFlux_dTempAbove(iLayer ) ) endif end do ! (looping through energy states in the snow+soil domain) endif ! (if the subset includes energy state variables in the snow+soil domain) ! ----- ! * liquid water fluxes for the snow domain... ! -------------------------------------------- if(nSnowOnlyHyd>0)then do iLayer=1,nSnow ! loop through layers in the snow domain ! - check that the snow layer is desired if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) case default; convLiq2tot = 1._dp end select ! - diagonal elements aJac(ixDiag,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) ! - lower-diagonal elements if(iLayer > 1)then if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer-1),watState),watState) = 0._dp ! sub-diagonal: no dependence on other layers endif ! - upper diagonal elements if(iLayer < nSnow)then if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),watState),watState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot ! dVol(below)/dLiq(above) -- (-) endif ! - compute cross-derivative terms for energy ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content if(nSnowOnlyNrg>0)then ! (define the energy state) nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! (cross-derivative terms for the current layer) aJac(ixOffDiag(nrgState,watState),watState) = -(1._dp - mLayerFracLiqSnow(iLayer))*LH_fus*iden_water ! (dF/dLiq) aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) ! (cross-derivative terms for the layer below) if(iLayer < nSnow)then aJac(ixOffDiag(ixSnowOnlyHyd(iLayer+1),nrgState),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 endif ! (if there is a water state in the layer below the current layer in the given state subset) endif ! (if the energy state for the current layer is within the state subset) endif ! (if state variables exist for energy in snow+soil layers) end do ! (looping through liquid water states in the snow domain) endif ! (if the subset includes hydrology state variables in the snow domain) ! ----- ! * liquid water fluxes for the soil domain... ! -------------------------------------------- if(nSoilOnlyHyd>0)then do iLayer=1,nSoil ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle ! - define state indices watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector ! - compute the diagonal elements ! all terms *excluding* baseflow aJac(ixDiag,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) ! - compute the lower-diagonal elements if(iLayer > 1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),watState),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) endif ! - compute the upper-diagonal elements if(iLayer<nSoil)then if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer+1),watState),watState) = (dt/mLayerDepth(jLayer+1))*(-dq_dHydStateAbove(iLayer)) endif end do ! (looping through hydrology states in the soil domain) endif ! (if the subset includes hydrology state variables in the soil domain) ! ----- ! * liquid water fluxes for the aquifer... ! ---------------------------------------- if(ixAqWat/=integerMissing) aJac(ixDiag,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) ! ----- ! * derivative in liquid water fluxes w.r.t. temperature for the soil domain... ! ----------------------------------------------------------------------------- if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then do iLayer=1,nSoilOnlyHyd ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle ! - define index of hydrology state variable within the state subset watState = ixSoilOnlyHyd(iLayer) ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector ! - define the energy state variable nrgState = ixNrgLayer(jLayer) ! index within the full state vector ! only compute derivatives if the energy state for the current layer is within the state subset if(nrgstate/=integerMissing)then ! - compute the Jacobian for the layer itself aJac(ixOffDiag(watState,nrgState),nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance ! - include derivatives w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then aJac(ixOffDiag(watState,ixVegHyd),ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 aJac(ixOffDiag(watState,ixCasNrg),ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) aJac(ixOffDiag(watState,ixVegNrg),ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) endif aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(ixOffDiag(watState,ixTopNrg),ixTopNrg) ! dVol/dT (K-1) endif ! melt-freeze: compute derivative in energy with respect to mass if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(ixOffDiag(nrgState,watState),watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content else aJac(ixOffDiag(nrgState,watState),watState) = 0._dp endif ! - compute lower diagonal elements if(iLayer>1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer-1),nrgState),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 endif ! compute upper-diagonal elements if(iLayer<nSoil)then if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixOffDiag(ixSoilOnlyHyd(iLayer+1),nrgState),nrgState) = (dt/mLayerDepth(jLayer+1))*(-dq_dNrgStateAbove(iLayer)) ! K-1 endif endif ! (if the energy state for the current layer is within the state subset) end do ! (looping through soil layers) endif ! (if there are state variables for both water and energy in the soil domain) if(globalPrintFlag)then print*, '** banded analytical Jacobian:' write(*,'(a4,1x,100(i17,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) do iLayer=kl+1,nBands write(*,'(i4,1x,100(e17.10,1x))') iLayer, (aJac(iLayer,jLayer),jLayer=min(iJac1,nState),min(iJac2,nState)) end do end if !print*, 'PAUSE: banded analytical Jacobian'; read(*,*) ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* ! * PART 2: FULL MATRIX ! ********************************************************************************************************************************************************* ! ********************************************************************************************************************************************************* case(ixFullMatrix) ! check if(size(aJac,1)/=size(dMat) .or. size(aJac,2)/=size(dMat))then message=trim(message)//'unexpected shape of the Jacobian matrix: expect aJac(nState,nState)' err=20; return end if ! ----- ! * energy and liquid fluxes over vegetation... ! --------------------------------------------- if(computeVegFlux)then ! (derivatives only defined when vegetation protrudes over the surface) ! * liquid water fluxes for vegetation canopy (-) if(ixVegHyd/=integerMissing) aJac(ixVegHyd,ixVegHyd) = -scalarFracLiqVeg*(dCanopyEvaporation_dCanLiq - scalarCanopyLiqDeriv)*dt + 1._dp ! * cross-derivative terms for canopy water if(ixVegHyd/=integerMissing)then ! cross-derivative terms w.r.t. system temperatures (kg m-2 K-1) if(ixCasNrg/=integerMissing) aJac(ixVegHyd,ixCasNrg) = -dCanopyEvaporation_dTCanair*dt if(ixVegNrg/=integerMissing) aJac(ixVegHyd,ixVegNrg) = -dCanopyEvaporation_dTCanopy*dt + dt*scalarCanopyLiqDeriv*dCanLiq_dTcanopy if(ixTopNrg/=integerMissing) aJac(ixVegHyd,ixTopNrg) = -dCanopyEvaporation_dTGround*dt ! cross-derivative terms w.r.t. canopy water (kg-1 m2) if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegHyd) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarFracLiqVeg*scalarCanopyLiqDeriv)/iden_water ! cross-derivative terms w.r.t. canopy liquid water (J m-1 kg-1) ! NOTE: dIce/dLiq = (1 - scalarFracLiqVeg); dIce*LH_fus/canopyDepth = J m-3; dLiq = kg m-2 if(ixVegNrg/=integerMissing) aJac(ixVegNrg,ixVegHyd) = (dt/canopyDepth) *(-dCanopyNetFlux_dCanLiq) - (1._dp - scalarFracLiqVeg)*LH_fus/canopyDepth ! dF/dLiq if(ixTopNrg/=integerMissing) aJac(ixTopNrg,ixVegHyd) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanLiq) endif ! cross-derivative terms w.r.t. canopy temperature (K-1) if(ixVegNrg/=integerMissing)then if(ixTopHyd/=integerMissing) aJac(ixTopHyd,ixVegNrg) = (dt/mLayerDepth(1))*(-scalarSoilControl*scalarCanopyLiqDeriv*dCanLiq_dTcanopy)/iden_water endif ! energy fluxes with the canopy air space (J m-3 K-1) if(ixCasNrg/=integerMissing)then aJac(ixCasNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanairTemp) + dMat(ixCasNrg) if(ixVegNrg/=integerMissing) aJac(ixCasNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dCanopyTemp) if(ixTopNrg/=integerMissing) aJac(ixCasNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanairNetFlux_dGroundTemp) endif ! energy fluxes with the vegetation canopy (J m-3 K-1) if(ixVegNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixVegNrg,ixCasNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanairTemp) aJac(ixVegNrg,ixVegNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dCanopyTemp) + dMat(ixVegNrg) if(ixTopNrg/=integerMissing) aJac(ixVegNrg,ixTopNrg) = (dt/canopyDepth)*(-dCanopyNetFlux_dGroundTemp) endif ! energy fluxes with the surface (J m-3 K-1) if(ixTopNrg/=integerMissing)then if(ixCasNrg/=integerMissing) aJac(ixTopNrg,ixCasNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanairTemp) if(ixVegNrg/=integerMissing) aJac(ixTopNrg,ixVegNrg) = (dt/mLayerDepth(1))*(-dGroundNetFlux_dCanopyTemp) endif endif ! if there is a need to compute energy fluxes within vegetation ! ----- ! * energy fluxes for the snow+soil domain... ! ------------------------------------------- if(nSnowSoilNrg>0)then do iLayer=1,nLayers ! loop through all layers in the snow+soil domain ! check if the state is in the subset if(ixSnowSoilNrg(iLayer)==integerMissing) cycle ! - define index within the state subset and the full state vector jState = ixSnowSoilNrg(iLayer) ! index within the state subset ! - diagonal elements aJac(jState,jState) = (dt/mLayerDepth(iLayer))*(-dNrgFlux_dTempBelow(iLayer-1) + dNrgFlux_dTempAbove(iLayer)) + dMat(jState) ! - lower-diagonal elements if(iLayer > 1)then if(ixSnowSoilNrg(iLayer-1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer-1),jState) = (dt/mLayerDepth(iLayer-1))*( dNrgFlux_dTempBelow(iLayer-1) ) endif ! - upper diagonal elements if(iLayer < nLayers)then if(ixSnowSoilNrg(iLayer+1)/=integerMissing) aJac(ixSnowSoilNrg(iLayer+1),jState) = (dt/mLayerDepth(iLayer+1))*(-dNrgFlux_dTempAbove(iLayer ) ) endif end do ! (looping through energy states in the snow+soil domain) endif ! (if the subset includes energy state variables in the snow+soil domain) ! ----- ! * liquid water fluxes for the snow domain... ! -------------------------------------------- if(nSnowOnlyHyd>0)then do iLayer=1,nSnow ! loop through layers in the snow domain ! - check that the snow layer is desired if(ixSnowOnlyHyd(iLayer)==integerMissing) cycle ! - define state indices for the current layer watState = ixSnowOnlyHyd(iLayer) ! hydrology state index within the state subset ! compute factor to convert liquid water derivative to total water derivative select case( ixHydType(iLayer) ) case(iname_watLayer); convLiq2tot = mLayerFracLiqSnow(iLayer) case default; convLiq2tot = 1._dp end select ! - diagonal elements aJac(watState,watState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot + dMat(watState) ! - lower-diagonal elements if(iLayer > 1)then if(ixSnowOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer-1),watState) = 0._dp ! sub-diagonal: no dependence on other layers endif ! - upper diagonal elements if(iLayer < nSnow)then if(ixSnowOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSnowOnlyHyd(iLayer+1),watState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*convLiq2tot ! dVol(below)/dLiq(above) -- (-) endif ! - compute cross-derivative terms for energy ! NOTE: increase in volumetric liquid water content balanced by a decrease in volumetric ice content if(nSnowOnlyNrg>0)then ! (define the energy state) nrgState = ixSnowOnlyNrg(iLayer) ! index within the full state vector if(nrgstate/=integerMissing)then ! (energy state for the current layer is within the state subset) ! (cross-derivative terms for the current layer) aJac(nrgState,watState) = -(1._dp - mLayerFracLiqSnow(iLayer))*LH_fus*iden_water ! (dF/dLiq) aJac(watState,nrgState) = (dt/mLayerDepth(iLayer))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! (dVol/dT) ! (cross-derivative terms for the layer below) if(iLayer < nSnow)then aJac(ixSnowOnlyHyd(iLayer+1),nrgState) = -(dt/mLayerDepth(iLayer+1))*iLayerLiqFluxSnowDeriv(iLayer)*mLayerdTheta_dTk(iLayer) ! dVol(below)/dT(above) -- K-1 endif ! (if there is a water state in the layer below the current layer in the given state subset) endif ! (if the energy state for the current layer is within the state subset) endif ! (if state variables exist for energy in snow+soil layers) end do ! (looping through liquid water states in the snow domain) endif ! (if the subset includes hydrology state variables in the snow domain) ! ----- ! * liquid water fluxes for the soil domain... ! -------------------------------------------- if(nSoilOnlyHyd>0)then do iLayer=1,nSoil ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle ! - define state indices watState = ixSoilOnlyHyd(iLayer) ! hydrology state index within the state subset ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector ! - compute the diagonal elements ! all terms *excluding* baseflow aJac(watState,watState) = (dt/mLayerDepth(jLayer))*(-dq_dHydStateBelow(iLayer-1) + dq_dHydStateAbove(iLayer)) + dMat(watState) ! - compute the lower-diagonal elements if(iLayer > 1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),watState) = (dt/mLayerDepth(jLayer-1))*( dq_dHydStateBelow(iLayer-1)) endif ! - compute the upper-diagonal elements if(iLayer<nSoil)then if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer+1),watState) = (dt/mLayerDepth(jLayer+1))*(-dq_dHydStateAbove(iLayer)) endif ! - include terms for baseflow if(computeBaseflow .and. nSoilOnlyHyd==nSoil)then do pLayer=1,nSoil qState = ixSoilOnlyHyd(pLayer) ! hydrology state index within the state subset aJac(watState,qState) = aJac(watState,qState) + (dt/mLayerDepth(jLayer))*dBaseflow_dMatric(iLayer,pLayer) end do endif end do ! (looping through hydrology states in the soil domain) endif ! (if the subset includes hydrology state variables in the soil domain) ! ----- ! * liquid water fluxes for the aquifer... ! ---------------------------------------- if(ixAqWat/=integerMissing) aJac(ixAqWat,ixAqWat) = -dBaseflow_dAquifer*dt + dMat(ixAqWat) ! ----- ! * derivative in liquid water fluxes w.r.t. temperature for the soil domain... ! ----------------------------------------------------------------------------- if(nSoilOnlyHyd>0 .and. nSoilOnlyNrg>0)then do iLayer=1,nSoilOnlyHyd ! - check that the soil layer is desired if(ixSoilOnlyHyd(iLayer)==integerMissing) cycle ! - define index of hydrology state variable within the state subset watState = ixSoilOnlyHyd(iLayer) ! - define indices of the soil layers jLayer = iLayer+nSnow ! index of layer in the snow+soil vector ! - define the energy state variable nrgState = ixNrgLayer(jLayer) ! index within the full state vector ! only compute derivatives if the energy state for the current layer is within the state subset if(nrgstate/=integerMissing)then ! - compute the Jacobian for the layer itself aJac(watState,nrgState) = (dt/mLayerDepth(jLayer))*(-dq_dNrgStateBelow(iLayer-1) + dq_dNrgStateAbove(iLayer)) ! dVol/dT (K-1) -- flux depends on ice impedance ! - include derivatives w.r.t. ground evaporation if(nSnow==0 .and. iLayer==1)then ! upper-most soil layer if(computeVegFlux)then aJac(watState,ixVegHyd) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dCanLiq/iden_water) ! dVol/dLiq (kg m-2)-1 aJac(watState,ixCasNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanair/iden_water) ! dVol/dT (K-1) aJac(watState,ixVegNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTCanopy/iden_water) ! dVol/dT (K-1) endif aJac(watState,ixTopNrg) = (dt/mLayerDepth(jLayer))*(-dGroundEvaporation_dTGround/iden_water) + aJac(watState,ixTopNrg) ! dVol/dT (K-1) endif ! melt-freeze: compute derivative in energy with respect to mass if(mLayerdTheta_dTk(jLayer) > verySmall)then ! ice is present aJac(nrgState,watState) = -dVolTot_dPsi0(iLayer)*LH_fus*iden_water ! dNrg/dMat (J m-3 m-1) -- dMat changes volumetric water, and hence ice content else aJac(nrgState,watState) = 0._dp endif ! - compute lower diagonal elements if(iLayer>1)then if(ixSoilOnlyHyd(iLayer-1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer-1),nrgState) = (dt/mLayerDepth(jLayer-1))*( dq_dNrgStateBelow(iLayer-1)) ! K-1 endif ! compute upper-diagonal elements if(iLayer<nSoil)then if(ixSoilOnlyHyd(iLayer+1)/=integerMissing) aJac(ixSoilOnlyHyd(iLayer+1),nrgState) = (dt/mLayerDepth(jLayer+1))*(-dq_dNrgStateAbove(iLayer)) ! K-1 endif endif ! (if the energy state for the current layer is within the state subset) end do ! (looping through soil layers) endif ! (if there are state variables for both water and energy in the soil domain) ! print the Jacobian if(globalPrintFlag)then print*, '** analytical Jacobian (full):' write(*,'(a4,1x,100(i12,1x))') 'xCol', (iLayer, iLayer=min(iJac1,nState),min(iJac2,nState)) do iLayer=min(iJac1,nState),min(iJac2,nState) write(*,'(i4,1x,100(e12.5,1x))') iLayer, aJac(min(iJac1,nState):min(iJac2,nState),iLayer) end do end if ! *** ! check case default; err=20; message=trim(message)//'unable to identify option for the type of matrix'; return end select ! type of matrix ! end association to variables in the data structures end associate end subroutine computJacob ! ********************************************************************************************************** ! private function: get the off-diagonal index in the band-diagonal matrix ! ********************************************************************************************************** function ixOffDiag(jState,iState) implicit none integer(i4b),intent(in) :: jState ! off-diagonal state integer(i4b),intent(in) :: iState ! diagonal state integer(i4b) :: ixOffDiag ! off-diagonal index in gthe band-diagonal matrix ixOffDiag = ixBandOffset + jState - iState end function ixOffDiag end module computJacob_module