LCOV - code coverage report
Current view: top level - physics/cam - hb_diff.F90 (source / functions) Hit Total Coverage
Test: coverage.info Lines: 152 171 88.9 %
Date: 2025-01-13 21:54:50 Functions: 5 6 83.3 %

          Line data    Source code
       1             : module hb_diff
       2             :   !---------------------------------------------------------------------------------
       3             :   ! Module to compute mixing coefficients associated with turbulence in the 
       4             :   ! planetary boundary layer and elsewhere.  PBL coefficients are based on Holtslag 
       5             :   ! and Boville, 1991.
       6             :   !
       7             :   ! Public interfaces:
       8             :   !    init_hb_diff     initializes time independent coefficients
       9             :   !    compute_hb_diff  computes eddy diffusivities and counter-gradient fluxes
      10             :   !
      11             :   ! Private methods:
      12             :   !       trbintd         initializes time dependent variables
      13             :   !       pblintd         initializes time dependent variables that depend pbl depth
      14             :   !       austausch_atm   computes free atmosphere exchange coefficients
      15             :   !       austausch_pbl   computes pbl exchange coefficients
      16             :   !
      17             :   !---------------------------Code history--------------------------------
      18             :   ! Standardized:      J. Rosinski, June 1992
      19             :   ! Reviewed:          P. Rasch, B. Boville, August 1992
      20             :   ! Reviewed:          P. Rasch, April 1996
      21             :   ! Reviewed:          B. Boville, April 1996
      22             :   ! rewritten:         B. Boville, May 2000
      23             :   ! rewritten:         B. Stevens, August 2000
      24             :   ! modularized:       J. McCaa, September 2004
      25             :   !---------------------------------------------------------------------------------
      26             :   use shr_kind_mod, only: r8 => shr_kind_r8
      27             :   use spmd_utils,   only: masterproc          ! output from hb_init should be eliminated
      28             :   use ppgrid,       only: pver, pverp, pcols  ! these should be passed in
      29             :   use cam_logfile,  only: iulog
      30             : 
      31             :   implicit none
      32             :   private
      33             :   save
      34             : 
      35             :   ! Public interfaces
      36             :   public init_hb_diff
      37             :   public compute_hb_diff
      38             :   public compute_hb_free_atm_diff
      39             :   public pblintd
      40             :   !
      41             :   ! PBL limits
      42             :   !
      43             :   real(r8), parameter :: pblmaxp   = 4.e4_r8        ! pbl max depth in pressure units
      44             :   real(r8), parameter :: zkmin     = 0.01_r8        ! Minimum kneutral*f(ri)
      45             :   !
      46             :   ! PBL Parameters
      47             :   !
      48             :   real(r8), parameter :: onet  = 1._r8/3._r8 ! 1/3 power in wind gradient expression
      49             :   real(r8), parameter :: betam = 15.0_r8  ! Constant in wind gradient expression
      50             :   real(r8), parameter :: betas =  5.0_r8  ! Constant in surface layer gradient expression
      51             :   real(r8), parameter :: betah = 15.0_r8  ! Constant in temperature gradient expression 
      52             :   real(r8), parameter :: fakn  =  7.2_r8  ! Constant in turbulent prandtl number
      53             :   real(r8), parameter :: fak   =  8.5_r8  ! Constant in surface temperature excess         
      54             :   real(r8), parameter :: ricr  =  0.3_r8  ! Critical richardson number
      55             :   real(r8), parameter :: sffrac=  0.1_r8  ! Surface layer fraction of boundary layer
      56             :   real(r8), parameter :: binm  = betam*sffrac       ! betam * sffrac
      57             :   real(r8), parameter :: binh  = betah*sffrac       ! betah * sffrac
      58             : 
      59             :   ! Pbl constants set using values from other parts of code
      60             : 
      61             :   real(r8) :: cpair      ! Specific heat of dry air
      62             :   real(r8) :: g          ! Gravitational acceleration
      63             :   real(r8) :: ml2(pverp) ! Mixing lengths squared
      64             :   real(r8) :: vk         ! Von Karman's constant
      65             :   real(r8) :: ccon       ! fak * sffrac * vk
      66             : 
      67             :   integer :: npbl       ! Maximum number of levels in pbl from surface
      68             :   integer :: ntop_turb  ! Top level to which turbulent vertical diffusion is applied.
      69             :   integer :: nbot_turb  ! Bottom level to which turbulent vertical diff is applied.
      70             : 
      71             : !===============================================================================
      72             : CONTAINS
      73             : !===============================================================================
      74             : 
      75        1536 : subroutine init_hb_diff(gravx, cpairx, ntop_eddy, nbot_eddy, pref_mid, &
      76        1536 :                         vkx, eddy_scheme)
      77             : 
      78             :    !----------------------------------------------------------------------- 
      79             :    ! 
      80             :    ! Initialize time independent variables of turbulence/pbl package.
      81             :    ! 
      82             :    !-----------------------------------------------------------------------
      83             : 
      84             :    !------------------------------Arguments--------------------------------
      85             :    real(r8), intent(in) :: gravx     ! acceleration of gravity
      86             :    real(r8), intent(in) :: cpairx    ! specific heat of dry air
      87             :    real(r8), intent(in) :: pref_mid(pver)! reference pressures at midpoints
      88             :    real(r8), intent(in) :: vkx       ! Von Karman's constant
      89             :    integer, intent(in)  :: ntop_eddy ! Top level to which eddy vert diff is applied.
      90             :    integer, intent(in)  :: nbot_eddy ! Bottom level to which eddy vert diff is applied.
      91             :    character(len=16),  intent(in) :: eddy_scheme
      92             : 
      93             :    !---------------------------Local workspace-----------------------------
      94             :    integer :: k                     ! vertical loop index
      95             :    !-----------------------------------------------------------------------
      96             : 
      97             :    ! Basic constants
      98        1536 :    cpair = cpairx
      99        1536 :    g     = gravx
     100        1536 :    vk    = vkx
     101        1536 :    ccon  = fak*sffrac*vk
     102        1536 :    ntop_turb = ntop_eddy
     103        1536 :    nbot_turb = nbot_eddy
     104             : 
     105             :    ! Set the square of the mixing lengths.
     106        1536 :    ml2(ntop_turb) = 0._r8
     107       39936 :    do k = ntop_turb+1, nbot_turb
     108       38400 :       ml2(k) = 30.0_r8**2                 ! HB scheme: length scale = 30m  
     109       39936 :       if  ( eddy_scheme .eq. 'HBR' ) then      
     110           0 :          ml2(k) = 1.0_r8**2               ! HBR scheme: length scale = 1m  
     111             :       end if
     112             :    end do
     113        1536 :    ml2(nbot_turb+1) = 0._r8
     114             : 
     115             :    ! Limit pbl height to regions below 400 mb
     116             :    ! npbl = max number of levels (from bottom) in pbl
     117             : 
     118        1536 :    npbl = 0
     119       41472 :    do k=nbot_turb,ntop_turb,-1
     120       41472 :       if (pref_mid(k) >= pblmaxp) then
     121       13824 :          npbl = npbl + 1
     122             :       end if
     123             :    end do
     124        1536 :    npbl = max(npbl,1)
     125             : 
     126        1536 :    if (masterproc) then
     127           2 :       write(iulog,*)'INIT_HB_DIFF: PBL height will be limited to bottom ',npbl, &
     128           4 :          ' model levels. Top is ',pref_mid(pverp-npbl),' pascals'
     129             :    end if
     130             : 
     131        1536 : end subroutine init_hb_diff
     132             : 
     133             : !===============================================================================
     134             : 
     135     1489176 :   subroutine compute_hb_diff(ncol                  , &
     136             :        th      ,t       ,q       ,z       ,zi      , &
     137             :        pmid    ,u       ,v       ,taux    ,tauy    , &
     138             :        shflx   ,qflx    ,obklen  ,ustar   ,pblh    , &
     139             :        kvm     ,kvh     ,kvq     ,cgh     ,cgs     , &
     140             :        tpert   ,qpert   ,cldn    ,ocnfrac ,tke     , &
     141             :        ri      , &
     142     1489176 :        eddy_scheme)
     143             :     !-----------------------------------------------------------------------
     144             :     !
     145             :     ! Purpose: 
     146             :     !  Interface routines for calcualtion and diatnostics of turbulence related
     147             :     !  coefficients
     148             :     !
     149             :     ! Author: B. Stevens (rewrite August 2000)
     150             :     ! 
     151             :     !-----------------------------------------------------------------------
     152             : 
     153             :     use pbl_utils, only: virtem, calc_ustar, calc_obklen, austausch_atm
     154             : 
     155             :     !------------------------------Arguments--------------------------------
     156             :     !
     157             :     ! Input arguments
     158             :     !
     159             :     integer, intent(in) :: ncol                       ! number of atmospheric columns
     160             : 
     161             :     real(r8), intent(in)  :: th(pcols,pver)           ! potential temperature [K]
     162             :     real(r8), intent(in)  :: t(pcols,pver)            ! temperature (used for density)
     163             :     real(r8), intent(in)  :: q(pcols,pver)            ! specific humidity [kg/kg]
     164             :     real(r8), intent(in)  :: z(pcols,pver)            ! height above surface [m]
     165             :     real(r8), intent(in)  :: zi(pcols,pverp)          ! height above surface [m]
     166             :     real(r8), intent(in)  :: u(pcols,pver)            ! zonal velocity
     167             :     real(r8), intent(in)  :: v(pcols,pver)            ! meridional velocity
     168             :     real(r8), intent(in)  :: taux(pcols)              ! zonal stress [N/m2]
     169             :     real(r8), intent(in)  :: tauy(pcols)              ! meridional stress [N/m2]
     170             :     real(r8), intent(in)  :: shflx(pcols)             ! sensible heat flux
     171             :     real(r8), intent(in)  :: qflx(pcols)              ! water vapor flux
     172             :     real(r8), intent(in)  :: pmid(pcols,pver)         ! midpoint pressures
     173             :     real(r8), intent(in)  :: cldn(pcols,pver)         ! new cloud fraction
     174             :     real(r8), intent(in)  :: ocnfrac(pcols)           ! Land fraction
     175             :     character(len=16), intent(in) :: eddy_scheme
     176             : 
     177             :     !
     178             :     ! Output arguments
     179             :     !
     180             :     real(r8), intent(out) :: kvm(pcols,pverp)         ! eddy diffusivity for momentum [m2/s]
     181             :     real(r8), intent(out) :: kvh(pcols,pverp)         ! eddy diffusivity for heat [m2/s]
     182             :     real(r8), intent(out) :: kvq(pcols,pverp)         ! eddy diffusivity for constituents [m2/s]
     183             :     real(r8), intent(out) :: cgh(pcols,pverp)         ! counter-gradient term for heat [J/kg/m]
     184             :     real(r8), intent(out) :: cgs(pcols,pverp)         ! counter-gradient star (cg/flux)
     185             :     real(r8), intent(out) :: tpert(pcols)             ! convective temperature excess
     186             :     real(r8), intent(out) :: qpert(pcols)             ! convective humidity excess
     187             :     real(r8), intent(out) :: ustar(pcols)             ! surface friction velocity [m/s]
     188             :     real(r8), intent(out) :: obklen(pcols)            ! Obukhov length
     189             :     real(r8), intent(out) :: pblh(pcols)              ! boundary-layer height [m]
     190             :     real(r8), intent(out) :: tke(pcols,pverp)         ! turbulent kinetic energy (estimated)
     191             :     real(r8), intent(out) :: ri(pcols,pver)           ! richardson number: n2/s2
     192             :     !
     193             :     !---------------------------Local workspace-----------------------------
     194             :     !
     195             :     real(r8) :: thv(pcols,pver)         ! virtual temperature
     196             :     real(r8) :: rrho(pcols)             ! 1./bottom level density
     197             :     real(r8) :: wstar(pcols)            ! convective velocity scale [m/s]
     198             :     real(r8) :: kqfs(pcols)             ! kinematic surf constituent flux (kg/m2/s)
     199             :     real(r8) :: khfs(pcols)             ! kinimatic surface heat flux
     200             :     real(r8) :: kbfs(pcols)             ! surface buoyancy flux
     201             :     real(r8) :: kvf(pcols,pverp)        ! free atmospheric eddy diffsvty [m2/s]
     202             :     real(r8) :: s2(pcols,pver)          ! shear squared
     203             :     real(r8) :: n2(pcols,pver)          ! brunt vaisaila frequency
     204             :     real(r8) :: bge(pcols)              ! buoyancy gradient enhancment
     205             :     integer  :: ktopbl(pcols)           ! index of first midpoint inside pbl
     206             :     !
     207             :     ! Initialize time dependent variables that do not depend on pbl height
     208             :     !
     209             : 
     210             :     ! virtual temperature
     211   456576744 :     call virtem(ncol, (pver-ntop_turb+1), th(:ncol,ntop_turb:),q(:ncol,ntop_turb:), thv(:ncol,ntop_turb:))
     212             : 
     213             :     ! Compute ustar, Obukhov length, and kinematic surface fluxes.
     214             :     call calc_ustar(ncol, t(:ncol,pver),pmid(:ncol,pver),taux(:ncol),tauy(:ncol), &
     215     1489176 :          rrho(:ncol),ustar(:ncol))
     216             :     call calc_obklen(ncol, th(:ncol,pver), thv(:ncol,pver), qflx(:ncol),  &
     217             :                      shflx(:ncol),   rrho(:ncol),     ustar(:ncol), &
     218             :                      khfs(:ncol),    kqfs(:ncol),     kbfs(:ncol),  &
     219     1489176 :                      obklen(:ncol))
     220             :     ! Calculate s2, n2, and Richardson number.
     221             :     call trbintd(ncol    ,                            &
     222             :          thv     ,z       ,u       ,v       , &
     223     1489176 :          s2      ,n2      ,ri      )
     224             :     !
     225             :     ! Initialize time dependent variables that do depend on pbl height
     226             :     !
     227             :     call  pblintd(ncol    ,                            &
     228             :          thv     ,z       ,u       ,v       , &
     229             :          ustar   ,obklen  ,kbfs    ,pblh    ,wstar   , &
     230     1489176 :          zi      ,cldn    ,ocnfrac ,bge     )
     231             :     !
     232             :     ! Get free atmosphere exchange coefficients
     233             :     !
     234             :     call austausch_atm(pcols, ncol, pver, ntop_turb, nbot_turb, &
     235     1489176 :          ml2, ri, s2, kvf)
     236             :     ! 
     237             :     ! Get pbl exchange coefficients
     238             :     !
     239             :     call austausch_pbl(ncol                          , &
     240             :          z       ,kvf     ,kqfs    ,khfs    ,kbfs    , &
     241             :          obklen  ,ustar   ,wstar   ,pblh    ,kvm     , &
     242             :          kvh     ,cgh     ,cgs     ,tpert   ,qpert   , &
     243     1489176 :          ktopbl  ,tke     ,bge     ,eddy_scheme)
     244             :     !
     245             : 
     246   672865128 :     kvq(:ncol,:) = kvh(:ncol,:)
     247     1489176 :   end subroutine compute_hb_diff
     248             : 
     249           0 :   subroutine compute_hb_free_atm_diff(ncol,          &
     250             :        th      ,t       ,q       ,z       ,          &
     251             :        pmid    ,u       ,v       ,taux    ,tauy    , &
     252             :        shflx   ,qflx    ,obklen  ,ustar   ,          &
     253             :        kvm     ,kvh     ,kvq     ,cgh     ,cgs     , &
     254             :        ri      )
     255             :     !-----------------------------------------------------------------------
     256             :     !
     257             :     ! This is a version of compute_hb_diff that only computes free
     258             :     ! atmosphere exchange (no PBL computations)
     259             :     !
     260             :     ! Author: B. Stevens (rewrite August 2000)
     261             :     !         Modified by Thomas Toniazzo and Peter H. Lauritzen (June 2023)
     262             :     !
     263             :     !-----------------------------------------------------------------------
     264             : 
     265             :     use pbl_utils, only: virtem, calc_ustar, calc_obklen, austausch_atm_free
     266             : 
     267             :     !------------------------------Arguments--------------------------------
     268             :     !
     269             :     ! Input arguments
     270             :     !
     271             :     integer, intent(in) :: ncol                       ! number of atmospheric columns
     272             : 
     273             :     real(r8), intent(in)  :: th(pcols,pver)           ! potential temperature [K]
     274             :     real(r8), intent(in)  :: t(pcols,pver)            ! temperature (used for density)
     275             :     real(r8), intent(in)  :: q(pcols,pver)            ! specific humidity [kg/kg]
     276             :     real(r8), intent(in)  :: z(pcols,pver)            ! height above surface [m]
     277             :     real(r8), intent(in)  :: u(pcols,pver)            ! zonal velocity
     278             :     real(r8), intent(in)  :: v(pcols,pver)            ! meridional velocity
     279             :     real(r8), intent(in)  :: taux(pcols)              ! zonal stress [N/m2]
     280             :     real(r8), intent(in)  :: tauy(pcols)              ! meridional stress [N/m2]
     281             :     real(r8), intent(in)  :: shflx(pcols)             ! sensible heat flux
     282             :     real(r8), intent(in)  :: qflx(pcols)              ! water vapor flux
     283             :     real(r8), intent(in)  :: pmid(pcols,pver)         ! midpoint pressures
     284             :     !
     285             :     ! Output arguments
     286             :     !
     287             :     real(r8), intent(out) :: kvm(pcols,pverp)         ! eddy diffusivity for momentum [m2/s]
     288             :     real(r8), intent(out) :: kvh(pcols,pverp)         ! eddy diffusivity for heat [m2/s]
     289             :     real(r8), intent(out) :: kvq(pcols,pverp)         ! eddy diffusivity for constituents [m2/s]
     290             :     real(r8), intent(out) :: cgh(pcols,pverp)         ! counter-gradient term for heat [J/kg/m]
     291             :     real(r8), intent(out) :: cgs(pcols,pverp)         ! counter-gradient star (cg/flux)
     292             :     real(r8), intent(out) :: ustar(pcols)             ! surface friction velocity [m/s]
     293             :     real(r8), intent(out) :: obklen(pcols)            ! Obukhov length
     294             :     real(r8), intent(out) :: ri(pcols,pver)           ! richardson number: n2/s2
     295             :     !
     296             :     !---------------------------Local workspace-----------------------------
     297             :     !
     298             :     real(r8) :: thv(pcols,pver)         ! virtual potential temperature
     299             :     real(r8) :: rrho(pcols)             ! 1./bottom level density
     300             :     real(r8) :: kqfs(pcols)             ! kinematic surf constituent flux (kg/m2/s)
     301             :     real(r8) :: khfs(pcols)             ! kinimatic surface heat flux 
     302             :     real(r8) :: kbfs(pcols)             ! surface buoyancy flux 
     303             :     real(r8) :: kvf(pcols,pverp)        ! free atmospheric eddy diffsvty [m2/s]
     304             :     real(r8) :: s2(pcols,pver)          ! shear squared
     305             :     real(r8) :: n2(pcols,pver)          ! brunt vaisaila frequency
     306             : 
     307             :     ! virtual potential temperature
     308           0 :     call virtem(ncol, (pver-ntop_turb+1), th(:ncol,ntop_turb:),q(:ncol,ntop_turb:), thv(:ncol,ntop_turb:))
     309             : 
     310             :     ! Compute ustar, Obukhov length, and kinematic surface fluxes.
     311             :     call calc_ustar(ncol, t(:ncol,pver),pmid(:ncol,pver),taux(:ncol),tauy(:ncol), &
     312           0 :          rrho(:ncol),ustar(:ncol))
     313             :     call calc_obklen(ncol, th(:ncol,pver), thv(:ncol,pver), qflx(:ncol),  &
     314             :                      shflx(:ncol),   rrho(:ncol),     ustar(:ncol), &
     315             :                      khfs(:ncol),    kqfs(:ncol),     kbfs(:ncol),  &
     316           0 :                      obklen(:ncol))
     317             :     ! Calculate s2, n2, and Richardson number.
     318             :     call trbintd(ncol    ,                            &
     319             :          thv     ,z       ,u       ,v       , &
     320           0 :          s2      ,n2      ,ri      )
     321             :     !
     322             :     ! Get free atmosphere exchange coefficients
     323             :     !
     324             :     call austausch_atm_free(pcols, ncol, pver, ntop_turb, nbot_turb, &
     325           0 :          ml2, ri, s2, kvf)
     326             : 
     327           0 :     kvq(:ncol,:) = kvf(:ncol,:)
     328           0 :     kvm(:ncol,:) = kvf(:ncol,:)
     329           0 :     kvh(:ncol,:) = kvf(:ncol,:)
     330           0 :     cgh(:ncol,:) = 0._r8
     331           0 :     cgs(:ncol,:) = 0._r8
     332             : 
     333           0 :   end subroutine compute_hb_free_atm_diff
     334             : 
     335             : 
     336             :   !
     337             :   !===============================================================================
     338     1489176 :   subroutine trbintd(ncol    ,                            &
     339             :        thv     ,z       ,u       ,v       , &
     340             :        s2      ,n2      ,ri      )
     341             : 
     342             :     !----------------------------------------------------------------------- 
     343             :     ! 
     344             :     ! Purpose: 
     345             :     !  Time dependent initialization
     346             :     ! 
     347             :     ! Method: 
     348             :     !  Diagnosis of variables that do not depend on mixing assumptions or
     349             :     !  PBL depth
     350             :     !
     351             :     ! Author: B. Stevens (extracted from pbldiff, August, 2000)
     352             :     ! 
     353             :     !-----------------------------------------------------------------------
     354             :     !------------------------------Arguments--------------------------------
     355             :     !
     356             :     ! Input arguments
     357             :     !
     358             :     integer, intent(in) :: ncol                      ! number of atmospheric columns
     359             : 
     360             :     real(r8), intent(in)  :: thv(pcols,pver)         ! virtual temperature
     361             :     real(r8), intent(in)  :: z(pcols,pver)           ! height above surface [m]
     362             :     real(r8), intent(in)  :: u(pcols,pver)           ! windspeed x-direction [m/s]
     363             :     real(r8), intent(in)  :: v(pcols,pver)           ! windspeed y-direction [m/s]
     364             : 
     365             :     !
     366             :     ! Output arguments
     367             :     !
     368             :     real(r8), intent(out) :: s2(pcols,pver)          ! shear squared
     369             :     real(r8), intent(out) :: n2(pcols,pver)          ! brunt vaisaila frequency
     370             :     real(r8), intent(out) :: ri(pcols,pver)          ! richardson number: n2/s2
     371             :     !
     372             :     !---------------------------Local workspace-----------------------------
     373             :     !
     374             :     integer  :: i                        ! longitude index
     375             :     integer  :: k                        ! level index
     376             : 
     377             :     real(r8) :: vvk                      ! velocity magnitude squared
     378             :     real(r8) :: dvdz2                    ! velocity shear squared
     379             :     real(r8) :: dz                       ! delta z between midpoints
     380             :     !
     381             :     ! Compute shear squared (s2), brunt vaisaila frequency (n2) and related richardson
     382             :     ! number (ri). Use virtual temperature to compute n2.
     383             :     !
     384             : 
     385    38718576 :     do k=ntop_turb,nbot_turb-1
     386   623133576 :        do i=1,ncol
     387   584415000 :           dvdz2   = (u(i,k)-u(i,k+1))**2 + (v(i,k)-v(i,k+1))**2
     388   584415000 :           dvdz2   = max(dvdz2,1.e-36_r8)
     389   584415000 :           dz      = z(i,k) - z(i,k+1)
     390   584415000 :           s2(i,k) = dvdz2/(dz**2)
     391   584415000 :           n2(i,k) = g*2.0_r8*( thv(i,k) - thv(i,k+1))/((thv(i,k) + thv(i,k+1))*dz)
     392   621644400 :           ri(i,k) = n2(i,k)/s2(i,k)
     393             :        end do
     394             :     end do
     395             : 
     396     1489176 :     return
     397             :   end subroutine trbintd
     398             :   !
     399             :   !===============================================================================
     400     1489176 :   subroutine pblintd(ncol    ,                            &
     401             :        thv     ,z       ,u       ,v       , &
     402             :        ustar   ,obklen  ,kbfs    ,pblh    ,wstar   , &
     403             :        zi      ,cldn    ,ocnfrac ,bge     )
     404             :     !----------------------------------------------------------------------- 
     405             :     ! 
     406             :     ! Purpose: 
     407             :     ! Diagnose standard PBL variables
     408             :     ! 
     409             :     ! Method: 
     410             :     ! Diagnosis of PBL depth and related variables.  In this case only wstar.
     411             :     ! The PBL depth follows:
     412             :     !    Holtslag, A.A.M., and B.A. Boville, 1993:
     413             :     !    Local versus Nonlocal Boundary-Layer Diffusion in a Global Climate
     414             :     !    Model. J. Clim., vol. 6., p. 1825--1842.
     415             :     !
     416             :     ! Updated by Holtslag and Hack to exclude the surface layer from the
     417             :     ! definition of the boundary layer Richardson number. Ri is now defined
     418             :     ! across the outer layer of the pbl (between the top of the surface
     419             :     ! layer and the pbl top) instead of the full pbl (between the surface and
     420             :     ! the pbl top). For simiplicity, the surface layer is assumed to be the
     421             :     ! region below the first model level (otherwise the boundary layer depth
     422             :     ! determination would require iteration).
     423             :     !
     424             :     ! Modified for boundary layer height diagnosis: Bert Holtslag, june 1994
     425             :     ! >>>>>>>>>  (Use ricr = 0.3 in this formulation)
     426             :     ! 
     427             :     ! Author: B. Stevens (extracted from pbldiff, August 2000)
     428             :     ! 
     429             :     !-----------------------------------------------------------------------
     430             :     !------------------------------Arguments--------------------------------
     431             :     !
     432             :     ! Input arguments
     433             :     !
     434             :     integer, intent(in) :: ncol                      ! number of atmospheric columns
     435             : 
     436             :     real(r8), intent(in)  :: thv(pcols,pver)         ! virtual temperature
     437             :     real(r8), intent(in)  :: z(pcols,pver)           ! height above surface [m]
     438             :     real(r8), intent(in)  :: u(pcols,pver)           ! windspeed x-direction [m/s]
     439             :     real(r8), intent(in)  :: v(pcols,pver)           ! windspeed y-direction [m/s]
     440             :     real(r8), intent(in)  :: ustar(pcols)            ! surface friction velocity [m/s]
     441             :     real(r8), intent(in)  :: obklen(pcols)           ! Obukhov length
     442             :     real(r8), intent(in)  :: kbfs(pcols)             ! sfc kinematic buoyancy flux [m^2/s^3]
     443             :     real(r8), intent(in)  :: zi(pcols,pverp)         ! height above surface [m]
     444             :     real(r8), intent(in)  :: cldn(pcols,pver)        ! new cloud fraction
     445             :     real(r8), intent(in)  :: ocnfrac(pcols)          ! Land fraction
     446             : 
     447             :     !
     448             :     ! Output arguments
     449             :     !
     450             :     real(r8), intent(out) :: wstar(pcols)            ! convective sclae velocity [m/s]
     451             :     real(r8), intent(out) :: pblh(pcols)             ! boundary-layer height [m]
     452             :     real(r8), intent(out) :: bge(pcols)              ! buoyancy gradient enhancment
     453             :     !
     454             :     !---------------------------Local parameters----------------------------
     455             :     !
     456             :     real(r8), parameter   :: tiny = 1.e-36_r8           ! lower bound for wind magnitude
     457             :     real(r8), parameter   :: fac  = 100._r8             ! ustar parameter in height diagnosis 
     458             :     !
     459             :     !---------------------------Local workspace-----------------------------
     460             :     !
     461             :     integer  :: i                       ! longitude index
     462             :     integer  :: k                       ! level index
     463             : 
     464             :     real(r8) :: phiminv(pcols)          ! inverse phi function for momentum
     465             :     real(r8) :: phihinv(pcols)          ! inverse phi function for heat
     466             :     real(r8) :: rino(pcols,pver)        ! bulk Richardson no. from level to ref lev
     467             :     real(r8) :: tlv(pcols)              ! ref. level pot tmp + tmp excess
     468             :     real(r8) :: vvk                     ! velocity magnitude squared
     469             : 
     470             :     logical  :: unstbl(pcols)           ! pts w/unstbl pbl (positive virtual ht flx)
     471             :     logical  :: check(pcols)            ! True=>chk if Richardson no.>critcal
     472             :     logical  :: ocncldcheck(pcols)      ! True=>if ocean surface and cloud in lowest layer
     473             :     !
     474             :     ! Compute Obukhov length virtual temperature flux and various arrays for use later:
     475             :     !
     476    24865776 :     do i=1,ncol
     477    23376600 :        check(i)     = .true.
     478    23376600 :        rino(i,pver) = 0.0_r8
     479    24865776 :        pblh(i)      = z(i,pver)
     480             :     end do
     481             :     !
     482             :     !
     483             :     ! PBL height calculation:  Scan upward until the Richardson number between
     484             :     ! the first level and the current level exceeds the "critical" value.
     485             :     !
     486    13402584 :     do k=pver-1,pver-npbl+1,-1
     487   200415384 :        do i=1,ncol
     488   198926208 :           if (check(i)) then
     489    54183391 :              vvk = (u(i,k) - u(i,pver))**2 + (v(i,k) - v(i,pver))**2 + fac*ustar(i)**2
     490    54183391 :              vvk = max(vvk,tiny)
     491    54183391 :              rino(i,k) = g*(thv(i,k) - thv(i,pver))*(z(i,k)-z(i,pver))/(thv(i,pver)*vvk)
     492    54183391 :              if (rino(i,k) >= ricr) then
     493    23376600 :                 pblh(i) = z(i,k+1) + (ricr - rino(i,k+1))/(rino(i,k) - rino(i,k+1)) * &
     494    23376600 :                      (z(i,k) - z(i,k+1))
     495    23376600 :                 check(i) = .false.
     496             :              end if
     497             :           end if
     498             :        end do
     499             :     end do
     500             :     !
     501             :     ! Estimate an effective surface temperature to account for surface fluctuations
     502             :     !
     503    24865776 :     do i=1,ncol
     504    23376600 :        if (check(i)) pblh(i) = z(i,pverp-npbl)
     505    23376600 :        unstbl(i) = (kbfs(i) > 0._r8)
     506    23376600 :        check(i)  = (kbfs(i) > 0._r8)
     507    24865776 :        if (check(i)) then
     508    20481355 :           phiminv(i)   = (1._r8 - binm*pblh(i)/obklen(i))**onet
     509    20481355 :           rino(i,pver) = 0.0_r8
     510    20481355 :           tlv(i)       = thv(i,pver) + kbfs(i)*fak/( ustar(i)*phiminv(i) )
     511             :        end if
     512             :     end do
     513             :     !
     514             :     ! Improve pblh estimate for unstable conditions using the convective temperature excess:
     515             :     !
     516    24865776 :     do i = 1,ncol
     517    24865776 :        bge(i) = 1.e-8_r8
     518             :     end do
     519    13402584 :     do k=pver-1,pver-npbl+1,-1
     520   200415384 :        do i=1,ncol
     521   198926208 :           if (check(i)) then
     522    55488017 :              vvk = (u(i,k) - u(i,pver))**2 + (v(i,k) - v(i,pver))**2 + fac*ustar(i)**2
     523    55488017 :              vvk = max(vvk,tiny)
     524    55488017 :              rino(i,k) = g*(thv(i,k) - tlv(i))*(z(i,k)-z(i,pver))/(thv(i,pver)*vvk)
     525    55488017 :              if (rino(i,k) >= ricr) then
     526    20481355 :                 pblh(i) = z(i,k+1) + (ricr - rino(i,k+1))/(rino(i,k) - rino(i,k+1))* &
     527    20481355 :                      (z(i,k) - z(i,k+1))
     528    20481355 :                 bge(i) = 2._r8*g/(thv(i,k)+thv(i,k+1))*(thv(i,k)-thv(i,k+1))/(z(i,k)-z(i,k+1))*pblh(i)
     529    20481355 :                 if (bge(i).lt.0._r8) then
     530         563 :                    bge(i) = 1.e-8_r8
     531             :                 endif
     532    20481355 :                 check(i) = .false.
     533             :              end if
     534             :           end if
     535             :        end do
     536             :     end do
     537             :     !
     538             :     ! PBL height must be greater than some minimum mechanical mixing depth
     539             :     ! Several investigators have proposed minimum mechanical mixing depth
     540             :     ! relationships as a function of the local friction velocity, u*.  We
     541             :     ! make use of a linear relationship of the form h = c u* where c=700.
     542             :     ! The scaling arguments that give rise to this relationship most often
     543             :     ! represent the coefficient c as some constant over the local coriolis
     544             :     ! parameter.  Here we make use of the experimental results of Koracin
     545             :     ! and Berkowicz (1988) [BLM, Vol 43] for wich they recommend 0.07/f
     546             :     ! where f was evaluated at 39.5 N and 52 N.  Thus we use a typical mid
     547             :     ! latitude value for f so that c = 0.07/f = 700.  Also, do not allow 
     548             :     ! PBL to exceed some maximum (npbl) number of allowable points
     549             :     !
     550    24865776 :     do i=1,ncol
     551    23376600 :        if (check(i)) pblh(i) = z(i,pverp-npbl)
     552    23376600 :        pblh(i) = max(pblh(i),700.0_r8*ustar(i))
     553    24865776 :        wstar(i) = (max(0._r8,kbfs(i))*g*pblh(i)/thv(i,pver))**onet
     554             :     end do
     555             :     !
     556             :     ! Final requirement on PBL heightis that it must be greater than the depth
     557             :     ! of the lowest model level over ocean if there is any cloud diagnosed in 
     558             :     ! the lowest model level.  This is to deal with the inadequacies of the 
     559             :     ! current "dry" formulation of the boundary layer, where this test is 
     560             :     ! used to identify circumstances where there is marine stratus in the 
     561             :     ! lowest level, and to provide a weak ventilation of the layer to avoid
     562             :     ! a pathology in the cloud scheme (locking in low-level stratiform cloud)
     563             :     ! If over an ocean surface, and any cloud is diagnosed in the 
     564             :     ! lowest level, set pblh to 50 meters higher than top interface of lowest level
     565             :     !
     566             :     !  jrm This is being applied everywhere (not just ocean)!
     567    24865776 :     do i=1,ncol
     568    23376600 :        ocncldcheck(i) = .false.
     569    23376600 :        if (cldn(i,pver).ge.0.0_r8) ocncldcheck(i) = .true.
     570    24865776 :        if (ocncldcheck(i)) pblh(i) = max(pblh(i),zi(i,pver) + 50._r8)
     571             :     end do
     572             :     !
     573     1489176 :     return
     574             :   end subroutine pblintd
     575             :   !
     576             :   !===============================================================================
     577     1489176 :   subroutine austausch_pbl(ncol                    , &
     578             :        z       ,kvf     ,kqfs    ,khfs    ,kbfs    , &
     579             :        obklen  ,ustar   ,wstar   ,pblh    ,kvm     , &
     580             :        kvh     ,cgh     ,cgs     ,tpert   ,qpert   , &
     581     1489176 :        ktopbl  ,tke     ,bge     ,eddy_scheme)
     582             :     !----------------------------------------------------------------------- 
     583             :     ! 
     584             :     ! Purpose: 
     585             :     ! Atmospheric Boundary Layer Computation
     586             :     ! 
     587             :     ! Method: 
     588             :     ! Nonlocal scheme that determines eddy diffusivities based on a
     589             :     ! specified boundary layer height and a turbulent velocity scale;
     590             :     ! also, countergradient effects for heat and moisture, and constituents
     591             :     ! are included, along with temperature and humidity perturbations which
     592             :     ! measure the strength of convective thermals in the lower part of the
     593             :     ! atmospheric boundary layer.
     594             :     !
     595             :     ! For more information, see Holtslag, A.A.M., and B.A. Boville, 1993:
     596             :     ! Local versus Nonlocal Boundary-Layer Diffusion in a Global Climate
     597             :     ! Model. J. Clim., vol. 6., p. 1825--1842.
     598             :     !
     599             :     ! Updated by Holtslag and Hack to exclude the surface layer from the
     600             :     ! definition of the boundary layer Richardson number. Ri is now defined
     601             :     ! across the outer layer of the pbl (between the top of the surface
     602             :     ! layer and the pbl top) instead of the full pbl (between the surface and
     603             :     ! the pbl top). For simiplicity, the surface layer is assumed to be the
     604             :     ! region below the first model level (otherwise the boundary layer depth
     605             :     ! determination would require iteration).
     606             :     !
     607             :     ! Author: B. Boville, B. Stevens (rewrite August 2000)
     608             :     ! 
     609             :     !------------------------------Arguments--------------------------------
     610             :     !
     611             :     ! Input arguments
     612             :     !
     613             :     integer, intent(in) :: ncol                     ! number of atmospheric columns
     614             : 
     615             :     real(r8), intent(in) :: z(pcols,pver)           ! height above surface [m]
     616             :     real(r8), intent(in) :: kvf(pcols,pverp)        ! free atmospheric eddy diffsvty [m2/s]
     617             :     real(r8), intent(in) :: kqfs(pcols)             ! kinematic surf cnstituent flux (kg/m2/s)
     618             :     real(r8), intent(in) :: khfs(pcols)             ! kinimatic surface heat flux 
     619             :     real(r8), intent(in) :: kbfs(pcols)             ! surface buoyancy flux 
     620             :     real(r8), intent(in) :: pblh(pcols)             ! boundary-layer height [m]
     621             :     real(r8), intent(in) :: obklen(pcols)           ! Obukhov length
     622             :     real(r8), intent(in) :: ustar(pcols)            ! surface friction velocity [m/s]
     623             :     real(r8), intent(in) :: wstar(pcols)            ! convective velocity scale [m/s]
     624             :     real(r8), intent(in) :: bge(pcols)              ! buoyancy gradient enhancment
     625             :     character(len=16), intent(in) :: eddy_scheme
     626             : 
     627             :     !
     628             :     ! Output arguments
     629             :     !
     630             :     real(r8), intent(out) :: kvm(pcols,pverp)       ! eddy diffusivity for momentum [m2/s]
     631             :     real(r8), intent(out) :: kvh(pcols,pverp)       ! eddy diffusivity for heat [m2/s]
     632             :     real(r8), intent(out) :: cgh(pcols,pverp)       ! counter-gradient term for heat [J/kg/m]
     633             :     real(r8), intent(out) :: cgs(pcols,pverp)       ! counter-gradient star (cg/flux)
     634             :     real(r8), intent(out) :: tpert(pcols)           ! convective temperature excess
     635             :     real(r8), intent(out) :: qpert(pcols)           ! convective humidity excess
     636             : 
     637             :     integer,  intent(out) :: ktopbl(pcols)          ! index of first midpoint inside pbl
     638             :     real(r8), intent(out) :: tke(pcols,pverp)       ! turbulent kinetic energy (estimated)
     639             :     !
     640             :     !---------------------------Local workspace-----------------------------
     641             :     !
     642             :     integer  :: i                       ! longitude index
     643             :     integer  :: k                       ! level index
     644             : 
     645             :     real(r8) :: phiminv(pcols)          ! inverse phi function for momentum
     646             :     real(r8) :: phihinv(pcols)          ! inverse phi function for heat
     647             :     real(r8) :: wm(pcols)               ! turbulent velocity scale for momentum
     648             :     real(r8) :: zp(pcols)               ! current level height + one level up 
     649             :     real(r8) :: fak1(pcols)             ! k*ustar*pblh     
     650             :     real(r8) :: fak2(pcols)             ! k*wm*pblh
     651             :     real(r8) :: fak3(pcols)             ! fakn*wstar/wm
     652             :     real(r8) :: pblk(pcols)             ! level eddy diffusivity for momentum
     653             :     real(r8) :: pr(pcols)               ! Prandtl number for eddy diffusivities
     654             :     real(r8) :: zl(pcols)               ! zmzp / Obukhov length
     655             :     real(r8) :: zh(pcols)               ! zmzp / pblh
     656             :     real(r8) :: zzh(pcols)              ! (1-(zmzp/pblh))**2
     657             :     real(r8) :: zmzp                    ! level height halfway between zm and zp
     658             :     real(r8) :: term                    ! intermediate calculation
     659             :     real(r8) :: kve                     ! diffusivity at entrainment layer in unstable cases 
     660             : 
     661             :     logical  :: unstbl(pcols)           ! pts w/unstbl pbl (positive virtual ht flx)
     662             :     logical  :: pblpt(pcols)            ! pts within pbl
     663             :     !
     664             :     ! Initialize height independent arrays
     665             :     !
     666             : 
     667             :     !drb initialize variables for runtime error checking
     668     1489176 :     kvm = 0._r8 
     669     1489176 :     kvh = 0._r8
     670     1489176 :     kve = 0._r8
     671     1489176 :     cgh = 0._r8
     672     1489176 :     cgs = 0._r8
     673     1489176 :     tpert = 0._r8
     674     1489176 :     qpert = 0._r8
     675     1489176 :     ktopbl = 0._r8
     676     1489176 :     tke = 0._r8
     677             : 
     678    24865776 :     do i=1,ncol
     679    23376600 :        unstbl(i) = (kbfs(i) > 0._r8)
     680    23376600 :        pblk(i) = 0.0_r8
     681    23376600 :        fak1(i) = ustar(i)*pblh(i)*vk
     682    24865776 :        if (unstbl(i)) then
     683    20481355 :           phiminv(i) = (1._r8 - binm*pblh(i)/obklen(i))**onet
     684    20481355 :           phihinv(i) = sqrt(1._r8 - binh*pblh(i)/obklen(i))
     685    20481355 :           wm(i)      = ustar(i)*phiminv(i)
     686    20481355 :           fak2(i)    = wm(i)*pblh(i)*vk
     687    20481355 :           fak3(i)    = fakn*wstar(i)/wm(i)
     688    20481355 :           tpert(i)   = max(khfs(i)*fak/wm(i),0._r8)
     689    20481355 :           qpert(i)   = max(kqfs(i)*fak/wm(i),0._r8)
     690             :        else
     691     2895245 :           tpert(i)   = max(khfs(i)*fak/ustar(i),0._r8)
     692     2895245 :           qpert(i)   = max(kqfs(i)*fak/ustar(i),0._r8)
     693             :        end if
     694             :     end do
     695             :     !
     696             :     ! Initialize output arrays with free atmosphere values
     697             :     !
     698    41696928 :     do k=1,pverp
     699   672865128 :        do i=1,ncol
     700   631168200 :           kvm(i,k) = kvf(i,k)
     701   631168200 :           kvh(i,k) = kvf(i,k)
     702   631168200 :           cgh(i,k) = 0.0_r8
     703   671375952 :           cgs(i,k) = 0.0_r8
     704             :        end do
     705             :     end do
     706             :     !
     707             :     ! Main level loop to compute the diffusivities and counter-gradient terms. These terms are 
     708             :     ! only calculated at points determined to be in the interior of the pbl (pblpt(i)==.true.),
     709             :     ! and then calculations are directed toward regime: stable vs unstable, surface vs outer 
     710             :     ! layer.
     711             :     !
     712    13402584 :     do k=pver,pver-npbl+2,-1
     713   200415384 :        do i=1,ncol
     714   187012800 :           pblpt(i) = (z(i,k) < pblh(i))
     715   198926208 :           if (pblpt(i)) then
     716    62477665 :              ktopbl(i) = k
     717    62477665 :              zp(i)  = z(i,k-1)
     718             :              if (zkmin == 0.0_r8 .and. zp(i) > pblh(i)) zp(i) = pblh(i)
     719    62477665 :              zmzp    = 0.5_r8*(z(i,k) + zp(i)) ! we think this is an approximation to the interface height (where KVs are calculated)
     720    62477665 :              zh(i)   = zmzp/pblh(i)
     721    62477665 :              zl(i)   = zmzp/obklen(i)
     722    62477665 :              zzh(i)  = zh(i)*max(0._r8,(1._r8 - zh(i)))**2
     723    62477665 :              if (unstbl(i)) then
     724    55500358 :                 if (zh(i) < sffrac) then
     725      115635 :                    term     = (1._r8 - betam*zl(i))**onet
     726      115635 :                    pblk(i)  = fak1(i)*zzh(i)*term
     727      115635 :                    pr(i)    = term/sqrt(1._r8 - betah*zl(i))
     728             :                 else
     729    55384723 :                    pblk(i)  = fak2(i)*zzh(i)
     730    55384723 :                    pr(i)    = phiminv(i)/phihinv(i) + ccon*fak3(i)/fak
     731    55384723 :                    cgs(i,k) = fak3(i)/(pblh(i)*wm(i))
     732    55384723 :                    cgh(i,k) = khfs(i)*cgs(i,k)*cpair
     733             :                 end if
     734             :              else
     735     6977307 :                 if (zl(i) <= 1._r8) then
     736     5674742 :                    pblk(i) = fak1(i)*zzh(i)/(1._r8 + betas*zl(i))
     737             :                 else
     738     1302565 :                    pblk(i) = fak1(i)*zzh(i)/(betas + zl(i))
     739             :                 end if
     740     6977307 :                 pr(i)    = 1._r8
     741             :              end if
     742    62477665 :              kvm(i,k) = max(pblk(i),kvf(i,k))
     743    62477665 :              kvh(i,k) = max(pblk(i)/pr(i),kvf(i,k))
     744             :           end if
     745             :        end do
     746             :     end do
     747             : 
     748             :     !
     749             :     ! Check whether last allowed midpoint is within pbl
     750             :     !
     751             : 
     752     1489176 :     if  ( eddy_scheme .eq. 'HBR' ) then  
     753             :        ! apply new diffusivity at entrainment zone 
     754           0 :        do i = 1,ncol
     755           0 :           if (bge(i) > 1.e-7_r8) then
     756           0 :              k = ktopbl(i)
     757           0 :              kve = 0.2_r8*(wstar(i)**3+5._r8*ustar(i)**3)/bge(i)
     758           0 :              kvm(i,k) = kve
     759           0 :              kvh(i,k) = kve
     760             :           end if
     761             :        end do
     762             :     end if
     763             : 
     764             :     ! Crude estimate of tke (tke=0 above boundary layer)
     765    16380936 :     do k = max(pverp-npbl,2),pverp
     766   250146936 :        do i = 1, ncol
     767   248657760 :           if (z(i,k-1) < pblh(i)) then
     768    62477665 :              tke(i,k) = ( kvm(i,k) / pblh(i) ) ** 2
     769             :           endif
     770             :        end do
     771             :     end do
     772     1489176 :     return
     773             :   end subroutine austausch_pbl
     774             : 
     775             : end module hb_diff

Generated by: LCOV version 1.14