module hb_diff 2,5
  !---------------------------------------------------------------------------------
  ! Module to compute mixing coefficients associated with turbulence in the 
  ! planetary boundary layer and elsewhere.  PBL coefficients are based on Holtslag 
  ! and Boville, 1991.
  !
  ! Public interfaces:
  !    init_hb_diff     initializes time independent coefficients
  !    compute_hb_diff  computes eddy diffusivities and counter-gradient fluxes
  !
  ! Private methods:
  !       trbintd         initializes time dependent variables
  !       pblintd         initializes time dependent variables that depend pbl depth
  !       austausch_atm   computes free atmosphere exchange coefficients
  !       austausch_pbl   computes pbl exchange coefficients
  !
  !---------------------------Code history--------------------------------
  ! Standardized:      J. Rosinski, June 1992
  ! Reviewed:          P. Rasch, B. Boville, August 1992
  ! Reviewed:          P. Rasch, April 1996
  ! Reviewed:          B. Boville, April 1996
  ! rewritten:         B. Boville, May 2000
  ! rewritten:         B. Stevens, August 2000
  ! modularized:       J. McCaa, September 2004
  !---------------------------------------------------------------------------------
  use shr_kind_mod, only: r8 => shr_kind_r8
  use spmd_utils,   only: masterproc          ! output from hb_init should be eliminated
  use ppgrid,       only: pver, pverp, pcols  ! these should be passed in
  use constituents, only: pcnst               ! these should be passed in
  use cam_logfile,  only: iulog

  implicit none
  private
  save

  ! Public interfaces
  public init_hb_diff
  public compute_hb_diff
  !
  ! PBL limits
  !
  real(r8), parameter :: ustar_min = 0.01_r8        ! min permitted value of ustar
  real(r8), parameter :: pblmaxp   = 4.e4_r8        ! pbl max depth in pressure units
  real(r8), parameter :: zkmin     = 0.01_r8        ! Minimum kneutral*f(ri)
  !
  ! PBL Parameters
  !
  real(r8), parameter :: onet  = 1._r8/3._r8 ! 1/3 power in wind gradient expression
  real(r8), parameter :: betam = 15.0_r8  ! Constant in wind gradient expression
  real(r8), parameter :: betas =  5.0_r8  ! Constant in surface layer gradient expression
  real(r8), parameter :: betah = 15.0_r8  ! Constant in temperature gradient expression 
  real(r8), parameter :: fakn  =  7.2_r8  ! Constant in turbulent prandtl number
  real(r8), parameter :: fak   =  8.5_r8  ! Constant in surface temperature excess         
  real(r8), parameter :: ricr  =  0.3_r8  ! Critical richardson number
  real(r8), parameter :: sffrac=  0.1_r8  ! Surface layer fraction of boundary layer
  real(r8), parameter :: binm  = betam*sffrac       ! betam * sffrac
  real(r8), parameter :: binh  = betah*sffrac       ! betah * sffrac
  !
  ! Pbl constants set using values from other parts of code
  !
  real(r8) :: cpair      ! Specific heat of dry air
  real(r8) :: rair       ! Gas const for dry air
  real(r8) :: zvir       ! rh2o/rair - 1
  real(r8) :: g          ! Gravitational acceleration
  real(r8) :: ml2(pverp) ! Mixing lengths squared
  real(r8) :: vk         ! Von Karman's constant
  real(r8) :: ccon       ! fak * sffrac * vk

  integer :: npbl       ! Maximum number of levels in pbl from surface
  integer :: ntop_turb  ! Top level to which turbulent vertical diffusion is applied.
  integer :: nbot_turb  ! Bottom level to which turbulent vertical diff is applied.

CONTAINS
  !
  !===============================================================================

  subroutine init_hb_diff(gravx,      cpairx, rairx, zvirx, ntop_eddy,  & 1
       nbot_eddy,  hypm,   vkx , eddy_scheme)
    !----------------------------------------------------------------------- 
    ! 
    ! Purpose:  
    ! Initialize time independent variables of turbulence/pbl package.
    ! 
    ! Author: B. Boville, B. Stevens (August 2000)
    ! 
    !-----------------------------------------------------------------------
    !------------------------------Arguments--------------------------------
    real(r8), intent(in) :: gravx     ! acceleration of gravity
    real(r8), intent(in) :: cpairx    ! specific heat of dry air
    real(r8), intent(in) :: rairx     ! gas constant for dry air
    real(r8), intent(in) :: zvirx     ! rh2o/rair - 1
    real(r8), intent(in) :: hypm(pver)! reference pressures at midpoints
    real(r8), intent(in) :: vkx       ! Von Karman's constant
    integer, intent(in)  :: ntop_eddy ! Top level to which eddy vert diff is applied.
    integer, intent(in)  :: nbot_eddy ! Bottom level to which eddy vert diff is applied.
    character(len=16),  intent(in) :: eddy_scheme

    !---------------------------Local workspace-----------------------------
    integer :: k                     ! vertical loop index
    !-----------------------------------------------------------------------
    !
    ! Basic constants
    !
    cpair = cpairx
    rair  = rairx
    g     = gravx
    zvir  = zvirx
    vk    = vkx
    ccon  = fak*sffrac*vk
    ntop_turb = ntop_eddy
    nbot_turb = nbot_eddy
    !
    ! Set the square of the mixing lengths.
    !
    ml2(ntop_turb) = 0._r8
    do k = ntop_turb+1, nbot_turb
       ml2(k) = 30.0_r8**2                 ! HB scheme: length scale = 30m  
       if  ( eddy_scheme .eq. 'HBR' ) then      
          ml2(k) = 1.0_r8**2               ! HBR scheme: length scale = 1m  
       end if
    end do
    ml2(nbot_turb+1) = 0._r8
    !
    ! Limit pbl height to regions below 400 mb
    ! npbl = max number of levels (from bottom) in pbl
    !
    npbl = 0
    do k=nbot_turb,ntop_turb,-1
       if (hypm(k) >= pblmaxp) then
          npbl = npbl + 1
       end if
    end do
    npbl = max(npbl,1)

    if (masterproc) then
       write(iulog,*)'INIT_HB_DIFF: PBL height will be limited to bottom ',npbl, &
            ' model levels. Top is ',hypm(pverp-npbl),' pascals'
    end if

    return
  end subroutine init_hb_diff
  !
  !===============================================================================

  subroutine compute_hb_diff(lchnk, ncol,            & 1,4
       th      ,t       ,q       ,z       ,zi      , &
       pmid    ,u       ,v       ,taux    ,tauy    , &
       shflx   ,cflx    ,obklen  ,ustar   ,pblh    , &
       kvm     ,kvh     ,kvq     ,cgh     ,cgs     , &
       tpert   ,qpert   ,cldn    ,ocnfrac ,tke     , &
       eddy_scheme)
    !----------------------------------------------------------------------- 
    ! 
    ! Purpose: 
    !  Interface routines for calcualtion and diatnostics of turbulence related
    !  coefficients
    !
    ! Author: B. Stevens (rewrite August 2000)
    ! 
    !-----------------------------------------------------------------------
    !------------------------------Arguments--------------------------------
    !
    ! Input arguments
    !
    integer, intent(in) :: lchnk                      ! chunk index (for debug only)
    integer, intent(in) :: ncol                       ! number of atmospheric columns

    real(r8), intent(in)  :: th(pcols,pver)           ! potential temperature [K]
    real(r8), intent(in)  :: t(pcols,pver)            ! temperature (used for density)
    real(r8), intent(in)  :: q(pcols,pver,pcnst)      ! specific humidity [kg/kg]
    real(r8), intent(in)  :: z(pcols,pver)            ! height above surface [m]
    real(r8), intent(in)  :: zi(pcols,pverp)          ! height above surface [m]
    real(r8), intent(in)  :: u(pcols,pver)            ! zonal velocity
    real(r8), intent(in)  :: v(pcols,pver)            ! meridional velocity
    real(r8), intent(in)  :: taux(pcols)              ! zonal stress [N/m2]
    real(r8), intent(in)  :: tauy(pcols)              ! meridional stress [N/m2]
    real(r8), intent(in)  :: shflx(pcols)             ! sensible heat flux
    real(r8), intent(in)  :: cflx(pcols,pcnst)        ! constituent flux
    real(r8), intent(in)  :: pmid(pcols,pver)         ! midpoint pressures
    real(r8), intent(in)  :: cldn(pcols,pver)         ! new cloud fraction
    real(r8), intent(in)  :: ocnfrac(pcols)           ! Land fraction
    character(len=16), intent(in) :: eddy_scheme

    !
    ! Output arguments
    !
    real(r8) :: kqfs(pcols,pcnst)                     ! kinematic surf constituent flux (kg/m2/s)
    real(r8), intent(out) :: kvm(pcols,pverp)         ! eddy diffusivity for momentum [m2/s]
    real(r8), intent(out) :: kvh(pcols,pverp)         ! eddy diffusivity for heat [m2/s]
    real(r8), intent(out) :: kvq(pcols,pverp)         ! eddy diffusivity for constituents [m2/s]
    real(r8), intent(out) :: cgh(pcols,pverp)         ! counter-gradient term for heat [J/kg/m]
    real(r8), intent(out) :: cgs(pcols,pverp)         ! counter-gradient star (cg/flux)
    real(r8), intent(out) :: tpert(pcols)             ! convective temperature excess
    real(r8), intent(out) :: qpert(pcols)             ! convective humidity excess
    real(r8), intent(out) :: ustar(pcols)             ! surface friction velocity [m/s]
    real(r8), intent(out) :: obklen(pcols)            ! Obukhov length
    real(r8), intent(out) :: pblh(pcols)              ! boundary-layer height [m]
    real(r8), intent(out) :: tke(pcols,pverp)         ! turbulent kinetic energy (estimated)

    integer :: ktopbl(pcols)            ! index of first midpoint inside pbl
    integer :: ktopblmn                 ! min value of ktopbl
    !
    !---------------------------Local workspace-----------------------------
    !
    real(r8) :: wstar(pcols)            ! convective velocity scale [m/s]
    real(r8) :: khfs(pcols)             ! kinimatic surface heat flux 
    real(r8) :: kbfs(pcols)             ! surface buoyancy flux 
    real(r8) :: kvf(pcols,pverp)        ! free atmospheric eddy diffsvty [m2/s]
    real(r8) :: s2(pcols,pver)          ! shear squared
    real(r8) :: n2(pcols,pver)          ! brunt vaisaila frequency
    real(r8) :: ri(pcols,pver)          ! richardson number: n2/s2
    real(r8) :: bge(pcols)              ! buoyancy gradient enhancment
    !
    ! Initialize time dependent variables that do not depend on pbl height
    !
    call trbintd(ncol    ,                            &
         th      ,q       ,z       ,u       ,v       , &
         t       ,pmid    ,cflx    ,shflx   ,taux    , &
         tauy    ,ustar   ,obklen  ,kqfs    ,khfs    , &
         kbfs    ,s2      ,n2      ,ri      )
    !
    ! Initialize time dependent variables that do depend on pbl height
    !
    call  pblintd(ncol    ,                            &
         th      ,q       ,z       ,u       ,v       , &
         ustar   ,obklen  ,kbfs    ,pblh    ,wstar   , &
         zi      ,cldn    ,ocnfrac ,bge     )
    !
    ! Get free atmosphere exchange coefficients
    !
    call austausch_atm(ncol    ,ri      ,s2      ,kvf     )
    ! 
    ! Get pbl exchange coefficients
    !
    call austausch_pbl(lchnk, ncol,                    &
         z       ,kvf     ,kqfs    ,khfs    ,kbfs    , &
         obklen  ,ustar   ,wstar   ,pblh    ,kvm     , &
         kvh     ,cgh     ,cgs     ,tpert   ,qpert   , &
         ktopbl  ,ktopblmn,tke     ,bge     ,eddy_scheme)
    !

    kvq(:ncol,:) = kvh(:ncol,:)
    return
  end subroutine compute_hb_diff
  !
  !===============================================================================

  subroutine trbintd(ncol    ,                            & 2,2
       th      ,q       ,z       ,u       ,v       , &
       t       ,pmid    ,cflx    ,shflx   ,taux    , &
       tauy    ,ustar   ,obklen  ,kqfs    ,khfs    , &
       kbfs    ,s2      ,n2      ,ri      )
    !----------------------------------------------------------------------- 
    ! 
    ! Purpose: 
    !  Time dependent initialization
    ! 
    ! Method: 
    !  Diagnosis of variables that do not depend on mixing assumptions or
    !  PBL depth
    !
    ! Author: B. Stevens (extracted from pbldiff, August, 2000)
    ! 
    !-----------------------------------------------------------------------
    !------------------------------Arguments--------------------------------
    !
    ! Input arguments
    !
    integer, intent(in) :: ncol                      ! number of atmospheric columns

    real(r8), intent(in)  :: th(pcols,pver)          ! potential temperature [K]
    real(r8), intent(in)  :: q(pcols,pver,pcnst)     ! specific humidity [kg/kg]
    real(r8), intent(in)  :: z(pcols,pver)           ! height above surface [m]
    real(r8), intent(in)  :: u(pcols,pver)           ! windspeed x-direction [m/s]
    real(r8), intent(in)  :: v(pcols,pver)           ! windspeed y-direction [m/s]
    real(r8), intent(in)  :: t(pcols,pver)           ! temperature (used for density)
    real(r8), intent(in)  :: pmid(pcols,pver)        ! midpoint pressures
    real(r8), intent(in)  :: cflx(pcols,pcnst)       ! surface constituent flux (kg/m2/s)
    real(r8), intent(in)  :: shflx(pcols)            ! surface heat flux (W/m2)
    real(r8), intent(in)  :: taux(pcols)             ! surface u stress [N/m2]
    real(r8), intent(in)  :: tauy(pcols)             ! surface v stress [N/m2]

    !
    ! Output arguments
    !
    real(r8), intent(out) :: ustar(pcols)            ! surface friction velocity [m/s]
    real(r8), intent(out) :: obklen(pcols)           ! Obukhov length
    real(r8), intent(out) :: khfs(pcols)             ! surface kinematic heat flux [mK/s]
    real(r8), intent(out) :: kbfs(pcols)             ! sfc kinematic buoyancy flux [m^2/s^3]
    real(r8), intent(out) :: kqfs(pcols,pcnst)       ! sfc kinematic constituent flux [m/s]
    real(r8), intent(out) :: s2(pcols,pver)          ! shear squared
    real(r8), intent(out) :: n2(pcols,pver)          ! brunt vaisaila frequency
    real(r8), intent(out) :: ri(pcols,pver)          ! richardson number: n2/s2
    !
    !---------------------------Local workspace-----------------------------
    !
    integer  :: i                        ! longitude index
    integer  :: k                        ! level index
    integer  :: m                        ! constituent index

    real(r8) :: thvsrf(pcols)            ! sfc (bottom) level virtual temperature
    real(r8) :: thv(pcols,pver)          ! bulk Richardson no. from level to ref lev
    real(r8) :: rrho(pcols)              ! 1./bottom level density (temporary)
    real(r8) :: vvk                      ! velocity magnitude squared
    real(r8) :: dvdz2                    ! velocity shear squared
    real(r8) :: dz                       ! delta z between midpoints
    !
    ! Compute ustar, and kinematic surface fluxes from surface energy fluxes
    !
    do i=1,ncol
       rrho(i)   = rair*t(i,pver)/pmid(i,pver)
       ustar(i)  = max(sqrt(sqrt(taux(i)**2 + tauy(i)**2)*rrho(i)),ustar_min)
       khfs(i)   = shflx(i)*rrho(i)/cpair
    end do
    do m=1,pcnst
       do i=1,ncol
          kqfs(i,m)= cflx(i,m)*rrho(i)
       end do
    end do
    !
    ! Compute Obukhov length virtual temperature flux and various arrays for use later:
    !
    do i=1,ncol
       kbfs(i)      = khfs(i) + 0.61_r8*th(i,pver)*kqfs(i,1)
       thvsrf(i)    = th(i,pver)*(1.0_r8 + 0.61_r8*q(i,pver,1))
       obklen(i)    = -thvsrf(i)*ustar(i)**3/(g*vk*(kbfs(i) + sign(1.e-10_r8,kbfs(i))))
    end do
    !
    ! Compute shear squared (s2), brunt vaisaila frequency (n2) and related richardson
    ! number (ri).  For the n2 calcualtion use the dry theta_v derived from virtem
    !
    call virtem(ncol, pcols, pver, th      ,q(1,1,1),0.61_r8 ,thv)
    do k=ntop_turb,nbot_turb-1
       do i=1,ncol
          dvdz2   = (u(i,k)-u(i,k+1))**2 + (v(i,k)-v(i,k+1))**2
          dvdz2   = max(dvdz2,1.e-36_r8)
          dz      = z(i,k) - z(i,k+1)
          s2(i,k) = dvdz2/(dz**2)
          n2(i,k) = g*2.0_r8*( thv(i,k) - thv(i,k+1))/((thv(i,k) + thv(i,k+1))*dz)
          ri(i,k) = n2(i,k)/s2(i,k)
       end do
    end do

    return
  end subroutine trbintd
  !
  !===============================================================================

  subroutine pblintd(ncol    ,                            & 1
       th      ,q       ,z       ,u       ,v       , &
       ustar   ,obklen  ,kbfs    ,pblh    ,wstar   , &
       zi      ,cldn    ,ocnfrac ,bge     )
    !----------------------------------------------------------------------- 
    ! 
    ! Purpose: 
    ! Diagnose standard PBL variables
    ! 
    ! Method: 
    ! Diagnosis of PBL depth and related variables.  In this case only wstar.
    ! The PBL depth follows:
    !    Holtslag, A.A.M., and B.A. Boville, 1993:
    !    Local versus Nonlocal Boundary-Layer Diffusion in a Global Climate
    !    Model. J. Clim., vol. 6., p. 1825--1842.
    !
    ! Updated by Holtslag and Hack to exclude the surface layer from the
    ! definition of the boundary layer Richardson number. Ri is now defined
    ! across the outer layer of the pbl (between the top of the surface
    ! layer and the pbl top) instead of the full pbl (between the surface and
    ! the pbl top). For simiplicity, the surface layer is assumed to be the
    ! region below the first model level (otherwise the boundary layer depth
    ! determination would require iteration).
    !
    ! Modified for boundary layer height diagnosis: Bert Holtslag, june 1994
    ! >>>>>>>>>  (Use ricr = 0.3 in this formulation)
    ! 
    ! Author: B. Stevens (extracted from pbldiff, August 2000)
    ! 
    !-----------------------------------------------------------------------
    !------------------------------Arguments--------------------------------
    !
    ! Input arguments
    !
    integer, intent(in) :: ncol                      ! number of atmospheric columns

    real(r8), intent(in)  :: th(pcols,pver)          ! potential temperature [K]
    real(r8), intent(in)  :: q(pcols,pver,pcnst)     ! specific humidity [kg/kg]
    real(r8), intent(in)  :: z(pcols,pver)           ! height above surface [m]
    real(r8), intent(in)  :: u(pcols,pver)           ! windspeed x-direction [m/s]
    real(r8), intent(in)  :: v(pcols,pver)           ! windspeed y-direction [m/s]
    real(r8), intent(in)  :: ustar(pcols)            ! surface friction velocity [m/s]
    real(r8), intent(in)  :: obklen(pcols)           ! Obukhov length
    real(r8), intent(in)  :: kbfs(pcols)             ! sfc kinematic buoyancy flux [m^2/s^3]
    real(r8), intent(in)  :: zi(pcols,pverp)         ! height above surface [m]
    real(r8), intent(in)  :: cldn(pcols,pver)        ! new cloud fraction
    real(r8), intent(in)  :: ocnfrac(pcols)          ! Land fraction

    !
    ! Output arguments
    !
    real(r8), intent(out) :: wstar(pcols)            ! convective sclae velocity [m/s]
    real(r8), intent(out) :: pblh(pcols)             ! boundary-layer height [m]
    real(r8), intent(out) :: bge(pcols)              ! buoyancy gradient enhancment
    !
    !---------------------------Local parameters----------------------------
    !
    real(r8), parameter   :: tiny = 1.e-36_r8           ! lower bound for wind magnitude
    real(r8), parameter   :: fac  = 100._r8             ! ustar parameter in height diagnosis 
    !
    !---------------------------Local workspace-----------------------------
    !
    integer  :: i                       ! longitude index
    integer  :: k                       ! level index

    real(r8) :: thvref(pcols)           ! reference level virtual temperature
    real(r8) :: phiminv(pcols)          ! inverse phi function for momentum
    real(r8) :: phihinv(pcols)          ! inverse phi function for heat
    real(r8) :: rino(pcols,pver)        ! bulk Richardson no. from level to ref lev
    real(r8) :: tlv(pcols)              ! ref. level pot tmp + tmp excess
    real(r8) :: tkv                     ! model level potential temperature
    real(r8) :: vvk                     ! velocity magnitude squared
    real(r8) :: tkvb, tkvt              ! model level potential temperature

    logical  :: unstbl(pcols)           ! pts w/unstbl pbl (positive virtual ht flx)
    logical  :: check(pcols)            ! True=>chk if Richardson no.>critcal
    logical  :: ocncldcheck(pcols)      ! True=>if ocean surface and cloud in lowest layer
    !
    ! Compute Obukhov length virtual temperature flux and various arrays for use later:
    !
    do i=1,ncol
       check(i)     = .true.
       rino(i,pver) = 0.0_r8
       thvref(i)    = th(i,pver)*(1.0_r8 + 0.61_r8*q(i,pver,1))
       pblh(i)      = z(i,pver)
    end do
    !
    !
    ! PBL height calculation:  Scan upward until the Richardson number between
    ! the first level and the current level exceeds the "critical" value.
    !
    do k=pver-1,pver-npbl+1,-1
       do i=1,ncol
          if (check(i)) then
             vvk = (u(i,k) - u(i,pver))**2 + (v(i,k) - v(i,pver))**2 + fac*ustar(i)**2
             vvk = max(vvk,tiny)
             tkv = th(i,k)*(1._r8 + 0.61_r8*q(i,k,1))
             rino(i,k) = g*(tkv - thvref(i))*(z(i,k)-z(i,pver))/(thvref(i)*vvk)
             if (rino(i,k) >= ricr) then
                pblh(i) = z(i,k+1) + (ricr - rino(i,k+1))/(rino(i,k) - rino(i,k+1)) * &
                     (z(i,k) - z(i,k+1))
                check(i) = .false.
             end if
          end if
       end do
    end do
    !
    ! Estimate an effective surface temperature to account for surface fluctuations
    !
    do i=1,ncol
       if (check(i)) pblh(i) = z(i,pverp-npbl)
       unstbl(i) = (kbfs(i) > 0._r8)
       check(i)  = (kbfs(i) > 0._r8)
       if (check(i)) then
          phiminv(i)   = (1._r8 - binm*pblh(i)/obklen(i))**onet
          rino(i,pver) = 0.0_r8
          tlv(i)       = thvref(i) + kbfs(i)*fak/( ustar(i)*phiminv(i) )
       end if
    end do
    !
    ! Improve pblh estimate for unstable conditions using the convective temperature excess:
    !
    do i = 1,ncol
       bge(i) = 1.e-8
    end do
    do k=pver-1,pver-npbl+1,-1
       do i=1,ncol
          if (check(i)) then
             vvk = (u(i,k) - u(i,pver))**2 + (v(i,k) - v(i,pver))**2 + fac*ustar(i)**2
             vvk = max(vvk,tiny)
             tkv = th(i,k)*(1._r8 + 0.61_r8*q(i,k,1))
             rino(i,k) = g*(tkv - tlv(i))*(z(i,k)-z(i,pver))/(thvref(i)*vvk)
             if (rino(i,k) >= ricr) then
                pblh(i) = z(i,k+1) + (ricr - rino(i,k+1))/(rino(i,k) - rino(i,k+1))* &
                     (z(i,k) - z(i,k+1))
                tkvt = th(i,k)*(1._r8 + 0.61_r8*q(i,k,1))
                tkvb = th(i,k+1)*(1._r8 + 0.61_r8*q(i,k+1,1))
                bge(i) = 2.*g/(tkvt+tkvb)*(tkvt-tkvb)/(z(i,k)-z(i,k+1))*pblh(i)
                if (bge(i).lt.0.) then
                   bge(i) = 1.e-8
                endif
                check(i) = .false.
             end if
          end if
       end do
    end do
    !
    ! PBL height must be greater than some minimum mechanical mixing depth
    ! Several investigators have proposed minimum mechanical mixing depth
    ! relationships as a function of the local friction velocity, u*.  We
    ! make use of a linear relationship of the form h = c u* where c=700.
    ! The scaling arguments that give rise to this relationship most often
    ! represent the coefficient c as some constant over the local coriolis
    ! parameter.  Here we make use of the experimental results of Koracin
    ! and Berkowicz (1988) [BLM, Vol 43] for wich they recommend 0.07/f
    ! where f was evaluated at 39.5 N and 52 N.  Thus we use a typical mid
    ! latitude value for f so that c = 0.07/f = 700.  Also, do not allow 
    ! PBL to exceed some maximum (npbl) number of allowable points
    !
    do i=1,ncol
       if (check(i)) pblh(i) = z(i,pverp-npbl)
       pblh(i) = max(pblh(i),700.0_r8*ustar(i))
       wstar(i) = (max(0._r8,kbfs(i))*g*pblh(i)/thvref(i))**onet
    end do
    !
    ! Final requirement on PBL heightis that it must be greater than the depth
    ! of the lowest model level over ocean if there is any cloud diagnosed in 
    ! the lowest model level.  This is to deal with the inadequacies of the 
    ! current "dry" formulation of the boundary layer, where this test is 
    ! used to identify circumstances where there is marine stratus in the 
    ! lowest level, and to provide a weak ventilation of the layer to avoid
    ! a pathology in the cloud scheme (locking in low-level stratiform cloud)
    ! If over an ocean surface, and any cloud is diagnosed in the 
    ! lowest level, set pblh to 50 meters higher than top interface of lowest level
    !
    !  jrm This is being applied everywhere (not just ocean)!
    do i=1,ncol
       ocncldcheck(i) = .false.
       if (cldn(i,pver).ge.0.0_r8) ocncldcheck(i) = .true.
       if (ocncldcheck(i)) pblh(i) = max(pblh(i),zi(i,pver) + 50._r8)
    end do
    !
    return
  end subroutine pblintd
  !
  !===============================================================================

  subroutine austausch_atm(ncol    ,ri      ,s2      ,kvf     ) 2
    !----------------------------------------------------------------------- 
    ! 
    ! Purpose: 
    !  Computes exchange coefficients for free turbulent flows. 
    ! 
    ! Method: 
    !
    ! The free atmosphere diffusivities are based on standard mixing length
    ! forms for the neutral diffusivity multiplied by functns of Richardson
    ! number. K = l^2 * |dV/dz| * f(Ri). The same functions are used for
    ! momentum, potential temperature, and constitutents. 
    !
    ! The stable Richardson num function (Ri>0) is taken from Holtslag and
    ! Beljaars (1989), ECMWF proceedings. f = 1 / (1 + 10*Ri*(1 + 8*Ri))
    ! The unstable Richardson number function (Ri<0) is taken from  CCM1.
    ! f = sqrt(1 - 18*Ri)
    ! 
    ! Author: B. Stevens (rewrite, August 2000)
    ! 
    !-----------------------------------------------------------------------
    !------------------------------Arguments--------------------------------
    !
    ! Input arguments
    !
    integer, intent(in) :: ncol                     ! number of atmospheric columns

    real(r8), intent(in)  ::  s2(pcols,pver)        ! shear squared
    real(r8), intent(in)  ::  ri(pcols,pver)        ! richardson no
    !
    ! Output arguments
    !
    real(r8), intent(out) :: kvf(pcols,pverp)       ! coefficient for heat and tracers
    !
    !---------------------------Local workspace-----------------------------
    !
    real(r8) :: fofri                  ! f(ri)
    real(r8) :: kvn                    ! neutral Kv

    integer  :: i                      ! longitude index
    integer  :: k                      ! vertical index
    !
    !-----------------------------------------------------------------------
    !
    ! The surface diffusivity is always zero
    !
    kvf(:ncol,pverp) = 0.0_r8
    !
    ! Set the vertical diffusion coefficient above the top diffusion level
    ! Note that nbot_turb != pver is not supported
    !
    kvf(:ncol,1:ntop_turb) = 0.0_r8
    !
    ! Compute the free atmosphere vertical diffusion coefficients: kvh = kvq = kvm. 
    !
    do k = ntop_turb, nbot_turb-1
       do i=1,ncol
          if (ri(i,k) < 0.0_r8) then
             fofri = sqrt(max(1._r8 - 18._r8*ri(i,k),0._r8))
          else 
             fofri = 1.0_r8/(1.0_r8 + 10.0_r8*ri(i,k)*(1.0_r8 + 8.0_r8*ri(i,k)))    
          end if
          kvn = ml2(k)*sqrt(s2(i,k))
          kvf(i,k+1) = max(zkmin,kvn*fofri)
       end do
    end do

    return
  end subroutine austausch_atm
  !
  !===============================================================================

  subroutine austausch_pbl(lchnk ,ncol    ,          & 1,2
       z       ,kvf     ,kqfs    ,khfs    ,kbfs    , &
       obklen  ,ustar   ,wstar   ,pblh    ,kvm     , &
       kvh     ,cgh     ,cgs     ,tpert   ,qpert   , &
       ktopbl  ,ktopblmn,tke     ,bge     ,eddy_scheme)
    !----------------------------------------------------------------------- 
    ! 
    ! Purpose: 
    ! Atmospheric Boundary Layer Computation
    ! 
    ! Method: 
    ! Nonlocal scheme that determines eddy diffusivities based on a
    ! specified boundary layer height and a turbulent velocity scale;
    ! also, countergradient effects for heat and moisture, and constituents
    ! are included, along with temperature and humidity perturbations which
    ! measure the strength of convective thermals in the lower part of the
    ! atmospheric boundary layer.
    !
    ! For more information, see Holtslag, A.A.M., and B.A. Boville, 1993:
    ! Local versus Nonlocal Boundary-Layer Diffusion in a Global Climate
    ! Model. J. Clim., vol. 6., p. 1825--1842.
    !
    ! Updated by Holtslag and Hack to exclude the surface layer from the
    ! definition of the boundary layer Richardson number. Ri is now defined
    ! across the outer layer of the pbl (between the top of the surface
    ! layer and the pbl top) instead of the full pbl (between the surface and
    ! the pbl top). For simiplicity, the surface layer is assumed to be the
    ! region below the first model level (otherwise the boundary layer depth
    ! determination would require iteration).
    !
    ! Author: B. Boville, B. Stevens (rewrite August 2000)
    ! 
    !-----------------------------------------------------------------------
!++ debug code to be removed after validation of PBL codes
     use phys_debug, only: phys_debug_hbdiff1
!++ debug code to be removed after validation of PBL codes
    !------------------------------Arguments--------------------------------
    !
    ! Input arguments
    !
    integer, intent(in) :: lchnk                    ! local chunk index (for debug only)
    integer, intent(in) :: ncol                     ! number of atmospheric columns

    real(r8), intent(in) :: z(pcols,pver)           ! height above surface [m]
    real(r8), intent(in) :: kvf(pcols,pverp)        ! free atmospheric eddy diffsvty [m2/s]
    real(r8), intent(in) :: kqfs(pcols,pcnst)       ! kinematic surf cnstituent flux (kg/m2/s)
    real(r8), intent(in) :: khfs(pcols)             ! kinimatic surface heat flux 
    real(r8), intent(in) :: kbfs(pcols)             ! surface buoyancy flux 
    real(r8), intent(in) :: pblh(pcols)             ! boundary-layer height [m]
    real(r8), intent(in) :: obklen(pcols)           ! Obukhov length
    real(r8), intent(in) :: ustar(pcols)            ! surface friction velocity [m/s]
    real(r8), intent(in) :: wstar(pcols)            ! convective velocity scale [m/s]
    real(r8), intent(in) :: bge(pcols)              ! buoyancy gradient enhancment
    character(len=16), intent(in) :: eddy_scheme

    !
    ! Output arguments
    !
    real(r8), intent(out) :: kvm(pcols,pverp)       ! eddy diffusivity for momentum [m2/s]
    real(r8), intent(out) :: kvh(pcols,pverp)       ! eddy diffusivity for heat [m2/s]
    real(r8), intent(out) :: cgh(pcols,pverp)       ! counter-gradient term for heat [J/kg/m]
    real(r8), intent(out) :: cgs(pcols,pverp)       ! counter-gradient star (cg/flux)
    real(r8), intent(out) :: tpert(pcols)           ! convective temperature excess
    real(r8), intent(out) :: qpert(pcols)           ! convective humidity excess

    integer, intent(out) :: ktopbl(pcols)           ! index of first midpoint inside pbl
    integer, intent(out) :: ktopblmn                ! min value of ktopbl
    real(r8), intent(out) :: tke(pcols,pverp)         ! turbulent kinetic energy (estimated)
    !
    !---------------------------Local workspace-----------------------------
    !
    integer  :: i                       ! longitude index
    integer  :: k                       ! level index

    real(r8) :: phiminv(pcols)          ! inverse phi function for momentum
    real(r8) :: phihinv(pcols)          ! inverse phi function for heat
    real(r8) :: wm(pcols)               ! turbulent velocity scale for momentum
    real(r8) :: zp(pcols)               ! current level height + one level up 
    real(r8) :: fak1(pcols)             ! k*ustar*pblh     
    real(r8) :: fak2(pcols)             ! k*wm*pblh
    real(r8) :: fak3(pcols)             ! fakn*wstar/wm
    real(r8) :: pblk(pcols)             ! level eddy diffusivity for momentum
    real(r8) :: pr(pcols)               ! Prandtl number for eddy diffusivities
    real(r8) :: zl(pcols)               ! zmzp / Obukhov length
    real(r8) :: zh(pcols)               ! zmzp / pblh
    real(r8) :: zzh(pcols)              ! (1-(zmzp/pblh))**2
    real(r8) :: zmzp                    ! level height halfway between zm and zp
    real(r8) :: term                    ! intermediate calculation
    real(r8) :: kve                     ! diffusivity at entrainment layer in unstable cases 

    logical  :: unstbl(pcols)           ! pts w/unstbl pbl (positive virtual ht flx)
    logical  :: pblpt(pcols)            ! pts within pbl
    !
    ! Initialize height independent arrays
    !

    !drb initialize variables for runtime error checking
    kvm = 0._r8	
    kvh = 0._r8
    kve = 0._r8
    cgh = 0._r8
    cgs = 0._r8
    tpert = 0._r8
    qpert = 0._r8
    ktopbl = 0._r8
    ktopblmn = 0._r8
    tke = 0._r8

    do i=1,ncol
       unstbl(i) = (kbfs(i) > 0._r8)
       pblk(i) = 0.0_r8
       fak1(i) = ustar(i)*pblh(i)*vk
       if (unstbl(i)) then
          phiminv(i) = (1._r8 - binm*pblh(i)/obklen(i))**onet
          phihinv(i) = sqrt(1._r8 - binh*pblh(i)/obklen(i))
          wm(i)      = ustar(i)*phiminv(i)
          fak2(i)    = wm(i)*pblh(i)*vk
          fak3(i)    = fakn*wstar(i)/wm(i)
          tpert(i)   = max(khfs(i)*fak/wm(i),0._r8)
          qpert(i)   = max(kqfs(i,1)*fak/wm(i),0._r8)
       else
          tpert(i)   = max(khfs(i)*fak/ustar(i),0._r8)
          qpert(i)   = max(kqfs(i,1)*fak/ustar(i),0._r8)
       end if
    end do
    !
    ! Initialize output arrays with free atmosphere values
    !
    do k=1,pverp
       do i=1,ncol
          kvm(i,k) = kvf(i,k)
          kvh(i,k) = kvf(i,k)
          cgh(i,k) = 0.0_r8
          cgs(i,k) = 0.0_r8
       end do
    end do
    !
    ! Main level loop to compute the diffusivities and counter-gradient terms. These terms are 
    ! only calculated at points determined to be in the interior of the pbl (pblpt(i)==.true.),
    ! and then calculations are directed toward regime: stable vs unstable, surface vs outer 
    ! layer.
    !
    do k=pver,pver-npbl+2,-1
       do i=1,ncol
          pblpt(i) = (z(i,k) < pblh(i))
          if (pblpt(i)) then
             ktopbl(i) = k
             zp(i)  = z(i,k-1)
             if (zkmin == 0.0_r8 .and. zp(i) > pblh(i)) zp(i) = pblh(i)
             zmzp    = 0.5_r8*(z(i,k) + zp(i)) ! we think this is an approximation to the interface height (where KVs are calculated)
             zh(i)   = zmzp/pblh(i)
             zl(i)   = zmzp/obklen(i)
             zzh(i)  = zh(i)*max(0._r8,(1._r8 - zh(i)))**2
             if (unstbl(i)) then
                if (zh(i) < sffrac) then
                   term     = (1._r8 - betam*zl(i))**onet
                   pblk(i)  = fak1(i)*zzh(i)*term
                   pr(i)    = term/sqrt(1._r8 - betah*zl(i))
                else
                   pblk(i)  = fak2(i)*zzh(i)
                   pr(i)    = phiminv(i)/phihinv(i) + ccon*fak3(i)/fak
                   cgs(i,k) = fak3(i)/(pblh(i)*wm(i))
                   cgh(i,k) = khfs(i)*cgs(i,k)*cpair
                end if
             else
                if (zl(i) <= 1._r8) then
                   pblk(i) = fak1(i)*zzh(i)/(1._r8 + betas*zl(i))
                else
                   pblk(i) = fak1(i)*zzh(i)/(betas + zl(i))
                end if
                pr(i)    = 1._r8
             end if
             kvm(i,k) = max(pblk(i),kvf(i,k))
             kvh(i,k) = max(pblk(i)/pr(i),kvf(i,k))
          end if
       end do
    end do

!++ debug code to be removed after validation of PBL codes
    call phys_debug_hbdiff1(lchnk, pblh, zl, zh)
!++ debug code to be removed after validation of PBL codes

    !
    ! Check whether last allowed midpoint is within pbl, determine ktopblmn
    !
    ktopblmn = pver
    k = pver-npbl+1
    do i = 1, ncol
       if (z(i,k) < pblh(i)) ktopbl(i) = k
       ktopblmn = min(ktopblmn, ktopbl(i))
    end do

    if  ( eddy_scheme .eq. 'HBR' ) then  
       ! apply new diffusivity at entrainment zone 
       do i = 1,ncol
          if (bge(i) > 1.e-7) then
             k = ktopbl(i)
             kve = 0.2*(wstar(i)**3+5.*ustar(i)**3)/bge(i)
             kvm(i,k) = kve
             kvh(i,k) = kve
          end if
       end do
    end if

    ! Crude estimate of tke (tke=0 above boundary layer)
    do k = ktopblmn,pverp
       do i = 1, ncol
          tke(i,k) = ( kvm(i,k) / pblh(i) ) ** 2
       end do
    end do
    return
  end subroutine austausch_pbl
end module hb_diff