#include <misc.h>
#include <params.h>


subroutine grmult (ztodt   ,rcoslat ,coslat  ,coriol  ,div     , & 1,8
                   q3      ,t3      ,u3      ,v3      ,u3sld   , &
                   v3sld   ,ed1     ,ql      ,qm      ,tl      , &
                   tm      ,phis    ,phisl   ,phism   ,dpsl    , &
                   dpsm    ,omga    ,pmid    ,pdel    ,ps      , &
                   logps   ,rpmid   ,etamid  ,fu      ,fv      , &
                   t2      ,t3m1    ,u3m1    ,v3m1    ,etadot  , &
                   dpslon  ,dpslat  ,urhs    ,vrhs    ,trhs    , &
                   prhs    ,etadotm1,lnpssld ,prhssld ,tarrsld , &
                   parrsld ,nlon    )
!-----------------------------------------------------------------------
!
! Purpose:
! Compute non-linear dynamics terms in grid point space (in preparation
! for SLD interpolation)
!
!---------------------------Code history--------------------------------
!
! Author:  J. Olson
!
!-----------------------------------------------------------------------
!
! $Id: grmult.F90,v 1.6.2.2 2002/06/15 13:48:24 erik Exp $
! $Author: erik $
!
!-----------------------------------------------------------------------

  use shr_kind_mod, only: r8 => shr_kind_r8
  use pmgrid
  use pspect
  use comslt
  use commap
  use physconst, only: rair, zvir, cappa, cpvir
  use time_manager, only: is_first_step

  implicit none

#include <comhyb.h>
!------------------------------Arguments--------------------------------
!
  real(r8), intent(in)   :: ztodt                 ! delta-t
  real(r8), intent(in)   :: rcoslat               ! 1/(cos lat)
  real(r8), intent(in)   :: coslat                ! cos(lat)
  real(r8), intent(in)   :: coriol                ! Coriolis parameter
  real(r8), intent(in)   :: div     (plond,plev)  ! divergence
  real(r8), intent(in)   :: q3      (plond,plev)  ! specific humidity
  real(r8), intent(in)   :: t3      (plond,plev)  ! temperature
  real(r8), intent(in)   :: u3      (plond,plev)  ! zonal wind
  real(r8), intent(in)   :: v3      (plond,plev)  ! meridional wind
  real(r8), intent(out)  :: u3sld   (plond,plev)  ! u-wind (time n) (used for advection)
  real(r8), intent(out)  :: v3sld   (plond,plev)  ! v-wind (time n) (used for advection)
  real(r8), intent(in)   :: ed1     (plond,plev)  ! (1/ps)etadot(dp/deta) (time n-1)
  real(r8), intent(in)   :: ql      (plond,plev)  ! longitudinal derivative of q
  real(r8), intent(in)   :: qm      (plond,plev)  ! latitudinal  derivative of q
  real(r8), intent(in)   :: tl      (plond,plev)  ! zonal derivative of T
  real(r8), intent(in)   :: tm      (plond,plev)  ! meridional derivative of T
  real(r8), intent(in)   :: phis    (plond)       ! Phi at surface
  real(r8), intent(in)   :: phisl   (plond)       ! longitudinal derivative of phis
  real(r8), intent(in)   :: phism   (plond)       ! latitudinal  devivative of phis
  real(r8), intent(in)   :: dpsl    (plond)       ! longitudinal component of grad ln(ps)
  real(r8), intent(in)   :: dpsm    (plond)       ! latitudinal  component of grad ln(ps)
  real(r8), intent(in)   :: omga    (plond,plev)  ! vertical pressure velocity
  real(r8), intent(in)   :: pmid    (plond,plev)  ! pressure at full levels
  real(r8), intent(in)   :: pdel    (plond,plev)  ! layer thicknesses (pressure)
  real(r8), intent(in)   :: ps      (plond)       ! Surface pressure (n)
  real(r8), intent(in)   :: logps   (plond)       ! log(ps)
  real(r8), intent(in)   :: rpmid   (plond,plev)  ! 1./pmid
  real(r8), intent(in)   :: etamid  (plev)        ! midpoint values of eta (a+b)
  real(r8), intent(inout):: fu      (plond,plev)  ! nonlinear term - u momentum eqn
  real(r8), intent(inout):: fv      (plond,plev)  ! nonlinear term - v momentum eqn
  real(r8), intent(inout):: t2      (plond,plev)  ! nonlinear term - temperature
  real(r8), intent(out)  :: t3m1    (plond,plev)  ! T at time n-1
  real(r8), intent(inout):: u3m1    (plond,plev)  ! U at previous time step
  real(r8), intent(inout):: v3m1    (plond,plev)  ! V at previous time step
  real(r8), intent(out)  :: etadot  (plond,plevp) ! vertical velocity in eta coordinates
  real(r8), intent(out)  :: dpslon  (plond,plev)  ! Pressure gradient term
  real(r8), intent(out)  :: dpslat  (plond,plev)  ! Pressure gradient term
  real(r8), intent(inout):: urhs    (plond,plev)  ! RHS of U  eqn valid for mid-point
  real(r8), intent(inout):: vrhs    (plond,plev)  ! RHS of V  eqn valid for mid-point
  real(r8), intent(inout):: trhs    (plond,plev)  ! RHS of T  eqn valid for mid-point
  real(r8), intent(inout):: prhs    (plond,plev)  ! RHS of Ps eqn valid for mid-point
  real(r8), intent(inout):: etadotm1(plond,plevp) ! etadot for time n-1
  real(r8), intent(out)  :: lnpssld (plond,plev)  ! RHS Ps term for SLD
  real(r8), intent(out)  :: prhssld (plond,plev)  ! RHS Ps term for SLD
  real(r8), intent(out)  :: tarrsld (plond,plev)  ! T  at arr. pt. (SLD)
  real(r8), intent(out)  :: parrsld (plond,plev)  ! Ps at arr. pt. (SLD)
  integer , intent(in)   :: nlon                  ! number of longitudes for this latitude
!
!---------------------------Local workspace-----------------------------
!
  real(r8) tmp1                  ! temporary workspace
  real(r8) tmp2                  ! temporary workspace
  real(r8) tmp                   ! temporary workspace
  real(r8) tmpk                  ! workspace
  real(r8) tmpkp1                ! workspace
  real(r8) u3l     (plond,plev)  ! u-wind used locally only
  real(r8) v3l     (plond,plev)  ! v-wind used locally only
  real(r8) tv      (plond,plev)  ! virtual temperature
  real(r8) ddpk    (plond)       ! partial sum of div*delta p
  real(r8) ddpn    (plond)       ! complete sum of div*delta p
  real(r8) vkdp    (plond,plev)  ! V dot grad(ln(ps))
  real(r8) vpdsk   (plond)       ! partial sum  V dot grad(ln(ps)) delta b
  real(r8) vpdsn   (plond)       ! complete sum V dot grad(ln(ps)) delta b
  real(r8) lpsstar (plond)       ! Reference ln(Ps) (used to define a new 
!                                ! perturbation Ps)
  real(r8) lpsstarl(plond)       ! long. grad of reference ln(Ps)
  real(r8) lpsstarm(plond)       ! lat.  grad of reference ln(Ps)
  real(r8) rtv     (plond,plev)  ! rair*(tv+t0)
  real(r8) dt                    ! time step
  real(r8) hsl     (plond,plev)  ! zonal      deriv of hydrostatic term
  real(r8) hsm     (plond,plev)  ! meridional deriv of hydrostatic term
  real(r8) pspsl   (plond)       ! Ps*d(lnPs)/d(long.)
  real(r8) pspsm   (plond)       ! Ps*d(lnPs)/d(lat. )
  real(r8) ed1p    (plond,plevp) ! (1/ps)etadot(dp/deta) (time n-1)
  real(r8) rtvl                  ! zonal      derivative of R*Tv
  real(r8) rtvm                  ! meridional derivative of R*Tv
  real(r8) abp0                  ! constant for grad(H(n)) matrix
!
! Arrays which hold results from the RHS of the prognostic equations.
! Results will be interpolated to trajectory departure points in the routine
! SCANSLT.
!
  real(r8) tsld0a  (plond,plev)  ! RHS of T  eqn valid for mid-point
  real(r8) usldm   (plond,plev)  ! RHS of U  eqn valid for departure pt (n)
  real(r8) vsldm   (plond,plev)  ! RHS of V  eqn valid for departure pt (n)
  real(r8) tsldm   (plond,plev)  ! RHS of T  eqn valid for departure pt (n)
  real(r8) psldm   (plond,plev)  ! RHS of Ps eqn valid for departure pt (n)
  real(r8) urhsl   (plond,plev)  ! RHS of U  eqn valid for mid-point (n+1/2)
  real(r8) vrhsl   (plond,plev)  ! RHS of V  eqn valid for mid-point (n+1/2)
  real(r8) trhsl   (plond,plev)  ! RHS of T  eqn valid for mid-point (n+1/2)
  real(r8) prhsl   (plond,plev)  ! RHS of Ps eqn valid for mid-point (n+1/2)

  real(r8) onemeps               ! 1 - epssld (SLD decentering coefficient)
  real(r8) onepeps               ! 1 + epssld (SLD decentering coefficient)
  real(r8) detai(plevp)          ! interval between interfaces
  real(r8) facm1                 ! interpolation factor for time n-1
  real(r8) facm2                 ! interpolation factor for time n-2

  integer i,l,k                  ! longitude, level indices
  integer npr                    ! index
!
!-----------------------------------------------------------------------
!
  onemeps = 1. - epssld
  onepeps = 1. + epssld
  facm1   =  3./2.
  facm2   = -1./2.
!
  do k = 1,plev
     detai  (k) = (hyai(k+1) + hybi(k+1)) - (hyai(k) + hybi(k))
  end do
!
! Compute U/V for time n + 1/2
! The first formula will be used later for trajectory calculation
! The second will be used locally in the Hortal Temperature correction
!
  do k = 1,plev
     do i = 1,nlon
        u3sld(i,k) = 2.   *u3(i,k) -       u3m1(i,k)
        v3sld(i,k) = 2.   *v3(i,k) -       v3m1(i,k)
        u3l  (i,k) = facm1*u3(i,k) + facm2*u3m1(i,k)
        v3l  (i,k) = facm1*v3(i,k) + facm2*v3m1(i,k)
     end do
  end do
!
! Zero auxiliary fields
!
  tmp = 1./(rair*t0(plev))
  do i=1,nlon
     ddpk    (i) = 0.0
     ddpn    (i) = 0.0
     vpdsk   (i) = 0.0
     vpdsn   (i) = 0.0
     lpsstar (i) = -phis (i)*tmp
     lpsstarl(i) = -phisl(i)*tmp
     lpsstarm(i) = -phism(i)*tmp
     pspsl   (i) = ps(i)*dpsl(i)
     pspsm   (i) = ps(i)*dpsm(i)
     etadot(i,1) = 0.0
     ed1p  (i,1) = 0.0
     etadot(i,plevp) = 0.0
  end do
!
! Virtual temperature
!
  call virtem(nlon, plond, plev, t3      ,q3      ,zvir    ,tv)
!
! calculate some auxiliary quantities
!
  do k=1,plev
     do i=1,nlon
        ed1p(i,k+1) = ed1(i,k)
        rtv(i,k) = rair*tv(i,k)
!
! sum(plev)(div(k)*dp(k))
!
        ddpn(i) = ddpn(i) + div(i,k)*pdel(i,k)
     end do
  end do
!
! sum(plev)(v(k)*grad(lnps)*db(k))
!
  do k=nprlev,plev
     do i=1,nlon
        vkdp(i,k)= rcoslat*(u3(i,k)*pspsl(i) + v3(i,k)*pspsm(i))
        vpdsn(i) = vpdsn(i) + vkdp(i,k)*hybd(k)
     end do
  end do
!
! Compute etadot (top and bottom = 0.)
!
  do k = 1,plev-1
!
! Compute etadot(dp/deta)(k+1/2) and sum(k)(div(j)*dp(j))
!
     do i=1,nlon
        ddpk(i) = ddpk(i) + div(i,k)*pdel(i,k)
#ifdef HADVTEST
!
!jr Set etadot to zero for horizontal advection test
!
        etadot(i,k+1) = 0.
#else
        etadot(i,k+1) = -ddpk(i)
#endif
     end do
!
! sum(k)(v(j)*grad(ps)*db(j))
!
     if (k.ge.nprlev) then
        do i=1,nlon
           vpdsk(i) = vpdsk(i) + vkdp(i,k)*hybd(k)
#ifdef HADVTEST
!
!jr Set etadot to zero for horizontal advection test
!
           etadot(i,k+1) = 0.
#else
           etadot(i,k+1) = etadot(i,k+1) - vpdsk(i) + hybi(k+1)*(ddpn(i)+vpdsn(i))
#endif
        end do
     end if
!
! Convert eta-dot(dp/deta) to eta-dot
!
     tmp = etamid(k+1) - etamid(k)
     do i = 1,nlon
        etadot(i,k+1) = etadot(i,k+1)*tmp/(pmid(i,k+1) - pmid(i,k))
     end do
  end do
!
! Zonal and meridional derivatives of the hydrostatic term in the
! momentum equations.
!
  do k = 1,plev
     do i = 1,nlon
        tmp1     = (1. + zvir*q3(i,k))
        tmp2     = t3(i,k)*zvir
        rtvl     = rair*( tmp1*tl(i,k) + tmp2*ql(i,k) )
        rtvm     = rair*( tmp1*tm(i,k) + tmp2*qm(i,k) )
!
        tmp      = rpmid(i,k)*pdel(i,k)
        hsl(i,k) = -rtvl*tmp
        hsm(i,k) = -rtvm*tmp
     end do
     if(k .ge. nprlev) then
        abp0 = ps0*(hyam(k)*(hybi(k+1) - hybi(k)) - hybm(k)*(hyai(k+1) - hyai(k)))
        do i = 1,nlon
           tmp      = rtv(i,k)*abp0/(pmid(i,k)*pmid(i,k))
           hsl(i,k) = hsl(i,k) - pspsl(i)*tmp
           hsm(i,k) = hsm(i,k) - pspsm(i)*tmp
        end do
     endif
  end do
!
! Calculate RHS of all prognostic eqns
!
  do k = 1,plev
     tmp  = cappa*t0(k)*hypi(plevp)/hypm(k)
     tmp1 = hypd(k)/hypi(plevp)
     do i = 1,nlon
!
! Surface Pressure eqn
!
        prhsl(i,k) =  div(i,k)*(tmp1 - pdel(i,k)/ps(i))
        psldm(i,k) = -div(i,k)*tmp1 - ed1p(i,k+1) + ed1p(i,k)
!
! Temperature eqn
!
        trhsl (i,k) = cappa*tv(i,k)/(1. + cpvir*q3(i,k))*omga(i,k)*rpmid(i,k)
        trhsl (i,k) = trhsl(i,k) - omga(i,k)/ps(i)*tmp
        tsldm (i,k) = 0.5*(ed1p(i,k+1) + ed1p(i,k))*tmp
!
! ... horizontal advection portion of Hortal Temperature correction
!
        trhsl (i,k) = trhsl(i,k) - &
                      rcoslat*( u3(i,k)*lpsstarl(i) + v3(i,k)*lpsstarm(i) )*hortalc(k)
!
! ... Ritchie damping term for Temperature eqn.
!
        tsld0a(i,k) = rcoslat*( u3l(i,k)*lpsstarl(i) + v3l(i,k)*lpsstarm(i) )
!
! U/V eqns (includes only the diagonal portion of the hydrostatic term)
!
        urhsl(i,k) = 0.5*hsl(i,k) + href(k,k)*tl(i,k) + bps(k)*dpsl(i)
        vrhsl(i,k) = 0.5*hsm(i,k) + href(k,k)*tm(i,k) + bps(k)*dpsm(i)
        usldm(i,k) = -phisl(i) + v3(i,k)*coriol*coslat -href(k,k)*tl(i,k) - bps(k)*dpsl(i)
        vsldm(i,k) = -phism(i) - u3(i,k)*coriol*coslat -href(k,k)*tm(i,k) - bps(k)*dpsm(i)
     end do
!
! Add pressure gradient terms to momentum tendencies
!
     if (k.ge.nprlev) then
        do i=1,nlon
           tmp        = rtv(i,k)*hybm(k)*rpmid(i,k)
           dpslon(i,k) = rcoslat*tmp*pspsl(i)
           dpslat(i,k) = rcoslat*tmp*pspsm(i)
           urhsl(i,k) = urhsl(i,k) - dpslon(i,k)*coslat
           vrhsl(i,k) = vrhsl(i,k) - dpslat(i,k)*coslat
        end do
     else
        do i = 1,nlon
           dpslon(i,k) = 0.
           dpslat(i,k) = 0.
        end do
     end if
  end do
!
! Interior levels of the hydrostatic term
!
  do k=1,plev-1
     do l=k+1,plev
        do i=1,nlon
           urhsl(i,k) = urhsl(i,k) + href(l,k)*tl(i,l) + hsl(i,l)
           vrhsl(i,k) = vrhsl(i,k) + href(l,k)*tm(i,l) + hsm(i,l)
!
           usldm(i,k) = usldm(i,k) - href(l,k)*tl(i,l)
           vsldm(i,k) = vsldm(i,k) - href(l,k)*tm(i,l)
        end do
     end do
  end do

  if(is_first_step()) then
     do k = 1,plevp
        do i = 1,nlon
           etadotm1(i,k) = etadot(i,k)
        end do
     end do
  endif
!
! The modified etadotm1 will be used later for trajectory calculation in SCANSLT
!
  do k = 1,plevp
     do i = 1,nlon
        etadotm1(i,k) = 2.*etadot(i,k) - etadotm1(i,k)
     end do
  end do
!
! Compute vertical advection portion of Hortal Temperature correction
!
  npr = nprlev - 1
  if(npr .lt. 1) npr = 1
  do k = npr,plev-1
     tmpk   = 0.5*hdel(k-1)/detai(k)
     tmpkp1 = 0.5*hdel(k  )/detai(k)
     do i = 1,nlon
        trhsl(i,k) = trhsl(i,k) - (etadot(i,k  )*tmpk + etadot(i,k+1)*tmpkp1)*lpsstar(i)
     end do
  end do
!
! ... bottom level
!
  tmpk = 0.5*hdel(plev-1)/detai(plev)
  do i = 1,nlon
     trhsl(i,plev) = trhsl(i,plev) - etadot(i,plev)*tmpk*lpsstar(i)
  end do
!
! Compute (by extrapolation) RHS terms for time n + 1/2
!
  if(is_first_step()) then
     do k = 1,plev
        do i = 1,nlon
           urhs  (i,k) = urhsl (i,k)
           vrhs  (i,k) = vrhsl (i,k)
           trhs  (i,k) = trhsl (i,k)
           prhs  (i,k) = prhsl (i,k)
        end do
     end do
  endif
!
  do k = 1,plev
     do i = 1,nlon
        tmp         =       urhsl (i,k)
        urhsl (i,k) = facm1*urhsl (i,k) + facm2*urhs  (i,k)
        urhs  (i,k) = tmp
        tmp         =       vrhsl (i,k)
        vrhsl (i,k) = facm1*vrhsl (i,k) + facm2*vrhs  (i,k)
        vrhs  (i,k) = tmp
        tmp         =       trhsl (i,k)
        trhsl (i,k) = facm1*trhsl (i,k) + facm2*trhs  (i,k)
        trhs  (i,k) = tmp
        tmp         =       prhsl (i,k)
        prhsl (i,k) = facm1*prhsl (i,k) + facm2*prhs  (i,k)
        prhs  (i,k) = tmp
     end do
  end do
!
! Prepare SLD arrays for interpolation by SCANSLT
!
! Append appropriate coefficients (including decentering epsilon values -- See
! Notes 13,14,15)
!
  dt   = 0.5*ztodt
  tmp1 = 0.5*ztodt/coslat
  do k = 1,plev
     tmp = cappa*t0(k)*hypi(plevp)/hypm(k)
     do i = 1,nlon
        urhsl (i,k) = tmp1*urhsl (i,k)
        vrhsl (i,k) = tmp1*vrhsl (i,k)
        trhsl (i,k) = dt  *trhsl (i,k)
        tsld0a(i,k) = dt  *tsld0a(i,k)
        prhsl (i,k) = dt  *prhsl (i,k)
        t2    (i,k) = ztodt*t2   (i,k)
        fu    (i,k) = ztodt*fu   (i,k)
        fv    (i,k) = ztodt*fv   (i,k)
!
! Combine terms.
! (Time split the midpoint results between arrival and departure
!  points)
!
        tmp2          = lpsstar(i)*hortalc(k)
        u3m1    (i,k) = (u3  (i,k)*coslat + onemeps*usldm(i,k)*dt)/coslat + &
                                            onemeps*urhsl(i,k) + &
                                            onemeps*fu   (i,k)*0.5
        v3m1    (i,k) = (v3  (i,k)*coslat + onemeps*vsldm(i,k)*dt)/coslat + &
                                            onemeps*vrhsl(i,k) + &
                                            onemeps*fv   (i,k)*0.5
#ifdef HADVTEST
        t3m1    (i,k) = t3  (i,k)
#else
        t3m1    (i,k) = t3  (i,k) +         onemeps*tsldm(i,k)*dt + &
                                            onemeps*trhsl(i,k) - tmp2 + &
                                            onemeps*t2(i,k)*0.5
#endif
        prhssld (i,k) = (psldm(i,k)*dt + prhsl(i,k))*onemeps
        tarrsld (i,k) = (trhsl(i,k) + &
                         hybm(k)*tmp*tsld0a(i,k))*onepeps + tmp2 + onepeps*t2(i,k)*0.5
        parrsld (i,k) = (prhsl(i,k) - hybd(k)*tsld0a(i,k))*onepeps
        lnpssld (i,k) = logps(i) - lpsstar(i) - tsld0a(i,k)*onemeps
        fu      (i,k) = urhsl(i,k)*coslat*onepeps + fu(i,k)*coslat*onepeps*0.5
        fv      (i,k) = vrhsl(i,k)*coslat*onepeps + fv(i,k)*coslat*onepeps*0.5
     end do
  end do
#ifdef VADVTEST
  do k=2,plev
     do i=1,nlon
        etadot(i,k) = -0.5/(12.*86400.)
     end do
  end do
#endif
!
  return
end subroutine grmult