#include <misc.h> #include <preproc.h> Module DryDepVelocity 1,9 !----------------------------------------------------------------------- ! ! Purpose: ! Deposition velocity (m/s) ! ! Method: ! This code simulates dry deposition velocities using the Wesely scheme. ! Details of this method can be found in: ! ! M.L Wesely. Parameterization of surface resistances to gaseous dry deposition ! in regional-scale numericl models. 1989. Atmospheric Environment vol.23 No.6 ! pp. 1293-1304. ! ! In Wesely (1998) "the magnitude of the dry deposition velocity can be found ! as: ! ! |vd|=(ra+rb+rc)^-1 ! ! where ra is the aerodynamic resistance (common to all gases) between a ! specific height and the surface, rb is the quasilaminar sublayer resistance ! (whose only dependence on the porperties of the gas of interest is its ! molecular diffusivity in air), and rc is the bulk surface resistance". ! ! In this subroutine both ra and rb are calculated elsewhere in CLM. Thus ra ! and rb were "globalized" in order to gain access to them for the calculation. ! "ram1" is the CLM variable used for ra. ram1 was globalized in the following ! subroutines; BareGroundFluxes.F90, Biogeophysics_lake.F90, CanopyFluxes.F90, ! and clmtype.F90. ! ! "rb" is the CLM variable used for rb in the Wesely equation above. rb was ! globalized in the following subroutines; clmtype.F90 ! ! In Wesely (1989) rc is estimated for five seasonal categories and 11 landuse ! types. For each season and landuse type, Wesely compiled data into a ! look-up-table for several parameters used to calculate rc. In this subroutine ! the same values are used as found in wesely's look-up-tables, the only ! difference is that this subroutine uses a CLM generated LAI to select values ! from the look-up-table instead of seasonality. Inaddition, Wesely(1989) ! land use types are "mapped" into CLM plant function types (PFT). ! ! Subroutine written to operate at the patch level. ! ! Output: ! ! vd(n_species) !Dry deposition velocity [m s-1] for each molecule or species ! ! Author: Beth Holland and James Sulzman ! ! Modified: Francis Vitt -- 30 Mar 2007 !----------------------------------------------------------------------- use shr_kind_mod, only : r8 => shr_kind_r8 use clmtype use abortutils, only : endrun use clm_time_manager, only : get_nstep, get_curr_date, get_curr_time use clm_atmlnd, only : clm_a2l use spmdMod, only : masterproc use seq_drydep_mod, only : n_drydep, drydep_list use seq_drydep_mod, only : drydep_method, DD_XLND use seq_drydep_mod, only : index_o3=>o3_ndx, index_o3a=>o3a_ndx, index_so2=>so2_ndx, index_h2=>h2_ndx, & index_co=>co_ndx, index_ch4=>ch4_ndx, index_pan=>pan_ndx implicit none save private public :: depvel_compute CONTAINS !----------------------------------------------------------------------- ! computes the dry deposition velocity of tracers !----------------------------------------------------------------------- subroutine depvel_compute( lbp , ubp ) 1,8 use shr_const_mod , only : tmelt => shr_const_tkfrz use seq_drydep_mod , only : seq_drydep_setHCoeff, mapping, drat, foxd, & rcls, h2_a, h2_b, h2_c, ri, rac, rclo, rlu, & rgss, rgso use clm_varcon , only : istsoil use clm_varctl , only : iulog use pftvarcon , only : noveg, ndllf_evr_tmp_tree, ndllf_evr_brl_tree, & ndllf_dcd_brl_tree, nbrdlf_evr_trp_tree, & nbrdlf_evr_tmp_tree, nbrdlf_dcd_trp_tree, & nbrdlf_dcd_tmp_tree, nbrdlf_dcd_brl_tree, & nbrdlf_evr_shrub, nbrdlf_dcd_tmp_shrub, & nbrdlf_dcd_brl_shrub, nc3_arctic_grass, & nc3_nonarctic_grass, nc4_grass, ncorn, nwheat implicit none !----Arguments----------------------------------------------------- integer, intent(in) :: lbp, ubp ! pft bounds ! ------------------------ local variables ------------------------ ! local pointers to implicit in arguments integer , pointer :: plandunit(:) !pft's landunit index integer , pointer :: ivt(:) !landunit type integer , pointer :: itypveg(:) !vegetation type for current pft integer , pointer :: pgridcell(:) !pft's gridcell index real(r8), pointer :: pwtgcell(:) !weight of pft relative to corresponding gridcell real(r8), pointer :: elai(:) !one-sided leaf area index with burying by snow real(r8), pointer :: forc_t(:) !atmospheric temperature (Kelvin) real(r8), pointer :: forc_q(:) !atmospheric specific humidity (kg/kg) real(r8), pointer :: forc_psrf(:) !surface pressure (Pa) real(r8), pointer :: latdeg(:) !latitude (degrees) real(r8), pointer :: londeg(:) !longitude (degrees) real(r8), pointer :: forc_rain(:) !rain rate [mm/s] real(r8), pointer :: forc_snow(:) !snow rate [mm/s] real(r8), pointer :: forc_lwrad(:) !direct beam radiation (visible only) real(r8), pointer :: forc_solad(:,:) !direct beam radiation (visible only) real(r8), pointer :: forc_solai(:,:) !direct beam radiation (visible only) real(r8), pointer :: ram1(:) !aerodynamical resistance real(r8), pointer :: vds(:) !aerodynamical resistance real(r8), pointer :: rssun(:) !stomatal resistance real(r8), pointer :: rssha(:) !shaded stomatal resistance (s/m) real(r8), pointer :: fsun(:) !sunlit fraction of canopy real(r8), pointer :: rb1(:) !leaf boundary layer resistance [s/m] real(r8), pointer :: annlai(:,:) !12 months of monthly lai from input data set real(r8), pointer :: mlaidiff(:) !difference in lai between month one and month two real(r8), pointer :: velocity(:,:) real(r8), pointer :: snowdp(:) ! snow height (m) integer, pointer :: pcolumn(:) ! column index associated with each pft integer :: c integer , pointer :: itypelun(:) ! landunit type real(r8), pointer :: h2osoi_vol(:,:) ! volumetric soil water (0<=h2osoi_vol<=watsat) real(r8) :: soilw, var_soilw, fact_h2, dv_soil_h2 ! new local variables integer :: pi,g, l integer :: ispec integer :: length integer :: wesveg !wesely vegegation index integer :: clmveg !clm veg index from ivegtype integer :: i integer :: index_season !seasonal index based on LAI. This indexs wesely data tables integer :: nstep !current step integer :: indexp real(r8) :: pg ! surface pressure real(r8) :: tc ! temperature in celsius real(r8) :: rs ! constant for calculating rsmx real(r8) :: es ! saturation vapor pressur real(r8) :: ws ! saturation mixing ratio real(r8) :: rmx ! resistance by vegetation real(r8) :: qs ! saturation specific humidity real(r8) :: dewm ! multiplier for rs when dew occurs real(r8) :: crs ! multiplier to calculate crs real(r8) :: rdc ! part of lower canopy resistance real(r8) :: rain ! rain fall real(r8) :: spec_hum ! specific humidity real(r8) :: solar_flux ! solar radiation(direct beam) W/m2 real(r8) :: lat ! latitude in degrees real(r8) :: lon ! longitude in degrees real(r8) :: sfc_temp ! surface temp real(r8) :: minlai ! minimum of monthly lai real(r8) :: maxlai ! maximum of monthly lai real(r8) :: rds logical :: has_dew logical :: has_rain real(r8), parameter :: rain_threshold = 1.e-7_r8 ! of the order of 1cm/day expressed in m/s ! local arrays: dependent on species only ! real(r8), dimension(n_drydep) :: rsmx !vegetative resistance (plant mesophyll) real(r8), dimension(n_drydep) :: rclx !lower canopy resistance real(r8), dimension(n_drydep) :: rlux !vegetative resistance (upper canopy) real(r8), dimension(n_drydep) :: rgsx !gournd resistance real(r8), dimension(n_drydep) :: heff real(r8) :: rc !combined surface resistance real(r8) :: cts !correction to flu rcl and rgs for frost real(r8) :: rlux_o3 ! constants real(r8), parameter :: slope = 0._r8 ! Used to calculate rdc in (lower canopy resistance) integer, parameter :: wveg_unset = -1 ! Unset Wesley vegetation type character(len=32), parameter :: subname = "depvel_compute" !------------------------------------------------------------------------------------- ! jfl : mods for PAN !------------------------------------------------------------------------------------- real(r8) :: dv_pan real(r8) :: c0_pan(11) = (/ 0.000_r8, 0.006_r8, 0.002_r8, 0.009_r8, 0.015_r8, & 0.006_r8, 0.000_r8, 0.000_r8, 0.000_r8, 0.002_r8, 0.002_r8 /) real(r8) :: k_pan (11) = (/ 0.000_r8, 0.010_r8, 0.005_r8, 0.004_r8, 0.003_r8, & 0.005_r8, 0.000_r8, 0.000_r8, 0.000_r8, 0.075_r8, 0.002_r8 /) !----------------------------------------------------------------------- if ( n_drydep == 0 .or. drydep_method /= DD_XLND ) return ! local pointers to original implicit out arrays ! Assign local pointers to derived subtypes components (column-level) forc_t => clm_a2l%forc_t forc_q => clm_a2l%forc_q forc_psrf => clm_a2l%forc_psrf forc_rain => clm_a2l%forc_rain latdeg => clm3%g%latdeg londeg => clm3%g%londeg ivt => clm3%g%l%c%p%itype elai => clm3%g%l%c%p%pps%elai ram1 => clm3%g%l%c%p%pps%ram1 vds => clm3%g%l%c%p%pps%vds fsun => clm3%g%l%c%p%pps%fsun rssun => clm3%g%l%c%p%pps%rssun rssha => clm3%g%l%c%p%pps%rssha rb1 => clm3%g%l%c%p%pps%rb1 mlaidiff => clm3%g%l%c%p%pps%mlaidiff annlai => clm3%g%l%c%p%pps%annlai forc_solai => clm_a2l%forc_solai forc_solad => clm_a2l%forc_solad pwtgcell => clm3%g%l%c%p%wtgcell pgridcell => clm3%g%l%c%p%gridcell plandunit => clm3%g%l%c%p%landunit pcolumn => clm3%g%l%c%p%column itypelun => clm3%g%l%itype h2osoi_vol => clm3%g%l%c%cws%h2osoi_vol velocity => clm3%g%l%c%p%pdd%drydepvel ! cm/sec snowdp => clm3%g%l%c%cps%snowdp ! Assign local pointers to original implicit out arrays !_________________________________________________________________ ! ! Begin loop through pfts pft_loop: do pi = lbp,ubp gcell_wght: if (pwtgcell(pi)>0._r8) then c = pcolumn(pi) g = pgridcell(pi) l = plandunit(pi) !solar_flux = forc_lwrad !rename CLM variables to fit with Dry Dep variables pg = forc_psrf(g) spec_hum = forc_q(g) rain = forc_rain(g) sfc_temp = forc_t(g) lat = latdeg(g) lon = londeg(g) solar_flux = forc_solad(g,1) clmveg = ivt(pi) soilw = h2osoi_vol(c,1) ! print *,'bb',pi,cps%npfts,lat,lon,clmveg !map CLM veg type into Wesely veg type wesveg = wveg_unset if (clmveg == noveg ) wesveg = 8 if (clmveg == ndllf_evr_tmp_tree ) wesveg = 5 if (clmveg == ndllf_evr_brl_tree ) wesveg = 5 if (clmveg == ndllf_dcd_brl_tree ) wesveg = 5 if (clmveg == nbrdlf_evr_trp_tree ) wesveg = 4 if (clmveg == nbrdlf_evr_tmp_tree ) wesveg = 4 if (clmveg == nbrdlf_dcd_trp_tree ) wesveg = 4 if (clmveg == nbrdlf_dcd_tmp_tree ) wesveg = 4 if (clmveg == nbrdlf_dcd_brl_tree ) wesveg = 4 if (clmveg == nbrdlf_evr_shrub ) wesveg = 11 if (clmveg == nbrdlf_dcd_tmp_shrub) wesveg = 11 if (clmveg == nbrdlf_dcd_brl_shrub) wesveg = 11 if (clmveg == nc3_arctic_grass ) wesveg = 3 if (clmveg == nc3_nonarctic_grass ) wesveg = 3 if (clmveg == nc4_grass ) wesveg = 3 if (clmveg == ncorn ) wesveg = 2 if (clmveg == nwheat ) wesveg = 2 if (wesveg == wveg_unset )then write(iulog,*) 'clmveg = ', clmveg, 'itypelun = ', itypelun(l) call endrun( subname//': Not able to determine Wesley vegetation type') end if ! creat seasonality index used to index wesely data tables from LAI, Bascially !if elai is between max lai from input data and half that max the index_season=1 !mail1j and mlai2j are the two monthly lai values pulled from a CLM input data set !/fs/cgd/csm/inputdata/lnd/clm2/rawdata/mksrf_lai.nc. lai for dates in the middle !of the month are interpolated using using these values and stored in the variable !elai (done elsewhere). If the difference between mlai1j and mlai2j is greater !than zero it is assumed to be fall and less than zero it is assumed to be spring. !wesely seasonal "index_season" ! 1 - midsummer with lush vegetation ! 2 - Autumn with unharvested cropland ! 3 - Late autumn after frost, no snow ! 4 - Winter, snow on ground and subfreezing ! 5 - Transitional spring with partially green short annuals !mlaidiff=jan-feb minlai=minval(annlai(:,pi)) maxlai=maxval(annlai(:,pi)) index_season = -1 if ( itypelun(l) /= istsoil )then wesveg = 8 index_season = 4 else if ( snowdp(c) > 0 ) then index_season = 4 else if(elai(pi).gt.0.5_r8*maxlai) then index_season = 1 endif if (index_season<0) then if (elai(pi).lt.(minlai+0.05*(maxlai-minlai))) then index_season = 3 endif endif if (index_season<0) then if (mlaidiff(pi).gt.0.0_r8) then index_season = 2 elseif (mlaidiff(pi).lt.0.0_r8) then index_season = 5 elseif (mlaidiff(pi).eq.0.0_r8) then index_season = 3 endif endif if (index_season<0) then call endrun( subname//': not able to determine season') endif ! saturation specific humidity ! es = 611_r8*exp(5414.77_r8*((1._r8/tmelt)-(1._r8/sfc_temp))) ws = .622_r8*es/(pg-es) qs = ws/(1._r8+ws) has_dew = .false. if( qs <= spec_hum ) then has_dew = .true. end if if( sfc_temp < tmelt ) then has_dew = .false. end if has_rain = rain > rain_threshold if ( has_dew .or. has_rain ) then dewm = 3._r8 else dewm = 1._r8 end if ! ! constant in determining rs ! crs = 1.e36_r8 tc = sfc_temp - tmelt if(sfc_temp.gt.tmelt.and.sfc_temp.lt.313.15_r8) then crs = (1._r8+(200._r8/(solar_flux+.1_r8))**2) * (400._r8/(tc*(40._r8-tc))) endif ! ! rdc (lower canopy res) ! rdc=100._r8*(1._r8+1000._r8/(solar_flux+10._r8))/(1._r8+1000._r8*slope) ! surface resistance : depends on both land type and species ! land types are computed seperately, then resistance is computed as average of values ! following wesely rc=(1/(rs+rm) + 1/rlu +1/(rdc+rcl) + 1/(rac+rgs))**-1 ! ! compute rsmx = 1/(rs+rm) : multiply by 3 if surface is wet ! !******************************************************* call seq_drydep_setHCoeff( sfc_temp, heff(:n_drydep) ) !********************************************************* species_loop1: do ispec=1, n_drydep if(mapping(ispec).le.0) cycle if(ispec.eq.index_o3.or.ispec.eq.index_o3a.or.ispec.eq.index_so2) then rmx=0._r8 else rmx=1._r8/((heff(ispec)/3000._r8)+(100._r8*foxd(ispec))) endif ! correction for frost cts = 1000._r8*exp( -tc - 4._r8 ) ! correction for frost rgsx(ispec) = cts + 1._r8/((heff(ispec)/(1.e5_r8*rgss(index_season,wesveg))) + & (foxd(ispec)/rgso(index_season,wesveg))) !------------------------------------------------------------------------------------- ! special case for H2 and CO;; CH4 is set ot a fraction of dv(H2) !------------------------------------------------------------------------------------- if( ispec == index_h2 .or. ispec == index_co .or. ispec == index_ch4 ) then if( ispec == index_co ) then fact_h2 = 1.0_r8 elseif ( ispec == index_h2 ) then fact_h2 = 0.5_r8 elseif ( ispec == index_ch4 ) then fact_h2 = 50.0_r8 end if !------------------------------------------------------------------------------------- ! no deposition on snow, ice, desert, and water !------------------------------------------------------------------------------------- if( wesveg == 1 .or. wesveg == 7 .or. wesveg == 8 .or. index_season == 4 ) then rgsx(ispec) = 1.e36_r8 else var_soilw = max( .1_r8,min( soilw,.3_r8 ) ) if( wesveg == 3 ) then var_soilw = log( var_soilw ) end if dv_soil_h2 = h2_c(wesveg) + var_soilw*(h2_b(wesveg) + var_soilw*h2_a(wesveg)) if( dv_soil_h2 > 0._r8 ) then rgsx(ispec) = fact_h2/(dv_soil_h2*1.e-4_r8) end if end if end if if(wesveg.eq.7) then ! over water rclx(ispec)=1.e36_r8 rsmx(ispec)=1.e36_r8 rlux(ispec)=1.e36_r8 else rs=ri(index_season,wesveg)*crs ! ??? fvitt rs=(fsun(pi)*rssun(pi))+(rssha(pi)*(1.-fsun(pi))) -- made the same as mo_drydep rsmx(ispec) = (dewm*rs*drat(ispec)+rmx) endif !------------------------------------------------------------------------------------- ! jfl : special case for PAN !------------------------------------------------------------------------------------- if( ispec == index_pan ) then dv_pan = c0_pan(wesveg) * (1._r8 - exp( -k_pan(wesveg)*(dewm*rs*drat(ispec))*1.e-2_r8 )) if( dv_pan > 0._r8 .and. index_season /= 4 ) then rsmx(ispec) = ( 1._r8/dv_pan ) end if end if rclx(ispec) = cts + 1._r8/((heff(ispec)/(1.e5_r8*rcls(index_season,wesveg))) + & (foxd(ispec)/rclo(index_season,wesveg))) rlux(ispec) = cts + rlu(index_season,wesveg)/(1.e-5_r8*heff(ispec)+foxd(ispec)) end do species_loop1 ! ! no effect over water ! no_water: if(wesveg.ne.7) then ! ! no effect if sfc_temp < O C ! non_freezing: if(sfc_temp.gt.tmelt) then if( has_dew ) then rlux_o3 = 1._r8/((1._r8/3000._r8)+(1._r8/(3._r8*rlu(index_season,wesveg)))) if (index_o3 > 0) then rlux(index_o3) = rlux_o3 endif if (index_o3a > 0) then rlux(index_o3a) = rlux_o3 endif endif if(rain.gt..0001_r8) then rlux_o3 = 1._r8/((1._r8/1000._r8)+(1._r8/(3._r8*rlu(index_season,wesveg)))) if (index_o3 > 0) then rlux(index_o3) = rlux_o3 endif if (index_o3a > 0) then rlux(index_o3a) = rlux_o3 endif endif if ( index_o3 > 0 ) then rclx(index_o3) = cts + rclo(index_season,wesveg) rlux(index_o3) = cts + rlux(index_o3) end if if ( index_o3a > 0 ) then rclx(index_o3a) = cts + rclo(index_season,wesveg) rlux(index_o3a) = cts + rlux(index_o3a) end if species_loop2: do ispec=1,n_drydep if(mapping(ispec).le.0) cycle if(ispec.ne.index_o3.and.ispec.ne.index_o3a.and.ispec.ne.index_so2) then if( has_dew ) then rlux(ispec)=1._r8/((1._r8/(3._r8*rlux(ispec)))+ & (1.e-7_r8*heff(ispec))+(foxd(ispec)/rlux_o3)) endif elseif(ispec.eq.index_so2) then if( has_dew ) then rlux(ispec) = 100._r8 endif if(rain.gt..0001_r8) then rlux(ispec) = 1._r8/((1._r8/5000._r8)+(1._r8/(3._r8*rlu(index_season,wesveg)))) endif rclx(ispec) = cts + rcls(index_season,wesveg) rlux(ispec) = cts + rlux(ispec) if( has_dew .or. has_rain ) then rlux(ispec)=50._r8 endif endif end do species_loop2 endif non_freezing endif no_water rds = 1._r8/vds(pi) species_loop3: do ispec=1,n_drydep if(mapping(ispec).le.0) cycle ! ! compute rc ! rc = 1._r8/((1._r8/rsmx(ispec))+(1._r8/rlux(ispec)) + & (1._r8/(rdc+rclx(ispec)))+(1._r8/(rac(index_season,wesveg)+rgsx(ispec)))) select case( drydep_list(ispec) ) case ( 'SO4' ) velocity(pi,ispec) = (1._r8/(ram1(pi)+rds))*100._r8 case ( 'NH4','NH4NO3' ) velocity(pi,ispec) = (1._r8/(ram1(pi)+0.5_r8*rds))*100._r8 case ( 'Pb' ) velocity(pi,ispec) = 0.2_r8 case ( 'CB1', 'CB2', 'OC1', 'OC2' ) velocity(pi,ispec) = 0.10_r8 case default velocity(pi,ispec) = (1._r8/(ram1(pi)+rb1(pi)+rc))*100._r8 end select end do species_loop3 endif gcell_wght end do pft_loop end subroutine depvel_compute end module DryDepVelocity