mo_gas_optics_utils.F90 Source File


This file depends on

sourcefile~~mo_gas_optics_utils.f90~~EfferentGraph sourcefile~mo_gas_optics_utils.f90 mo_gas_optics_utils.F90 sourcefile~mo_rte_kind.f90 mo_rte_kind.F90 sourcefile~mo_gas_optics_utils.f90->sourcefile~mo_rte_kind.f90 sourcefile~mo_rte_util_array.f90 mo_rte_util_array.F90 sourcefile~mo_gas_optics_utils.f90->sourcefile~mo_rte_util_array.f90 sourcefile~mo_gas_optics_constants.f90 mo_gas_optics_constants.F90 sourcefile~mo_gas_optics_utils.f90->sourcefile~mo_gas_optics_constants.f90 sourcefile~mo_rte_util_array.f90->sourcefile~mo_rte_kind.f90 sourcefile~mo_gas_optics_constants.f90->sourcefile~mo_rte_kind.f90 sourcefile~mo_gas_optics_constants.f90->sourcefile~mo_rte_util_array.f90

Contents


Source Code

!> Physical constants, planetary/atmospheric parameters, and utility functions for
!>    low-level gas optics calculations including Planck source functions.
!>
!> layer mass for each species
!> layer number density for each species (TK)
!> The latter two don't have C bindings
!
! Copyright 2026-, 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_gas_optics_utils
  use mo_rte_kind,       only : wp, wl
  use mo_gas_optics_constants, &
                         only: boltzmann_k, planck_h, lightspeed, &
                               m_h2o, m_dry, avogad, R_univ_gconst, grav
  use mo_rte_util_array, only : zero_array

  implicit none

  interface compute_Planck_source
    module procedure compute_Planck_source_2D, compute_Planck_source_1D
  end interface

  private :: B_nu
  public  :: compute_Planck_source, get_layer_mass, get_layer_number

contains
  ! -------------------------------------------------------------------------------------------------
  ! Planck source functions
  ! -------------------------------------------------------------------------------------------------
  !
  ! Planck function (gets wrapped by 1D, 2D codes)
  !
  elemental function B_nu(T, nu)
    real(wp), intent(in) :: T, nu
    real(wp)             :: B_nu
    B_nu = 100._wp*2._wp*planck_h*((nu*100._wp)**3)*(lightspeed**2) / &
         (exp((planck_h * lightspeed * nu * 100._wp) / (boltzmann_k * T)) - 1._wp)
  end function
  ! -----------------------------------------
  subroutine compute_Planck_source_2D(&
      ncol, nlay, nnu, &
      nus, dnus, T, &
      source) bind(C, name="rte_compute_Planck_source_2D")
    integer,  &
      intent(in ) :: ncol, nlay, nnu
    real(wp), dimension(nnu), &
      intent(in ) :: nus, dnus
    real(wp), dimension(ncol, nlay), &
      intent(in ) :: T
    real(wp), dimension(ncol, nlay, nnu), &
      intent(out) :: source

     ! Local variables
    integer :: icol, ilay, inu

   !$acc                         parallel loop    collapse(3) copyin(nus,dnus,T) copyout(source)
   !$omp target teams distribute parallel do simd collapse(3)
   do inu = 1, nnu
      do ilay = 1, nlay
        do icol = 1, ncol
          source(icol, ilay, inu) = B_nu(T(icol, ilay), nus(inu)) * dnus(inu)
        end do
      end do
    end do

  end subroutine compute_Planck_source_2D
  ! -----------------------------------------
  subroutine compute_Planck_source_1D(&
      ncol, nnu, &
      nus, dnus, T, &
      source) bind(C, name="rte_compute_Planck_source_1D")
    integer,  &
      intent(in ) :: ncol, nnu
    real(wp), dimension(nnu), &
      intent(in ) :: nus, dnus
    real(wp), dimension(ncol), &
      intent(in ) :: T
    real(wp), dimension(ncol, nnu), &
      intent(out) :: source

     ! Local variables
     integer :: icol, ilay, inu

    !$acc                         parallel loop    collapse(2) copyin(nus,dnus,T)  copyout(source)
    !$omp target teams distribute parallel do simd collapse(2) map(to:nus,dnus,T) map(from:source)
    do inu = 1, nnu
      do icol = 1, ncol
        source(icol, inu) = B_nu(T(icol), nus(inu)) * dnus(inu)
      end do
    end do

  end subroutine compute_Planck_source_1D
  ! -------------------------------------------------------------------------------------------------
  ! Layer mass (kg), layer number density
  ! -------------------------------------------------------------------------------------------------
  subroutine get_layer_mass(ncol, nlay, ngas, vmr, plev, mol_weights, m_dry, layer_mass)
    !
    !> mass (kg m^-2) each gas in the layer
    !>
    integer, intent(in)                                  :: ncol, nlay, ngas
    real(wp), dimension(ngas, ncol, nlay  ), intent(in ) :: vmr
    real(wp), dimension(      ncol, nlay+1), intent(in ) :: plev
    real(wp), dimension(ngas),               intent(in ) :: mol_weights
    real(wp),                                intent(in ) :: m_dry
    real(wp), dimension(ngas, ncol, nlay),   intent(out) :: layer_mass

    integer :: igas, icol, ilay
    ! Convert pressures and vmr to layer masses (ngas, ncol, nlay)
    ! mmr = vmr * (Mgas/Mair)
    ! layer_mass = mmr * dp / g
    !$acc                         parallel loop    collapse(3) copyin(vmr,mol_weights,plev)  copyout(layer_mass)
    !$omp target teams distribute parallel do simd collapse(3) map(to:vmr,mol_weights,plev) map(from:layer_mass)
    do ilay = 1, nlay
      do icol = 1, ncol
        do igas = 1, ngas
          layer_mass(igas, icol, ilay) = vmr(igas, icol, ilay) * &
            (mol_weights(igas) / m_dry) * &
            abs(plev(icol, ilay+1) - plev(icol, ilay)) / grav
        end do
      end do
    end do
  end subroutine get_layer_mass
  !--------------------------------------------------------------------------------------------------------------------
  function get_layer_number(ncol, nlay, vmr_h2o, plev) result(col_dry)
    !
    !> Number density (#/cm^-2) of dry air molecules
    !>    "col_dry" in RRTMGP
    ! input
    integer, intent(in)                           :: ncol, nlay
    real(wp), dimension(ncol, nlay  ), intent(in) :: vmr_h2o  ! volume mixing ratio of water vapor to dry air
    real(wp), dimension(ncol, nlay+1), intent(in) :: plev     ! Layer boundary pressures [Pa]
    ! output
    real(wp), dimension(ncol, nlay) :: col_dry ! Column dry amount
    ! ------------------------------------------------
    real(wp):: delta_plev, m_air, fact
    integer :: icol, ilev
    ! ------------------------------------------------
    !$acc                parallel loop gang vector collapse(2) copyin(plev,vmr_h2o)  copyout(col_dry)
    !$omp target teams distribute parallel do simd collapse(2) map(to:plev,vmr_h2o) map(from:col_dry)
    do ilev = 1, nlay
      do icol = 1, ncol
        delta_plev = abs(plev(icol,ilev) - plev(icol,ilev+1))
        ! Get average mass of moist air per mole of moist air
        fact = 1._wp / (1.+vmr_h2o(icol,ilev))
        m_air = (m_dry + m_h2o * vmr_h2o(icol,ilev)) * fact
        col_dry(icol,ilev) = 10._wp * delta_plev * avogad * fact/(1000._wp*m_air*100._wp*grav)
      end do
    end do
  end function get_layer_number
end module mo_gas_optics_utils