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