module rayleigh_friction 3,4 !--------------------------------------------------------------------------------- ! Module to apply rayleigh friction in region of model top. ! We specify a decay rate profile that is largest at the model top and ! drops off vertically using a hyperbolic tangent profile. ! We compute the tendencies in u and v using an Euler backward scheme. ! We then apply the negative of the kinetic energy tendency to "s", the dry ! static energy. ! ! calling sequence: ! ! rayleigh_friction_init initializes rayleigh friction constants ! rayleigh_friction_tend computes rayleigh friction tendencies ! !---------------------------Code history-------------------------------- ! This is a new routine written by Art Mirin in collaboration with Phil Rasch. ! Initial coding for this version: Art Mirin, May 2007. !--------------------------------------------------------------------------------- use shr_kind_mod, only: r8 => shr_kind_r8 use ppgrid, only: pver use spmd_utils, only: masterproc use cam_logfile, only: iulog implicit none private ! Make default type private to the module save ! ! Public interfaces ! public rayleigh_friction_init ! Initialization public rayleigh_friction_tend ! Computation of tendencies ! ! Public data ! integer, public :: rayk0 = 2 ! vertical level at which rayleigh friction term is centered real (r8), public :: raykrange = 0.d0 ! range of rayleigh friction profile ! if 0, range is set to satisfy x=2 (see below) real (r8), public :: raytau0 = 0.d0 ! approximate value of decay time at model top (days) ! if 0., no rayleigh friction is applied ! ! Private data ! real (r8) :: krange ! range of rayleigh friction profile real (r8) :: tau0 ! approximate value of decay time at model top real (r8) :: otau0 ! inverse of tau0 real (r8) :: otau(pver) ! inverse decay time versus vertical level ! We apply a profile of the form otau0 * [1 + tanh (x)] / 2 , where ! x = (k0 - k) / krange. The default is for x to equal 2 at k=1, meaning ! krange = (k0 - 1) / 2. The default is applied when raykrange is set to 0. ! If otau0 = 0, no term is applied. contains !=============================================================================== subroutine rayleigh_friction_init() 1 !------------------------------Arguments-------------------------------- !---------------------------Local storage------------------------------- real (r8) x integer k !----------------------------------------------------------------------- ! Compute tau array !----------------------------------------------------------------------- krange = raykrange if (raykrange .eq. 0.d0) krange = (rayk0 - 1) / 2._r8 tau0 = (86400._r8) * raytau0 ! convert to seconds otau0 = 0._r8 if (tau0 .ne. 0.d0) otau0 = 1._r8/tau0 do k = 1, pver x = (rayk0 - k) / krange otau(k) = otau0 * (1 + tanh(x)) / (2._r8) enddo if (masterproc) then write (iulog,*) 'Rayleigh friction - rayk0 = ', rayk0 write (iulog,*) 'Rayleigh friction - raykrange = ', raykrange write (iulog,*) 'Rayleigh friction - raytau0 = ', raytau0 write (iulog,*) 'Rayleigh friction - krange = ', krange write (iulog,*) 'Rayleigh friction - otau0 = ', otau0 write (iulog,*) 'Rayleigh friction decay rate profile' do k = 1, pver write (iulog,*) ' k = ', k, ' otau = ', otau(k) enddo end if return end subroutine rayleigh_friction_init !========================================================================================= subroutine rayleigh_friction_tend( & 1,2 ztodt ,state ,ptend ) !----------------------------------------------------------------------- ! interface routine for rayleigh friction !----------------------------------------------------------------------- use physics_types, only: physics_state, physics_ptend, physics_ptend_init !------------------------------Arguments-------------------------------- real(r8), intent(in) :: ztodt ! physics timestep type(physics_state), intent(in) :: state ! physics state variables type(physics_ptend), intent(out) :: ptend ! individual parameterization tendencies ! !---------------------------Local storage------------------------------- integer :: ncol ! number of atmospheric columns integer :: k ! level real(r8) :: rztodt ! 1./ztodt real(r8) :: c1, c2, c3 ! temporary variables !----------------------------------------------------------------------- call physics_ptend_init(ptend) ptend%name = "rayleigh friction" if (otau0 .eq. 0._r8) return rztodt = 1._r8/ztodt ncol = state%ncol ! u, v and s are modified by rayleigh friction ptend%ls = .TRUE. ptend%lu = .TRUE. ptend%lv = .TRUE. do k = 1, pver c2 = 1._r8 / (1._r8 + otau(k)*ztodt) c1 = -otau(k) * c2 c3 = 0.5_r8 * (1._r8 - c2*c2) * rztodt ptend%u(:ncol,k) = c1 * state%u(:ncol,k) ptend%v(:ncol,k) = c1 * state%v(:ncol,k) ptend%s(:ncol,k) = c3 * (state%u(:ncol,k)**2 + state%v(:ncol,k)**2) enddo return end subroutine rayleigh_friction_tend end module rayleigh_friction