#include <misc.h>
#include <preproc.h>


module DUSTMod 2,8

#if (defined DUST)

!----------------------------------------------------------------------- 
!BOP
!
! !MODULE: DUSTMod
!
! !DESCRIPTION: 
! Routines in this module calculate Dust mobilization and dry deposition for dust.
! Simulates dust mobilization due to wind from the surface into the 
! lowest atmospheric layer. On output flx_mss_vrt_dst(ndst) is the surface dust 
! emission (kg/m**2/s) [ + = to atm].
! Calculates the turbulent component of dust dry deposition, (the turbulent deposition 
! velocity through the lowest atmospheric layer). CAM will calculate the settling 
! velocity through the whole atmospheric column. The two calculations will determine 
! the dust dry deposition flux to the surface.
!                              
! !USES:
  use shr_kind_mod, only: r8 => shr_kind_r8 
  use clmtype
  use clm_varpar  , only : dst_src_nbr, ndst, sz_nbr
  use clm_varcon  , only : grav, istsoil
  use clm_varctl  , only : iulog
  use abortutils  , only : endrun
  use subgridAveMod, only: p2l_1d
  use clm_varcon, only: spval
!  
! !PUBLIC TYPES
  implicit none
  save
!
! !PUBLIC MEMBER FUNCTIONS:
!
  public Dustini        ! Initialize variables used in subroutine Dust
  public DustEmission   ! Dust mobilization 
  public DustDryDep     ! Turbulent dry deposition for dust
!
! !REVISION HISTORY
! Created by Sam Levis, updated to clm2.1 by Mariana Vertenstein
! Source: C. Zender's dust model
!
!EOP
!
! Data private to this module
!
  private
  real(r8) ovr_src_snk_mss(dst_src_nbr,ndst)  
  real(r8) tmp1          !Factor in saltation computation (named as in Charlie's code)
  real(r8) dmt_vwr(ndst) ![m] Mass-weighted mean diameter resolved
  real(r8) stk_crc(ndst) ![frc] Correction to Stokes settling velocity
  real(r8) dns_aer       ![kg m-3] Aerosol density
!------------------------------------------------------------------------

contains

!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: DustEmission
!
! !INTERFACE:

  subroutine DustEmission (lbp, ubp, lbc,ubc,lbl,ubl,num_nolakep, filter_nolakep) 1,4
!
! !DESCRIPTION: 
! Dust mobilization. This code simulates dust mobilization due to wind
! from the surface into the lowest atmospheric layer
! On output flx_mss_vrt_dst(ndst) is the surface dust emission 
! (kg/m**2/s) [ + = to atm]
! Source: C. Zender's dust model
!
! !USES
   use clm_atmlnd   , only : clm_a2l
   use shr_const_mod, only : SHR_CONST_RHOFW
!
! !ARGUMENTS:
    implicit none
    integer, intent(in) :: lbp, ubp,lbc,ubc,ubl,lbl                    ! pft bounds
    integer, intent(in) :: num_nolakep                 ! number of column non-lake points in pft filter
    integer, intent(in) :: filter_nolakep(num_nolakep) ! pft filter for non-lake points
!
! !LOCAL VARIABLES
!
! local pointers to implicit in arguments
!
    integer , pointer :: pcolumn(:)         ! pft's column index
    integer , pointer :: plandunit(:)       ! pft's landunit index
    integer , pointer :: pgridcell(:)       ! pft's gridcell index
    integer , pointer :: ityplun(:)         ! landunit type
    real(r8), pointer :: tlai(:)            ! one-sided leaf area index, no burying by snow
    real(r8), pointer :: tsai(:)            ! one-sided stem area index, no burying by snow
    real(r8), pointer :: frac_sno(:)        ! fraction of ground covered by snow (0 to 1)
    real(r8), pointer :: gwc_thr(:)         ! threshold gravimetric soil moisture based on clay content
    real(r8), pointer :: forc_rho(:)        ! density (kg/m**3)
    real(r8), pointer :: fv(:)              ! friction velocity (m/s) (for dust model)
    real(r8), pointer :: u10(:)             ! 10-m wind (m/s) (created for dust model)
    real(r8), pointer :: mbl_bsn_fct(:)     ! basin factor
    real(r8), pointer :: mss_frc_cly_vld(:) ! [frc] Mass fraction clay limited to 0.20
    real(r8), pointer :: h2osoi_vol(:,:)    ! volumetric soil water (0<=h2osoi_vol<=watsat)
    real(r8), pointer :: h2osoi_liq(:,:)    ! liquid soil water (kg/m2)
    real(r8), pointer :: h2osoi_ice(:,:)    ! frozen soil water (kg/m2)
    real(r8), pointer :: watsat(:,:)        ! saturated volumetric soil water

! local pointers to implicit out arguments
!
    real(r8), pointer :: flx_mss_vrt_dst(:,:)   ! surface dust emission (kg/m**2/s) 
    real(r8), pointer :: flx_mss_vrt_dst_tot(:) ! total dust flux into atmosphere 

! !REVISION HISTORY
! Created by Sam Levis
! Migrated to new data structures by Peter Thornton and Mariana Vertenstein
! !Created by Peter Thornton and Mariana Vertenstein
!
!
! !OTHER LOCAL VARIABLES:
!EOP
!
    integer  :: fp,p,c,l,g,m,n      ! indices
    real(r8) :: liqfrac             ! fraction of total water that is liquid
    real(r8) :: wnd_frc_rat         ! [frc] Wind friction threshold over wind friction
    real(r8) :: wnd_frc_slt_dlt     ! [m s-1] Friction velocity increase from saltatn
    real(r8) :: wnd_rfr_dlt         ! [m s-1] Reference windspeed excess over threshld
    real(r8) :: dst_slt_flx_rat_ttl
    real(r8) :: flx_mss_hrz_slt_ttl
    real(r8) :: flx_mss_vrt_dst_ttl(lbp:ubp)
    real(r8) :: frc_thr_wet_fct
    real(r8) :: frc_thr_rgh_fct
    real(r8) :: wnd_frc_thr_slt
    real(r8) :: wnd_rfr_thr_slt
    real(r8) :: wnd_frc_slt
    real(r8) :: lnd_frc_mbl(lbp:ubp)
    real(r8) :: bd
    real(r8) :: gwc_sfc
    real(r8) :: ttlai(lbp:ubp)
    real(r8) :: tlai_lu(lbl:ubl)
!    
! constants
!
    real(r8), parameter :: cst_slt = 2.61_r8           ! [frc] Saltation constant
    real(r8), parameter :: flx_mss_fdg_fct = 5.0e-4_r8 ! [frc] Empir. mass flx tuning eflx_lh_vegt
    real(r8), parameter :: vai_mbl_thr = 0.3_r8        ! [m2 m-2] VAI threshold quenching dust mobilization
    real(r8), pointer :: wtlunit(:)         ! weight of pft relative to landunit
    real(r8) :: sumwt(lbl:ubl)              ! sum of weights
    logical  :: found                       ! temporary for error check
    integer  :: index

!------------------------------------------------------------------------

    ! Assign local pointers to derived type scalar members (gridcell-level)

    forc_rho        => clm_a2l%forc_rho

    ! Assign local pointers to derived type scalar members (landunit-level)

    ityplun         => clm3%g%l%itype

    ! Assign local pointers to derived type scalar members (column-level)

    frac_sno        => clm3%g%l%c%cps%frac_sno
    gwc_thr         => clm3%g%l%c%cps%gwc_thr
    mbl_bsn_fct     => clm3%g%l%c%cps%mbl_bsn_fct
    mss_frc_cly_vld => clm3%g%l%c%cps%mss_frc_cly_vld
    h2osoi_vol      => clm3%g%l%c%cws%h2osoi_vol
    h2osoi_liq      => clm3%g%l%c%cws%h2osoi_liq
    h2osoi_ice      => clm3%g%l%c%cws%h2osoi_ice
    watsat          => clm3%g%l%c%cps%watsat

    ! Assign local pointers to derived type scalar members (pft-level)

    pgridcell       => clm3%g%l%c%p%gridcell
    plandunit       => clm3%g%l%c%p%landunit
    pcolumn         => clm3%g%l%c%p%column
    tlai            => clm3%g%l%c%p%pps%tlai
    tsai            => clm3%g%l%c%p%pps%tsai
    fv              => clm3%g%l%c%p%pps%fv
    u10             => clm3%g%l%c%p%pps%u10
    flx_mss_vrt_dst => clm3%g%l%c%p%pdf%flx_mss_vrt_dst
    flx_mss_vrt_dst_tot => clm3%g%l%c%p%pdf%flx_mss_vrt_dst_tot
   !local pointers from subgridAveMod/p2l_1d
    wtlunit         => clm3%g%l%c%p%wtlunit

    ttlai(:) = 0._r8
! make lai average at landunit level
    do fp = 1,num_nolakep
       p = filter_nolakep(fp)
       ttlai(p) = tlai(p)+tsai(p)
    enddo

    tlai_lu(:) = spval
    sumwt(:) = 0._r8
    do p = lbp,ubp
       if (ttlai(p) /= spval .and. wtlunit(p) /= 0._r8) then
          c = pcolumn(p)
          l = plandunit(p)
          if (sumwt(l) == 0._r8) tlai_lu(l) = 0._r8
          tlai_lu(l) = tlai_lu(l) + ttlai(p) * wtlunit(p)
          sumwt(l) = sumwt(l) + wtlunit(p)
       end if
    end do
    found = .false.
    do l = lbl,ubl
       if (sumwt(l) > 1.0_r8 + 1.e-6_r8) then
          found = .true.
          index = l
          exit
       else if (sumwt(l) /= 0._r8) then
          tlai_lu(l) = tlai_lu(l)/sumwt(l)
       end if
    end do
    if (found) then
       write(iulog,*) 'p2l_1d error: sumwt is greater than 1.0 at l= ',index
       call endrun()
    end if

! Loop through pfts

! initialize variables which get passed to the atmosphere
    flx_mss_vrt_dst(lbp:ubp,:)=0._r8

!dir$ concurrent
!cdir nodep
    do fp = 1,num_nolakep
       p = filter_nolakep(fp)
       c = pcolumn(p)
       l = plandunit(p)
       
       ! the following code from subr. lnd_frc_mbl_get was adapted for lsm use
       ! purpose: return fraction of each gridcell suitable for dust mobilization
       
       ! the "bare ground" fraction of the current sub-gridscale cell decreases
       ! linearly from 1 to 0 as VAI(=tlai+tsai) increases from 0 to vai_mbl_thr
       ! if ice sheet, wetland, or lake, no dust allowed
       
       if (ityplun(l) == istsoil) then
          if (tlai_lu(l) < vai_mbl_thr) then
             lnd_frc_mbl(p) = 1.0_r8 - (tlai_lu(l))/vai_mbl_thr
          else
             lnd_frc_mbl(p) = 0.0_r8
          endif
          lnd_frc_mbl(p) = lnd_frc_mbl(p) * (1.0_r8 - frac_sno(c))
       else          
          lnd_frc_mbl(p) = 0.0_r8   
       end if
    end do
    
!dir$ concurrent
!cdir nodep
    do fp = 1,num_nolakep
       p = filter_nolakep(fp)
       if (lnd_frc_mbl(p)>1.0_r8 .or. lnd_frc_mbl(p)<0.0_r8) then
          write(iulog,*)'Error dstmbl: pft= ',p,' lnd_frc_mbl(p)= ',lnd_frc_mbl(p)
          call endrun 
       end if
    end do
    
    ! reset history output variables before next if-statement to avoid output = inf
    
!dir$ concurrent
!cdir nodep
    do fp = 1,num_nolakep
       p = filter_nolakep(fp)
       flx_mss_vrt_dst_tot(p) = 0.0_r8
    end do
    do n = 1, ndst
!dir$ concurrent
!cdir nodep
       do fp = 1,num_nolakep
          p = filter_nolakep(fp)
          flx_mss_vrt_dst(p,n) = 0.0_r8
       end do
    end do
    
!dir$ concurrent
!cdir nodep
    do fp = 1,num_nolakep
       p = filter_nolakep(fp)
       c = pcolumn(p)
       l = plandunit(p)
       g = pgridcell(p)
       
       ! only perform the following calculations if lnd_frc_mbl is non-zero 
       
       if (lnd_frc_mbl(p) > 0.0_r8) then

          ! the following comes from subr. frc_thr_rgh_fct_get
          ! purpose: compute factor by which surface roughness increases threshold
          !          friction velocity (currently a constant)
          
          frc_thr_rgh_fct = 1.0_r8
          
          ! the following comes from subr. frc_thr_wet_fct_get
          ! purpose: compute factor by which soil moisture increases threshold friction velocity
          ! adjust threshold velocity for inhibition by moisture
          ! modified 4/5/2002 (slevis) to use gravimetric instead of volumetric
          ! water content
          
          bd = (1._r8-watsat(c,1))*2.7e3_r8      ![kg m-3] Bulk density of dry surface soil
          gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd    ![kg kg-1] Gravimetric H2O cont
          if (gwc_sfc > gwc_thr(c)) then
             frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8)
          else
             frc_thr_wet_fct = 1.0_r8
          end if
          
          ! slevis: adding liqfrac here, because related to effects from soil water

          liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) )
          
          ! the following lines come from subr. dst_mbl
          ! purpose: adjust threshold friction velocity to acct for moisture and
          !          roughness. The ratio tmp1 / sqrt(forc_rho) comes from
          !          subr. wnd_frc_thr_slt_get which computes dry threshold
          !          friction velocity for saltation
          
          wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(g)) * frc_thr_wet_fct * frc_thr_rgh_fct
          
          ! reset these variables which will be updated in the following if-block
          
          wnd_frc_slt = fv(p)
          flx_mss_hrz_slt_ttl = 0.0_r8
          flx_mss_vrt_dst_ttl(p) = 0.0_r8
          
          ! the following line comes from subr. dst_mbl
          ! purpose: threshold saltation wind speed
          
          wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p)
          
          ! the following if-block comes from subr. wnd_frc_slt_get 
          ! purpose: compute the saltating friction velocity
          ! theory: saltation roughens the boundary layer, AKA "Owen's effect"
          
          if (u10(p) >= wnd_rfr_thr_slt) then
             wnd_rfr_dlt = u10(p) - wnd_rfr_thr_slt
             wnd_frc_slt_dlt = 0.003_r8 * wnd_rfr_dlt * wnd_rfr_dlt
             wnd_frc_slt = fv(p) + wnd_frc_slt_dlt
          end if
          
          ! the following comes from subr. flx_mss_hrz_slt_ttl_Whi79_get
          ! purpose: compute vertically integrated streamwise mass flux of particles
          
          if (wnd_frc_slt > wnd_frc_thr_slt) then
             wnd_frc_rat = wnd_frc_thr_slt / wnd_frc_slt
             flx_mss_hrz_slt_ttl = cst_slt * forc_rho(g) * (wnd_frc_slt**3.0_r8) * &
                  (1.0_r8 - wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) / grav
             
             ! the following loop originates from subr. dst_mbl
             ! purpose: apply land sfc and veg limitations and global tuning factor
             ! slevis: multiply flx_mss_hrz_slt_ttl by liqfrac to incude the effect 
             ! of frozen soil
             
             flx_mss_hrz_slt_ttl = flx_mss_hrz_slt_ttl * lnd_frc_mbl(p) * mbl_bsn_fct(c) * &
                  flx_mss_fdg_fct * liqfrac
          end if
          
          ! the following comes from subr. flx_mss_vrt_dst_ttl_MaB95_get
          ! purpose: diagnose total vertical mass flux of dust from vertically
          !          integrated streamwise mass flux
          
          dst_slt_flx_rat_ttl = 100.0_r8 * exp( log(10.0_r8) * (13.4_r8 * mss_frc_cly_vld(c) - 6.0_r8) )
          flx_mss_vrt_dst_ttl(p) = flx_mss_hrz_slt_ttl * dst_slt_flx_rat_ttl
          
       end if   ! lnd_frc_mbl > 0.0

    end do

    ! the following comes from subr. flx_mss_vrt_dst_prt in C. Zender's code
    ! purpose: partition total vertical mass flux of dust into transport bins
          
    do n = 1, ndst
       do m = 1, dst_src_nbr
!dir$ concurrent
!cdir nodep
          do fp = 1,num_nolakep
             p = filter_nolakep(fp)
             if (lnd_frc_mbl(p) > 0.0_r8) then
                flx_mss_vrt_dst(p,n) = flx_mss_vrt_dst(p,n) +  ovr_src_snk_mss(m,n) * flx_mss_vrt_dst_ttl(p)
             end if
          end do
       end do
    end do

    do n = 1, ndst
!dir$ concurrent
!cdir nodep
       do fp = 1,num_nolakep
          p = filter_nolakep(fp)
          if (lnd_frc_mbl(p) > 0.0_r8) then
             flx_mss_vrt_dst_tot(p) = flx_mss_vrt_dst_tot(p) + flx_mss_vrt_dst(p,n)
          end if
       end do
    end do

  end subroutine DustEmission

!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: subroutine DustDryDep
!
! !INTERFACE:
!

  subroutine DustDryDep (lbp, ubp) 2,3
!
! !DESCRIPTION: 
!
! Determine Turbulent dry deposition for dust. Calculate the turbulent 
! component of dust dry deposition, (the turbulent deposition velocity 
! through the lowest atmospheric layer. CAM will calculate the settling 
! velocity through the whole atmospheric column. The two calculations 
! will determine the dust dry deposition flux to the surface.
! Note: Same process should occur over oceans. For the coupled CCSM,
! we may find it more efficient to let CAM calculate the turbulent dep
! velocity over all surfaces. This would require passing the
! aerodynamic resistance, ram(1), and the friction velocity, fv, from
! the land to the atmosphere component. In that case, dustini need not
! calculate particle diamter (dmt_vwr) and particle density (dns_aer).
! Source: C. Zender's dry deposition code
!
! !USES
!
    use shr_const_mod, only : SHR_CONST_PI, SHR_CONST_RDAIR, SHR_CONST_BOLTZ
    use clm_atmlnd   , only : clm_a2l
!
! !ARGUMENTS:
!
    implicit none
    integer, intent(in) :: lbp, ubp     ! pft bounds
!
! !LOCAL VARIABLES
!
! local pointers to implicit in arguments
!
    integer , pointer :: pgridcell(:)   ! pft's gridcell index
    real(r8), pointer :: pwtgcell(:)    ! weight of pft relative to corresponding gridcell
    real(r8), pointer :: forc_t(:)      ! atm temperature (K)
    real(r8), pointer :: forc_pbot(:)   ! atm pressure (Pa)
    real(r8), pointer :: forc_rho(:)    ! atm density (kg/m**3)
    real(r8), pointer :: fv(:)          ! friction velocity (m/s)
    real(r8), pointer :: ram1(:)        ! aerodynamical resistance (s/m)
    real(r8), pointer :: vlc_trb(:,:)   ! Turbulent deposn velocity (m/s)
    real(r8), pointer :: vlc_trb_1(:)   ! Turbulent deposition velocity 1
    real(r8), pointer :: vlc_trb_2(:)   ! Turbulent deposition velocity 2
    real(r8), pointer :: vlc_trb_3(:)   ! Turbulent deposition velocity 3
    real(r8), pointer :: vlc_trb_4(:)   ! Turbulent deposition velocity 4
!
! !REVISION HISTORY
! Created by Sam Levis
!
!
! !LOCAL VARIABLES
!EOP
!
    integer  :: p,g,m,n               ! indices
    real(r8) :: vsc_dyn_atm(lbp:ubp)  ! [kg m-1 s-1] Dynamic viscosity of air
    real(r8) :: vsc_knm_atm(lbp:ubp)  ! [m2 s-1] Kinematic viscosity of atmosphere
    real(r8) :: shm_nbr_xpn           ! [frc] Sfc-dep exponent for aerosol-diffusion dependence on Schmidt number
    real(r8) :: shm_nbr               ! [frc] Schmidt number
    real(r8) :: stk_nbr               ! [frc] Stokes number
    real(r8) :: mfp_atm               ! [m] Mean free path of air
    real(r8) :: dff_aer               ! [m2 s-1] Brownian diffusivity of particle
    real(r8) :: rss_trb               ! [s m-1] Resistance to turbulent deposition
    real(r8) :: slp_crc(lbp:ubp,ndst) ! [frc] Slip correction factor
    real(r8) :: vlc_grv(lbp:ubp,ndst) ! [m s-1] Settling velocity
    real(r8) :: rss_lmn(lbp:ubp,ndst) ! [s m-1] Quasi-laminar layer resistance
    real(r8) :: tmp                   ! temporary 

! constants

    real(r8),parameter::shm_nbr_xpn_lnd=-2._r8/3._r8 ![frc] shm_nbr_xpn over land
!------------------------------------------------------------------------

    ! Assign local pointers to derived type members (gridcell-level)

    forc_pbot => clm_a2l%forc_pbot
    forc_rho  => clm_a2l%forc_rho
    forc_t    => clm_a2l%forc_t

    ! Assign local pointers to derived type members (pft-level)

    pgridcell => clm3%g%l%c%p%gridcell
    pwtgcell  => clm3%g%l%c%p%wtgcell  
    fv        => clm3%g%l%c%p%pps%fv
    ram1      => clm3%g%l%c%p%pps%ram1
    vlc_trb   => clm3%g%l%c%p%pdf%vlc_trb
    vlc_trb_1 => clm3%g%l%c%p%pdf%vlc_trb_1
    vlc_trb_2 => clm3%g%l%c%p%pdf%vlc_trb_2
    vlc_trb_3 => clm3%g%l%c%p%pdf%vlc_trb_3
    vlc_trb_4 => clm3%g%l%c%p%pdf%vlc_trb_4

!dir$ concurrent
!cdir nodep
    do p = lbp,ubp
       if (pwtgcell(p)>0._r8) then
          g = pgridcell(p)

          ! from subroutine dst_dps_dry (consider adding sanity checks from line 212)
          ! when code asks to use midlayer density, pressure, temperature,
          ! I use the data coming in from the atmosphere, ie forc_t, forc_pbot, forc_rho
          
          ! Quasi-laminar layer resistance: call rss_lmn_get
          ! Size-independent thermokinetic properties
          
          vsc_dyn_atm(p) = 1.72e-5_r8 * ((forc_t(g)/273.0_r8)**1.5_r8) * 393.0_r8 / &
               (forc_t(g)+120.0_r8)      ![kg m-1 s-1] RoY94 p. 102
          mfp_atm = 2.0_r8 * vsc_dyn_atm(p) / &   ![m] SeP97 p. 455
               (forc_pbot(g)*sqrt(8.0_r8/(SHR_CONST_PI*SHR_CONST_RDAIR*forc_t(g))))
          vsc_knm_atm(p) = vsc_dyn_atm(p) / forc_rho(g) ![m2 s-1] Kinematic viscosity of air
          
          do m = 1, ndst
             slp_crc(p,m) = 1.0_r8 + 2.0_r8 * mfp_atm * &
                  (1.257_r8+0.4_r8*exp(-1.1_r8*dmt_vwr(m)/(2.0_r8*mfp_atm))) / &
                  dmt_vwr(m)   ![frc] Slip correction factor SeP97 p. 464
             vlc_grv(p,m) = (1.0_r8/18.0_r8) * dmt_vwr(m) * dmt_vwr(m) * dns_aer * &
                  grav * slp_crc(p,m) / vsc_dyn_atm(p)   ![m s-1] Stokes' settling velocity SeP97 p. 466
             vlc_grv(p,m) = vlc_grv(p,m) * stk_crc(m)    ![m s-1] Correction to Stokes settling velocity
          end do
       end if
    end do

    do m = 1, ndst
!dir$ concurrent
!cdir nodep
       do p = lbp,ubp
          if (pwtgcell(p)>0._r8) then
             g = pgridcell(p)
             
             stk_nbr = vlc_grv(p,m) * fv(p) * fv(p) / (grav * vsc_knm_atm(p))  ![frc] SeP97 p.965
             dff_aer = SHR_CONST_BOLTZ * forc_t(g) * slp_crc(p,m) / &          ![m2 s-1]
                  (3.0_r8*SHR_CONST_PI * vsc_dyn_atm(p) * dmt_vwr(m))          !SeP97 p.474
             shm_nbr = vsc_knm_atm(p) / dff_aer                                ![frc] SeP97 p.972
             shm_nbr_xpn = shm_nbr_xpn_lnd                                     ![frc]
             
             ! fxm: Turning this on dramatically reduces
             ! deposition velocity in low wind regimes
             ! Schmidt number exponent is -2/3 over solid surfaces and
             ! -1/2 over liquid surfaces SlS80 p. 1014
             ! if (oro(i)==0.0) shm_nbr_xpn=shm_nbr_xpn_ocn else shm_nbr_xpn=shm_nbr_xpn_lnd
             ! [frc] Surface-dependent exponent for aerosol-diffusion dependence on Schmidt # 
             
             tmp = shm_nbr**shm_nbr_xpn + 10.0_r8**(-3.0_r8/stk_nbr)
             rss_lmn(p,m) = 1.0_r8 / (tmp * fv(p)) ![s m-1] SeP97 p.972,965
          end if
       end do
    end do

    ! Lowest layer: Turbulent deposition (CAM will calc. gravitational dep)

    do m = 1, ndst
!dir$ concurrent
!cdir nodep
       do p = lbp,ubp
          if (pwtgcell(p)>0._r8) then
             rss_trb = ram1(p) + rss_lmn(p,m) + ram1(p) * rss_lmn(p,m) * vlc_grv(p,m) ![s m-1]
             vlc_trb(p,m) = 1.0_r8 / rss_trb                                          ![m s-1]
          end if
       end do
    end do

!dir$ concurrent
!cdir nodep
    do p = lbp,ubp
       if (pwtgcell(p)>0._r8) then
          vlc_trb_1(p) = vlc_trb(p,1)
          vlc_trb_2(p) = vlc_trb(p,2)
          vlc_trb_3(p) = vlc_trb(p,3)
          vlc_trb_4(p) = vlc_trb(p,4)
       end if
    end do 

  end subroutine DustDryDep

!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: subroutine Dustini
!
! !INTERFACE:
!

  subroutine Dustini() 2,14
!
! !DESCRIPTION: 
!
! Compute source efficiency factor from topography
! Initialize other variables used in subroutine Dust:
! ovr_src_snk_mss(m,n) and tmp1.
! Define particle diameter and density needed by atm model
! as well as by dry dep model
! Source: Paul Ginoux (for source efficiency factor)
! Modifications by C. Zender and later by S. Levis
! Rest of subroutine from C. Zender's dust model
!
! !USES
    use shr_const_mod, only: SHR_CONST_PI, SHR_CONST_RDAIR
    use decompMod, only : get_proc_bounds
!
! !ARGUMENTS:
    implicit none
!
! !REVISION HISTORY
! Created by Samual Levis
!
! !LOCAL VARIABLES
!
! local pointers to implicit in arguments
!
    real(r8), pointer :: mbl_bsn_fct(:) !basin factor
!
!
! !LOCAL VARIABLES
!EOP
!
    integer  :: fc,c,l,m,n              ! indices
    real(r8) :: ovr_src_snk_frc
    real(r8) :: sqrt2lngsdi             ! [frc] Factor in erf argument
    real(r8) :: lndmaxjovrdmdni         ! [frc] Factor in erf argument
    real(r8) :: lndminjovrdmdni         ! [frc] Factor in erf argument
    real(r8) :: ryn_nbr_frc_thr_prx_opt ! [frc] Threshold friction Reynolds number approximation for optimal size
    real(r8) :: ryn_nbr_frc_thr_opt_fnc ! [frc] Threshold friction Reynolds factor for saltation calculation
    real(r8) :: icf_fct                 ! Interpartical cohesive forces factor for saltation calc
    real(r8) :: dns_fct                 ! Density ratio factor for saltation calculation
    real(r8) :: dmt_min(ndst)           ! [m] Size grid minimum
    real(r8) :: dmt_max(ndst)           ! [m] Size grid maximum
    real(r8) :: dmt_ctr(ndst)           ! [m] Diameter at bin center
    real(r8) :: dmt_dlt(ndst)           ! [m] Width of size bin
    real(r8) :: slp_crc(ndst)           ! [frc] Slip correction factor
    real(r8) :: vlm_rsl(ndst)           ! [m3 m-3] Volume concentration resolved
    real(r8) :: vlc_stk(ndst)           ! [m s-1] Stokes settling velocity
    real(r8) :: vlc_grv(ndst)           ! [m s-1] Settling velocity
    real(r8) :: ryn_nbr_grv(ndst)       ! [frc] Reynolds number at terminal velocity
    real(r8) :: cff_drg_grv(ndst)       ! [frc] Drag coefficient at terminal velocity
    real(r8) :: tmp                     ! temporary 
    real(r8) :: ln_gsd                  ! [frc] ln(gsd)
    real(r8) :: gsd_anl                 ! [frc] Geometric standard deviation
    real(r8) :: dmt_vma                 ! [m] Mass median diameter analytic She84 p.75 Tabl.1
    real(r8) :: dmt_nma                 ! [m] Number median particle diameter
    real(r8) :: lgn_dst                 ! Lognormal distribution at sz_ctr
    real(r8) :: eps_max                 ! [frc] Relative accuracy for convergence
    real(r8) :: eps_crr                 ! [frc] Current relative accuracy
    real(r8) :: itr_idx                 ! [idx] Counting index
    real(r8) :: dns_mdp                 ! [kg m-3] Midlayer density
    real(r8) :: mfp_atm                 ! [m] Mean free path of air
    real(r8) :: vsc_dyn_atm             ! [kg m-1 s-1] Dynamic viscosity of air
    real(r8) :: vsc_knm_atm             ! [kg m-1 s-1] Kinematic viscosity of air
    real(r8) :: vlc_grv_old             ! [m s-1] Previous gravitational settling velocity
    real(r8) :: series_ratio            ! Factor for logarithmic grid
    real(r8) :: lngsdsqrttwopi_rcp      ! Factor in lognormal distribution
    real(r8) :: sz_min(sz_nbr)          ! [m] Size Bin minima
    real(r8) :: sz_max(sz_nbr)          ! [m] Size Bin maxima
    real(r8) :: sz_ctr(sz_nbr)          ! [m] Size Bin centers
    real(r8) :: sz_dlt(sz_nbr)          ! [m] Size Bin widths
    
    ! constants
    real(r8) :: dmt_vma_src(dst_src_nbr) =    &     ! [m] Mass median diameter
         (/ 0.832e-6_r8 , 4.82e-6_r8 , 19.38e-6_r8 /)        ! BSM96 p. 73 Table 2
    real(r8) :: gsd_anl_src(dst_src_nbr) =    &     ! [frc] Geometric std deviation
         (/ 2.10_r8     ,  1.90_r8   , 1.60_r8     /)        ! BSM96 p. 73 Table 2
    real(r8) :: mss_frc_src(dst_src_nbr) =    &     ! [frc] Mass fraction 
         (/ 0.036_r8, 0.957_r8, 0.007_r8 /)                  ! BSM96 p. 73 Table 2
    real(r8) :: dmt_grd(5) =                  &     ! [m] Particle diameter grid
         (/ 0.1e-6_r8, 1.0e-6_r8, 2.5e-6_r8, 5.0e-6_r8, 10.0e-6_r8 /)
    real(r8), parameter :: dmt_slt_opt = 75.0e-6_r8    ! [m] Optim diam for saltation
    real(r8), parameter :: dns_slt = 2650.0_r8         ! [kg m-3] Density of optimal saltation particles

    ! declare erf intrinsic function
    real(r8) :: dum     ! dummy variable for erf test
#if (defined AIX) 
#define ERF erf
#else
#define ERF derf
    real(r8) derf
#endif
    integer :: begp, endp   ! per-proc beginning and ending pft indices
    integer :: begc, endc   ! per-proc beginning and ending column indices 
    integer :: begl, endl   ! per-proc beginning and ending landunit indices
    integer :: begg, endg   ! per-proc gridcell ending gridcell indices
!------------------------------------------------------------------------

    ! Assign local pointers to derived type scalar members (column-level)

    mbl_bsn_fct => clm3%g%l%c%cps%mbl_bsn_fct

    ! Sanity check on erf: erf() in SGI /usr/lib64/mips4/libftn.so is bogus

    dum = 1.0_r8
    if (abs(0.8427_r8-ERF(dum))/0.8427_r8>0.001_r8) then
       write(iulog,*) 'erf(1.0) = ',ERF(dum)
       write(iulog,*) 'Dustini: Error function error'
       call endrun
    end if
    dum = 0.0_r8
    if (ERF(dum) /= 0.0_r8) then
       write(iulog,*) 'erf(0.0) = ',ERF(dum)
       write(iulog,*) 'Dustini: Error function error'
       call endrun
    end if

    ! the following comes from (1) szdstlgn.F subroutine ovr_src_snk_frc_get
    !                      and (2) dstszdst.F subroutine dst_szdst_ini
    ! purpose(1): given one set (the "source") of lognormal distributions,
    !             and one set of bin boundaries (the "sink"), compute and return
    !             the overlap factors between the source and sink distributions
    ! purpose(2): set important statistics of size distributions

    do m = 1, dst_src_nbr
       sqrt2lngsdi = sqrt(2.0_r8) * log(gsd_anl_src(m))
       do n = 1, ndst
          lndmaxjovrdmdni = log(dmt_grd(n+1)/dmt_vma_src(m))
          lndminjovrdmdni = log(dmt_grd(n  )/dmt_vma_src(m))
          ovr_src_snk_frc = 0.5_r8 * (ERF(lndmaxjovrdmdni/sqrt2lngsdi) - &
                                   ERF(lndminjovrdmdni/sqrt2lngsdi))
          ovr_src_snk_mss(m,n) = ovr_src_snk_frc * mss_frc_src(m)
       end do
    end do

    ! The following code from subroutine wnd_frc_thr_slt_get was placed 
    ! here because tmp1 needs to be defined just once

    ryn_nbr_frc_thr_prx_opt = 0.38_r8 + 1331.0_r8 * (100.0_r8*dmt_slt_opt)**1.56_r8

    if (ryn_nbr_frc_thr_prx_opt < 0.03_r8) then
       write(iulog,*) 'dstmbl: ryn_nbr_frc_thr_prx_opt < 0.03'
       call endrun
    else if (ryn_nbr_frc_thr_prx_opt < 10.0_r8) then
       ryn_nbr_frc_thr_opt_fnc = -1.0_r8 + 1.928_r8 * (ryn_nbr_frc_thr_prx_opt**0.0922_r8)
       ryn_nbr_frc_thr_opt_fnc = 0.1291_r8 * 0.1291_r8 / ryn_nbr_frc_thr_opt_fnc
    else
       ryn_nbr_frc_thr_opt_fnc = 1.0_r8 - 0.0858_r8 * exp(-0.0617_r8*(ryn_nbr_frc_thr_prx_opt-10.0_r8))
       ryn_nbr_frc_thr_opt_fnc = 0.120_r8 * 0.120_r8 * ryn_nbr_frc_thr_opt_fnc * ryn_nbr_frc_thr_opt_fnc
    end if

    icf_fct = 1.0_r8 + 6.0e-07_r8 / (dns_slt * grav * (dmt_slt_opt**2.5_r8))
    dns_fct = dns_slt * grav * dmt_slt_opt
    tmp1 = sqrt(icf_fct * dns_fct * ryn_nbr_frc_thr_opt_fnc)

    ! Set basin factor to 1 for now

    call get_proc_bounds(begg, endg, begl, endl, begc, endc, begp, endp)
    do c = begc, endc
      l = clm3%g%l%c%landunit(c)
      if (.not. clm3%g%l%lakpoi(l)) then
         mbl_bsn_fct(c) = 1.0_r8
      end if
    end do

    ! Introducing particle diameter. Needed by atm model and by dry dep model.
    ! Taken from Charlie Zender's subroutines dst_psd_ini, dst_sz_rsl,
    ! grd_mk (dstpsd.F90) and subroutine lgn_evl (psdlgn.F90)
    
    ! Charlie allows logarithmic or linear option for size distribution
    ! however, he hardwires the distribution to logarithmic in his code
    ! therefore, I take his logarithmic code only
    ! furthermore, if dst_nbr == 4, he overrides the automatic grid calculation
    ! he currently works with dst_nbr = 4, so I only take the relevant code
    ! if ndst ever becomes different from 4, must add call grd_mk (dstpsd.F90)
    ! as done in subroutine dst_psd_ini
    ! note that here ndst = dst_nbr
    
    ! Override automatic grid with preset grid if available

    if (ndst == 4) then
       do n = 1, ndst
          dmt_min(n) = dmt_grd(n)                       ![m] Max diameter in bin
          dmt_max(n) = dmt_grd(n+1)                     ![m] Min diameter in bin
          dmt_ctr(n) = 0.5_r8 * (dmt_min(n)+dmt_max(n)) ![m] Diameter at bin ctr
          dmt_dlt(n) = dmt_max(n)-dmt_min(n)            ![m] Width of size bin
       end do
    else
       write(iulog,*) 'Dustini error: ndst must equal to 4 with current code'
       call endrun                                      !see more comments above
    end if                                              !end if ndst == 4

    ! Bin physical properties

    gsd_anl = 2.0_r8      ! [frc] Geometric std dev PaG77 p. 2080 Table1
    ln_gsd = log(gsd_anl)
    dns_aer = 2.5e+3_r8   ! [kg m-3] Aerosol density

    ! Set a fundamental statistic for each bin

    dmt_vma = 3.5000e-6_r8 ! [m] Mass median diameter analytic She84 p.75 Table1

    ! Compute analytic size statistics
    ! Convert mass median diameter to number median diameter (call vma2nma)

    dmt_nma = dmt_vma * exp(-3.0_r8*ln_gsd*ln_gsd) ! [m]

    ! Compute resolved size statistics for each size distribution
    ! In C. Zender's code call dst_sz_rsl

    do n = 1, ndst

       series_ratio = (dmt_max(n)/dmt_min(n))**(1.0_r8/sz_nbr)
       sz_min(1) = dmt_min(n)
       do m = 2, sz_nbr                            ! Loop starts at 2
          sz_min(m) = sz_min(m-1) * series_ratio
       end do

       ! Derived grid values
       do m = 1, sz_nbr-1                          ! Loop ends at sz_nbr-1
          sz_max(m) = sz_min(m+1)                  ! [m]
       end do
       sz_max(sz_nbr) = dmt_max(n)                 ! [m]

       ! Final derived grid values
       do m = 1, sz_nbr
          sz_ctr(m) = 0.5_r8 * (sz_min(m)+sz_max(m))
          sz_dlt(m) = sz_max(m)-sz_min(m)
       end do

       lngsdsqrttwopi_rcp = 1.0_r8 / (ln_gsd*sqrt(2.0_r8*SHR_CONST_PI))
       dmt_vwr(n) = 0.0_r8 ! [m] Mass wgted diameter resolved
       vlm_rsl(n) = 0.0_r8 ! [m3 m-3] Volume concentration resolved

       do m = 1, sz_nbr

          ! Evaluate lognormal distribution for these sizes (call lgn_evl)
          tmp = log(sz_ctr(m)/dmt_nma) / ln_gsd
          lgn_dst = lngsdsqrttwopi_rcp * exp(-0.5_r8*tmp*tmp) / sz_ctr(m)

          ! Integrate moments of size distribution
          dmt_vwr(n) = dmt_vwr(n) + sz_ctr(m) *                    &
               SHR_CONST_PI / 6.0_r8 * (sz_ctr(m)**3.0_r8) * & ![m3] Volume
               lgn_dst * sz_dlt(m)                ![# m-3] Number concentrn
          vlm_rsl(n) = vlm_rsl(n) +                                &
               SHR_CONST_PI / 6.0_r8 * (sz_ctr(m)**3.0_r8) * & ![m3] Volume
               lgn_dst * sz_dlt(m)                ![# m-3] Number concentrn

       end do

       dmt_vwr(n) = dmt_vwr(n) / vlm_rsl(n) ![m] Mass weighted diameter resolved

    end do

    ! calculate correction to Stokes' settling velocity (subroutine stk_crc_get)

    eps_max = 1.0e-4_r8
    dns_mdp = 100000._r8 / (295.0_r8*SHR_CONST_RDAIR) ![kg m-3] const prs_mdp & tpt_vrt

    ! Size-independent thermokinetic properties

    vsc_dyn_atm = 1.72e-5_r8 * ((295.0_r8/273.0_r8)**1.5_r8) * 393.0_r8 / &
         (295.0_r8+120.0_r8)      ![kg m-1 s-1] RoY94 p.102 tpt_mdp=295.0
    mfp_atm = 2.0_r8 * vsc_dyn_atm / &  !SeP97 p. 455 constant prs_mdp, tpt_mdp
         (100000._r8*sqrt(8.0_r8/(SHR_CONST_PI*SHR_CONST_RDAIR*295.0_r8)))
    vsc_knm_atm = vsc_dyn_atm / dns_mdp ![m2 s-1] Kinematic viscosity of air

    do m = 1, ndst
       slp_crc(m) = 1.0_r8 + 2.0_r8 * mfp_atm *                      &
            (1.257_r8+0.4_r8*exp(-1.1_r8*dmt_vwr(m)/(2.0_r8*mfp_atm))) / &
            dmt_vwr(m)                      ! [frc] Slip correction factor SeP97 p.464
       vlc_stk(m) = (1.0_r8/18.0_r8) * dmt_vwr(m) * dmt_vwr(m) * dns_aer * &
            grav * slp_crc(m) / vsc_dyn_atm ! [m s-1] SeP97 p.466
    end do

    ! For Reynolds number flows Re < 0.1 Stokes' velocity is valid for
    ! vlc_grv SeP97 p. 466 (8.42). For larger Re, inertial effects become
    ! important and empirical drag coefficients must be employed
    ! Implicit equation for Re, Cd, and Vt is SeP97 p. 467 (8.44)
    ! Using Stokes' velocity rather than iterative solution with empirical
    ! drag coefficient causes 60% errors for D = 200 um SeP97 p. 468

    ! Iterative solution for drag coefficient, Reynolds number, and terminal veloc
    do m = 1, ndst

       ! Initialize accuracy and counter
       eps_crr = eps_max + 1.0_r8  ![frc] Current relative accuracy
       itr_idx = 0                 ![idx] Counting index

       ! Initial guess for vlc_grv is exact for Re < 0.1
       vlc_grv(m) = vlc_stk(m)     ![m s-1]

       do while(eps_crr > eps_max)

          ! Save terminal velocity for convergence test
          vlc_grv_old = vlc_grv(m) ![m s-1]
          ryn_nbr_grv(m) = vlc_grv(m) * dmt_vwr(m) / vsc_knm_atm !SeP97 p.460

          ! Update drag coefficient based on new Reynolds number
          if (ryn_nbr_grv(m) < 0.1_r8) then
             cff_drg_grv(m) = 24.0_r8 / ryn_nbr_grv(m) !Stokes' law Sep97 p.463 (8.32)
          else if (ryn_nbr_grv(m) < 2.0_r8) then
             cff_drg_grv(m) = (24.0_r8/ryn_nbr_grv(m)) *    &
                  (1.0_r8 + 3.0_r8*ryn_nbr_grv(m)/16.0_r8 + &
                  9.0_r8*ryn_nbr_grv(m)*ryn_nbr_grv(m)*     &
                  log(2.0_r8*ryn_nbr_grv(m))/160.0_r8)        !Sep97 p.463 (8.32)
          else if (ryn_nbr_grv(m) < 500.0_r8) then
             cff_drg_grv(m) = (24.0_r8/ryn_nbr_grv(m)) * &
                  (1.0_r8 + 0.15_r8*ryn_nbr_grv(m)**0.687_r8) !Sep97 p.463 (8.32)
          else if (ryn_nbr_grv(m) < 2.0e5_r8) then
             cff_drg_grv(m) = 0.44_r8                         !Sep97 p.463 (8.32)
          else
             write(iulog,'(a,es9.2)') "ryn_nbr_grv(m) = ",ryn_nbr_grv(m)
             write(iulog,*)'Dustini error: Reynolds number too large in stk_crc_get()'
             call endrun 
          end if

          ! Update terminal velocity based on new Reynolds number and drag coeff
          ! [m s-1] Terminal veloc SeP97 p.467 (8.44)

          vlc_grv(m) = sqrt(4.0_r8 * grav * dmt_vwr(m) * slp_crc(m) * dns_aer / &
               (3.0_r8*cff_drg_grv(m)*dns_mdp))   
          eps_crr = abs((vlc_grv(m)-vlc_grv_old)/vlc_grv(m)) !Relative convergence
          if (itr_idx == 12) then
             ! Numerical pingpong may occur when Re = 0.1, 2.0, or 500.0
             ! due to discontinuities in derivative of drag coefficient
             vlc_grv(m) = 0.5_r8 * (vlc_grv(m)+vlc_grv_old)  ! [m s-1]
          end if
          if (itr_idx > 20) then
             write(iulog,*) 'Dustini error: Terminal velocity not converging ',&
                  ' in stk_crc_get(), breaking loop...'
             goto 100                                        !to next iteration
          end if
          itr_idx = itr_idx + 1

       end do                                                !end while

100    continue   !Label to jump to when iteration does not converge
    end do   !end loop over size

    ! Compute factors to convert Stokes' settling velocities to
    ! actual settling velocities

    do m = 1, ndst
       stk_crc(m) = vlc_grv(m) / vlc_stk(m)
    end do

  end subroutine Dustini

#endif

end module DUSTMod