module ref_pres
!--------------------------------------------------------------------------
! 
! Provides access to reference pressures for use by the physics
! parameterizations.  The pressures are provided by the dynamical core
! since it determines the grid used by the physics.
! 
! Note that the init method for this module is called before the init
! method in physpkg; therefore, most physics modules can use these
! reference pressures during their init phases.
! 
!--------------------------------------------------------------------------

use shr_kind_mod, only: r8=>shr_kind_r8
use ppgrid,       only: pver, pverp

implicit none
public
save

! Reference pressures (Pa)
real(r8), protected :: pref_edge(pverp)     ! Layer edges
real(r8), protected :: pref_mid(pver)       ! Layer midpoints
real(r8), protected :: pref_mid_norm(pver)  ! Layer midpoints normalized by
                                            ! surface pressure ('eta' coordinate)

real(r8), protected :: ptop_ref             ! Top of model
real(r8), protected :: psurf_ref            ! Surface pressure

! Number of top levels using pure pressure representation
integer, protected :: num_pr_lev

! Pressure used to set troposphere cloud physics top (Pa)
real(r8), protected :: trop_cloud_top_press = 0._r8
! Top level for troposphere cloud physics
integer, protected :: trop_cloud_top_lev

! Pressure used to set MAM process top (Pa)
real(r8), protected :: clim_modal_aero_top_press = 0._r8
! Top level for MAM processes that impact climate
integer, protected :: clim_modal_aero_top_lev

! Molecular diffusion is calculated only if the model top is below this
! pressure (Pa).
real(r8), protected :: do_molec_press = 0.1_r8
! Pressure used to set bottom of molecular diffusion region (Pa).
real(r8), protected :: molec_diff_bot_press = 50._r8
! Flag for molecular diffusion, and molecular diffusion level index.
logical, protected :: do_molec_diff = .false.
integer, protected :: nbot_molec = 0

!====================================================================================
contains
!====================================================================================

subroutine ref_pres_readnl(nlfile)

   use spmd_utils,      only: masterproc
   use cam_abortutils,  only: endrun
   use namelist_utils,  only: find_group_name
   use units,           only: getunit, freeunit
   use mpishorthand

   character(len=*), intent(in) :: nlfile  ! filepath for file containing namelist input

   ! Local variables
   integer :: unitn, ierr
   character(len=*), parameter :: subname = 'ref_pres_readnl'

   namelist /ref_pres_nl/ trop_cloud_top_press, clim_modal_aero_top_press,&
        do_molec_press, molec_diff_bot_press
   !-----------------------------------------------------------------------------

   if (masterproc) then
      unitn = getunit()
      open( unitn, file=trim(nlfile), status='old' )
      call find_group_name(unitn, 'ref_pres_nl', status=ierr)
      if (ierr == 0) then
         read(unitn, ref_pres_nl, iostat=ierr)
         if (ierr /= 0) then
            call endrun(subname // ':: ERROR reading namelist')
         end if
      end if
      close(unitn)
      call freeunit(unitn)

      ! Check that top for modal aerosols is not lower than
      ! top for clouds.
      if (clim_modal_aero_top_press > trop_cloud_top_press) &
           call endrun("ERROR: clim_modal_aero_top press must be less &
           &than or equal to trop_cloud_top_press.")
   end if

#ifdef SPMD
   ! Broadcast namelist variables
   call mpibcast(trop_cloud_top_press,            1 , mpir8,   0, mpicom)
   call mpibcast(clim_modal_aero_top_press,       1 , mpir8,   0, mpicom)
   call mpibcast(do_molec_press,                  1 , mpir8,   0, mpicom)
   call mpibcast(molec_diff_bot_press,            1 , mpir8,   0, mpicom)
#endif

end subroutine ref_pres_readnl

!====================================================================================

subroutine ref_pres_init(pref_edge_in, pref_mid_in, num_pr_lev_in)

   ! Initialize reference pressures

   ! arguments
   real(r8), intent(in) :: pref_edge_in(:) ! reference pressure at layer edges (Pa)
   real(r8), intent(in) :: pref_mid_in(:)  ! reference pressure at layer midpoints (Pa)
   integer,  intent(in) :: num_pr_lev_in   ! number of top levels using pure pressure representation
   !---------------------------------------------------------------------------

   pref_edge = pref_edge_in
   pref_mid  = pref_mid_in
   num_pr_lev = num_pr_lev_in

   ptop_ref = pref_edge(1)
   psurf_ref = pref_edge(pverp)

   pref_mid_norm = pref_mid/psurf_ref

   ! Find level corresponding to the top of troposphere clouds.
   trop_cloud_top_lev = press_lim_idx(trop_cloud_top_press, &
      top=.true.)

   ! Find level corresponding to the top for MAM processes.
   clim_modal_aero_top_lev = press_lim_idx(clim_modal_aero_top_press, &
      top=.true.)

   ! Find level corresponding to the molecular diffusion bottom.
   do_molec_diff = (ptop_ref < do_molec_press)
   if (do_molec_diff) then
      nbot_molec = press_lim_idx(molec_diff_bot_press, &
         top=.false.)
   end if

end subroutine ref_pres_init

!====================================================================================

! Convert pressure limiters to the appropriate level.
pure function press_lim_idx(p, top) result(k_lim)
  ! Pressure
  real(r8), intent(in) :: p
  ! Is this a top or bottom limit?
  logical,  intent(in) :: top
  integer :: k_lim, k

  if (top) then
     k_lim = pver+1
     do k = 1, pver
        if (pref_mid(k) > p) then
           k_lim = k
           exit
        end if
     end do
  else
     k_lim = 0
     do k = pver, 1, -1
        if (pref_mid(k) < p) then
           k_lim = k
           exit
        end if
     end do
  end if

end function press_lim_idx

!====================================================================================

end module ref_pres
