mo_cloud_optics_rrtmgp_kernels.F90 Source File


Contents


Source Code

! This code is part of
! RRTM for GCM Applications - Parallel (RRTMGP)
!
! Copyright 2024-,  Atmospheric and Environmental Research, 
!    Trustees of Columbia University.  All right reserved.
!
! Use and duplication is permitted under the terms of the
!    BSD 3-clause license, see http://opensource.org/licenses/BSD-3-Clause
!
module mo_cloud_optics_rrtmgp_kernels
  use mo_rte_kind,      only : wp, wl
  implicit none
  private
  public :: compute_cld_from_table, compute_cld_from_pade
  interface pade_eval
    module procedure pade_eval_nbnd, pade_eval_1
  end interface pade_eval
contains
  !---------------------------------------------------------------------------
  !
  ! Linearly interpolate values from a lookup table with "nsteps" evenly-spaced
  !   elements starting at "offset." The table's second dimension is band.
  ! Returns 0 where the mask is false.
  ! We could also try gather/scatter for efficiency
  !
  subroutine compute_cld_from_table(ncol, nlay, nbnd, mask, lwp, re, &
                                    nsteps, step_size, offset,       &
                                    tau_table, ssa_table, asy_table, &
                                    tau, taussa, taussag) bind(C, name="rrtmgp_compute_cld_from_table")
    integer,                                intent(in) :: ncol, nlay, nbnd, nsteps
    logical(wl), dimension(ncol,nlay),      intent(in) :: mask
    real(wp),    dimension(ncol,nlay),      intent(in) :: lwp, re
    real(wp),                               intent(in) :: step_size, offset
    real(wp),    dimension(nsteps,   nbnd), intent(in) :: tau_table, ssa_table, asy_table
    real(wp),    dimension(ncol,nlay,nbnd)             :: tau, taussa, taussag
    ! ---------------------------
    integer  :: icol, ilay, ibnd
    integer  :: index
    real(wp) :: fint
    real(wp) :: t, ts  ! tau, tau*ssa, tau*ssa*g
    ! ---------------------------
    !$acc parallel loop gang vector default(present) collapse(3)
    !$omp target teams distribute parallel do simd collapse(3)
    do ibnd = 1, nbnd
      do ilay = 1,nlay
        do icol = 1, ncol
          if(mask(icol,ilay)) then
            index = min(floor((re(icol,ilay) - offset)/step_size)+1, nsteps-1)
            fint = (re(icol,ilay) - offset)/step_size - (index-1)
            t   = lwp(icol,ilay) * &
                  (tau_table(index,  ibnd) + fint * (tau_table(index+1,ibnd) - tau_table(index,ibnd)))
            ts  = t              * &
                  (ssa_table(index,  ibnd) + fint * (ssa_table(index+1,ibnd) - ssa_table(index,ibnd)))
            taussag(icol,ilay,ibnd) =  &
                  ts             * &
                  (asy_table(index,  ibnd) + fint * (asy_table(index+1,ibnd) - asy_table(index,ibnd)))
            taussa (icol,ilay,ibnd) = ts
            tau    (icol,ilay,ibnd) = t
          else
            tau    (icol,ilay,ibnd) = 0._wp
            taussa (icol,ilay,ibnd) = 0._wp
            taussag(icol,ilay,ibnd) = 0._wp
          end if
        end do
      end do
    end do
  end subroutine compute_cld_from_table
  !---------------------------------------------------------------------------
  !
  ! Pade functions
  !
  !---------------------------------------------------------------------------
  subroutine compute_cld_from_pade(ncol, nlay, nbnd, nsizes, &
                                   mask, lwp, re,            &
                                   m_ext, n_ext, re_bounds_ext, coeffs_ext, &
                                   m_ssa, n_ssa, re_bounds_ssa, coeffs_ssa, &
                                   m_asy, n_asy, re_bounds_asy, coeffs_asy, &
                                   tau, taussa, taussag) bind(C, name="rrtmgp_compute_cld_from_pade")
    integer,                        intent(in) :: ncol, nlay, nbnd, nsizes
    logical(wl),  &
              dimension(ncol,nlay), intent(in) :: mask
    real(wp), dimension(ncol,nlay), intent(in) :: lwp, re
    real(wp), dimension(nsizes+1),  intent(in) :: re_bounds_ext, re_bounds_ssa, re_bounds_asy
    integer,                        intent(in) :: m_ext, n_ext
    real(wp), dimension(nbnd,nsizes,0:m_ext+n_ext), &
                                    intent(in) :: coeffs_ext
    integer,                        intent(in) :: m_ssa, n_ssa
    real(wp), dimension(nbnd,nsizes,0:m_ssa+n_ssa), &
                                    intent(in) :: coeffs_ssa
    integer,                        intent(in) :: m_asy, n_asy
    real(wp), dimension(nbnd,nsizes,0:m_asy+n_asy), &
                                    intent(in) :: coeffs_asy
    real(wp), dimension(ncol,nlay,nbnd)        :: tau, taussa, taussag
    ! ---------------------------
    integer  :: icol, ilay, ibnd, irad
    real(wp) :: t, ts

    !$acc parallel loop gang vector default(present) collapse(3)
    !$omp target teams distribute parallel do simd collapse(3)
    do ibnd = 1, nbnd
      do ilay = 1, nlay
        do icol = 1, ncol
          if(mask(icol,ilay)) then
            !
            ! Finds index into size regime table
            ! This works only if there are precisely three size regimes (four bounds) and it's
            !   previously guaranteed that size_bounds(1) <= size <= size_bounds(4)
            !
            irad = min(floor((re(icol,ilay) - re_bounds_ext(2))/re_bounds_ext(3))+2, 3)
            t   = lwp(icol,ilay) *     &
                  pade_eval(ibnd, nbnd, nsizes, m_ext, n_ext, irad, re(icol,ilay), coeffs_ext)

            irad = min(floor((re(icol,ilay) - re_bounds_ssa(2))/re_bounds_ssa(3))+2, 3)
            ! Pade approximants for co-albedo can sometimes be negative
            ts  = t              * (1._wp - max(0._wp, &
                  pade_eval(ibnd, nbnd, nsizes, m_ssa, n_ssa, irad, re(icol,ilay), coeffs_ssa)))

            irad = min(floor((re(icol,ilay) - re_bounds_asy(2))/re_bounds_asy(3))+2, 3)
            taussag(icol,ilay,ibnd) =  &
                  ts             *     &
                  pade_eval(ibnd, nbnd, nsizes, m_asy, n_asy, irad, re(icol,ilay), coeffs_asy)

            taussa (icol,ilay,ibnd) = ts
            tau    (icol,ilay,ibnd) = t
          else
            tau    (icol,ilay,ibnd) = 0._wp
            taussa (icol,ilay,ibnd) = 0._wp
            taussag(icol,ilay,ibnd) = 0._wp
          end if
        end do
      end do
    end do

  end subroutine compute_cld_from_pade
  !---------------------------------------------------------------------------
  !
  ! Evaluate Pade approximant of order [m/n]
  !
  function pade_eval_nbnd(nbnd, nrads, m, n, irad, re, pade_coeffs)
    integer,                intent(in) :: nbnd, nrads, m, n, irad
    real(wp), dimension(nbnd, nrads, 0:m+n), &
                            intent(in) :: pade_coeffs
    real(wp),               intent(in) :: re
    real(wp), dimension(nbnd)          :: pade_eval_nbnd

    integer :: iband
    real(wp) :: numer, denom
    integer  :: i

    do iband = 1, nbnd
      denom = pade_coeffs(iband,irad,n+m)
      do i = n-1+m, 1+m, -1
        denom = pade_coeffs(iband,irad,i)+re*denom
      end do
      denom =  1._wp                     +re*denom

      numer = pade_coeffs(iband,irad,m)
      do i = m-1, 1, -1
        numer = pade_coeffs(iband,irad,i)+re*numer
      end do
      numer = pade_coeffs(iband,irad,0)  +re*numer

      pade_eval_nbnd(iband) = numer/denom
    end do
  end function pade_eval_nbnd
  !---------------------------------------------------------------------------
  !
  ! Evaluate Pade approximant of order [m/n]
  !
  function pade_eval_1(iband, nbnd, nrads, m, n, irad, re, pade_coeffs)
    !$acc routine seq
    !$omp declare target
    !
    integer,                intent(in) :: iband, nbnd, nrads, m, n, irad
    real(wp), dimension(nbnd, nrads, 0:m+n), &
                            intent(in) :: pade_coeffs
    real(wp),               intent(in) :: re
    real(wp)                           :: pade_eval_1

    real(wp) :: numer, denom
    integer  :: i

    denom = pade_coeffs(iband,irad,n+m)
    do i = n-1+m, 1+m, -1
      denom = pade_coeffs(iband,irad,i)+re*denom
    end do
    denom =  1._wp                     +re*denom

    numer = pade_coeffs(iband,irad,m)
    do i = m-1, 1, -1
      numer = pade_coeffs(iband,irad,i)+re*numer
    end do
    numer = pade_coeffs(iband,irad,0)  +re*numer

    pade_eval_1 = numer/denom
  end function pade_eval_1

end module mo_cloud_optics_rrtmgp_kernels