! 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 vegSWavRad_module ! data types USE nrtype USE data_types,only:var_i ! x%var(:) (i4b) USE data_types,only:var_dlength ! x%var(:)%dat (dp) ! physical constants USE multiconst,only:Tfreeze ! temperature at freezing (K) ! named variables for structure elements USE var_lookup,only:iLookTYPE,iLookPROG,iLookDIAG,iLookFLUX ! model decisions USE globalData,only:model_decisions ! model decision structure USE var_lookup,only:iLookDECISIONS ! named variables for elements of the decision structure ! look-up values for the choice of canopy shortwave radiation method USE mDecisions_module,only: & noah_mp, & ! full Noah-MP implementation (including albedo) CLM_2stream, & ! CLM 2-stream model (see CLM documentation) UEB_2stream, & ! UEB 2-stream model (Mahat and Tarboton, WRR 2011) NL_scatter, & ! Simplified method Nijssen and Lettenmaier (JGR 1999) BeersLaw ! Beer's Law (as implemented in VIC) ! ------------------------------------------------------------------------------------------------- ! privacy implicit none private public::vegSWavRad ! dimensions integer(i4b),parameter :: nBands=2 ! number of spectral bands for shortwave radiation ! named variables integer(i4b),parameter :: ist = 1 ! Surface type: IST=1 => soil; IST=2 => lake integer(i4b),parameter :: isc = 4 ! Soil color type integer(i4b),parameter :: ice = 0 ! Surface type: ICE=0 => soil; ICE=1 => sea-ice ! spatial indices integer(i4b),parameter :: iLoc = 1 ! i-location integer(i4b),parameter :: jLoc = 1 ! j-location ! algorithmic parameters real(dp),parameter :: missingValue=-9999._dp ! missing value, used when diagnostic or state variables are undefined real(dp),parameter :: verySmall=1.e-6_dp ! used as an additive constant to check if substantial difference among real numbers real(dp),parameter :: mpe=1.e-6_dp ! prevents overflow error if division by zero real(dp),parameter :: dx=1.e-6_dp ! finite difference increment contains ! ************************************************************************************************ ! public subroutine vegSWavRad: muster program to compute sw radiation in vegetation ! ************************************************************************************************ subroutine vegSWavRad(& dt, & ! intent(in): time step (s) -- only used in Noah-MP radiation, to compute albedo nSnow, & ! intent(in): number of snow layers nSoil, & ! intent(in): number of soil layers nLayers, & ! intent(in): total number of layers computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) type_data, & ! intent(in): classification of veg, soil etc. for a local HRU 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 flux variables err,message) ! intent(out): error control ! external routines USE NOAHMP_ROUTINES,only:radiation ! subroutine to calculate albedo and shortwave radiaiton in the canopy implicit none ! dummy variables real(dp),intent(in) :: dt ! time step (s) -- only used in Noah-MP radiation, to compute albedo integer(i4b),intent(in) :: nSnow ! number of snow layers integer(i4b),intent(in) :: nSoil ! number of soil layers integer(i4b),intent(in) :: nLayers ! total number of layers logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) type(var_i),intent(in) :: type_data ! classification of veg, soil etc. for a local HRU type(var_dlength),intent(inout) :: prog_data ! model prognostic variables for a local HRU type(var_dlength),intent(inout) :: diag_data ! model diagnostic variables for a local HRU type(var_dlength),intent(inout) :: flux_data ! model flux variables integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables character(LEN=256) :: cmessage ! error message of downwind routine real(dp) :: snowmassPlusNewsnow ! sum of snow mass and new snowfall (kg m-2 [mm]) real(dp) :: scalarGroundSnowFraction ! snow cover fraction on the ground surface (-) real(dp),parameter :: scalarVegFraction=1._dp ! vegetation fraction (=1 forces no canopy gaps and open areas in radiation routine) real(dp) :: scalarTotalReflectedSolar ! total reflected solar radiation (W m-2) real(dp) :: scalarTotalAbsorbedSolar ! total absorbed solar radiation (W m-2) real(dp) :: scalarCanopyReflectedSolar ! solar radiation reflected from the canopy (W m-2) real(dp) :: scalarGroundReflectedSolar ! solar radiation reflected from the ground (W m-2) real(dp) :: scalarBetweenCanopyGapFraction ! between canopy gap fraction for beam (-) real(dp) :: scalarWithinCanopyGapFraction ! within canopy gap fraction for beam (-) ! ---------------------------------------------------------------------------------------------------------------------------------- ! make association between local variables and the information in the data structures associate(& ! input: control vegTypeIndex => type_data%var(iLookTYPE%vegTypeIndex), & ! intent(in): vegetation type index ix_canopySrad => model_decisions(iLookDECISIONS%canopySrad)%iDecision, & ! intent(in): index defining method for canopy shortwave radiation ! input: forcing at the upper boundary scalarSnowfall => flux_data%var(iLookFLUX%scalarSnowfall)%dat(1), & ! intent(in): computed snowfall rate (kg m-2 s-1) spectralIncomingDirect => flux_data%var(iLookFLUX%spectralIncomingDirect)%dat(1:nBands), & ! intent(in): incoming direct solar radiation in each wave band (w m-2) spectralIncomingDiffuse => flux_data%var(iLookFLUX%spectralIncomingDiffuse)%dat(1:nBands), & ! intent(in): incoming diffuse solar radiation in each wave band (w m-2) ! input: snow states scalarSWE => prog_data%var(iLookPROG%scalarSWE)%dat(1), & ! intent(in): snow water equivalent on the ground (kg m-2) scalarSnowDepth => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1), & ! intent(in): snow depth on the ground surface (m) mLayerVolFracLiq => prog_data%var(iLookPROG%mLayerVolFracLiq)%dat(nSnow+1:nLayers), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) spectralSnowAlbedoDiffuse => prog_data%var(iLookPROG%spectralSnowAlbedoDiffuse)%dat(1:nBands), & ! intent(in): diffuse albedo of snow in each spectral band (-) scalarSnowAlbedo => prog_data%var(iLookPROG%scalarSnowAlbedo)%dat(1), & ! intent(inout): snow albedo (-) ! input: ground and canopy temperature scalarGroundTemp => prog_data%var(iLookPROG%mLayerTemp)%dat(1), & ! intent(in): ground temperature (K) scalarCanopyTemp => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1), & ! intent(in): vegetation temperature (K) ! input: surface characteristix scalarSnowAge => diag_data%var(iLookDIAG%scalarSnowAge)%dat(1), & ! intent(inout): non-dimensional snow age (-) scalarCosZenith => diag_data%var(iLookDIAG%scalarCosZenith)%dat(1), & ! intent(in): cosine of the solar zenith angle (0-1) spectralSnowAlbedoDirect => diag_data%var(iLookDIAG%spectralSnowAlbedoDirect)%dat(1:nBands), & ! intent(in): direct albedo of snow in each spectral band (-) ! input: vegetation characteristix scalarExposedLAI => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1), & ! intent(in): exposed leaf area index after burial by snow (m2 m-2) scalarExposedSAI => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1), & ! intent(in): exposed stem area index after burial by snow (m2 m-2) scalarCanopyWetFraction => diag_data%var(iLookDIAG%scalarCanopyWetFraction)%dat(1), & ! intent(in): canopy wetted fraction (-) ! output: canopy properties scalarCanopySunlitFraction => diag_data%var(iLookDIAG%scalarCanopySunlitFraction)%dat(1), & ! intent(out): sunlit fraction of canopy (-) scalarCanopySunlitLAI => diag_data%var(iLookDIAG%scalarCanopySunlitLAI)%dat(1), & ! intent(out): sunlit leaf area (-) scalarCanopyShadedLAI => diag_data%var(iLookDIAG%scalarCanopyShadedLAI)%dat(1), & ! intent(out): shaded leaf area (-) spectralAlbGndDirect => diag_data%var(iLookDIAG%spectralAlbGndDirect)%dat, & ! intent(out): direct albedo of underlying surface (1:nBands) (-) spectralAlbGndDiffuse => diag_data%var(iLookDIAG%spectralAlbGndDiffuse)%dat, & ! intent(out): diffuse albedo of underlying surface (1:nBands) (-) scalarGroundAlbedo => diag_data%var(iLookDIAG%scalarGroundAlbedo)%dat(1), & ! intent(out): albedo of the ground surface (-) ! output: canopy sw radiation fluxes scalarCanopySunlitPAR => flux_data%var(iLookFLUX%scalarCanopySunlitPAR)%dat(1), & ! intent(out): average absorbed par for sunlit leaves (w m-2) scalarCanopyShadedPAR => flux_data%var(iLookFLUX%scalarCanopyShadedPAR)%dat(1), & ! intent(out): average absorbed par for shaded leaves (w m-2) spectralBelowCanopyDirect => flux_data%var(iLookFLUX%spectralBelowCanopyDirect)%dat, & ! intent(out): downward direct flux below veg layer for each spectral band W m-2) spectralBelowCanopyDiffuse => flux_data%var(iLookFLUX%spectralBelowCanopyDiffuse)%dat, & ! intent(out): downward diffuse flux below veg layer for each spectral band (W m-2) scalarBelowCanopySolar => flux_data%var(iLookFLUX%scalarBelowCanopySolar)%dat(1), & ! intent(out): solar radiation transmitted below the canopy (W m-2) scalarCanopyAbsorbedSolar => flux_data%var(iLookFLUX%scalarCanopyAbsorbedSolar)%dat(1), & ! intent(out): solar radiation absorbed by canopy (W m-2) scalarGroundAbsorbedSolar => flux_data%var(iLookFLUX%scalarGroundAbsorbedSolar)%dat(1) & ! intent(out): solar radiation absorbed by ground (W m-2) ) ! associating local variables with the information in the data structures ! ------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='vegSWavRad/' ! * preliminaries... ! ------------------ ! compute the sum of snow mass and new snowfall (kg m-2 [mm]) snowmassPlusNewsnow = scalarSWE + scalarSnowfall*dt ! compute the ground snow fraction if(nSnow > 0)then scalarGroundSnowFraction = 1._dp else scalarGroundSnowFraction = 0._dp end if ! (if there is snow on the ground) ! * compute radiation fluxes... ! ----------------------------- select case(ix_canopySrad) ! ***** unchanged Noah-MP routine case(noah_mp) call radiation(& ! input vegTypeIndex, & ! intent(in): vegetation type index ist, isc, ice, & ! intent(in): indices to define surface type, soil color, and ice type (constant) nSoil, & ! intent(in): number of soil layers scalarSWE, & ! intent(in): snow water equivalent (kg m-2) snowmassPlusNewsnow, & ! intent(in): sum of snow mass and new snowfall (kg m-2 [mm]) dt, & ! intent(in): time step (s) scalarCosZenith, & ! intent(in): cosine of the solar zenith angle (0-1) scalarSnowDepth*1000._dp, & ! intent(in): snow depth on the ground surface (mm) scalarGroundTemp, & ! intent(in): ground temperature (K) scalarCanopyTemp, & ! intent(in): canopy temperature (K) scalarGroundSnowFraction, & ! intent(in): snow cover fraction (0-1) scalarSnowfall, & ! intent(in): snowfall (kg m-2 s-1 [mm/s]) scalarCanopyWetFraction, & ! intent(in): fraction of canopy that is wet scalarExposedLAI, & ! intent(in): exposed leaf area index after burial by snow (m2 m-2) scalarExposedSAI, & ! intent(in): exposed stem area index after burial by snow (m2 m-2) mLayerVolFracLiq(1:nSoil), & ! intent(in): volumetric fraction of liquid water in each soil layer (-) spectralIncomingDirect(1:nBands), & ! intent(in): incoming direct solar radiation in each wave band (w m-2) spectralIncomingDiffuse(1:nBands), & ! intent(in): incoming diffuse solar radiation in each wave band (w m-2) scalarVegFraction, & ! intent(in): vegetation fraction (=1 forces no canopy gaps and open areas in radiation routine) iLoc, jLoc, & ! intent(in): spatial location indices ! output scalarSnowAlbedo, & ! intent(inout): snow albedo (-) scalarSnowAge, & ! intent(inout): non-dimensional snow age (-) scalarCanopySunlitFraction, & ! intent(out): sunlit fraction of canopy (-) scalarCanopySunlitLAI, & ! intent(out): sunlit leaf area (-) scalarCanopyShadedLAI, & ! intent(out): shaded leaf area (-) scalarCanopySunlitPAR, & ! intent(out): average absorbed par for sunlit leaves (w m-2) scalarCanopyShadedPAR, & ! intent(out): average absorbed par for shaded leaves (w m-2) scalarCanopyAbsorbedSolar, & ! intent(out): solar radiation absorbed by canopy (W m-2) scalarGroundAbsorbedSolar, & ! intent(out): solar radiation absorbed by ground (W m-2) scalarTotalReflectedSolar, & ! intent(out): total reflected solar radiation (W m-2) scalarTotalAbsorbedSolar, & ! intent(out): total absorbed solar radiation (W m-2) scalarCanopyReflectedSolar, & ! intent(out): solar radiation reflected from the canopy (W m-2) scalarGroundReflectedSolar, & ! intent(out): solar radiation reflected from the ground (W m-2) scalarBetweenCanopyGapFraction, & ! intent(out): between canopy gap fraction for beam (-) scalarWithinCanopyGapFraction ) ! intent(out): within canopy gap fraction for beam (-) ! **** all other options case(CLM_2stream,UEB_2stream,NL_scatter,BeersLaw) call canopy_SW(& ! input: model control vegTypeIndex, & ! intent(in): index of vegetation type isc, & ! intent(in): index of soil type computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) ix_canopySrad, & ! intent(in): index of method used for transmission of shortwave rad through the canopy ! input: model variables scalarCosZenith, & ! intent(in): cosine of direct zenith angle (0-1) spectralIncomingDirect(1:nBands), & ! intent(in): incoming direct solar radiation in each wave band (w m-2) spectralIncomingDiffuse(1:nBands), & ! intent(in): incoming diffuse solar radiation in each wave band (w m-2) spectralSnowAlbedoDirect(1:nBands), & ! intent(in): direct albedo of snow in each spectral band (-) spectralSnowAlbedoDiffuse(1:nBands), & ! intent(in): diffuse albedo of snow in each spectral band (-) scalarExposedLAI, & ! intent(in): exposed leaf area index after burial by snow (m2 m-2) scalarExposedSAI, & ! intent(in): exposed stem area index after burial by snow (m2 m-2) scalarVegFraction, & ! intent(in): vegetation fraction (=1 forces no canopy gaps and open areas in radiation routine) scalarCanopyWetFraction, & ! intent(in): fraction of lai, sai that is wetted (-) scalarGroundSnowFraction, & ! intent(in): fraction of ground that is snow covered (-) mLayerVolFracLiq(1), & ! intent(in): volumetric liquid water content in the upper-most soil layer (-) scalarCanopyTemp, & ! intent(in): canopy temperature (k) ! output spectralBelowCanopyDirect, & ! intent(out): downward direct flux below veg layer for each spectral band W m-2) spectralBelowCanopyDiffuse, & ! intent(out): downward diffuse flux below veg layer for each spectral band (W m-2) scalarBelowCanopySolar, & ! intent(out): solar radiation transmitted below the canopy (W m-2) spectralAlbGndDirect, & ! intent(out): direct albedo of underlying surface (1:nBands) (-) spectralAlbGndDiffuse, & ! intent(out): diffuse albedo of underlying surface (1:nBands) (-) scalarGroundAlbedo, & ! intent(out): albedo of the ground surface (-) scalarCanopyAbsorbedSolar, & ! intent(out): solar radiation absorbed by the vegetation canopy (W m-2) scalarGroundAbsorbedSolar, & ! intent(out): solar radiation absorbed by the ground (W m-2) scalarCanopySunlitFraction, & ! intent(out): sunlit fraction of canopy (-) scalarCanopySunlitLAI, & ! intent(out): sunlit leaf area (-) scalarCanopyShadedLAI, & ! intent(out): shaded leaf area (-) scalarCanopySunlitPAR, & ! intent(out): average absorbed par for sunlit leaves (w m-2) scalarCanopyShadedPAR, & ! intent(out): average absorbed par for shaded leaves (w m-2) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if case default; err=20; message=trim(message)//'unable to identify option for canopy sw radiation'; return end select ! (option for canopy sw radiation) ! end association between local variables and the information in the data structures end associate end subroutine vegSWavRad ! ************************************************************************************************ ! private subroutine canopy_SW: various options to compute canopy sw radiation fluxes ! ************************************************************************************************ subroutine canopy_SW(& ! input: model control vegTypeIndex, & ! intent(in): index of vegetation type isc, & ! intent(in): index of soil color computeVegFlux, & ! intent(in): logical flag to compute vegetation fluxes (.false. if veg buried by snow) ix_canopySrad, & ! intent(in): index of method used for transmission of shortwave rad through the canopy ! input: model variables scalarCosZenith, & ! intent(in): cosine of direct zenith angle (0-1) spectralIncomingDirect, & ! intent(in): incoming direct solar radiation in each wave band (w m-2) spectralIncomingDiffuse, & ! intent(in): incoming diffuse solar radiation in each wave band (w m-2) spectralSnowAlbedoDirect, & ! intent(in): direct albedo of snow in each spectral band (-) spectralSnowAlbedoDiffuse, & ! intent(in): diffuse albedo of snow in each spectral band (-) scalarExposedLAI, & ! intent(in): exposed leaf area index after burial by snow (m2 m-2) scalarExposedSAI, & ! intent(in): exposed stem area index after burial by snow (m2 m-2) scalarVegFraction, & ! intent(in): vegetation fraction (=1 forces no canopy gaps and open areas in radiation routine) scalarCanopyWetFraction, & ! intent(in): fraction of lai, sai that is wetted (-) scalarGroundSnowFraction, & ! intent(in): fraction of ground that is snow covered (-) scalarVolFracLiqUpper, & ! intent(in): volumetric liquid water content in the upper-most soil layer (-) scalarCanopyTempTrial, & ! intent(in): canopy temperature (K) ! output spectralBelowCanopyDirect, & ! intent(out): downward direct flux below veg layer (W m-2) spectralBelowCanopyDiffuse, & ! intent(out): downward diffuse flux below veg layer (W m-2) scalarBelowCanopySolar, & ! intent(out): radiation transmitted below the canopy (W m-2) spectralAlbGndDirect, & ! intent(out): direct albedo of underlying surface (1:nBands) (-) spectralAlbGndDiffuse, & ! intent(out): diffuse albedo of underlying surface (1:nBands) (-) scalarGroundAlbedo, & ! intent(out): albedo of the ground surface (-) scalarCanopyAbsorbedSolar, & ! intent(out): radiation absorbed by the vegetation canopy (W m-2) scalarGroundAbsorbedSolar, & ! intent(out): radiation absorbed by the ground (W m-2) scalarCanopySunlitFraction, & ! intent(out): sunlit fraction of canopy (-) scalarCanopySunlitLAI, & ! intent(out): sunlit leaf area (-) scalarCanopyShadedLAI, & ! intent(out): shaded leaf area (-) scalarCanopySunlitPAR, & ! intent(out): average absorbed par for sunlit leaves (w m-2) scalarCanopyShadedPAR, & ! intent(out): average absorbed par for shaded leaves (w m-2) err,message) ! intent(out): error control ! utilities USE expIntegral_module,only:expInt ! function to calculate the exponential integral ! Noah-MP modules USE NOAHMP_ROUTINES,only:twoStream ! two-stream radiative transfer ! Noah vegetation tables USE NOAHMP_VEG_PARAMETERS, only: RHOS,RHOL ! Noah-MP: stem and leaf reflectance for each wave band USE NOAHMP_VEG_PARAMETERS, only: TAUS,TAUL ! Noah-MP: stem and leaf transmittance for each wave band ! input integer(i4b),intent(in) :: vegTypeIndex ! vegetation type index integer(i4b),intent(in) :: isc ! soil color index logical(lgt),intent(in) :: computeVegFlux ! logical flag to compute vegetation fluxes (.false. if veg buried by snow) integer(i4b),intent(in) :: ix_canopySrad ! choice of canopy shortwave radiation method real(dp),intent(in) :: scalarCosZenith ! cosine of the solar zenith angle (0-1) real(dp),intent(in) :: spectralIncomingDirect(:) ! incoming direct solar radiation in each wave band (w m-2) real(dp),intent(in) :: spectralIncomingDiffuse(:) ! incoming diffuse solar radiation in each wave band (w m-2) real(dp),intent(in) :: spectralSnowAlbedoDirect(:) ! direct albedo of snow in each spectral band (-) real(dp),intent(in) :: spectralSnowAlbedoDiffuse(:) ! diffuse albedo of snow in each spectral band (-) real(dp),intent(in) :: scalarExposedLAI ! exposed leaf area index after burial by snow (m2 m-2) real(dp),intent(in) :: scalarExposedSAI ! exposed stem area index after burial by snow (m2 m-2) real(dp),intent(in) :: scalarVegFraction ! vegetation fraction (=1 forces no canopy gaps and open areas in radiation routine) real(dp),intent(in) :: scalarCanopyWetFraction ! fraction of canopy that is wet (-) real(dp),intent(in) :: scalarGroundSnowFraction ! fraction of ground that is snow covered (-) real(dp),intent(in) :: scalarVolFracLiqUpper ! volumetric liquid water content in the upper-most soil layer (-) real(dp),intent(in) :: scalarCanopyTempTrial ! trial value of canopy temperature (K) ! output real(dp),intent(out) :: spectralBelowCanopyDirect(:) ! downward direct flux below veg layer (W m-2) real(dp),intent(out) :: spectralBelowCanopyDiffuse(:) ! downward diffuse flux below veg layer (W m-2) real(dp),intent(out) :: scalarBelowCanopySolar ! radiation transmitted below the canopy (W m-2) real(dp),intent(out) :: spectralAlbGndDirect(:) ! direct albedo of underlying surface (1:nBands) (-) real(dp),intent(out) :: spectralAlbGndDiffuse(:) ! diffuse albedo of underlying surface (1:nBands) (-) real(dp),intent(out) :: scalarGroundAlbedo ! albedo of the ground surface (-) real(dp),intent(out) :: scalarCanopyAbsorbedSolar ! radiation absorbed by the vegetation canopy (W m-2) real(dp),intent(out) :: scalarGroundAbsorbedSolar ! radiation absorbed by the ground (W m-2) real(dp),intent(out) :: scalarCanopySunlitFraction ! sunlit fraction of canopy (-) real(dp),intent(out) :: scalarCanopySunlitLAI ! sunlit leaf area (-) real(dp),intent(out) :: scalarCanopyShadedLAI ! shaded leaf area (-) real(dp),intent(out) :: scalarCanopySunlitPAR ! average absorbed par for sunlit leaves (w m-2) real(dp),intent(out) :: scalarCanopyShadedPAR ! average absorbed par for shaded leaves (w m-2) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! ----------------------------------------------------------------------------------------------------------------------------------------------------------------- ! local variables ! ----------------------------------------------------------------------------------------------------------------------------------------------------------------- ! general integer(i4b),parameter :: ixVisible=1 ! index of the visible wave band integer(i4b),parameter :: ixNearIR=2 ! index of the near infra-red wave band integer(i4b) :: iBand ! index of wave band integer(i4b) :: ic ! 0=unit incoming direct; 1=unit incoming diffuse character(LEN=256) :: cmessage ! error message of downwind routine ! variables used in Nijssen-Lettenmaier method real(dp),parameter :: multScatExp=0.81_dp ! multiple scattering exponent (-) real(dp),parameter :: bulkCanopyAlbedo=0.25_dp ! bulk canopy albedo (-), smaller than actual canopy albedo because of shading in the canopy real(dp),dimension(1:nBands) :: spectralIncomingSolar ! total incoming solar radiation in each spectral band (W m-2) real(dp),dimension(1:nBands) :: spectralGroundAbsorbedDirect ! total direct radiation absorbed at the ground surface (W m-2) real(dp),dimension(1:nBands) :: spectralGroundAbsorbedDiffuse ! total diffuse radiation absorbed at the ground surface (W m-2) real(dp) :: Fdirect ! fraction of direct radiation (-) real(dp) :: tauInitial ! transmission in the absence of scattering and multiple reflections (-) real(dp) :: tauTotal ! transmission due to scattering and multiple reflections (-) ! variables used in Mahat-Tarboton method real(dp),parameter :: Frad_vis=0.5_dp ! fraction of radiation in the visible wave band (-) real(dp),parameter :: gProjParam=0.5_dp ! projected leaf and stem area in the solar direction (-) real(dp),parameter :: bScatParam=0.5_dp ! back scatter parameter (-) real(dp) :: transCoef ! transmission coefficient (-) real(dp) :: transCoefPrime ! "k-prime" coefficient (-) real(dp) :: groundAlbedoDirect ! direct ground albedo (-) real(dp) :: groundAlbedoDiffuse ! diffuse ground albedo (-) real(dp) :: tauInfinite ! direct transmission for an infinite canopy (-) real(dp) :: betaInfinite ! direct upward reflection factor for an infinite canopy (-) real(dp) :: tauFinite ! direct transmission for a finite canopy (-) real(dp) :: betaFinite ! direct reflectance for a finite canopy (-) real(dp) :: vFactor ! scaled vegetation area used to compute diffuse radiation (-) real(dp) :: expi ! exponential integral (-) real(dp) :: taudInfinite ! diffuse transmission for an infinite canopy (-) real(dp) :: taudFinite ! diffuse transmission for a finite canopy (-) real(dp) :: betadFinite ! diffuse reflectance for a finite canopy (-) real(dp) :: refMult ! multiple reflection factor (-) real(dp) :: fracRadAbsDown ! fraction of radiation absorbed by vegetation on the way down real(dp) :: fracRadAbsUp ! fraction of radiation absorbed by vegetation on the way up real(dp) :: tauDirect ! total transmission of direct radiation (-) real(dp) :: tauDiffuse ! total transmission of diffuse radiation (-) real(dp) :: fractionRefDirect ! fraction of direct radiaiton lost to space (-) real(dp) :: fractionRefDiffuse ! fraction of diffuse radiaiton lost to space (-) real(dp),dimension(1:nBands) :: spectralBelowCanopySolar ! total below-canopy radiation for each wave band (W m-2) real(dp),dimension(1:nBands) :: spectralTotalReflectedSolar ! total reflected radiaion for each wave band (W m-2) real(dp),dimension(1:nBands) :: spectralGroundAbsorbedSolar ! radiation absorbed by the ground in each wave band (W m-2) real(dp),dimension(1:nBands) :: spectralCanopyAbsorbedSolar ! radiation absorbed by the canopy in each wave band (W m-2) ! vegetation properties used in 2-stream real(dp) :: scalarExposedVAI ! one-sided leaf+stem area index (m2/m2) real(dp) :: weightLeaf ! fraction of exposed VAI that is leaf real(dp) :: weightStem ! fraction of exposed VAI that is stem real(dp),dimension(1:nBands) :: spectralVegReflc ! leaf+stem reflectance (1:nbands) real(dp),dimension(1:nBands) :: spectralVegTrans ! leaf+stem transmittance (1:nBands) ! output from two-stream -- direct-beam real(dp),dimension(1:nBands) :: spectralCanopyAbsorbedDirect ! flux abs by veg layer (per unit incoming flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralTotalReflectedDirect ! flux refl above veg layer (per unit incoming flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralDirectBelowCanopyDirect ! down dir flux below veg layer (per unit in flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralDiffuseBelowCanopyDirect ! down dif flux below veg layer (per unit in flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralCanopyReflectedDirect ! flux reflected by veg layer (per unit incoming flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralGroundReflectedDirect ! flux reflected by ground (per unit incoming flux), (1:nBands) ! output from two-stream -- diffuse real(dp),dimension(1:nBands) :: spectralCanopyAbsorbedDiffuse ! flux abs by veg layer (per unit incoming flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralTotalReflectedDiffuse ! flux refl above veg layer (per unit incoming flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralDirectBelowCanopyDiffuse ! down dir flux below veg layer (per unit in flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralDiffuseBelowCanopyDiffuse ! down dif flux below veg layer (per unit in flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralCanopyReflectedDiffuse ! flux reflected by veg layer (per unit incoming flux), (1:nBands) real(dp),dimension(1:nBands) :: spectralGroundReflectedDiffuse ! flux reflected by ground (per unit incoming flux), (1:nBands) ! output from two-stream -- scalar variables real(dp) :: scalarGproj ! projected leaf+stem area in solar direction real(dp) :: scalarBetweenCanopyGapFraction ! between canopy gap fraction for beam (-) real(dp) :: scalarWithinCanopyGapFraction ! within canopy gap fraction for beam (-) ! radiation fluxes real(dp) :: ext ! optical depth of direct beam per unit leaf + stem area real(dp) :: scalarCanopyShadedFraction ! shaded fraction of the canopy real(dp) :: fractionLAI ! fraction of vegetation that is leaves real(dp) :: visibleAbsDirect ! direct-beam radiation absorbed in the visible part of the spectrum (W m-2) real(dp) :: visibleAbsDiffuse ! diffuse radiation absorbed in the visible part of the spectrum (W m-2) ! ----------------------------------------------------------------------------------------------------------------------------------------------------------------- ! initialize error control err=0; message='canopy_SW/' ! compute the albedo of the ground surface call gndAlbedo(& ! input isc, & ! intent(in): index of soil color scalarGroundSnowFraction, & ! intent(in): fraction of ground that is snow covered (-) scalarVolFracLiqUpper, & ! intent(in): volumetric liquid water content in upper-most soil layer (-) spectralSnowAlbedoDirect, & ! intent(in): direct albedo of snow in each spectral band (-) spectralSnowAlbedoDiffuse, & ! intent(in): diffuse albedo of snow in each spectral band (-) ! output spectralAlbGndDirect, & ! intent(out): direct albedo of underlying surface (-) spectralAlbGndDiffuse, & ! intent(out): diffuse albedo of underlying surface (-) err,cmessage) ! intent(out): error control if(err/=0)then; message=trim(message)//trim(cmessage); return; end if ! initialize accumulated fluxes scalarBelowCanopySolar = 0._dp ! radiation transmitted below the canopy (W m-2) scalarCanopyAbsorbedSolar = 0._dp ! radiation absorbed by the vegetation canopy (W m-2) scalarGroundAbsorbedSolar = 0._dp ! radiation absorbed by the ground (W m-2) ! check for an early return (no radiation or no exposed canopy) if(.not.computeVegFlux .or. scalarCosZenith < tiny(scalarCosZenith))then ! set canopy radiation to zero scalarCanopySunlitFraction = 0._dp ! sunlit fraction of canopy (-) scalarCanopySunlitLAI = 0._dp ! sunlit leaf area (-) scalarCanopyShadedLAI = scalarExposedLAI ! shaded leaf area (-) scalarCanopySunlitPAR = 0._dp ! average absorbed par for sunlit leaves (w m-2) scalarCanopyShadedPAR = 0._dp ! average absorbed par for shaded leaves (w m-2) ! compute below-canopy radiation do iBand=1,nBands ! (set below-canopy radiation to incoming radiation) if(scalarCosZenith > tiny(scalarCosZenith))then spectralBelowCanopyDirect(iBand) = spectralIncomingDirect(iBand) spectralBelowCanopyDiffuse(iBand) = spectralIncomingDiffuse(iBand) else spectralBelowCanopyDirect(iBand) = 0._dp spectralBelowCanopyDiffuse(iBand) = 0._dp end if ! (accumulate radiation transmitted below the canopy) scalarBelowCanopySolar = scalarBelowCanopySolar + & ! contribution from all previous wave bands spectralBelowCanopyDirect(iBand) + spectralBelowCanopyDiffuse(iBand) ! contribution from current wave band ! (accumulate radiation absorbed by the ground) scalarGroundAbsorbedSolar = scalarGroundAbsorbedSolar + & ! contribution from all previous wave bands spectralBelowCanopyDirect(iBand)*(1._dp - spectralAlbGndDirect(iBand)) + & ! direct radiation from current wave band spectralBelowCanopyDiffuse(iBand)*(1._dp - spectralAlbGndDiffuse(iBand)) ! diffuse radiation from current wave band end do ! looping through wave bands return end if ! compute exposed leaf and stem area index scalarExposedVAI = scalarExposedLAI + scalarExposedSAI if(scalarExposedVAI < epsilon(scalarExposedVAI))then; err=20; message=trim(message)//'very small exposed vegetation area (covered with snow?)'; return; end if ! ============================================================================================================================================================ ! ============================================================================================================================================================ ! different options for radiation transmission select case(ix_canopySrad) ! ----------------------------------------------------------------------------------------------------------------------------------------------------------- ! Beer's Law case(BeersLaw) ! define transmission coefficient (-) scalarGproj = gProjParam transCoef = scalarGproj/scalarCosZenith ! compute transmission of direct radiation according to Beer's Law (-) tauTotal = exp(-transCoef*scalarExposedVAI) !print*, 'tauTotal = ', tauTotal ! compute ground albedo (-) groundAlbedoDirect = Frad_vis*spectralAlbGndDirect(ixVisible) + (1._dp - Frad_vis)*spectralAlbGndDirect(ixNearIR) groundAlbedoDiffuse = Frad_vis*spectralAlbGndDiffuse(ixVisible) + (1._dp - Frad_vis)*spectralAlbGndDiffuse(ixNearIR) ! compute radiation in each spectral band (W m-2) do iBand=1,nBands ! compute total incoming solar radiation spectralIncomingSolar(iBand) = spectralIncomingDirect(iBand) + spectralIncomingDiffuse(iBand) ! compute fraction of direct radiation Fdirect = spectralIncomingDirect(iBand) / (spectralIncomingSolar(iBand) + verySmall) if(Fdirect < 0._dp .or. Fdirect > 1._dp)then print*, 'spectralIncomingDirect(iBand) = ', spectralIncomingDirect(iBand) print*, 'spectralIncomingSolar(iBand) = ', spectralIncomingSolar(iBand) print*, 'Fdirect = ', Fdirect message=trim(message)//'BeersLaw: Fdirect is less than zero or greater than one' err=20; return end if ! compute ground albedo (-) scalarGroundAlbedo = Fdirect*groundAlbedoDirect + (1._dp - Fdirect)*groundAlbedoDiffuse if(scalarGroundAlbedo < 0._dp .or. scalarGroundAlbedo > 1._dp)then print*, 'groundAlbedoDirect = ', groundAlbedoDirect print*, 'groundAlbedoDiffuse = ', groundAlbedoDiffuse message=trim(message)//'BeersLaw: albedo is less than zero or greater than one' err=20; return end if ! compute below-canopy radiation (W m-2) spectralBelowCanopyDirect(iBand) = spectralIncomingDirect(iBand)*tauTotal ! direct radiation from current wave band spectralBelowCanopyDiffuse(iBand) = spectralIncomingDiffuse(iBand)*tauTotal ! diffuse radiation from current wave band spectralBelowCanopySolar(iBand) = spectralBelowCanopyDirect(iBand) + spectralBelowCanopyDiffuse(iBand) ! compute radiation absorbed by the ground in given wave band (W m-2) spectralGroundAbsorbedDirect(iBand) = (1._dp - scalarGroundAlbedo)*spectralBelowCanopyDirect(iBand) spectralGroundAbsorbedDiffuse(iBand) = (1._dp - scalarGroundAlbedo)*spectralBelowCanopyDiffuse(iBand) spectralGroundAbsorbedSolar(iBand) = spectralGroundAbsorbedDirect(iBand) + spectralGroundAbsorbedDiffuse(iBand) ! compute radiation absorbed by vegetation in current wave band (W m-2) fracRadAbsDown = (1._dp - tauTotal)*(1._dp - bulkCanopyAlbedo) ! (fraction of radiation absorbed on the way down) fracRadAbsUp = tauTotal*scalarGroundAlbedo*(1._dp - tauTotal) ! (fraction of radiation absorbed on the way up) spectralCanopyAbsorbedDirect(iBand) = spectralIncomingDirect(iBand)*(fracRadAbsDown + fracRadAbsUp) spectralCanopyAbsorbedDiffuse(iBand) = spectralIncomingDiffuse(iBand)*(fracRadAbsDown + fracRadAbsUp) spectralCanopyAbsorbedSolar(iBand) = spectralCanopyAbsorbedDirect(iBand) + spectralCanopyAbsorbedDiffuse(iBand) ! (check) if(spectralCanopyAbsorbedDirect(iBand) > spectralIncomingDirect(iBand) .or. spectralCanopyAbsorbedDiffuse(iBand) > spectralIncomingDiffuse(iBand))then print*, 'tauTotal = ', tauTotal print*, 'bulkCanopyAlbedo = ', bulkCanopyAlbedo print*, 'scalarGroundAlbedo = ', scalarGroundAlbedo message=trim(message)//'BeersLaw: problem with the canopy radiation balance' err=20; return end if ! compute solar radiation lost to space in given wave band (W m-2) spectralTotalReflectedDirect(iBand) = spectralIncomingDirect(iBand) - spectralGroundAbsorbedDirect(iBand) - spectralCanopyAbsorbedDirect(iBand) spectralTotalReflectedDiffuse(iBand) = spectralIncomingDiffuse(iBand) - spectralGroundAbsorbedDiffuse(iBand) - spectralCanopyAbsorbedDiffuse(iBand) spectralTotalReflectedSolar(iBand) = spectralTotalReflectedDirect(iBand) + spectralTotalReflectedDiffuse(iBand) if(spectralTotalReflectedDirect(iBand) < 0._dp .or. spectralTotalReflectedDiffuse(iBand) < 0._dp)then print*, 'scalarGroundAlbedo = ', scalarGroundAlbedo print*, 'tauTotal = ', tauTotal print*, 'fracRadAbsDown = ', fracRadAbsDown print*, 'fracRadAbsUp = ', fracRadAbsUp print*, 'spectralBelowCanopySolar(iBand) = ', spectralBelowCanopySolar(iBand) print*, 'spectralGroundAbsorbedSolar(iBand) = ', spectralGroundAbsorbedSolar(iBand) print*, 'spectralCanopyAbsorbedSolar(iBand) = ', spectralCanopyAbsorbedSolar(iBand) message=trim(message)//'BeersLaw: reflected radiation is less than zero' err=20; return end if ! save canopy radiation absorbed in visible wavelengths if(iBand == ixVisible)then visibleAbsDirect = spectralCanopyAbsorbedDirect(ixVisible) visibleAbsDiffuse = spectralCanopyAbsorbedDiffuse(ixVisible) end if ! accumulate fluxes scalarBelowCanopySolar = scalarBelowCanopySolar + spectralBelowCanopySolar(iBand) scalarGroundAbsorbedSolar = scalarGroundAbsorbedSolar + spectralGroundAbsorbedSolar(iBand) scalarCanopyAbsorbedSolar = scalarCanopyAbsorbedSolar + spectralCanopyAbsorbedSolar(iBand) end do ! (looping through spectral bands) ! ----------------------------------------------------------------------------------------------------------------------------------------------------------- ! method of Nijssen and Lettenmaier (JGR, 1999) case(NL_scatter) ! define transmission coefficient (-) scalarGproj = gProjParam transCoef = scalarGproj/scalarCosZenith ! compute transmission of direct radiation according to Beer's Law (-) tauFinite = exp(-transCoef*scalarExposedVAI) ! compute transmission of diffuse radiation (-) vFactor = scalarGproj*scalarExposedVAI expi = expInt(vFactor) taudFinite = (1._dp - vFactor)*exp(-vFactor) + (vFactor**2._dp)*expi ! compute ground albedo (-) groundAlbedoDirect = Frad_vis*spectralAlbGndDirect(ixVisible) + (1._dp - Frad_vis)*spectralAlbGndDirect(ixNearIR) groundAlbedoDiffuse = Frad_vis*spectralAlbGndDiffuse(ixVisible) + (1._dp - Frad_vis)*spectralAlbGndDiffuse(ixNearIR) ! compute radiation in each spectral band (W m-2) do iBand=1,nBands ! compute total incoming solar radiation spectralIncomingSolar(iBand) = spectralIncomingDirect(iBand) + spectralIncomingDiffuse(iBand) ! compute fraction of direct radiation Fdirect = spectralIncomingDirect(iBand) / (spectralIncomingSolar(iBand) + verySmall) if(Fdirect < 0._dp .or. Fdirect > 1._dp)then print*, 'spectralIncomingDirect(iBand) = ', spectralIncomingDirect(iBand) print*, 'spectralIncomingSolar(iBand) = ', spectralIncomingSolar(iBand) print*, 'Fdirect = ', Fdirect message=trim(message)//'NL_scatter: Fdirect is less than zero or greater than one' err=20; return end if ! compute ground albedo (-) scalarGroundAlbedo = Fdirect*groundAlbedoDirect + (1._dp - Fdirect)*groundAlbedoDiffuse if(scalarGroundAlbedo < 0._dp .or. scalarGroundAlbedo > 1._dp)then print*, 'groundAlbedoDirect = ', groundAlbedoDirect print*, 'groundAlbedoDiffuse = ', groundAlbedoDiffuse message=trim(message)//'NL_scatter: albedo is less than zero or greater than one' err=20; return end if ! compute initial transmission in the absence of scattering and multiple reflections (-) tauInitial = Fdirect*tauFinite + (1._dp - Fdirect)*taudFinite ! compute increase in transmission due to scattering (-) tauTotal = (tauInitial**multScatExp) ! compute multiple reflections factor refMult = 1._dp / (1._dp - scalarGroundAlbedo*bulkCanopyAlbedo*(1._dp - taudFinite**multScatExp) ) ! compute below-canopy radiation (W m-2) spectralBelowCanopyDirect(iBand) = spectralIncomingDirect(iBand)*tauTotal*refMult ! direct radiation from current wave band spectralBelowCanopyDiffuse(iBand) = spectralIncomingDiffuse(iBand)*tauTotal*refMult ! diffuse radiation from current wave band spectralBelowCanopySolar(iBand) = spectralBelowCanopyDirect(iBand) + spectralBelowCanopyDiffuse(iBand) ! compute radiation absorbed by the ground in given wave band (W m-2) spectralGroundAbsorbedDirect(iBand) = (1._dp - scalarGroundAlbedo)*spectralBelowCanopyDirect(iBand) spectralGroundAbsorbedDiffuse(iBand) = (1._dp - scalarGroundAlbedo)*spectralBelowCanopyDiffuse(iBand) spectralGroundAbsorbedSolar(iBand) = spectralGroundAbsorbedDirect(iBand) + spectralGroundAbsorbedDiffuse(iBand) ! compute radiation absorbed by vegetation in current wave band (W m-2) fracRadAbsDown = (1._dp - tauTotal)*(1._dp - bulkCanopyAlbedo) ! (fraction of radiation absorbed on the way down) fracRadAbsUp = tauTotal*refMult*scalarGroundAlbedo*(1._dp - taudFinite**multScatExp) ! (fraction of radiation absorbed on the way up) spectralCanopyAbsorbedDirect(iBand) = spectralIncomingDirect(iBand)*(fracRadAbsDown + fracRadAbsUp) spectralCanopyAbsorbedDiffuse(iBand) = spectralIncomingDiffuse(iBand)*(fracRadAbsDown + fracRadAbsUp) spectralCanopyAbsorbedSolar(iBand) = spectralCanopyAbsorbedDirect(iBand) + spectralCanopyAbsorbedDiffuse(iBand) ! compute solar radiation lost to space in given wave band (W m-2) spectralTotalReflectedDirect(iBand) = spectralIncomingDirect(iBand) - spectralGroundAbsorbedDirect(iBand) - spectralCanopyAbsorbedDirect(iBand) spectralTotalReflectedDiffuse(iBand) = spectralIncomingDiffuse(iBand) - spectralGroundAbsorbedDiffuse(iBand) - spectralCanopyAbsorbedDiffuse(iBand) spectralTotalReflectedSolar(iBand) = spectralTotalReflectedDirect(iBand) + spectralTotalReflectedDiffuse(iBand) if(spectralTotalReflectedDirect(iBand) < 0._dp .or. spectralTotalReflectedDiffuse(iBand) < 0._dp)then message=trim(message)//'NL-scatter: reflected radiation is less than zero' err=20; return end if ! save canopy radiation absorbed in visible wavelengths if(iBand == ixVisible)then visibleAbsDirect = spectralCanopyAbsorbedDirect(ixVisible) visibleAbsDiffuse = spectralCanopyAbsorbedDiffuse(ixVisible) end if ! accumulate fluxes scalarBelowCanopySolar = scalarBelowCanopySolar + spectralBelowCanopySolar(iBand) scalarGroundAbsorbedSolar = scalarGroundAbsorbedSolar + spectralGroundAbsorbedSolar(iBand) scalarCanopyAbsorbedSolar = scalarCanopyAbsorbedSolar + spectralCanopyAbsorbedSolar(iBand) end do ! (looping through spectral bands) ! ----------------------------------------------------------------------------------------------------------------------------------------------------------- ! method of Mahat and Tarboton (WRR, 2012) case(UEB_2stream) ! define transmission coefficient (-) scalarGproj = gProjParam transCoef = scalarGproj/scalarCosZenith ! define "k-prime" coefficient (-) transCoefPrime = sqrt(1._dp - bScatParam) ! compute ground albedo (-) groundAlbedoDirect = Frad_vis*spectralAlbGndDirect(ixVisible) + (1._dp - Frad_vis)*spectralAlbGndDirect(ixNearIR) groundAlbedoDiffuse = Frad_vis*spectralAlbGndDiffuse(ixVisible) + (1._dp - Frad_vis)*spectralAlbGndDiffuse(ixNearIR) ! compute transmission for an infinite canopy (-) tauInfinite = exp(-transCoef*transCoefPrime*scalarExposedVAI) ! compute upward reflection factor for an infinite canopy (-) betaInfinite = (1._dp - transCoefPrime)/(1._dp + transCoefPrime) ! compute transmission for a finite canopy (-) tauFinite = tauInfinite*(1._dp - betaInfinite**2._dp)/(1._dp - (betaInfinite**2._dp)*tauInfinite**2._dp) ! compute reflectance for a finite canopy (-) betaFinite = betaInfinite*(1._dp - tauInfinite**2._dp) / (1._dp - (betaInfinite**2._dp)*(tauInfinite**2._dp)) ! compute transmission of diffuse radiation (-) vFactor = transCoefPrime*scalarGproj*scalarExposedVAI expi = expInt(vFactor) taudInfinite = (1._dp - vFactor)*exp(-vFactor) + (vFactor**2._dp)*expi taudFinite = taudInfinite*(1._dp - betaInfinite**2._dp)/(1._dp - (betaInfinite**2._dp)*taudInfinite**2._dp) ! compute reflectance of diffuse radiation (-) betadFinite = betaInfinite*(1._dp - taudInfinite**2._dp) / (1._dp - (betaInfinite**2._dp)*(taudInfinite**2._dp)) ! compute total transmission of direct and diffuse radiation, accounting for multiple reflections (-) refMult = 1._dp / (1._dp - groundAlbedoDiffuse*betadFinite*(1._dp - taudFinite) ) tauDirect = tauFinite*refMult tauDiffuse = taudFinite*refMult ! compute fraction of radiation lost to space (-) fractionRefDirect = ( (1._dp - groundAlbedoDirect)*betaFinite + groundAlbedoDirect*tauFinite*taudFinite) * refMult fractionRefDiffuse = ( (1._dp - groundAlbedoDiffuse)*betadFinite + groundAlbedoDiffuse*taudFinite*taudFinite) * refMult ! compute radiation in each spectral band (W m-2) do iBand=1,nBands ! compute below-canopy radiation (W m-2) spectralBelowCanopyDirect(iBand) = spectralIncomingDirect(iBand)*tauFinite*refMult ! direct radiation from current wave band spectralBelowCanopyDiffuse(iBand) = spectralIncomingDiffuse(iBand)*taudFinite*refMult ! diffuse radiation from current wave band spectralBelowCanopySolar(iBand) = spectralBelowCanopyDirect(iBand) + spectralBelowCanopyDiffuse(iBand) ! compute radiation absorbed by the ground in given wave band (W m-2) spectralGroundAbsorbedDirect(iBand) = (1._dp - groundAlbedoDirect)*spectralBelowCanopyDirect(iBand) spectralGroundAbsorbedDiffuse(iBand) = (1._dp - groundAlbedoDiffuse)*spectralBelowCanopyDiffuse(iBand) spectralGroundAbsorbedSolar(iBand) = spectralGroundAbsorbedDirect(iBand) + spectralGroundAbsorbedDiffuse(iBand) ! compute radiation absorbed by vegetation in current wave band (W m-2) spectralCanopyAbsorbedDirect(iBand) = spectralIncomingDirect(iBand)*(1._dp - tauFinite)*(1._dp - betaFinite) + & ! (radiation absorbed on the way down) spectralBelowCanopyDirect(iBand)*groundAlbedoDirect*(1._dp - taudFinite) ! (radiation absorbed on the way up) spectralCanopyAbsorbedDiffuse(iBand) = spectralIncomingDiffuse(iBand)*(1._dp - taudFinite)*(1._dp - betadFinite) + & ! (radiation absorbed on the way down) spectralBelowCanopyDiffuse(iBand)*groundAlbedoDiffuse*(1._dp - taudFinite) ! (radiation absorbed on the way up) spectralCanopyAbsorbedSolar(iBand) = spectralCanopyAbsorbedDirect(iBand) + spectralCanopyAbsorbedDiffuse(iBand) ! compute solar radiation lost to space in given wave band (W m-2) spectralTotalReflectedDirect(iBand) = spectralIncomingDirect(iBand) - spectralGroundAbsorbedDirect(iBand) - spectralCanopyAbsorbedDirect(iBand) spectralTotalReflectedDiffuse(iBand) = spectralIncomingDiffuse(iBand) - spectralGroundAbsorbedDiffuse(iBand) - spectralCanopyAbsorbedDiffuse(iBand) spectralTotalReflectedSolar(iBand) = spectralTotalReflectedDirect(iBand) + spectralTotalReflectedDiffuse(iBand) if(spectralTotalReflectedDirect(iBand) < 0._dp .or. spectralTotalReflectedDiffuse(iBand) < 0._dp)then message=trim(message)//'UEB_2stream: reflected radiation is less than zero' err=20; return end if ! save canopy radiation absorbed in visible wavelengths if(iBand == ixVisible)then visibleAbsDirect = spectralCanopyAbsorbedDirect(ixVisible) visibleAbsDiffuse = spectralCanopyAbsorbedDiffuse(ixVisible) end if ! accumulate fluxes scalarBelowCanopySolar = scalarBelowCanopySolar + spectralBelowCanopySolar(iBand) scalarGroundAbsorbedSolar = scalarGroundAbsorbedSolar + spectralGroundAbsorbedSolar(iBand) scalarCanopyAbsorbedSolar = scalarCanopyAbsorbedSolar + spectralCanopyAbsorbedSolar(iBand) end do ! (looping through wave bands) ! ----------------------------------------------------------------------------------------------------------------------------------------------------------- ! CLM approach case(CLM_2stream) ! weight reflectance and transmittance by exposed leaf and stem area index weightLeaf = scalarExposedLAI / scalarExposedVAI weightStem = scalarExposedSAI / scalarExposedVAI do iBand = 1,nBands ! loop through spectral bands spectralVegReflc(iBand) = RHOL(vegTypeIndex,iBand)*weightLeaf + RHOS(vegTypeIndex,iBand)*weightStem spectralVegTrans(iBand) = TAUL(vegTypeIndex,iBand)*weightLeaf + TAUS(vegTypeIndex,iBand)*weightStem end do ! loop through wave bands do iBand=1,nBands ic = 0 ! two-stream approximation for direct-beam radiation (from CLM/Noah-MP) call twoStream(& ! input iBand, & ! intent(in): waveband number ic, & ! intent(in): 0=unit incoming direct; 1=unit incoming diffuse vegTypeIndex, & ! intent(in): vegetation type scalarCosZenith, & ! intent(in): cosine of direct zenith angle (0-1) scalarExposedVAI, & ! intent(in): one-sided leaf+stem area index (m2/m2) scalarCanopyWetFraction, & ! intent(in): fraction of lai, sai that is wetted (-) scalarCanopyTempTrial, & ! intent(in): surface temperature (k) spectralAlbGndDirect, & ! intent(in): direct albedo of underlying surface (1:nBands) (-) spectralAlbGndDiffuse, & ! intent(in): diffuse albedo of underlying surface (1:nBands) (-) spectralVegReflc, & ! intent(in): leaf+stem reflectance (1:nbands) spectralVegTrans, & ! intent(in): leaf+stem transmittance (1:nBands) scalarVegFraction, & ! intent(in): vegetation fraction (=1 forces no canopy gaps and open areas in radiation routine) ist, & ! intent(in): surface type iLoc,jLoc, & ! intent(in): grid indices ! output spectralCanopyAbsorbedDirect, & ! intent(out): flux abs by veg layer (per unit incoming flux), (1:nBands) spectralTotalReflectedDirect, & ! intent(out): flux refl above veg layer (per unit incoming flux), (1:nBands) spectralDirectBelowCanopyDirect, & ! intent(out): down dir flux below veg layer (per unit in flux), (1:nBands) spectralDiffuseBelowCanopyDirect, & ! intent(out): down dif flux below veg layer (per unit in flux), (1:nBands) scalarGproj, & ! intent(out): projected leaf+stem area in solar direction spectralCanopyReflectedDirect, & ! intent(out): flux reflected by veg layer (per unit incoming flux), (1:nBands) spectralGroundReflectedDirect, & ! intent(out): flux reflected by ground (per unit incoming flux), (1:nBands) ! input-output scalarBetweenCanopyGapFraction, & ! intent(inout): between canopy gap fraction for beam (-) scalarWithinCanopyGapFraction ) ! intent(inout): within canopy gap fraction for beam (-) ic = 1 ! two-stream approximation for diffuse radiation (from CLM/Noah-MP) call twoStream(& ! input iBand, & ! intent(in): waveband number ic, & ! intent(in): 0=unit incoming direct; 1=unit incoming diffuse vegTypeIndex, & ! intent(in): vegetation type scalarCosZenith, & ! intent(in): cosine of direct zenith angle (0-1) scalarExposedVAI, & ! intent(in): one-sided leaf+stem area index (m2/m2) scalarCanopyWetFraction, & ! intent(in): fraction of lai, sai that is wetted (-) scalarCanopyTempTrial, & ! intent(in): surface temperature (k) spectralAlbGndDirect, & ! intent(in): direct albedo of underlying surface (1:nBands) (-) spectralAlbGndDiffuse, & ! intent(in): diffuse albedo of underlying surface (1:nBands) (-) spectralVegReflc, & ! intent(in): leaf+stem reflectance (1:nbands) spectralVegTrans, & ! intent(in): leaf+stem transmittance (1:nBands) scalarVegFraction, & ! intent(in): vegetation fraction (=1 forces no canopy gaps and open areas in radiation routine) ist, & ! intent(in): surface type iLoc,jLoc, & ! intent(in): grid indices ! output spectralCanopyAbsorbedDiffuse, & ! intent(out): flux abs by veg layer (per unit incoming flux), (1:nBands) spectralTotalReflectedDiffuse, & ! intent(out): flux refl above veg layer (per unit incoming flux), (1:nBands) spectralDirectBelowCanopyDiffuse, & ! intent(out): down dir flux below veg layer (per unit in flux), (1:nBands) spectralDiffuseBelowCanopyDiffuse, & ! intent(out): down dif flux below veg layer (per unit in flux), (1:nBands) scalarGproj, & ! intent(out): projected leaf+stem area in solar direction spectralCanopyReflectedDiffuse, & ! intent(out): flux reflected by veg layer (per unit incoming flux), (1:nBands) spectralGroundReflectedDiffuse, & ! intent(out): flux reflected by ground (per unit incoming flux), (1:nBands) ! input-output scalarBetweenCanopyGapFraction, & ! intent(inout): between canopy gap fraction for beam (-) scalarWithinCanopyGapFraction ) ! intent(inout): within canopy gap fraction for beam (-) ! compute below-canopy radiation spectralBelowCanopyDirect(iBand) = spectralIncomingDirect(iBand)*spectralDirectBelowCanopyDirect(iBand) ! direct radiation spectralBelowCanopyDiffuse(iBand) = spectralIncomingDirect(iBand)*spectralDiffuseBelowCanopyDirect(iBand) + & ! direct radiation transmitted as diffuse spectralIncomingDiffuse(iBand)*spectralDiffuseBelowCanopyDiffuse(iBand) ! diffuse radiation transmitted as diffuse ! accumulate radiation transmitted below the canopy (W m-2) scalarBelowCanopySolar = scalarBelowCanopySolar + & ! contribution from all previous wave bands spectralBelowCanopyDirect(iBand) + spectralBelowCanopyDiffuse(iBand) ! contribution from current wave band ! accumulate radiation absorbed by the vegetation canopy (W m-2) scalarCanopyAbsorbedSolar = scalarCanopyAbsorbedSolar + & ! contribution from all previous wave bands spectralIncomingDirect(iBand)*spectralCanopyAbsorbedDirect(iBand) + & ! direct radiation from current wave band spectralIncomingDiffuse(iBand)*spectralCanopyAbsorbedDiffuse(iBand) ! diffuse radiation from current wave band ! accumulate radiation absorbed by the ground (W m-2) scalarGroundAbsorbedSolar = scalarGroundAbsorbedSolar + & ! contribution from all previous wave bands spectralBelowCanopyDirect(iBand)*(1._dp - spectralAlbGndDirect(iBand)) + & ! direct radiation from current wave band spectralBelowCanopyDiffuse(iBand)*(1._dp - spectralAlbGndDiffuse(iBand)) ! diffuse radiation from current wave band ! save canopy radiation absorbed in visible wavelengths ! NOTE: here flux is per unit incoming flux if(iBand == ixVisible)then visibleAbsDirect = spectralIncomingDirect(ixVisible)*spectralCanopyAbsorbedDirect(ixVisible) visibleAbsDiffuse = spectralIncomingDiffuse(ixVisible)*spectralCanopyAbsorbedDiffuse(ixVisible) end if end do ! (looping through wave bands) ! ----------------------------------------------------------------------------------------------------------------------------------------------------------- case default; err=20; message=trim(message)//'unable to identify option for canopy sw radiation'; return end select ! (option for canopy sw radiation) ! ============================================================================================================================================================ ! ============================================================================================================================================================ ! compute variables used in photosynthesis routines ! compute sunlit fraction of canopy (from CLM/Noah-MP) ext = scalarGproj/scalarCosZenith ! optical depth of direct beam per unit leaf + stem area scalarCanopySunlitFraction = (1._dp - exp(-ext*scalarExposedVAI)) / max(ext*scalarExposedVAI,mpe) if(scalarCanopySunlitFraction < 0.01_dp) scalarCanopySunlitFraction = 0._dp ! compute sunlit and shaded LAI scalarCanopyShadedFraction = 1._dp - scalarCanopySunlitFraction scalarCanopySunlitLAI = scalarExposedLAI*scalarCanopySunlitFraction scalarCanopyShadedLAI = scalarExposedLAI*scalarCanopyShadedFraction ! compute PAR for sunlit and shaded leaves (from CLM/Noah-MP) fractionLAI = scalarExposedLAI / max(scalarExposedVAI, mpe) if(scalarCanopySunlitFraction > tiny(scalarCanopySunlitFraction))then scalarCanopySunlitPAR = (visibleAbsDirect + scalarCanopySunlitFraction*visibleAbsDiffuse) * fractionLAI / max(scalarCanopySunlitLAI, mpe) scalarCanopyShadedPAR = ( scalarCanopyShadedFraction*visibleAbsDiffuse) * fractionLAI / max(scalarCanopyShadedLAI, mpe) else scalarCanopySunlitPAR = 0._dp scalarCanopyShadedPAR = (visibleAbsDirect + visibleAbsDiffuse) * fractionLAI / max(scalarCanopyShadedLAI, mpe) end if !print*, 'scalarCanopySunlitLAI, fractionLAI, visibleAbsDirect, visibleAbsDiffuse, scalarCanopySunlitPAR = ', & ! scalarCanopySunlitLAI, fractionLAI, visibleAbsDirect, visibleAbsDiffuse, scalarCanopySunlitPAR end subroutine canopy_SW ! ************************************************************************************************************************************* ! private subroutine gndAlbedo: compute the albedo of the ground surface ! ************************************************************************************************************************************* subroutine gndAlbedo(& ! input isc, & ! intent(in): index of soil color scalarGroundSnowFraction, & ! intent(in): fraction of ground that is snow covered (-) scalarVolFracLiqUpper, & ! intent(in): volumetric liquid water content in upper-most soil layer (-) spectralSnowAlbedoDirect, & ! intent(in): direct albedo of snow in each spectral band (-) spectralSnowAlbedoDiffuse, & ! intent(in): diffuse albedo of snow in each spectral band (-) ! output spectralAlbGndDirect, & ! intent(out): direct albedo of underlying surface (-) spectralAlbGndDiffuse, & ! intent(out): diffuse albedo of underlying surface (-) err,message) ! intent(out): error control ! -------------------------------------------------------------------------------------------------------------------------------------- ! identify parameters for soil albedo USE NOAHMP_RAD_PARAMETERS, only: ALBSAT,ALBDRY ! Noah-MP: saturated and dry soil albedos for each wave band ! -------------------------------------------------------------------------------------------------------------------------------------- ! input: model control integer(i4b),intent(in) :: isc ! index of soil color real(rkind),intent(in) :: scalarGroundSnowFraction ! fraction of ground that is snow covered (-) real(rkind),intent(in) :: scalarVolFracLiqUpper ! volumetric liquid water content in upper-most soil layer (-) real(rkind),intent(in) :: spectralSnowAlbedoDirect(:) ! direct albedo of snow in each spectral band (-) real(rkind),intent(in) :: spectralSnowAlbedoDiffuse(:) ! diffuse albedo of snow in each spectral band (-) ! output real(rkind),intent(out) :: spectralAlbGndDirect(:) ! direct albedo of underlying surface (-) real(rkind),intent(out) :: spectralAlbGndDiffuse(:) ! diffuse albedo of underlying surface (-) integer(i4b),intent(out) :: err ! error code character(*),intent(out) :: message ! error message ! local variables integer(i4b) :: iBand ! index of spectral band real(rkind) :: xInc ! soil water correction factor for soil albedo real(rkind),dimension(1:nBands) :: spectralSoilAlbedo ! soil albedo in each spectral band ! real(rkind),dimension(8) :: ALBSATarr1 ! real(rkind),dimension(8) :: ALBSATarr2 ! real(rkind),dimension(8) :: ALBDRYarr1 ! real(rkind),dimension(8) :: ALBDRYarr2 ! initialize error control err=0; message='gndAlbedo/' ! Initalize arrays as call to Noah Mp returns a bad value ! ALBSATarr1 = (/0.15000000596046448,0.10999999940395355,0.10000000149011612,9.0000003576278687E-002,7.9999998211860657E-002,7.0000000298023224E-002,5.9999998658895493E-002,5.0000000745058060E-002/) ! ALBSATarr2 = (/0.30000001192092896,0.21999999880790710,0.20000000298023224,0.18000000715255737,0.15999999642372131,0.14000000059604645,0.11999999731779099,0.10000000149011612/) ! ALBDRYarr1 = (/0.27000001072883606,0.21999999880790710,0.20000000298023224,0.18000000715255737,0.15999999642372131,0.14000000059604645,0.11999999731779099,0.10000000149011612/) ! ALBDRYarr2 = (/0.54000002145767212,0.43999999761581421,0.40000000596046448,0.36000001430511475,0.31999999284744263,0.28000000119209290,0.23999999463558197,0.20000000298023224/) ! compute soil albedo do iBand=1,nBands ! loop through spectral bands ! if(iBand == 1)then ! xInc = max(0.11_dp - 0.40_dp*scalarVolFracLiqUpper, 0._dp) ! spectralSoilAlbedo(iBand) = min(ALBSATarr1(isc)+xInc,ALBDRYarr1(isc)) ! else ! iBand = 2 ! xInc = max(0.11_dp - 0.40_dp*scalarVolFracLiqUpper, 0._dp) ! spectralSoilAlbedo(iBand) = min(ALBSATarr2(isc)+xInc,ALBDRYarr2(isc)) ! end if ! print*, "ALBSTAT = ",ALBSAT(isc,iBand) ! print*, "ALBDRY = ",ALBDRY(isc,iBand) xInc = max(0.11_dp - 0.40_dp*scalarVolFracLiqUpper, 0._dp) spectralSoilAlbedo(iBand) = min(ALBSAT(isc,iBand)+xInc,ALBDRY(isc,iBand)) end do ! (looping through spectral bands) ! compute surface albedo (weighted combination of snow and soil) do iBand=1,nBands spectralAlbGndDirect(iBand) = (1._dp - scalarGroundSnowFraction)*spectralSoilAlbedo(iBand) + scalarGroundSnowFraction*spectralSnowAlbedoDirect(iBand) spectralAlbGndDiffuse(iBand) = (1._dp - scalarGroundSnowFraction)*spectralSoilAlbedo(iBand) + scalarGroundSnowFraction*spectralSnowAlbedoDiffuse(iBand) end do ! (looping through spectral bands) end subroutine gndAlbedo end module vegSWavRad_module