module geopotential 4,3

!---------------------------------------------------------------------------------
! Compute geopotential from temperature or
! compute geopotential and temperature from dry static energy.
!
! The hydrostatic matrix elements must be consistent with the dynamics algorithm.
! The diagonal element is the itegration weight from interface k+1 to midpoint k.
! The offdiagonal element is the weight between interfaces.
! 
! Author: B.Boville, Feb 2001 from earlier code by Boville and S.J. Lin
!---------------------------------------------------------------------------------

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

  implicit none
  private
  save

  public geopotential_dse
  public geopotential_t

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

  subroutine geopotential_dse(                                & 2,1
       piln   , pmln   , pint   , pmid   , pdel   , rpdel  ,  &
       dse    , q      , phis   , rair   , gravit , cpair  ,  &
       zvir   , t      , zi     , zm     , ncol             )
!----------------------------------------------------------------------- 
! 
! Purpose: 
! Compute the temperature  and geopotential height (above the surface) at the
! midpoints and interfaces from the input dry static energy and pressures.
!
!-----------------------------------------------------------------------
!------------------------------Arguments--------------------------------
!
! Input arguments
    integer, intent(in) :: ncol                  ! Number of longitudes

    real(r8), intent(in) :: piln (pcols,pverp)   ! Log interface pressures
    real(r8), intent(in) :: pmln (pcols,pver)    ! Log midpoint pressures
    real(r8), intent(in) :: pint (pcols,pverp)   ! Interface pressures
    real(r8), intent(in) :: pmid (pcols,pver)    ! Midpoint pressures
    real(r8), intent(in) :: pdel (pcols,pver)    ! layer thickness
    real(r8), intent(in) :: rpdel(pcols,pver)    ! inverse of layer thickness
    real(r8), intent(in) :: dse  (pcols,pver)    ! dry static energy
    real(r8), intent(in) :: q    (pcols,pver)    ! specific humidity
    real(r8), intent(in) :: phis (pcols)         ! surface geopotential
    real(r8), intent(in) :: rair                 ! Gas constant for dry air
    real(r8), intent(in) :: gravit               ! Acceleration of gravity
    real(r8), intent(in) :: cpair                ! specific heat at constant p for dry air
    real(r8), intent(in) :: zvir                 ! rh2o/rair - 1

! Output arguments

    real(r8), intent(out) :: t(pcols,pver)       ! temperature
    real(r8), intent(out) :: zi(pcols,pverp)     ! Height above surface at interfaces
    real(r8), intent(out) :: zm(pcols,pver)      ! Geopotential height at mid level
!
!---------------------------Local variables-----------------------------
!
    logical  :: fvdyn              ! finite volume dynamics
    integer  :: i,k                ! Lon, level, level indices
    real(r8) :: hkk(pcols)         ! diagonal element of hydrostatic matrix
    real(r8) :: hkl(pcols)         ! off-diagonal element
    real(r8) :: rog                ! Rair / gravit
    real(r8) :: tv                 ! virtual temperature
    real(r8) :: tvfac              ! Tv/T
!
!-----------------------------------------------------------------------
    rog = rair/gravit

! Set dynamics flag
    fvdyn = dycore_is ('LR')

! The surface height is zero by definition.
    do i = 1,ncol
       zi(i,pverp) = 0.0_r8
    end do

! Compute the virtual temperature, zi, zm from bottom up
! Note, zi(i,k) is the interface above zm(i,k)
    do k = pver, 1, -1

! First set hydrostatic elements consistent with dynamics
       if (fvdyn) then
          do i = 1,ncol
             hkl(i) = piln(i,k+1) - piln(i,k)
             hkk(i) = 1._r8 - pint(i,k) * hkl(i) * rpdel(i,k)
          end do
       else
          do i = 1,ncol
             hkl(i) = pdel(i,k) / pmid(i,k)
             hkk(i) = 0.5_r8 * hkl(i)
          end do
       end if

! Now compute tv, t, zm, zi
       do i = 1,ncol
          tvfac   = 1._r8 + zvir * q(i,k)
          tv      = (dse(i,k) - phis(i) - gravit*zi(i,k+1)) / ((cpair / tvfac) + rair*hkk(i))

          t (i,k) = tv / tvfac

          zm(i,k) = zi(i,k+1) + rog * tv * hkk(i)
          zi(i,k) = zi(i,k+1) + rog * tv * hkl(i)
       end do
    end do

    return
  end subroutine geopotential_dse

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

  subroutine geopotential_t(                                 & 1,1
       piln   , pmln   , pint   , pmid   , pdel   , rpdel  , &
       t      , q      , rair   , gravit , zvir   ,          &
       zi     , zm     , ncol   )

!----------------------------------------------------------------------- 
! 
! Purpose: 
! Compute the geopotential height (above the surface) at the midpoints and 
! interfaces using the input temperatures and pressures.
!
!-----------------------------------------------------------------------


!------------------------------Arguments--------------------------------
!
! Input arguments
!
    integer, intent(in) :: ncol                  ! Number of longitudes

    real(r8), intent(in) :: piln (pcols,pverp)   ! Log interface pressures
    real(r8), intent(in) :: pmln (pcols,pver)    ! Log midpoint pressures
    real(r8), intent(in) :: pint (pcols,pverp)   ! Interface pressures
    real(r8), intent(in) :: pmid (pcols,pver)    ! Midpoint pressures
    real(r8), intent(in) :: pdel (pcols,pver)    ! layer thickness
    real(r8), intent(in) :: rpdel(pcols,pver)    ! inverse of layer thickness
    real(r8), intent(in) :: t    (pcols,pver)    ! temperature
    real(r8), intent(in) :: q    (pcols,pver)    ! specific humidity
    real(r8), intent(in) :: rair                 ! Gas constant for dry air
    real(r8), intent(in) :: gravit               ! Acceleration of gravity
    real(r8), intent(in) :: zvir                 ! rh2o/rair - 1

! Output arguments

    real(r8), intent(out) :: zi(pcols,pverp)     ! Height above surface at interfaces
    real(r8), intent(out) :: zm(pcols,pver)      ! Geopotential height at mid level
!
!---------------------------Local variables-----------------------------
!
    logical  :: fvdyn              ! finite volume dynamics
    integer  :: i,k                ! Lon, level indices
    real(r8) :: hkk(pcols)         ! diagonal element of hydrostatic matrix
    real(r8) :: hkl(pcols)         ! off-diagonal element
    real(r8) :: rog                ! Rair / gravit
    real(r8) :: tv                 ! virtual temperature
    real(r8) :: tvfac              ! Tv/T
!
!-----------------------------------------------------------------------
!
    rog = rair/gravit

! Set dynamics flag

    fvdyn = dycore_is ('LR')

! The surface height is zero by definition.

    do i = 1,ncol
       zi(i,pverp) = 0.0_r8
    end do

! Compute zi, zm from bottom up. 
! Note, zi(i,k) is the interface above zm(i,k)

    do k = pver, 1, -1

! First set hydrostatic elements consistent with dynamics

       if (fvdyn) then
          do i = 1,ncol
             hkl(i) = piln(i,k+1) - piln(i,k)
             hkk(i) = 1._r8 - pint(i,k) * hkl(i) * rpdel(i,k)
          end do
       else
          do i = 1,ncol
             hkl(i) = pdel(i,k) / pmid(i,k)
             hkk(i) = 0.5_r8 * hkl(i)
          end do
       end if

! Now compute tv, zm, zi

       do i = 1,ncol
          tvfac   = 1._r8 + zvir * q(i,k)
          tv      = t(i,k) * tvfac

          zm(i,k) = zi(i,k+1) + rog * tv * hkk(i)
          zi(i,k) = zi(i,k+1) + rog * tv * hkl(i)
       end do
    end do

    return
  end subroutine geopotential_t
end module geopotential