#include "rundeck_opts.h"


      SUBROUTINE OCEANS 1,423
!@sum  OCEANS integrates ocean source terms and dynamics
!@auth Gary Russell/Gavin Schmidt
!@ver  1.0
      USE CONSTANT, only : rhows,grav
      USE MODEL_COM, only : idacc,modd5s,msurf
#ifdef TRACERS_AGE_OCEAN
     *     ,dtsrc,JDperY,JHOUR
      USE CONSTANT, only: SDAY
      USE OCEAN, only : DXYPO
#endif
#ifdef TRACERS_OCEAN
      USE TRACER_COM, only : t_qlimit,ntm,n_age
      USE OCEAN, only : trmo,txmo,tymo,tzmo
C?*** For serial GM/straits computations, pack data into global arrays
      USE OCEAN, only : trmo_glob,txmo_glob,tymo_glob,tzmo_glob
#endif
      USE OCEAN, only : im,jm,lmo,ndyno,mo,g0m,gxmo,gymo,gzmo
     *     ,s0m,sxmo,symo,szmo,dts,dtofs,dto,dtolf,mdyno,msgso
     *     ,ogeoz,ogeoz_sv,opbot,ze,lmm,imaxj, UO,VONP,IVNP !,VOSP,IVSP
     *     ,OBottom_drag,OCoastal_drag,focean
C?*** For serial GM/straits computations, pack data into global arrays
      USE OCEAN, only : S0M_glob,SXMO_glob,SYMO_glob,SZMO_glob
      USE OCEAN, only : G0M_glob,GXMO_glob,GYMO_glob,GZMO_glob

      USE ODIAG, only : oijl=>oijl_loc,oij=>oij_loc
     *     ,ijl_mo,ijl_g0m,ijl_s0m,  ijl_gflx,ijl_sflx
     *     ,ijl_mfu,ijl_mfv,ijl_mfw, ijl_ggmfl,ijl_sgmfl,ij_ssh,ij_pb
#ifdef TRACERS_OCEAN
     *     ,toijl=>toijl_loc, toijl_conc,toijl_tflx,toijl_gmfl
#endif
      USE OCEAN_DYN, only : mmi,smu,smv,smw
      USE DOMAIN_DECOMP, only : grid,get, haveLatitude

C?*** For serial ODIF/GM/straits computations:
      USE DOMAIN_DECOMP, only : AM_I_ROOT, pack_data, unpack_data
      USE OCEAN, only : scatter_ocean, gather_ocean
      USE OCEAN, only : scatter_ocean_straits, gather_ocean_straits

      IMPLICIT NONE
      REAL*8, DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO) ::
     *     MM0,MM1,MMX,UM0,VM0,UM1,VM1
      INTEGER NS,I,J,L,mnow,n

c**** Extract domain decomposition info
      INTEGER :: J_0, J_1, J_0H
      CALL GET(grid, J_STRT = J_0, J_STOP = J_1, J_STRT_HALO = J_0H)

C**** initiallise work arrays
      MM0=0 ; MM1=0 ; MMX=0 ; UM0=0 ; VM0=0 ; UM1=0 ; VM1=0

C****
C**** Integrate Ocean Dynamics terms
C****
      OGEOZ_SV(:,:)=OGEOZ(:,:)

C**** Apply surface fluxes to ocean
      CALL GROUND_OC
         CALL CHECKO('GRNDOC')

C**** Apply ice/ocean and air/ocean stress to ocean
      CALL OSTRES
         CALL CHECKO('OSTRES')
         CALL TIMER (MNOW,MSURF)
         IF (MODD5S == 0) CALL DIAGCA (11)

C**** Calculate vertical diffusion
      CALL OCONV
         CALL CHECKO('OCONV ')

C**** Apply bottom and coastal drags
      if (OBottom_drag  == 1) CALL OBDRAG
      if (OCoastal_drag == 1) CALL OCOAST
         CALL TIMER (MNOW,MSGSO)

      IDACC(11) = IDACC(11) + 1

C****
C**** Integrate Ocean Dynamics
C****
C**** initialize summed mass fluxes
!$OMP PARALLEL DO  PRIVATE(L)
      DO L=1,LMO
        SMU(:,:,L) = 0. ; SMV(:,:,L) = 0. ; SMW(:,:,L) = 0.
      END DO
!$OMP END PARALLEL DO

      CALL OVTOM (MMI,UM0,VM0)

      CALL OPGF0

      NS=NDYNO
C**** Initial Forward step,  QMX = QM0 + DT*F(Q0)
      CALL OFLUX  (NS,MMI,.FALSE.)
      CALL OADVM (MM1,MMI,DTOFS)
C     CALL STADVM(MM1,MUST,DTOFS,.False.)
      CALL OADVV (UM1,VM1,UM0,VM0,DTOFS)
      CALL OPGF  (UM1,VM1,DTOFS)
C     CALL STPGF (MUST1,MUST,DTOFS)
      CALL OMTOV (MM1,UM1,VM1)
C**** Initial Backward step,  QM1 = QM0 + DT*F(Q0)
      CALL OFLUX  (NS,MMI,.FALSE.)
      CALL OADVM (MM1,MMI,DTO)
C     CALL STADVM(MM1,MUST,DTO,.False.)
      CALL OADVV (UM1,VM1,UM0,VM0,DTO)
      CALL OPGF  (UM1,VM1,DTO)
C     CALL STPGF (MUST1,MUST,DTO)
      CALL OMTOV (MM1,UM1,VM1)
C**** First even leap frog step,  Q2 = Q0 + 2*DT*F(Q1)
      CALL OFLUX  (NS,MMI,.TRUE.)
      CALL OADVM (MM0,MMI,DTOLF)
C     CALL STADVM(MM0,MUST1,DTOLF,.True.)
      CALL OADVV (UM0,VM0,UM0,VM0,DTOLF)
      CALL OPGF  (UM0,VM0,DTOLF)
C     CALL STPGF (MUST,MUST,DTOLF)
      CALL OMTOV (MM0,UM0,VM0)
      NS=NS-1
C**** Odd leap frog step,  Q3 = Q1 + 2*DT*F(Q2)
  420 Continue
      CALL OFLUX  (NS,MM1,.FALSE.)
      CALL OADVM (MM1,MM1,DTOLF)
C     CALL STADVM(MM1,MUST,DTOLF,.False.)
      CALL OADVV (UM1,VM1,UM1,VM1,DTOLF)
      CALL OPGF  (UM1,VM1,DTOLF)
C     CALL STPGF (MUST1,MUST1,DTOLF)
      CALL OMTOV (MM1,UM1,VM1)
      NS=NS-1
C**** Even leap frog step,  Q4 = Q2 + 2*DT*F(Q3)
      CALL OFLUX  (NS,MM0,.TRUE.)
      CALL OADVM (MM0,MM0,DTOLF)
C     CALL STADVM(MM0,MUST1,DTOLF,.True.)
      CALL OADVV (UM0,VM0,UM0,VM0,DTOLF)
      CALL OPGF  (UM0,VM0,DTOLF)
C     CALL STPGF (MUST,MUST,DTOLF)
      CALL OMTOV (MM0,UM0,VM0)
C**** Check for end of leap frog time scheme
      NS=NS-1
      IF(NS.GT.1)  GO TO 420
C     if(j_0 ==  1) UO(IVSP,1 ,:) = VOSP(:) ! not needed if Mod(IM,4)=0
      if(j_1 == JM) UO(IVNP,JM,:) = VONP(:) ! not needed if Mod(IM,4)=0
!$OMP PARALLEL DO  PRIVATE(L)
      DO L=1,LMO
        OIJL(:,:,L,IJL_MFU) = OIJL(:,:,L,IJL_MFU) + SMU(:,:,L)
        OIJL(:,:,L,IJL_MFV) = OIJL(:,:,L,IJL_MFV) + SMV(:,:,L)
        OIJL(:,:,L,IJL_MFW) = OIJL(:,:,L,IJL_MFW) + SMW(:,:,L)
      END DO
!$OMP END PARALLEL DO

C**** Advection of Potential Enthalpy and Salt
      CALL OADVT (G0M,GXMO,GYMO,GZMO,DTOLF,.FALSE.
     *        ,OIJL(1,J_0H,1,IJL_GFLX))
      CALL OADVT (S0M,SXMO,SYMO,SZMO,DTOLF,.TRUE.
     *        ,OIJL(1,J_0H,1,IJL_SFLX))

#ifdef TRACERS_OCEAN
      DO N=1,NTM
        CALL OADVT(TRMO(1,J_0H,1,N),TXMO(1,J_0H,1,N)
     *       ,TYMO(1,J_0H,1,N),TZMO(1,J_0H,1,N),DTOLF,t_qlimit(n)
     *       ,TOIJL(1,J_0H,1,TOIJL_TFLX,N))
      END DO
#endif
        CALL CHECKO ('OADVT ')
!$OMP PARALLEL DO PRIVATE(L)
        DO L=1,LMO
          OIJL(:,:,L,IJL_MO)  = OIJL(:,:,L,IJL_MO) +  MO(:,:,L)
          OIJL(:,:,L,IJL_G0M) = OIJL(:,:,L,IJL_G0M) + G0M(:,:,L)
          OIJL(:,:,L,IJL_S0M) = OIJL(:,:,L,IJL_S0M) + S0M(:,:,L)
        END DO
!$OMP END PARALLEL DO
        DO J=J_0,J_1
          DO I=1,IMAXJ(J)
            IF (FOCEAN(I,J).gt.0) THEN
              OIJ(I,J,IJ_SSH) = OIJ(I,J,IJ_SSH) + OGEOZ(I,J)
              OIJ(I,J,IJ_PB)  = OIJ(I,J,IJ_PB)  +
     +             (OPBOT(I,J)-ZE(LMM(I,J))*RHOWS*GRAV)
            END IF
          END DO
        END DO

#ifdef TRACERS_OCEAN
        DO N=1,NTM
!$OMP PARALLEL DO PRIVATE(L)
          DO L=1,LMO
            TOIJL(:,:,L,TOIJL_CONC,N)=TOIJL(:,:,L,TOIJL_CONC,N)
     *           +TRMO(:,:,L,N)
          END DO
!$OMP END PARALLEL DO
        END DO
#endif
        CALL TIMER (MNOW,MDYNO)

        IF (MODD5S == 0) CALL DIAGCA (12)

C**** Apply Wajowicz horizontal diffusion to UO and VO ocean currents
      CALL ODIFF(DTS)
      CALL CHECKO ('ODIFF0')

C**** Apply GM + Redi tracer fluxes
      CALL GMKDIF
      CALL GMFEXP(G0M,GXMO,GYMO,GZMO,.FALSE.,OIJL(1,J_0H,1,IJL_GGMFL))
      CALL GMFEXP(S0M,SXMO,SYMO,SZMO,.TRUE. ,OIJL(1,J_0H,1,IJL_SGMFL))
#ifdef TRACERS_OCEAN
      DO N = 1,NTM
        CALL GMFEXP(TRMO(1,J_0H,1,N),TXMO(1,J_0H,1,N),TYMO(1,J_0H,1,N),
     *    TZMO(1,J_0H,1,N),t_qlimit(n),TOIJL(1,J_0H,1,TOIJL_GMFL,N))
      END DO
#endif
      CALL CHECKO ('GMDIFF')
      CALL TIMER (MNOW,MSGSO)

c????          Non-parallelized parts : straits
c     straits: mo, G0M,Gx-zMO,S0M,Sx-zMO,TRMO,Tx-zMO,opress (ocean)

      call gather_ocean_straits (2)

      IF(AM_I_ROOT()) THEN
C****
C**** Acceleration and advection of tracers through ocean straits
C****
        CALL STPGF
        CALL STCONV
        CALL STBDRA
          CALL CHECKO_serial ('STBDRA')
        CALL STADV
          CALL CHECKO_serial ('STADV0')
      END IF

      call scatter_ocean_straits (2)
      call BCAST_straits (0)
c????      end of serialized part
        CALL CHECKO ('STADV ')
C**** remove STADVI since it is not really consistent with ICEDYN
c      CALL STADVI
c        CALL CHECKO ('STADVI')
#ifdef TRACERS_OCEAN
      CALL OC_TDECAY

#ifdef TRACERS_AGE_OCEAN
!at each time step set surface tracer conc=0 and add 1 below
!this is mass*age (kg*year)
!age=1/(JDperY*24*3600) in years
      TRMO(:,:,1,n_age)=0
      TXMO(:,:,1,n_age)=0 ; TYMO(:,:,1,n_age)=0 ; TZMO(:,:,1,n_age)=0
      DO J=J_0,J_1
      DO I=1,IMAXJ(J)
        IF (FOCEAN(I,J).gt.0) THEN
        DO L=2,LMO
           TRMO(I,J,L,n_age)= TRMO(I,J,L,n_age) +
     +                   dtsrc/(JDperY*SDAY) * MO(I,J,L) * DXYPO(J)
        ENDDO
        ENDIF
      ENDDO
      ENDDO
#endif
#endif
        CALL TIMER (MNOW,MSGSO)
      CALL TOC2SST
      RETURN

      END SUBROUTINE OCEANS


      SUBROUTINE init_OCEAN(iniOCEAN,istart) 1,123
!@sum init_OCEAN initiallises ocean variables
!@auth Original Development Team
!@ver  1.0
      USE FILEMANAGER, only : openunit,closeunit
      USE TIMINGS, only : timing,ntimeacc
      USE PARAM
      USE CONSTANT, only : twopi,radius,by3,grav,rhow
      USE MODEL_COM, only : dtsrc,kocean
      USE OCEAN, only : im,jm,lmo,focean,ze1,zerat,sigeo,dsigo,sigo,lmm
     *     ,lmu,lmv,hatmo,hocean,ze,dZO,mo,g0m,gxmo,gymo,gzmo,s0m,sxmo
     *     ,symo,szmo,uo,vo,dxypo,ogeoz,dts,dtolf,dto,dtofs,mdyno,msgso
     *     ,ndyno,imaxj,ogeoz_sv,bydts,lmo_min,j1o
     *     ,OBottom_drag,OCoastal_drag,oc_salt_mean
#ifdef TRACERS_OCEAN
     *     ,oc_tracer_mean,ntm
#endif
      USE OCFUNC, only : vgsp,tgsp,hgsp,agsp,bgsp,cgs
      USE SW2OCEAN, only : init_solar
      USE FLUXES, only : ogeoza, uosurf, vosurf

      USE DOMAIN_DECOMP, only : grid,get

      IMPLICIT NONE
      INTEGER I,J,L,N,iu_OIC,iu_OFTAB,IP1,IM1,LMIJ,I1,J1,I2,J2
     *     ,iu_TOPO,II,JJ
      REAL*4, DIMENSION(IM,JM,LMO):: MO4,G0M4,S0M4,GZM4,SZM4
      CHARACTER*80 TITLE
      REAL*8 FJEQ,SM,SG0,SGZ,SS0,SSZ
      LOGICAL, INTENT(IN) :: iniOCEAN
      INTEGER, INTENT(IN) :: istart
      LOGICAL :: iniStraits
c**** Extract domain decomposition info
      INTEGER :: J_0, J_1, J_0S, J_1S
      LOGICAL :: HAVE_NORTH_POLE
      CALL GET(grid, J_STRT = J_0, J_STOP = J_1
     *      ,J_STRT_SKP  = J_0S, J_STOP_SKP  = J_1S
     *      ,HAVE_NORTH_POLE = HAVE_NORTH_POLE)

C****
C**** Check that KOCEAN is set correctly
C****
      IF (KOCEAN == 0) THEN
        call stop_model(
     &       "Must have KOCEAN > 0 for interactive ocean runs",255)
      END IF
C****
C**** Select drag options
C****
      call sync_param("OBottom_drag",OBottom_drag)
      call sync_param("OCoastal_drag",OCoastal_drag)

C**** define initial condition options for global mean
      call sync_param("oc_salt_mean",oc_salt_mean)
#ifdef TRACERS_OCEAN
      call sync_param("oc_tracer_mean",oc_tracer_mean,ntm)
#endif

C****
C**** set up time steps from atmospheric model
C****
      call sync_param("DTO",DTO)

      DTS=DTSRC
      BYDTS=1d0/DTS
      NDYNO=2*NINT(.5*DTS/DTO)
      DTO=DTS/NDYNO
      DTOLF=2.*DTO
      DTOFS=2.*DTO*BY3
C**** Set up timing indexes
      CALL SET_TIMER(" OCEAN DYNAM",MDYNO)
      CALL SET_TIMER(" OCEAN PHYS.",MSGSO)
C****
C**** Arrays needed each ocean model run
C****
      CALL GEOMO
C**** Calculate ZE, SIGEO, DSIGO and SIGO
      DO 110 L=0,LMO
  110 ZE(L) = ZE1*(ZERAT**L-1d0)/(ZERAT-1d0)
      SIGEO(0) = 0.
      DO 120 L=1,LMO
      SIGEO(L) = ZE(L)/ZE(LMO)
      DSIGO(L) = SIGEO(L) - SIGEO(L-1)
      SIGO(L) = (SIGEO(L) + SIGEO(L-1))*5d-1
  120 dZO(L) = ZE(L) - ZE(L-1)
C**** Read in table function for specific volume
      CALL openunit("OFTAB",iu_OFTAB,.TRUE.,.TRUE.)
      READ  (iu_OFTAB) TITLE,VGSP
      WRITE (6,*) 'Read from unit ',iu_OFTAB,': ',TITLE
      READ  (iu_OFTAB) TITLE,TGSP
      WRITE (6,*) 'Read from unit ',iu_OFTAB,': ',TITLE
      READ  (iu_OFTAB) TITLE,CGS
      WRITE (6,*) 'Read from unit ',iu_OFTAB,': ',TITLE
      READ  (iu_OFTAB) TITLE,HGSP
      WRITE (6,*) 'Read from unit ',iu_OFTAB,': ',TITLE
      READ  (iu_OFTAB) TITLE,AGSP
      WRITE (6,*) 'Read from unit ',iu_OFTAB,': ',TITLE
      READ  (iu_OFTAB) TITLE,BGSP
      WRITE (6,*) 'Read from unit ',iu_OFTAB,': ',TITLE
      call closeunit(iu_OFTAB)

C**** READ IN LANDMASKS AND TOPOGRAPHIC DATA
      call openunit("TOPO_OC",iu_TOPO,.true.,.true.)
      CALL READT (iu_TOPO,0,FOCEAN,IM*JM,FOCEAN,1) ! Ocean fraction
      CALL READT (iu_TOPO,0,HATMO ,IM*JM,HATMO ,4) ! Atmo. Topography
      CALL READT (iu_TOPO,0,HOCEAN,IM*JM,HOCEAN,1) ! Ocean depths
      call closeunit(iu_TOPO)

C**** Calculate J1O = least J with some ocean
      Do 130 J=1,JM
  130 If (Sum(FOCEAN(:,J)) > 0)  GoTo 140
  140 J1O = J
      write(6,*) "Minimum J with some ocean:",J1O
C**** Fix to 4 for temporary consistency
      J1O=4
      write(6,*) "Fixed J1O:",J1O

C**** Calculate LMM and modify HOCEAN
      call sync_param("LMO_min",LMO_min)
      DO 170 J=1,JM    ! global arrays (fixed from now on)
      DO 170 I=1,IM
      LMM(I,J) =0
      IF(FOCEAN(I,J).LE.0.)  GO TO 170
      DO 150 L=LMO_min,LMO-1
  150 IF(HATMO(I,J)+HOCEAN(I,J) .le. 5d-1*(ZE(L)+ZE(L+1)))  GO TO 160
C     L=LMO
  160 LMM(I,J)=L
      HOCEAN(I,J) = -HATMO(I,J) + ZE(L)
  170 CONTINUE
C**** Calculate LMU
      I=IM
      DO 180 J=1,JM    ! global arrays (fixed from now on)
      DO 180 IP1=1,IM
      LMU(I,J) = MIN(LMM(I,J),LMM(IP1,J))
  180 I=IP1
C**** Calculate LMV
      DO 190 J=1,JM-1  ! global arrays (fixed from now on)
      DO 190 I=1,IM
 190  LMV(I,J) = MIN(LMM(I,J),LMM(I,J+1))
C****
      IF(iniOCEAN) THEN
C**** Initialize a run from ocean initial conditions
C???? For starters, let all processes read the IC
      CALL openunit("OIC",iu_OIC,.TRUE.,.TRUE.)
      READ  (iu_OIC,ERR=820) TITLE,MO4,G0M4,GZM4,S0M4,SZM4
      call closeunit(iu_OIC)
      WRITE (6,*) 'OIC read from unit ',iu_OIC,': ',TITLE
C**** Calculate layer mass from column mass and check for consistency
      DO 313 J=J_0,J_1
      DO 313 I=1,IM
      LMIJ=LMM(I,J)
      DO 311 L=1,LMIJ
  311 MO(I,J,L) = MO4(I,J,L)
      DO 312 L=LMIJ+1,LMO
  312 MO(I,J,L) = 0.
C**** if there is a problem try nearest neighbour
      IF((LMM(I,J).GT.0).AND.(ABS(MO(I,J,1)/ZE(1)-1d3).GT.5d1)) THEN
        WRITE (6,931) I,J,LMIJ,MO(I,J,1),ZE(1)
        II=0 ; JJ=0
        IF (MO4(I,J+1,1).gt.0 .and. LMM(I,J+1).ge.LMM(I,J)) THEN
          II=I ; JJ=J+1
        ELSEIF (MO4(I+1,J,1).gt.0 .and. LMM(I+1,J).ge.LMM(I,J)) THEN
          II=I+1 ; JJ=J
        ELSEIF (MO4(I,J-1,1).gt.0 .and. LMM(I,J-1).ge.LMM(I,J)) THEN
          II=I ; JJ=J-1
        ELSEIF (MO4(I-1,J,1).gt.0 .and. LMM(I-1,J).ge.LMM(I,J)) THEN
          II=I-1 ; JJ=J
        ELSEIF (MO4(I+1,J+1,1).gt.0 .and. LMM(I+1,J+1).ge.LMM(I,J)) THEN
          II=I+1 ; JJ=J+1
        ELSEIF (MO4(I-1,J+1,1).gt.0 .and. LMM(I-1,J+1).ge.LMM(I,J)) THEN
          II=I-1 ; JJ=J+1
        ELSEIF (MO4(I+1,J-1,1).gt.0 .and. LMM(I+1,J-1).ge.LMM(I,J)) THEN
          II=I+1 ; JJ=J-1
        ELSEIF (MO4(I-1,J-1,1).gt.0 .and. LMM(I-1,J-1).ge.LMM(I,J)) THEN
          II=I-1 ; JJ=J-1
        END IF
        IF (II.ne.0) THEN
          MO(I,J,1:LMM(I,J))=MO4(II,JJ,1:LMM(I,J))
          G0M4(I,J,1:LMM(I,J))=G0M4(II,JJ,1:LMM(I,J))
          S0M4(I,J,1:LMM(I,J))=S0M4(II,JJ,1:LMM(I,J))
          GZM4(I,J,1:LMM(I,J))=GZM4(II,JJ,1:LMM(I,J))
          SZM4(I,J,1:LMM(I,J))=SZM4(II,JJ,1:LMM(I,J))
          WRITE (6,*) "Inconsistency at ",I,J,"fixed from :",II,JJ
        END IF
      END IF
  313 CONTINUE
C**** Initialize velocity field to zero
      UO=0
      VO=0
C**** Define mean value of mass, potential heat, and salinity at poles
      DO 370 L=1,LMO
      if(HAVE_NORTH_POLE) then ! average polar ocean fields
        J=JM
        SM  = 0.
        SG0 = 0.
        SGZ = 0.
        SS0 = 0.
        SSZ = 0.
        DO I=1,IM
          SM  = SM  +   MO(I,J,L)
          SG0 = SG0 + G0M4(I,J,L)
          SGZ = SGZ + GZM4(I,J,L)
          SS0 = SS0 + S0M4(I,J,L)
          SSZ = SSZ + SZM4(I,J,L)
        end do
        DO I=1,IM
          MO(I,J,L)   = SM /IM
          G0M4(I,J,L) = SG0/IM
          GZM4(I,J,L) = SGZ/IM
          S0M4(I,J,L) = SS0/IM
          SZM4(I,J,L) = SSZ/IM
        end do
      end if
C**** Define East-West horizontal gradients
      GXMO=0
      GYMO=0
      SXMO=0
      SYMO=0
      IM1=IM-1
      I=IM
      DO 345 J=J_0S,J_1S
      DO 345 IP1=1,IM
      IF(LMM(I  ,J).LT.L)  GO TO 344
      IF(LMM(IM1,J).GE.L)  GO TO 342
      IF(LMM(IP1,J).LT.L)  GO TO 344
      GXMO(I,J,L) = .5*(G0M4(IP1,J,L)-G0M4(I,J,L))
      SXMO(I,J,L) = .5*(S0M4(IP1,J,L)-S0M4(I,J,L))
      GO TO 344
  342 IF(LMM(IP1,J).GE.L)  GO TO 343
      GXMO(I,J,L) = .5*(G0M4(I,J,L)-G0M4(IM1,J,L))
      SXMO(I,J,L) = .5*(S0M4(I,J,L)-S0M4(IM1,J,L))
      GO TO 344
  343 GXMO(I,J,L) = .25*(G0M4(IP1,J,L)-G0M4(IM1,J,L))
      SXMO(I,J,L) = .25*(S0M4(IP1,J,L)-S0M4(IM1,J,L))
  344 IM1=I
  345 I=IP1
C**** Define North-South horizontal gradients
      DO 354 J=J_0S,J_1S
      DO 354 I=1,IM
      IF(LMM(I,J  ).LT.L)  GO TO 354
      IF(LMM(I,J-1).GE.L)  GO TO 352
      IF(LMM(I,J+1).LT.L)  GO TO 354
      GYMO(I,J,L) = .5*(G0M4(I,J+1,L)-G0M4(I,J,L))
      SYMO(I,J,L) = .5*(S0M4(I,J+1,L)-S0M4(I,J,L))
      GO TO 354
  352 IF(LMM(I,J+1).GE.L)  GO TO 353
      GYMO(I,J,L) = .5*(G0M4(I,J,L)-G0M4(I,J-1,L))
      SYMO(I,J,L) = .5*(S0M4(I,J,L)-S0M4(I,J-1,L))
      GO TO 354
  353 GYMO(I,J,L) = .25*(G0M4(I,J+1,L)-G0M4(I,J-1,L))
      SYMO(I,J,L) = .25*(S0M4(I,J+1,L)-S0M4(I,J-1,L))
  354 CONTINUE
C**** Multiply specific quantities by mass
      DO 360 J=J_0,J_1
      DO 360 I=1,IM
      G0M(I,J,L)  = G0M4(I,J,L)*(MO(I,J,L)*DXYPO(J))
      GXMO(I,J,L) = GXMO(I,J,L)*(MO(I,J,L)*DXYPO(J))
      GYMO(I,J,L) = GYMO(I,J,L)*(MO(I,J,L)*DXYPO(J))
      GZMO(I,J,L) = GZM4(I,J,L)*(MO(I,J,L)*DXYPO(J))
      S0M(I,J,L)  = S0M4(I,J,L)*(MO(I,J,L)*DXYPO(J))
      SXMO(I,J,L) = SXMO(I,J,L)*(MO(I,J,L)*DXYPO(J))
      SYMO(I,J,L) = SYMO(I,J,L)*(MO(I,J,L)*DXYPO(J))
  360 SZMO(I,J,L) = SZM4(I,J,L)*(MO(I,J,L)*DXYPO(J))
  370 CONTINUE
C**** Initiallise geopotential field (needed by KPP)
      OGEOZ = 0.
      OGEOZ_SV = 0.

      END IF

C**** Extend ocean data to added layers at bottom if necessary
      if (istart.gt.0 .and. lmo_min .gt. 1) then
        do j=j_0s,j_1s
        do i=1,im
          if (lmm(i,j) == lmo_min .and. MO(i,j,lmo_min) == 0.) then
            do l=2,lmo_min
              if (MO(i,j,l) == 0.) then
                MO(i,j,l)   = (ZE(L)-ZE(L-1))*RHOW*
     *                     (1.+S0M(i,j,l-1)/(MO(i,j,l-1)*DXYPO(J)))
                G0M(i,j,l)  = G0M(i,j,l-1) *(MO(i,j,l)/MO(i,j,l-1))
                GXMO(i,j,l) = GXMO(i,j,l-1)*(MO(i,j,l)/MO(i,j,l-1))
                GYMO(i,j,l) = GYMO(i,j,l-1)*(MO(i,j,l)/MO(i,j,l-1))
                GZMO(i,j,l) = 0.
                S0M(i,j,l)  = S0M(i,j,l-1) *(MO(i,j,l)/MO(i,j,l-1))
                SXMO(i,j,l) = SXMO(i,j,l-1)*(MO(i,j,l)/MO(i,j,l-1))
                SYMO(i,j,l) = SYMO(i,j,l-1)*(MO(i,j,l)/MO(i,j,l-1))
                SZMO(i,j,l) = 0.
              end if
            end do
          end if
        end do
        end do
      end if

C**** zero out unphysical values (that might have come from a
C**** restart file with different topography)
      if (istart.lt.9) then
        DO J=J_0S,J_1S
          DO I=1,IMAXJ(J)
            VO(I,J,LMV(I,J)+1:LMO)=0.
          END DO
        END DO
        DO J=J_0,J_1
          DO I=1,IMAXJ(J)
            UO(I,J,LMU(I,J)+1:LMO)=0.
            MO(I,J,LMM(I,J)+1:LMO)=0.
          END DO
        END DO
      end if

C**** Initialize straits arrays
      iniStraits=iniOCEAN.or.(istart.lt.9)
      call init_STRAITS(iniStraits)

C**** Adjust global mean salinity if required (only at first start up)
      if (istart.gt.0 .and. istart.lt.9 .and. oc_salt_mean.ne.-999.)
     *     call adjust_mean_salt

C**** Initialize solar radiation penetration arrays
      call init_solar

C**** Initialize ocean diagnostics
      call init_ODIAG

C**** Initialize KPP mixing scheme
      call kmixinit(ZE)

#ifdef TRACERS_OCEAN
C**** Set diagnostics for ocean tracers
      call init_tracer_ocean
#endif

C**** Initialize some info passed to atmsophere
      uosurf=0 ; vosurf=0. ; ogeoza=0.

C**** Set atmospheric surface variables
      IF (ISTART.gt.0) CALL TOC2SST

      RETURN
C**** Terminate because of improper start up
  820 call stop_model('init_OCEAN: Error reading ocean IC',255)
C****
  931 FORMAT ('0Inconsistency between LMM and M:',3I4,2F10.1)

      RETURN
C****
      END SUBROUTINE init_OCEAN


      SUBROUTINE daily_OCEAN(end_of_day) 4,17
!@sum  daily_OCEAN performs the daily tasks for the ocean module
!@auth Original Development Team
!@ver  1.0
      USE CONSTANT, only : sday
      IMPLICIT NONE
      LOGICAL, INTENT(IN) :: end_of_day

C**** Only do this at end of the day
      IF (end_of_day) THEN

C**** Add glacial melt from Antarctica and Greenland
        CALL GLMELT(SDAY)

C**** set gtemp arrays for ocean
        CALL TOC2SST
      END IF
C****
      RETURN
      END SUBROUTINE daily_OCEAN


      SUBROUTINE io_ocean(kunit,iaction,ioerr) 5,43
!@sum  io_ocean is a driver to ocean related io routines
!@auth Gavin Schmidt
!@ver  1.0
      IMPLICIT NONE

      INTEGER kunit   !@var kunit unit number of read/write
      INTEGER iaction !@var iaction flag for reading or writing to file
!@var IOERR 1 (or -1) if there is (or is not) an error in i/o
      INTEGER, INTENT(INOUT) :: IOERR

      call io_ocdyn  (kunit,iaction,ioerr)
      call io_straits(kunit,iaction,ioerr)

      RETURN
C****
      END SUBROUTINE io_ocean


      SUBROUTINE io_ocdyn(kunit,iaction,ioerr) 2,12
!@sum  io_ocdyn reads and writes ocean arrays to file
!@auth Gavin Schmidt
!@ver  1.0
      USE MODEL_COM, only : ioread,iowrite,irsficno,irsfic
     *     ,irsficnt,irerun,lhead
      USE OCEAN

      USE DOMAIN_DECOMP, only : AM_I_ROOT

      IMPLICIT NONE

      INTEGER kunit   !@var kunit unit number of read/write
      INTEGER iaction !@var iaction flag for reading or writing to file
!@var IOERR 1 (or -1) if there is (or is not) an error in i/o
      INTEGER, INTENT(INOUT) :: IOERR
!@var HEADER Character string label for individual records
      CHARACTER*80 :: HEADER, MODULE_HEADER = "OCDYN01"
#ifdef TRACERS_OCEAN
!@var TRHEADER Character string label for individual records
      CHARACTER*80 :: TRHEADER, TRMODULE_HEADER = "TROCDYN02"

      write (TRMODULE_HEADER(lhead+1:80),'(a13,i3,a1,i3,a)')
     *     'R8 dim(im,jm,',LMO,',',NTM,'):TRMO,TX,TY,TZ'
#endif

      write (MODULE_HEADER(lhead+1:80),'(a13,i2,a)') 'R8 dim(im,jm,',
     *   LMO,'):M,U,V,G0,GX,GY,GZ,S0,SX,SY,SZ, OGZ,OGZSV'

      SELECT CASE (IACTION)
      CASE (:IOWRITE)            ! output to standard restart file
        call gather_ocean(0) ! mo,uo,vo,g0m,gx-z,s0m,sx-z,ogz's,tr
        if(AM_I_ROOT()) then
          WRITE (kunit,err=10) MODULE_HEADER,MO_glob,UO_glob,VO_glob
     *     ,G0M_glob,GXMO_glob,GYMO_glob,GZMO_glob
     *     ,S0M_glob,SXMO_glob,SYMO_glob,SZMO_glob
     *     ,OGEOZ_glob,OGEOZ_SV_glob
#ifdef TRACERS_OCEAN
          WRITE (kunit,err=10) TRMODULE_HEADER
     *     ,TRMO_glob,TXMO_glob,TYMO_glob,TZMO_glob
#endif
        end if
      CASE (IOREAD:)            ! input from restart file
        SELECT CASE (IACTION)
          CASE (IRSFICNO)   ! initial conditions (no ocean data)
            READ (kunit)
          CASE (ioread,irerun,irsfic) ! restarts
           if(AM_I_ROOT()) then
            READ (kunit,err=10) HEADER,MO_glob,UO_glob,VO_glob
     *        ,G0M_glob,GXMO_glob,GYMO_glob,GZMO_glob
     *        ,S0M_glob,SXMO_glob,SYMO_glob,SZMO_glob
     *        ,OGEOZ_glob,OGEOZ_SV_glob
            IF (HEADER(1:LHEAD).NE.MODULE_HEADER(1:LHEAD)) THEN
              PRINT*,"Discrepancy in module version ",HEADER
     *             ,MODULE_HEADER
              GO TO 10
            END IF
#ifdef TRACERS_OCEAN
            READ (kunit,err=10) TRHEADER
     *        ,TRMO_glob,TXMO_glob,TYMO_glob,TZMO_glob
            IF (TRHEADER(1:LHEAD).NE.TRMODULE_HEADER(1:LHEAD)) THEN
              PRINT*,"Discrepancy in module version ",TRHEADER
     *             ,TRMODULE_HEADER
              GO TO 10
            END IF
#endif
           end if
           call scatter_ocean (0)  ! mo,uo,vo,g0m,x-z,s0m,x-z,ogz's,tr
          CASE (irsficnt) ! restarts (never any tracer data)
           if(AM_I_ROOT()) then
            READ (kunit,err=10) HEADER,MO_glob,UO_glob,VO_glob
     *        ,G0M_glob,GXMO_glob,GYMO_glob,GZMO_glob
     *        ,S0M_glob,SXMO_glob,SYMO_glob,SZMO_glob
     *        ,OGEOZ_glob,OGEOZ_SV_glob
            IF (HEADER(1:LHEAD).NE.MODULE_HEADER(1:LHEAD)) THEN
              PRINT*,"Discrepancy in module version ",HEADER
     *             ,MODULE_HEADER
              GO TO 10
            END IF
           end if
           call scatter_ocean (-1) ! mo,uo,vo,g0m,gx-z,s0m,sx-z,ogz's
           return
        END SELECT
      END SELECT

      RETURN
 10   IOERR=1
      RETURN
C****
      END SUBROUTINE io_ocdyn


      SUBROUTINE CHECKO_serial(SUBR) 4,38
!@sum  CHECKO Checks whether Ocean variables are reasonable (serial version)
!@auth Original Development Team
!@ver  1.0
      USE CONSTANT, only : byrt3,teeny
      USE MODEL_COM, only : qcheck
#ifdef TRACERS_OCEAN
      USE TRACER_COM, only : ntm, trname, t_qlimit
#endif
      USE OCEAN, only : im,jm,lmo,dxypo,focean,imaxj, lmm, mo=>mo_glob
     *  ,g0m=>g0m_glob, gxmo=>gxmo_glob,gymo=>gymo_glob,gzmo=>gzmo_glob
     *  ,s0m=>s0m_glob, sxmo=>sxmo_glob,symo=>symo_glob,szmo=>szmo_glob
     *  ,uo=>uo_glob, vo=>vo_glob
#ifdef TRACERS_OCEAN
     *  ,trmo=>trmo_glob, txmo=>txmo_glob, tymo=>tymo_glob,
     *   tzmo=>tzmo_glob
#endif
      IMPLICIT NONE
      REAL*8 SALIM,GO1,SO1,relerr,errmax,temgs
      LOGICAL QCHECKO
      INTEGER I,J,L,n,imax,jmax,lmax
!@var SUBR identifies where CHECK was called from
      CHARACTER*6, INTENT(IN) :: SUBR

C**** Check for NaN/INF in ocean data
      IF (QCHECK) THEN
      CALL CHECK3B(MO  ,IM,1,JM,JM,LMO,SUBR,'mo ')
      CALL CHECK3B(G0M ,IM,1,JM,JM,LMO,SUBR,'g0m')
      CALL CHECK3B(GXMO,IM,1,JM,JM,LMO,SUBR,'gxm')
      CALL CHECK3B(GYMO,IM,1,JM,JM,LMO,SUBR,'gym')
      CALL CHECK3B(GZMO,IM,1,JM,JM,LMO,SUBR,'gzm')
      CALL CHECK3B(S0M ,IM,1,JM,JM,LMO,SUBR,'s0m')
      CALL CHECK3B(SXMO,IM,1,JM,JM,LMO,SUBR,'sxm')
      CALL CHECK3B(SYMO,IM,1,JM,JM,LMO,SUBR,'sym')
      CALL CHECK3B(SZMO,IM,1,JM,JM,LMO,SUBR,'szm')
      CALL CHECK3B(UO  ,IM,1,JM,JM,LMO,SUBR,'uo ')
      CALL CHECK3B(VO  ,IM,1,JM,JM,LMO,SUBR,'vo ')
#ifdef TRACERS_OCEAN
      CALL CHECK4B(TRMO,IM,1,JM,JM,LMO,NTM,SUBR,'tzm')
#endif

C**** Check for variables out of bounds
      QCHECKO=.FALSE.
      DO J=1,JM
      DO I=1,IMAXJ(J)
        IF(FOCEAN(I,J).gt.0.) THEN
C**** Check potential specific enthalpy/salinity
          DO L=1,LMM(I,J)
          GO1 = G0M(I,J,L)/(MO(I,J,L)*DXYPO(J))
          SO1 = S0M(I,J,L)/(MO(I,J,L)*DXYPO(J))
          IF(GO1.lt.-10000. .or. GO1.gt.200000.) THEN
            WRITE (6,*) 'After ',SUBR,': I,J,L,GO=',I,J,L,GO1,TEMGS(GO1
     *           ,SO1)
            IF (GO1.lt.-20000. .or. GO1.gt.200000.) QCHECKO=.TRUE.
          END IF
          IF(SO1.gt.0.045 .or. SO1.lt.0.) THEN
            WRITE (6,*) 'After ',SUBR,': I,J,L,SO=',I,J,L,1d3*SO1
            IF ((SO1.gt.0.05 .or. SO1.lt.0.) .and. .not. (I == 47.and.
     *           J == 30)) QCHECKO=.TRUE.
          END IF
          END DO
C**** Check all ocean currents
          DO L = 1,LMO
            IF(ABS(UO(I,J,L)).gt.7. .or. ABS(VO(I,J,L)).gt.5) THEN
              WRITE (6,*) 'After ',SUBR,': I,J,L,UO,VO=',I,J,L,UO(I,J,L)
     *             ,VO(I,J,L)
              QCHECKO=.TRUE.
            END IF
          END DO
C**** Check first layer ocean mass
          IF(MO(I,J,1).lt.2000. .or. MO(I,J,1).gt.20000.) THEN
            IF (I == 47.and.(J == 33.or.J == 34)) GOTO 230 ! not Caspian
            WRITE (6,*) 'After ',SUBR,': I,J,MO=',I,J,MO(I,J,1)
            QCHECKO=.TRUE.
          END IF
C**** Check ocean salinity in each eighth box for the first layer
 230      SALIM = .045
          IF(JM == 46 .and. I == 47 .and. J == 30) GOTO 240 ! not Persian Gulf   !.048
          IF(.5*ABS(SXMO(I,J,1))+.5*ABS(SYMO(I,J,1))+BYRT3*ABS(SZMO(I,J
     *         ,1)).lt.S0M(I,J,1) .and.(.5*ABS(SXMO(I,J,1))+.5
     *         *ABS(SYMO(I,J,1))+BYRT3*ABS(SZMO(I,J,1))+S0M(I,J,1))
     *         /(MO(I,J,1)*DXYPO(J)).lt.SALIM)  GO TO 240
          WRITE (6,*) 'After ',SUBR,': I,J,S0,SX,SY,SZ=',I,J,
     *         1d3*S0M(I,J,1)/(MO(I,J,1)*DXYPO(J)),1d3*SXMO(I,J,1)/(MO(I
     *         ,J,1)*DXYPO(J)),1d3*SYMO(I,J,1)/(MO(I,J,1)*DXYPO(J)),1d3
     *         *SZMO(I,J,1)/(MO(I,J,1)*DXYPO(J))
          QCHECKO=.TRUE.
 240      CONTINUE
        END IF
      END DO
      END DO

#ifdef TRACERS_OCEAN
      do n=1,ntm
C**** Check for negative tracers
        if (t_qlimit(n)) then
        do l=1,lmo
        do j=1,jm
        do i=1,imaxj(j)
          if (l.le.lmm(i,j)) then
            if (trmo(i,j,l,n).lt.0) then
              write(6,*) "Neg Tracer in ocean after ",subr,i,j,l,
     *             trname(n),trmo(i,j,l,n)
              QCHECKO=.true.
            end if
          end if
        end do
        end do
        end do
        end if
C**** Check conservation of water tracers in ocean
        if (trname(n) == 'Water') then
          errmax = 0. ; imax=1 ; jmax=1 ; lmax=1
          do l=1,lmo
          do j=1,jm
          do i=1,imaxj(j)
            if (l.le.lmm(i,j)) then
              relerr=max(
     *             abs(trmo(i,j,l,n)-mo(i,j,l)*dxypo(j)+s0m(i,j,l)),
     *             abs(txmo(i,j,l,n)+sxmo(i,j,l)),
     *             abs(tymo(i,j,l,n)+symo(i,j,l)),
     *             abs(tzmo(i,j,l,n)+szmo(i,j,l)))/
     *             (mo(i,j,l)*dxypo(j)-s0m(i,j,l))
            if (relerr.gt.errmax) then
              imax=i ; jmax=j ; lmax=l ; errmax=relerr
            end if
            end if
          end do
          end do
          end do
          print*,"Relative error in ocean fresh water mass after ",subr
     *         ,":",imax,jmax,lmax,errmax,trmo(imax,jmax,lmax,n),mo(imax
     *         ,jmax,lmax)*dxypo(jmax)-s0m(imax,jmax,lmax),txmo(imax
     *         ,jmax,lmax,n),-sxmo(imax,jmax,lmax),tymo(imax,jmax,lmax,n
     *         ),-symo(imax,jmax ,lmax),tzmo(imax,jmax,lmax,n),
     *         -szmo(imax,jmax,lmax)
        end if
      end do
#endif

      IF (QCHECKO)
     &     call stop_model("QCHECKO: Ocean Variables out of bounds",255)

      END IF
C****
      CALL CHECKOST(SUBR)
C****
      END SUBROUTINE CHECKO_serial


      SUBROUTINE CHECKO(SUBR) 15,75
!@sum  CHECKO Checks whether Ocean variables are reasonable (parallel version)
!@auth Original Development Team
!@ver  1.0
      USE CONSTANT, only : byrt3,teeny
      USE MODEL_COM, only : qcheck
#ifdef TRACERS_OCEAN
      USE TRACER_COM, only : ntm, trname, t_qlimit
#endif
      USE OCEAN
      USE DOMAIN_DECOMP, only : grid, GET, AM_I_ROOT
      IMPLICIT NONE
      REAL*8 SALIM,GO1,SO1,relerr,errmax,temgs
      LOGICAL QCHECKO
      INTEGER I,J,L,n,imax,jmax,lmax
!@var SUBR identifies where CHECK was called from
      CHARACTER*6, INTENT(IN) :: SUBR

c**** Extract domain decomposition info
      INTEGER :: J_0S, J_0, J_1, J_0H, J_1H, JM_loc
      CALL GET(grid, J_STRT_SKP = J_0S, J_STRT = J_0, J_STOP = J_1,
     *   J_STRT_HALO = J_0H, J_STOP_HALO = J_1H)

C**** Check for NaN/INF in ocean data
      IF (QCHECK) THEN
      JM_loc = J_1H - J_0H + 1
      CALL CHECK3B(MO  ,IM,J_0H,J_1H,JM,LMO,SUBR,'mo ')
      CALL CHECK3B(G0M ,IM,J_0H,J_1H,JM,LMO,SUBR,'g0m')
      CALL CHECK3B(GXMO,IM,J_0H,J_1H,JM,LMO,SUBR,'gxm')
      CALL CHECK3B(GYMO,IM,J_0H,J_1H,JM,LMO,SUBR,'gym')
      CALL CHECK3B(GZMO,IM,J_0H,J_1H,JM,LMO,SUBR,'gzm')
      CALL CHECK3B(S0M ,IM,J_0H,J_1H,JM,LMO,SUBR,'s0m')
      CALL CHECK3B(SXMO,IM,J_0H,J_1H,JM,LMO,SUBR,'sxm')
      CALL CHECK3B(SYMO,IM,J_0H,J_1H,JM,LMO,SUBR,'sym')
      CALL CHECK3B(SZMO,IM,J_0H,J_1H,JM,LMO,SUBR,'szm')
      CALL CHECK3B(UO  ,IM,J_0H,J_1H,JM,LMO,SUBR,'uo ')
      CALL CHECK3B(VO  ,IM,J_0H,J_1H,JM,LMO,SUBR,'vo ')
#ifdef TRACERS_OCEAN
      CALL CHECK4B(TRMO,IM,J_0H,J_1H,JM,LMO,NTM,SUBR,'trm')
      CALL CHECK4B(TXMO,IM,J_0H,J_1H,JM,LMO,NTM,SUBR,'txm')
      CALL CHECK4B(TYMO,IM,J_0H,J_1H,JM,LMO,NTM,SUBR,'tym')
      CALL CHECK4B(TZMO,IM,J_0H,J_1H,JM,LMO,NTM,SUBR,'tzm')
#endif

C**** Check for variables out of bounds
      QCHECKO=.FALSE.
      DO J=J_0,J_1
      DO I=1,IMAXJ(J)
        IF(FOCEAN(I,J).gt.0.) THEN
C**** Check potential specific enthalpy/salinity
          DO L=1,LMM(I,J)
          GO1 = G0M(I,J,L)/(MO(I,J,L)*DXYPO(J))
          SO1 = S0M(I,J,L)/(MO(I,J,L)*DXYPO(J))
          IF(GO1.lt.-10000. .or. GO1.gt.200000.) THEN
            WRITE (6,*) 'After ',SUBR,': I,J,L,GO=',I,J,L,GO1,TEMGS(GO1
     *           ,SO1)
            IF (GO1.lt.-20000. .or. GO1.gt.200000.) QCHECKO=.TRUE.
          END IF
          IF(SO1.gt.0.045 .or. SO1.lt.0.) THEN
            WRITE (6,*) 'After ',SUBR,': I,J,L,SO=',I,J,L,1d3*SO1
            IF ((SO1.gt.0.05 .or. SO1.lt.0.) .and. .not. (I == 47.and.
     *           J == 30)) QCHECKO=.TRUE.
          END IF
          END DO
C**** Check all ocean currents
          DO L = 1,LMO
            IF(ABS(UO(I,J,L)).gt.7. .or. ABS(VO(I,J,L)).gt.5) THEN
              WRITE (6,*) 'After ',SUBR,': I,J,L,UO,VO=',I,J,L,UO(I,J,L)
     *             ,VO(I,J,L)
              QCHECKO=.TRUE.
            END IF
          END DO
C**** Check first layer ocean mass
          IF(MO(I,J,1).lt.2000. .or. MO(I,J,1).gt.20000.) THEN
            IF (I == 47.and.(J == 33.or.J == 34)) GOTO 230 ! not Caspian
            WRITE (6,*) 'After ',SUBR,': I,J,MO=',I,J,MO(I,J,1)
            QCHECKO=.TRUE.
          END IF
C**** Check ocean salinity in each eighth box for the first layer
 230      SALIM = .045
          IF(JM == 46 .and. I == 47 .and. J == 30) GOTO 240 ! not Persian Gulf   !.048
          IF(.5*ABS(SXMO(I,J,1))+.5*ABS(SYMO(I,J,1))+BYRT3*ABS(SZMO(I,J
     *         ,1)).lt.S0M(I,J,1) .and.(.5*ABS(SXMO(I,J,1))+.5
     *         *ABS(SYMO(I,J,1))+BYRT3*ABS(SZMO(I,J,1))+S0M(I,J,1))
     *         /(MO(I,J,1)*DXYPO(J)).lt.SALIM)  GO TO 240
          WRITE (6,*) 'After ',SUBR,': I,J,S0,SX,SY,SZ=',I,J,
     *         1d3*S0M(I,J,1)/(MO(I,J,1)*DXYPO(J)),1d3*SXMO(I,J,1)/(MO(I
     *         ,J,1)*DXYPO(J)),1d3*SYMO(I,J,1)/(MO(I,J,1)*DXYPO(J)),1d3
     *         *SZMO(I,J,1)/(MO(I,J,1)*DXYPO(J))
          QCHECKO=.TRUE.
 240      CONTINUE
        END IF
      END DO
      END DO

#ifdef TRACERS_OCEAN
      do n=1,ntm
C**** Check for negative tracers
        if (t_qlimit(n)) then
        do l=1,lmo
        do j=j_0,j_1
        do i=1,imaxj(j)
          if (l.le.lmm(i,j)) then
            if (trmo(i,j,l,n).lt.0) then
              write(6,*) "Neg Tracer in ocean after ",subr,i,j,l,
     *             trname(n),trmo(i,j,l,n)
              QCHECKO=.true.
            end if
          end if
        end do
        end do
        end do
        end if
C**** Check conservation of water tracers in ocean
        if (trname(n) == 'Water') then
          errmax = 0. ; imax=1 ; jmax=1 ; lmax=1
          do l=1,lmo
          do j=j_0,j_1
          do i=1,imaxj(j)
            if (l.le.lmm(i,j)) then
              relerr=max(
     *             abs(trmo(i,j,l,n)-mo(i,j,l)*dxypo(j)+s0m(i,j,l)),
     *             abs(txmo(i,j,l,n)+sxmo(i,j,l)),
     *             abs(tymo(i,j,l,n)+symo(i,j,l)),
     *             abs(tzmo(i,j,l,n)+szmo(i,j,l)))/
     *             (mo(i,j,l)*dxypo(j)-s0m(i,j,l))
            if (relerr.gt.errmax) then
              imax=i ; jmax=j ; lmax=l ; errmax=relerr
            end if
            end if
          end do
          end do
          end do
          print*,"Relative error in ocean fresh water mass after ",subr
     *         ,":",imax,jmax,lmax,errmax,trmo(imax,jmax,lmax,n),mo(imax
     *         ,jmax,lmax)*dxypo(jmax)-s0m(imax,jmax,lmax),txmo(imax
     *         ,jmax,lmax,n),-sxmo(imax,jmax,lmax),tymo(imax,jmax,lmax,n
     *         ),-symo(imax,jmax ,lmax),tzmo(imax,jmax,lmax,n),
     *         -szmo(imax,jmax,lmax)
        end if
      end do
#endif

      IF (QCHECKO)
     &     call stop_model("QCHECKO: Ocean Variables out of bounds",255)

      END IF
C****
      IF (AM_I_ROOT()) CALL CHECKOST(SUBR)
C****
      END SUBROUTINE CHECKO


      SUBROUTINE conserv_OKE(OKE),10
!@sum  conserv_OKE calculates zonal ocean kinetic energy
!@auth Gavin Schmidt
!@ver  1.0
      USE CONSTANT, only : shw
      USE OCEAN, only : im,jm,lmo,ivnp,fim,imaxj,focean,mo,uo,vo,lmm
      USE DOMAIN_DECOMP, only : GRID, GET, SOUTH, HALO_UPDATE
      IMPLICIT NONE
!@var OKE zonal ocean kinetic energy per unit area (J/m**2)
      REAL*8, DIMENSION(GRID%J_STRT_HALO:GRID%J_STOP_HALO) :: OKE
      INTEGER I,J,L,IP1

      INTEGER :: J_0S, J_1S
      LOGICAL :: HAVE_NORTH_POLE

      CALL GET(grid, J_STRT_SKP=J_0S,    J_STOP_SKP=J_1S,
     &               HAVE_NORTH_POLE=HAVE_NORTH_POLE)

      CALL HALO_UPDATE(grid, VO, FROM=SOUTH)
      OKE=0.
      DO J=J_0S,J_1S
        I=IM
        DO IP1=1,IM
          DO L=1,LMM(I,J)
            OKE(J) = OKE(J)+((MO(I,J,L)+MO(IP1,J,L))*UO(I,J,L)*UO(I,J,L)
     *      + MO(I,J,L)*(VO(I,J-1,L)*VO(I,J-1,L) + VO(I,J,L)*VO(I,J,L)))
          END DO
          I=IP1
        END DO
        OKE(J)=OKE(J)*0.25
      END DO
      if(HAVE_NORTH_POLE) then
        DO L=1,LMM(1,JM)
          OKE(JM) = OKE(JM) +
     +      MO(1,JM,L) * (1.5*IM*(UO(IM,JM,L)**2 + UO(IVNP,JM,L)**2) +
     +                    Sum(VO(:,JM-1,L)*VO(:,JM-1,L)) )
        END DO
        OKE(JM)= OKE(JM)*0.25
      end if
C****
      RETURN
      END SUBROUTINE conserv_OKE


      SUBROUTINE conserv_OCE(OCEANE),24
!@sum  conserv_OCE calculates zonal ocean potential enthalpy(atmos grid)
!@auth Gavin Schmidt
!@ver  1.0
      USE GEOM, only : bydxyp
      USE OCEAN, only : im,jm,fim,imaxj,focean,g0m,lmm
      USE STRAITS, only : nmst,jst,g0mst
      USE DOMAIN_DECOMP, only : GRID, GET
      IMPLICIT NONE
!@var OCEANE zonal ocean potential enthalpy (J/m^2)
      REAL*8, DIMENSION(GRID%J_STRT_HALO:GRID%J_STOP_HALO) :: OCEANE
      INTEGER I,J,L,N

      INTEGER :: J_0, J_1
      LOGICAL :: HAVE_SOUTH_POLE, HAVE_NORTH_POLE

      CALL GET(grid, J_STRT=J_0,    J_STOP=J_1,
     &  HAVE_SOUTH_POLE=HAVE_SOUTH_POLE,HAVE_NORTH_POLE=HAVE_NORTH_POLE)

      DO J=J_0,J_1
        OCEANE(J)=0
        DO I=1,IMAXJ(J)
          DO L=1,LMM(I,J)
            OCEANE(J) = OCEANE(J) + G0M(I,J,L)*FOCEAN(I,J)*BYDXYP(J)
          END DO
        END DO
      END DO
      if(HAVE_SOUTH_POLE) OCEANE(1) =FIM*OCEANE(1)
      if(HAVE_NORTH_POLE) OCEANE(JM)=FIM*OCEANE(JM)
C**** include straits variables
      DO N=1,NMST
        J=JST(N,1)
       if(j.lt.j_0 .or. j.gt.j_1) cycle
        OCEANE(J)=OCEANE(J)+SUM(G0MST(:,N))*BYDXYP(J)
      END DO
C****
      RETURN
      END SUBROUTINE conserv_OCE


      SUBROUTINE conserv_OMS(OMASS) 2,10
!@sum  conserv_OMS calculates zonal ocean mass (on atmos grid)
!@auth Gavin Schmidt
!@ver  1.0
      USE GEOM, only : bydxyp
      USE OCEAN, only : im,jm,fim,imaxj,focean,mo,g0m,lmm,dxypo
      USE STRAITS, only : nmst,jst,mmst
      USE DOMAIN_DECOMP, only : GRID, GET, pack_data
      IMPLICIT NONE
!@var OMASS zonal ocean mass (kg/m^2)
      REAL*8, DIMENSION(GRID%J_STRT_HALO:GRID%J_STOP_HALO) :: OMASS
      REAL*8, DIMENSION(JM) :: OMSSV
      COMMON /OCCONS/OMSSV
      INTEGER I,J,L,N

      INTEGER :: J_0, J_1
      LOGICAL :: HAVE_SOUTH_POLE, HAVE_NORTH_POLE

      CALL GET(grid, J_STRT=J_0,    J_STOP=J_1,
     &  HAVE_SOUTH_POLE=HAVE_SOUTH_POLE,HAVE_NORTH_POLE=HAVE_NORTH_POLE)

      DO J=J_0,J_1
        OMASS(J)=0
        DO I=1,IMAXJ(J)
          DO L=1,LMM(I,J)
            OMASS(J) = OMASS(J) + MO(I,J,L)*FOCEAN(I,J)
          END DO
        END DO
        OMASS(J)=OMASS(J)*DXYPO(J)*BYDXYP(J)
      END DO
      if (HAVE_SOUTH_POLE) OMASS(1) =FIM*OMASS(1)
      if (HAVE_NORTH_POLE) OMASS(JM)=FIM*OMASS(JM)
C**** include straits variables
      DO N=1,NMST
        J=JST(N,1)
        if(j.lt.j_0 .or. j.gt.j_1) cycle
        OMASS(J)=OMASS(J)+SUM(MMST(:,N))*BYDXYP(J)
      END DO
C**** save mass for AM calculation
      OMSSV(J_0:J_1) = OMASS(J_0:J_1)
C****
      RETURN
      END SUBROUTINE conserv_OMS


      SUBROUTINE conserv_OAM(OAM),8
!@sum  conserv_OAM calculates zonal ocean angular momentum
!@auth Gavin Schmidt
!@ver  1.0
      USE CONSTANT, only : radius,omega
      USE OCEAN, only : im,jm,fim,imaxj,focean,mo,uo,cosm,cosq,lmu
      USE DOMAIN_DECOMP, only : GET, GRID
      IMPLICIT NONE
!@var OAM ocean angular momentum divided by area (kg/s)
      REAL*8, DIMENSION(GRID%J_STRT_HALO:GRID%J_STOP_HALO) :: OAM
      REAL*8, DIMENSION(JM) :: OMSSV
      COMMON /OCCONS/OMSSV
      INTEGER I,J,L,IP1
      REAL*8 UMILx2

      INTEGER :: J_0S, J_1S
      LOGICAL :: HAVE_NORTH_POLE

      CALL GET(grid, J_STRT_SKP=J_0S,    J_STOP_SKP=J_1S,
     &               HAVE_NORTH_POLE=HAVE_NORTH_POLE)
      OAM=0
      DO J=J_0S,J_1S
        UMILx2 = 0.
        I=IM
        DO IP1=1,IM
          DO L=1,LMU(I,J)
            UMILx2 = UMILx2 + UO(I,J,L)*(MO(I,J,L)+MO(IP1,J,L))
          END DO
          I=IP1
        END DO
c       OAM(J) = UMIL*COSPO(J) + OMSSV(J)*RADIUS*OMEGA*(COSVO(J-1)
c    *       *COSVO(J-1)+COSVO(J)*COSVO(J))
c       OAM(J)=0.5*RADIUS*OAM(J)
        OAM(J) = RADIUS*(OMSSV(J)*RADIUS*OMEGA*COSQ(J) +
     +                   .5*UMILx2*COSM(J))
      END DO

      if (HAVE_NORTH_POLE)
     *  OAM(JM) = RADIUS*OMSSV(JM)*RADIUS*OMEGA*COSQ(JM)
c     DO L=1,LMU(1,JM)
c       UMIL = UMIL + UO(1,JM,L)*MO(1,JM,L)
c     END DO
c     OAM(JM) = UMIL*COSPO(JM)*IM*2. +OMSSV(JM)*RADIUS*OMEGA
c    *     *COSVO(JM-1)*COSVO(JM-1)
c     OAM(JM)=0.5*RADIUS*OAM(JM)
C****
      RETURN
      END SUBROUTINE conserv_OAM


      SUBROUTINE conserv_OSL(OSALT) 4,10
!@sum  conserv_OSL calculates zonal ocean salt on atmos grid
!@auth Gavin Schmidt
!@ver  1.0
      USE GEOM, only : bydxyp
      USE OCEAN, only : im,jm,fim,imaxj,focean,mo,s0m,lmm
      USE STRAITS, only : nmst,jst,s0mst
      USE DOMAIN_DECOMP, only : GET, GRID
      IMPLICIT NONE
!@var OSALT zonal ocean salt (kg/m^2)
      REAL*8, DIMENSION(GRID%J_STRT_HALO:GRID%J_STOP_HALO) :: OSALT
      INTEGER I,J,L,N

      INTEGER :: J_0, J_1
      LOGICAL :: HAVE_SOUTH_POLE, HAVE_NORTH_POLE

      CALL GET(grid, J_STRT=J_0,    J_STOP=J_1,
     &  HAVE_SOUTH_POLE=HAVE_SOUTH_POLE,HAVE_NORTH_POLE=HAVE_NORTH_POLE)

      DO J=J_0,J_1
        OSALT(J)=0
        DO I=1,IMAXJ(J)
          DO L=1,LMM(I,J)
            OSALT(J) = OSALT(J) + S0M(I,J,L)*FOCEAN(I,J)*BYDXYP(J)
          END DO
        END DO
      END DO
      if (HAVE_SOUTH_POLE) OSALT(1) =FIM*OSALT(1)
      if (HAVE_NORTH_POLE) OSALT(JM)=FIM*OSALT(JM)
C**** include straits variables
      DO N=1,NMST
        J=JST(N,1)
        if(j.lt.j_0 .or. j.gt.j_1) cycle
        OSALT(J)=OSALT(J)+SUM(S0MST(:,N))*BYDXYP(J)
      END DO
C****
      RETURN
      END SUBROUTINE conserv_OSL


      Subroutine OVtoM (MM,UM,VM) 4,6
C****
C**** OVtoM converts density and velocity in concentration units
C**** to mass and momentum in mass units
C**** Input:  MO (kg/m^2), UO (m/s), VO (m/s)
C**** Define: VOSP from UO(IVSP,1), VONP from UO(IVNP,JM)
C**** Output: MM (kg), UM (kg*m/s), VM (kg*m/s)
C****
      Use OCEAN, Only: IM,JM,LMO, IVSP,IVNP, DXYP=>DXYPO, COSU,SINU,
     *                 COSI=>COSIC,SINI=>SINIC, MO,UO,VO, VONP
      Use DOMAIN_DECOMP, Only: GRID, HALO_UPDATE, NORTH
      Implicit None
      Real*8,Intent(Out),
     *  Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO) :: MM,UM,VM
      Integer*4 I,J,L, J1,JN,J1P,JNP,JNQ,JNR
      Logical*4 QSP,QNP
C****
C**** Extract domain decomposition band parameters
C**** ASSUME THAT J1 NEVER EQUALS 2 AND JN NEVER EQUALS JM-1
C****                          Band1  Band2  BandM
      J1  = GRID%J_STRT     !    1      5     JM-3   Band minimum
      JN  = GRID%J_STOP     !    4      8     JM     Band maximum
      J1P = Max(J1,2)       !    2      5     JM-3   Exclude SP
      JNP = Min(JN,JM-1)    !    4      8     JM-1   Exclude NP
      JNQ = Min(JN,JM-2)    !    4      8     JM-2   Exclude NP,NP-1
      JNR = Min(JN+1,JM-1)  !    5      9     JM-1   Halo, Exclude NP
C     QSP = J1==1           !    T      F      F
      QNP = JN==JM          !    F      F      T
C****
      Call HALO_UPDATE (GRID, MO, FROM=NORTH)
C****
!$OMP ParallelDo   Private (I,J,L)
      DO 50 L=1,LMO
C**** Define VOSP and VONP
C     If (QSP)  VOSP(L) = UO(IVSP,1 ,L)
      If (QNP)  VONP(L) = UO(IVNP,JM,L)
C**** Convert density to mass
      DO 10 J=J1,JNR
   10 MM(:,J,L) = MO(:,J,L)*DXYP(J)
C     If (QSP)  MM(1,1 ,L) = MO(1,1 ,L)*DXYP(1)
      If (QNP)  MM(1,JM,L) = MO(1,JM,L)*DXYP(JM)
C**** Convert U velocity to U momentum
      DO 30 J=J1P,JNP
      DO 20 I=1,IM-1
   20 UM(I ,J,L) = UO(I ,J,L)*(MM(I,J,L)+MM(I+1,J,L))*.5
   30 UM(IM,J,L) = UO(IM,J,L)*(MM(1,J,L)+MM(IM ,J,L))*.5
C**** Convert V velocity to V momentum
      DO 40 J=J1P,JNQ
   40 VM(:,J,L) = VO(:,J,L)*(MM(:,J,L)+MM(:,J+1,L))*.5
C     If (QSP)  VM(:, 1  ,L) = VO(:, 1  ,L)*(MM(:, 2  ,L)+MM(1, 1,L))*.5
      If (QNP)  VM(:,JM-1,L) = VO(:,JM-1,L)*(MM(:,JM-1,L)+MM(1,JM,L))*.5
C**** Convert U and V velocity to U and V momentum at poles
C     If (QSP)  UM(IM,1 ,L) = UO(IM,1 ,L)*MM(1,1 ,L)
      If (QNP)  UM(IM,JM,L) = UO(IM,JM,L)*MM(1,JM,L)
C     If (QSP)  UM(IVSP,1 ,L) =   VOSP(L)*MM(1,1 ,L)
      If (QNP)  UM(IVNP,JM,L) =   VONP(L)*MM(1,JM,L)
C**** Define MO, UO and VO at poles
C     If (QSP)  Then
C       MO(:,1 ,L) = MO(1 ,1 ,L)
C       UO(:,1 ,L) = UO(IM,1 ,L)*COSU(:) - VOSP(L)*SINU(:)
C       VO(:,0 ,L) = VOSP(L)*COSI(:) + UO(IM,1 ,L)*SINI(:)  ;  EndIf
      If (QNP)  Then
        MO(:,JM,L) = MO(1 ,JM,L)
        UO(:,JM,L) = UO(IM,JM,L)*COSU(:) + VONP(L)*SINU(:)
        VO(:,JM,L) = VONP(L)*COSI(:) - UO(IM,JM,L)*SINI(:)  ;  EndIf
   50 Continue
      Return
      EndSubroutine OVtoM


      Subroutine OMtoV (MM,UM,VM) 10,6
C****
C**** OMtov converts mass and momentum in mass units
C**** to density and velocity in concentration units
C**** Input:  MM (kg), UM (kg*m/s), VM (kg*m/s)
C**** Output: MO (kg/m^2), UO (m/s), VO (m/s)
C**** Define: VOSP from UM(IVSP,1)/MM, VONP from UM(IVNP,JM)/MM
C****
      Use OCEAN, Only: IM,JM,LMO,IVSP,IVNP,
     *                  LMOM=>LMM, LMOU=>LMU, LMOV=>LMV,
     *                  COSU,SINU, COSI=>COSIC,SINI=>SINIC,
     *                  zDXYP=>BYDXYPO, MO,UO,VO, VONP
      Use DOMAIN_DECOMP, Only: GRID, HALO_UPDATE, NORTH
      Implicit None
      Real*8, !!! Intent(IN), (except for HALO_UPDATEs)
     *  Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO) :: MM,UM,VM
      Integer*4 I,J,L, J1,JN,J1P,JNP,JNQ
      Logical*4 QSP,QNP
C****
C**** Extract domain decomposition band parameters
C**** ASSUME THAT J1 NEVER EQUALS 2 AND JN NEVER EQUALS JM-1
C****                          Band1  Band2  BandM
      J1  = GRID%J_STRT     !    1      5     JM-3   Band minimum
      JN  = GRID%J_STOP     !    4      8     JM     Band maximum
      J1P = Max(J1,2)       !    2      5     JM-3   Exclude SP
      JNP = Min(JN,JM-1)    !    4      8     JM-1   Exclude NP
      JNQ = Min(JN,JM-2)    !    4      8     JM-2   Exclude NP,NP-1
C     QSP = J1==1           !    T      F      F
      QNP = JN==JM          !    F      F      T
C****
      Call HALO_UPDATE (GRID, MM, FROM=NORTH)
C**** Convert mass to density
!$OMP ParallelDo   Private (I,J,L)
      Do 10 J=J1P,JNP
      Do 10 I=1,IM
      Do 10 L=1,LMOM(I,J)
   10 MO(I,J,L) = MM(I,J,L)*zDXYP(J)
C     If (QSP)  Then
C       Do L=1,LMOM(1,1)
C  11   MO(:,1,L) = MM(1,1,L)*zDXYP(1)  ;  EndIf
      If (QNP)  Then
        Do 12 L=1,LMOM(1,JM)
   12   MO(:,JM,L) = MM(1,JM,L)*zDXYP(JM)  ;  EndIf
C**** Convert U momentum to U velocity
!$OMP ParallelDo   Private (I,J,L)
      Do 30 J=J1P,JNP
      Do 20 I=1,IM-1
      Do 20 L=1,LMOU(I,J)
   20 UO(I,J,L) = UM(I,J,L)*2/(MM(I,J,L)+MM(I+1,J,L))
      Do 30 L=1,LMOU(IM,J)
   30 UO(IM,J,L) = UM(IM,J,L)*2/(MM(IM,J,L)+MM(1,J,L))
C**** Convert V momentum to V velocity
!$OMP ParallelDo   Private (I,J,L)
      Do 40 J=J1P,JNQ
      Do 40 I=1,IM
      Do 40 L=1,LMOV(I,J)
   40 VO(I,J,L) = VM(I,J,L)*2/(MM(I,J,L)+MM(I,J+1,L))
C     If (QSP)  Then
C       Do 41 I=1,IM
C       Do 41 L=1,LMOV(I,1)
C  41   VO(I,1,L) = VM(I,1,L)*2/(MM(1,1,L)+MM(I,2,L))  ;  EndIf
      If (QNP)  Then
        Do 42 I=1,IM
        Do 42 L=1,LMOV(I,JM-1)
   42   VO(I,JM-1,L) = VM(I,JM-1,L)*2/(MM(1,JM,L)+MM(I,JM-1,L))  ; EndIf
C**** Convert momentum to velocity at south pole, define UO and VO
C     If (QSP)  Then
C       Do 50 L=1,LMOM(1,1)
C       UO(IM,1,L) = UM(IM,1,L)/MM(1,1,L)
C       VOSP(L)  = UM(IVSP,1,L)/MM(1,1,L)
C       UO(:,1,L) = UO(IM,1,L)*COSU(:) - VOSP(L)*SINU(:)
C       VO(:,0,L) = VOSP(L)*COSI(:) + UO(IM,1,L)*SINI(:)
C  50   Continue  ;  EndIf
C**** Convert momentum to velocity at north pole, define UO and VO
      If (QNP)  Then
        Do 60 L=1,LMOM(1,JM)
        UO(IM,JM,L) = UM(IM,JM,L)/MM(1,JM,L)
        VONP(L)   = UM(IVNP,JM,L)/MM(1,JM,L)
        UO(:,JM,L) = UO(IM,JM,L)*COSU(:) + VONP(L)*SINU(:)
        VO(:,JM,L) = VONP(L)*COSI(:) - UO(IM,JM,L)*SINI(:)
   60   Continue  ;  EndIf
      Return
      EndSubroutine OMtoV


      Subroutine OFLUX (NS,MM,QEVEN) 10,12
C****
C**** OFLUX calculates the fluid fluxes
C**** Input:  M (kg/m^2), U (m/s), V (m/s)
C**** Output: MU (kg/s) = DY * U * M
C****         MV (kg/s) = DX * V * M
C****         MW (kg/s) = MW(L-1) + CONV-CONVs*dZO/ZOE + MM-MMs*dZO/ZOE
C****       CONV (kg/s) = MU-MU + MV-MV
C****
      Use OCEAN, Only: IM,JM,LMO, LMOM=>LMM, ZOE=>ZE,dZO, DTO,
     *                 DXYP=>DXYPO,DYP=>DYPO,DXV=>DXVO, MO,UO,VO
      Use OCEAN_DYN, Only: MU,MV,MW, SMU,SMV,SMW, CONV
      Use DOMAIN_DECOMP, Only: GRID, HALO_UPDATE, NORTH,SOUTH
      Implicit None
      Integer*4,Intent(In) :: NS     !  decrementing leap-frog step
      Logical*4,Intent(In) :: QEVEN  !  whether called from even step
      Real*8   ,Intent(In),
     *  Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO) :: MM
C****
      Real*8    zNSxDTO, MVS,dMVS(IM),dMVSm, MVN,dMVN(IM),dMVNm,
     *          CONVs,MMs
      Integer*4 I,J,L,LM,IMAX, J1,JN,J1P,J1H,JNP,JNQ
      Logical*4 QSP,QNP
C****
C**** Extract domain decomposition band parameters
C**** ASSUME THAT J1 NEVER EQUALS 2 AND JN NEVER EQUALS JM-1
C****                          Band1  Band2  BandM
      J1  = GRID%J_STRT     !    1      5     JM-3   Band minimum
      JN  = GRID%J_STOP     !    4      8     JM     Band maximum
      J1P = Max(J1,2)       !    2      5     JM-3   Exclude SP
      J1H = Max(J1-1,1)     !    1      4     JM-4   Halo minimum
      JNP = Min(JN,JM-1)    !    4      8     JM-1   Exclude NP
      JNQ = Min(JN,JM-2)    !    4      8     JM-2   Exclude NP,NP-1
C     QSP = J1==1           !    T      F      F
      QNP = JN==JM          !    F      F      T
C****
      Call HALO_UPDATE (GRID, MO, FROM=NORTH+SOUTH)
      Call HALO_UPDATE (GRID, VO, FROM=SOUTH)
      zNSxDTO = 1 / (NS*DTO)
C****
C**** Compute fluid fluxes for the C grid
C****
C**** Smooth the West-East velocity near the poles
!$OMP ParallelDo   Private (J,L)
      Do 110 L=1,LMO
      Do 110 J=J1P,JNP
  110 MU(:,J,L) = UO(:,J,L)
      Call OPFIL (MU)
C**** Compute MU, the West-East mass flux, at non-polar points
!$OMP ParallelDo   Private (I,J,L, MVS,dMVS,dMVSm, MVN,dMVN,dMVNm)
      DO 430 L=1,LMO
      DO 130 J=J1P,JNP
      DO 120 I=1,IM-1
  120 MU(I ,J,L) = .5*DYP(J)*MU(I ,J,L)*(MO(I,J,L)+MO(I+1,J,L))
  130 MU(IM,J,L) = .5*DYP(J)*MU(IM,J,L)*(MO(1,J,L)+MO(IM ,J,L))
C**** Compute MV, the South-North mass flux
      DO 210 J=J1H,JNP
  210 MV(:,J,L) = .5*DXV(J)*VO(:,J,L)*(MO(:,J,L)+MO(:,J+1,L))
C****
C**** Compute MU so that CONV is identical for each polar triangle
C****
C     If (QSP) then
C       MVS = Sum(MV(:,1,L)) / IM
C       dMVS(1) = 0
C       Do 310 I=2,IM
C 310   dMVS(I) = dMVS(I-1) + (MV(I,1,L)-MVS)
C       dMVSm   = Sum(dMVS(:)) / IM
C       MU(:,1,L) = dMVSm - dMVS(:)  ;  EndIf
      If (QNP) then
        MVN = Sum(MV(:,JM-1,L)) / IM
        dMVN(1) = 0
        Do 320 I=2,IM
  320   dMVN(I) = dMVN(I-1) + (MV(I,JM-1,L)-MVN)
        dMVNm   = Sum(dMVN(:)) / IM
        MU(:,JM,L) = dMVN(:) - dMVNm  ;  EndIf
C****
C**** Compute horizontal fluid convergence at non-polar points
C****
      Do 420 J=J1P,JNP
      Do 410 I=2,IM
  410 CONV(I,J,L) = MU(I-1,J,L)-MU(I,J,L) + (MV(I,J-1,L)-MV(I,J,L))
  420 CONV(1,J,L) = MU(IM ,J,L)-MU(1,J,L) + (MV(1,J-1,L)-MV(1,J,L))
C     If (QSP)  CONV(1,1 ,L) = - MVS
      If (QNP)  CONV(1,JM,L) =   MVN
  430 Continue
C****
C**** Compute vertically integrated column convergence and mass
C****
!$OMP ParallelDo   Private (I,J,L, IMAX,LM, CONVs,MMs)
      Do 630 J=J1,JN
      IMAX=IM  ;  If(J==1.or.J==JM) IMAX=1
      Do 630 I=1,IMAX
      LM=1
      If (LMOM(I,J) <= 1)  GoTo 620
      LM=LMOM(I,J)
      CONVs = Sum(CONV(I,J,1:LM)) / ZOE(LM)
      MMs   = Sum(  MM(I,J,1:LM)) / ZOE(LM)
C****
C**** Compute MW, the downward fluid flux
C****
      MW(I,J,1) = CONV(I,J,1) - CONVs*dZO(1) +
     +           (  MM(I,J,1) -   MMs*dZO(1)) * zNSxDTO
      Do 610 L=2,LM-1
  610 MW(I,J,L) = CONV(I,J,L) - CONVs*dZO(L) + MW(I,J,L-1) +
     +           (  MM(I,J,L) -   MMs*dZO(L)) * zNSxDTO
  620 MW(I,J,LM:LMO-1) = 0
  630 Continue
C****
C**** Sum mass fluxes to be used for advection of tracers
C****
      If (.not.QEVEN)  Return
!$OMP ParallelDo   Private (L)
      Do 710 L=1,LMO
      SMU(:,J1 :JN ,L) = SMU(:,J1 :JN ,L) + MU(:,J1 :JN ,L)
      SMV(:,J1H:JNP,L) = SMV(:,J1H:JNP,L) + MV(:,J1H:JNP,L)
      If (L==LMO)  GoTo 710
      SMW(:,J1P:JNP,L) = SMW(:,J1P:JNP,L) + MW(:,J1P:JNP,L)
  710 Continue
C     If (QSP)  SMW(1,1 ,:) = SMW(1,1 ,:) + MW(1,1 ,:)
      If (QNP)  SMW(1,JM,:) = SMW(1,JM,:) + MW(1,JM,:)
      Return
      EndSubroutine OFLUX


      Subroutine OPFIL (X) 4,16
C****
C**** OPFIL smoothes X in the zonal direction by reducing coefficients
C**** of its Fourier series for high wave numbers near the poles.
C**** The ocean polar filter here works with AVR4X5LD.Z12.gas1 .
C****
      Use CONSTANT, Only: TWOPI
      Use OCEAN, Only: IM,JM,LMO,J1O, DLON, DXP=>DXPO, DYP=>DYPO,
     *  JMPF=>J40S  !  greatest J in SH where polar filter is applied
      Use FILEMANAGER, Only: OPENUNIT, CLOSEUNIT
      Use DOMAIN_DECOMP, Only: GRID
      Implicit None
C****
      Real*8,Intent(InOut) ::
     *  X(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO)
      Integer*4,Parameter :: IMz2=IM/2  !  highest possible wave number
      Integer*4,Save ::
     *  JMEX,    !  exclude unfiltered latitudes, store J=JM-1 in JMEX
     *  NBASM,   !  maximum number of ocean basins at any latitude
     *  INDM,    !  number of entries in array REDUCO
     *  NMIN(JM) !  minimum wave number for Fourier smoothing
      Real*8,Save :: SMOOTH(IMz2,JM)  !  multiplies Fourier coefficient
C****
      Integer*2,Save,Allocatable,Dimension(:,:) ::
     *  NBAS    !  number of ocean basins for given layer and latitude
      Integer*2,Save,Allocatable,Dimension(:,:,:) ::
     *  IMINm1, !  western most cell in basin minus 1
     *  IWIDm1  !  number of cells in basin minus 1
      Integer*4,Save,Allocatable,Dimension(:,:) ::
     *  INDEX   !  index to concatenated matricies
      Real*4,Save,Allocatable,Dimension(:) ::
     *  REDUCO  !  concatenation of reduction matricies
C****
      Character*80 TITLE
      Integer*4 I,I1,INDX,IWm2, J,JA,JX, K,L, N,NB, IU_AVR
      Real*8 AN(0:IMz2), !  Fourier cosine coefficients
     *       BN(0:IMz2), !  Fourier sine coefficients
     *          Y(IM*2), !  original copy of X that wraps around IDL
     *       DRAT, REDUC
C****
      Integer*4,Save :: IFIRST=1, J1,JN,J1P,JNP
      If (IFIRST == 0)  GoTo 100
      IFIRST = 0
      JMEX = 2*JMPF-1
C**** Extract domain decomposition band parameters
C****                          Band1  Band2  BandM
      J1  = GRID%J_STRT     !    1      5     JM-3   Band minimum
      JN  = GRID%J_STOP     !    4      8     JM     Band maximum
      J1P = Max(J1,2)       !    2      5     JM-3   Exclude SP
      JNP = Min(JN,JM-1)    !    4      8     JM-1   Exclude NP
C**** Calculate SMOOTHing factor for longitudes without land cells
      Do 30 J=J1O,JM-1
      DRAT = DXP(J)/DYP(3)
      Do 20 N=IMz2,1,-1
      SMOOTH(N,J) = IMz2*DRAT/N
   20 If (SMOOTH(N,J) >= 1)  GoTo 30
C     N = 0
   30 NMIN(J) = N+1
C**** Read in reduction contribution matrices from disk
      Call OPENUNIT ('AVR',IU_AVR,.True.,.True.)
      Read (IU_AVR) TITLE,NBASM,INDM
      Allocate (IMINm1(NBASM,LMO,J1O:JMEX), NBAS(LMO,J1O:JMEX),
     *          IWIDm1(NBASM,LMO,J1O:JMEX), INDEX(IM,2:JMPF),
     *          REDUCO(INDM))
      Read (IU_AVR) TITLE,NBAS,IMINm1,IWIDm1,INDEX,REDUCO
      Call CLOSEUNIT (IU_AVR)
      Write (6,*) 'Read from unit',IU_AVR,': ',TITLE
 100  CONTINUE
C****
C**** Loop over J and L.  JX = eXclude unfiltered latitudes
C****                     JA = Absolute latitude
C****
!$OMP ParallelDo   Private (I,I1,INDX,IWm2, J,JA,JX, K,L, N,NB,
!$OMP*                      AN,BN, REDUC,Y)
      Do 410 J=Max(J1O,J1P),JNP
      JX=J  ;  If(J > JMPF) JX=J+2*JMPF-JM
      JA=J  ;  If(J > JMPF) JA=JM+1-J
      If (JA > JMPF)  GoTo 410  !  skip latitudes J=JMPF+1,JM-JMPF
      Do 400 L=1,LMO
      If (IWIDm1(1,L,JX) >= IM)  GoTo 300
C****
C**** Land cells exist at this latitude and layer, loop over ocean
C**** basins.
C****
      Do 270 NB=1,NBAS(L,JX)
      I1   = IMINm1(NB,L,JX) + 1
      IWm2 = IWIDm1(NB,L,JX) - 1
      INDX = INDEX(IWm2+1,JA)
      If (I1+IWm2 > IM)  GoTo 200
C**** Ocean basin does not wrap around the IDL.
C**** Copy X to temporary array Y and filter X in place.
      Do 110 I=I1,I1+IWm2
  110 Y(I) = X(I,J,L)
      Do 140 I=I1,I1+IWm2
      REDUC = 0
      Do 130 K=I1,I1+IWm2
      INDX=INDX+1
  130 REDUC = REDUC + REDUCO(INDX)*Y(K)
  140 X(I,J,L) = X(I,J,L) - REDUC
      GoTo 270
C**** Ocean basin wraps around the IDL.
C**** Copy X to temporary array Y and filter X in place.
  200 Do 210 I=I1,IM
  210 Y(I) = X(I,J,L)
      Do 220 I=IM+1,I1+IWm2
  220 Y(I) = X(I-IM,J,L)
      Do 240 I=I1,IM
      REDUC = 0
      Do 230 K=I1,I1+IWm2
      INDX=INDX+1
  230 REDUC = REDUC + REDUCO(INDX)*Y(K)
  240 X(I,J,L) = X(I,J,L) - REDUC
      Do 260 I=1,I1+IWm2-IM
      REDUC = 0
      Do 250 K=I1,I1+IWm2
      INDX=INDX+1
  250 REDUC = REDUC + REDUCO(INDX)*Y(K)
  260 X(I,J,L) = X(I,J,L) - REDUC
  270 Continue
      GoTo 400
C****
C**** No land cells at this latitude and layer,
C**** perform standard polar filter
C****
  300 Call FFT (X(1,J,L),AN,BN)
      Do 310 N=NMIN(J),IMz2-1
      AN(N) = AN(N)*SMOOTH(N,J)
  310 BN(N) = BN(N)*SMOOTH(N,J)
      AN(IMz2) = AN(IMz2)*SMOOTH(IMz2,J)
      Call FFTI (AN,BN,X(1,J,L))
C****
  400 Continue
  410 Continue
      Return
      EndSubroutine OPFIL


      Subroutine OADVM (MM2,MM0,DT) 10,6
C****
C**** OADVM calculates the updated mass
C**** Input:  MM0 (kg), DT (s), CONV (kg/s), MW (kg/s)
C**** Output: MM2 (kg) = MM0 + DT*[CONV + MW(L-1) - MW(L)]
C****
      Use OCEAN, Only: IM,JM,LMO, LMOM=>LMM
      Use OCEAN_DYN, Only: MW, CONV
      Use DOMAIN_DECOMP, Only: GRID
      Implicit None
      Real*8,Intent(Out):: MM2(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO)
      Real*8,Intent(In) :: MM0(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO)
     *                    ,DT
      Integer*4 I,J,L,LM,IMAX, J1,JN
C****
C**** Extract domain decomposition band parameters
C****                          Band1  Band2  BandM
      J1  = GRID%J_STRT     !    1      5     JM-3   Band minimum
      JN  = GRID%J_STOP     !    4      8     JM     Band maximum
C****
C**** Compute the new mass MM2
C****
!$OMP ParallelDo   Private (I,J, IMAX,LM)
      Do 20 J=J1,JN
      IMAX=IM  ;  If(J==1.or.J==JM) IMAX=1
      Do 20 I=1,IMAX
      If (LMOM(I,J)==0)  GoTo 20
      If (LMOM(I,J)==1)  Then
        MM2(I,J,1) = MM0(I,J,1) + DT*CONV(I,J,1)
        GoTo 20  ;  EndIf
      LM = LMOM(I,J)
      MM2(I,J,1) = MM0(I,J,1) + DT*(CONV(I,J,1)-MW(I,J,1))
      Do 10 L=2,LM-1
   10 MM2(I,J,L) = MM0(I,J,L) + DT*(CONV(I,J,L) + MW(I,J,L-1)-MW(I,J,L))
      MM2(I,J,LM) = MM0(I,J,LM) + DT*(CONV(I,J,LM) + MW(I,J,LM-1))
   20 Continue
      Return
      EndSubroutine OADVM


      Subroutine OADVV (UM2,VM2,UM0,VM0,DT1) 10,18
C****
C**** OADVV advects oceanic momentum (with coriolis force)
C**** Input:  MO (kg/m^2), UO (m/s), VO (m/s) = from odd solution
C****         MU (kg/s), MV (kg/s), MW (kg/s) = fluid fluxes
C**** Output: UM2 (kg*m/s) = UM0 + DT*(MU*U-MU*U + MV*U-MV*U + M*CM*V)
C****         VM2 (kg*m/s) = VM0 + DT*(MU*V-MU*V + MV*V-MV*V - M*CM*U)
C****
      Use CONSTANT, Only: RADIUS,OMEGA
      Use OCEAN, Only: IM,JM,LMO, IVSP,IVNP, DXV=>DXVO,
     *                 COSU,SINU, SINxY,TANxY, MO,UO,VO
      Use OCEAN_DYN, Only: MU,MV,MW
      Use DOMAIN_DECOMP, Only: GRID, HALO_UPDATE, NORTH,SOUTH
      Implicit None
      Real*8,Intent(Out),
     *  Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO) :: UM2,VM2
      Real*8,Intent(In),
     *  Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO) :: UM0,VM0
      Real*8,Intent(In) :: DT1
C****
      Real*8 DT2,DT4,DT6,DT12
      Real*8,Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO) ::
     *       DUM,DVM
      Real*8,Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO) ::
     *       UMVc,UMVw,UMVe
      Real*8 UMU,VMV,VMUc,VMUs,VMUn, FLUX, dUMSP,dVMSP,dUMNP,dVMNP,
     *       USINYJ(IM),UTANUJ(IM)
      Integer*4 I,J,L, J1,JN,J1P,J1H,JNP,JNR
      Logical*4 QSP,QNP
C****
C**** Extract domain decomposition band parameters
C**** ASSUME THAT J1 NEVER EQUALS 2 AND JN NEVER EQUALS JM-1
C****                          Band1  Band2  BandM
      J1  = GRID%J_STRT     !    1      5     JM-3   Band minimum
      JN  = GRID%J_STOP     !    4      8     JM     Band maximum
      J1P = Max(J1,2)       !    2      5     JM-3   Exclude SP
      J1H = Max(J1-1,1)     !    1      4     JM-4   Halo minimum
      JNP = Min(JN,JM-1)    !    4      8     JM-1   Exclude NP
      JNR = Min(JN+1,JM-1)  !    5      9     JM-1   Halo, Exclude NP
C     QSP = J1==1           !    T      F      F
      QNP = JN==JM          !    F      F      T
C****
C     Call HALO_UPDATE (GRID, MO, FROM=NORTH)        !  copied in OFLUX
      Call HALO_UPDATE (GRID, UO, FROM=NORTH+SOUTH)
      Call HALO_UPDATE (GRID, VO, FROM=NORTH)
C     Call HALO_UPDATE (GRID, VO, FROM=SOUTH)        !  copied in OFLUX
      Call HALO_UPDATE (GRID, MU, FROM=NORTH)
      Call HALO_UPDATE (GRID, MV, FROM=NORTH)
C     Call HALO_UPDATE (GRID, MV, FROM=SOUTH)        !  recalc in OFLUX
      Call HALO_UPDATE (GRID, MW, FROM=NORTH)
C****
      DT2  = DT1/2
      DT4  = DT1/4
      DT6  = DT1/6
      Dt12 = DT1/12
C****
C**** Horizontal advection of momentum
C****
!$OMP ParallelDo   Private (I,J,L, UMU, UMVc,UMVw,UMVe, USINYJ,
!$OMP*                             VMV, VMUc,VMUs,VMUn, UTANUJ)
      Do 480 L=1,LMO
C**** Zero out momentum changes
      DUM(:,:,L) = 0
      DVM(:,:,L) = 0
C**** Contribution of eastward flux to U momentum
      Do 310 J=J1,JN
      UMU = DT4*(UO(1,J,L)+UO(IM,J,L))*(MU(1,J,L)+MU(IM,J,L))
      DUM(1 ,J,L) = DUM(1 ,J,L) + UMU
      DUM(IM,J,L) = DUM(IM,J,L) - UMU
      Do 310 I=2,IM
      UMU = DT4*(UO(I,J,L)+UO(I-1,J,L))*(MU(I,J,L)+MU(I-1,J,L))
      DUM(I  ,J,L) = DUM(I  ,J,L) + UMU
  310 DUM(I-1,J,L) = DUM(I-1,J,L) - UMU
C**** Contribution of northward center to U momentum
      Do 320 J=J1H,JNP
      UMVc(IM,J) = DT6*(UO(IM,J,L)+UO(IM,J+1,L))*(MV(IM,J,L)+MV(1,J,L))
      DUM(IM,J  ,L) = DUM(IM,J  ,L) - UMVc(IM,J)
      DUM(IM,J+1,L) = DUM(IM,J+1,L) + UMVc(IM,J)
      Do 320 I=1,IM-1
      UMVc(I,J) = DT6*(UO(I,J,L)+UO(I,J+1,L))*(MV(I,J,L)+MV(I+1,J,L))
      DUM(I,J  ,L) = DUM(I,J  ,L) - UMVc(I,J)
  320 DUM(I,J+1,L) = DUM(I,J+1,L) + UMVc(I,J)
C**** Contribution of northward corner fluxes to U momentum
      Do 330 J=J1H,JNP
      UMVe(1,J) = DT12*(UO(IM,J,L)+UO(1 ,J+1,L))*MV(1,J,L)
      UMVw(1,J) = DT12*(UO(1 ,J,L)+UO(IM,J+1,L))*MV(1,J,L)
      DUM(1 ,J  ,L) = DUM(1 ,J  ,L) - UMVw(1,J)
      DUM(IM,J  ,L) = DUM(IM,J  ,L) - UMVe(1,J)
      DUM(1 ,J+1,L) = DUM(1 ,J+1,L) + UMVe(1,J)
      DUM(IM,J+1,L) = DUM(IM,J+1,L) + UMVw(1,J)
      Do 330 I=2,IM
      UMVe(I,J) = DT12*(UO(I-1,J,L)+UO(I  ,J+1,L))*MV(I,J,L)
      UMVw(I,J) = DT12*(UO(I  ,J,L)+UO(I-1,J+1,L))*MV(I,J,L)
      DUM(I  ,J  ,L) = DUM(I  ,J  ,L) - UMVw(I,J)
      DUM(I-1,J  ,L) = DUM(I-1,J  ,L) - UMVe(I,J)
      DUM(I  ,J+1,L) = DUM(I  ,J+1,L) + UMVe(I,J)
  330 DUM(I-1,J+1,L) = DUM(I-1,J+1,L) + UMVw(I,J)
C**** Contribution of eastward center flux to V momentum
      Do 340 J=J1,JNP
      VMUc = DT6*(VO(IM,J,L)+VO(1,J,L))*(MU(IM,J,L)+MU(IM,J+1,L))
      DVM(IM,J,L) = DVM(IM,J,L) - VMUc
      DVM(1 ,J,L) = DVM(1 ,J,L) + VMUc
      Do 340 I=1,IM-1
      VMUc = DT6*(VO(I,J,L)+VO(I+1,J,L))*(MU(I,J,L)+MU(I,J+1,L))
      DVM(I  ,J,L) = DVM(I  ,J,L) - VMUc
  340 DVM(I+1,J,L) = DVM(I+1,J,L) + VMUc
C**** Contribution of eastward corner and northward fluxes to V momentum
C     If (QSP)  Then
C       J = 1
C       VMV  = DT4 *(VO(IM,1,L)+V0(IM,0,L))*MV(IM,1,L)
C       VMUn = DT12*(V0(IM,0,L)+VO(1 ,1,L))*MU(IM,1,L)
C       VMUs = DT12*(VO(IM,1,L)+V0(1 ,0,L))*MU(IM,1,L)
C       DVM(IM,1,L) = DVM(IM,1,L) - VMUs + VMV
C       DVM(1 ,1,L) = DVM(1 ,1,L) + VMUn
C       Do 350 I=1,IM-1
C       VMV  = DT4* (VO(I,1,L)+V0(I  ,0,L))*MV(I,1,L)
C       VMUn = DT12*(V0(I,0,L)+VO(I+1,1,L))*MU(I,1,L)
C       VMUs = DT12*(VO(I,1,L)+V0(I+1,0,L))*MU(I,1,L)
C       DVM(I  ,1,L) = DVM(I  ,1,L) - VMUs + VMV
C 350   DVM(I+1,1,L) = DVM(I+1,1,L) + VMUn  ;  EndIf
      Do 360 J=J1P,JNR
      VMV  = DT4 *(VO(IM,J  ,L)+VO(IM,J-1,L))*(MV(IM,J,L)+MV(IM,J-1,L))
      VMUn = DT12*(VO(IM,J-1,L)+VO(1 ,J  ,L))* MU(IM,J,L)
      VMUs = DT12*(VO(IM,J  ,L)+VO(1 ,J-1,L))* MU(IM,J,L)
      DVM(IM,J  ,L) = DVM(IM,J  ,L) - VMUs + VMV
      DVM(IM,J-1,L) = DVM(IM,J-1,L) - VMUn - VMV
      DVM(1 ,J  ,L) = DVM(1 ,J  ,L) + VMUn
      DVM(1 ,J-1,L) = DVM(1 ,J-1,L) + VMUs
      Do 360 I=1,IM-1
      VMV  = DT4* (VO(I,J  ,L)+VO(I  ,J-1,L))*(MV(I,J,L)+MV(I,J-1,L))
      VMUn = DT12*(VO(I,J-1,L)+VO(I+1,J  ,L))* MU(I,J,L)
      VMUs = DT12*(VO(I,J  ,L)+VO(I+1,J-1,L))* MU(I,J,L)
      DVM(I  ,J  ,L) = DVM(I  ,J  ,L) - VMUs + VMV
      DVM(I  ,J-1,L) = DVM(I  ,J-1,L) - VMUn - VMV
      DVM(I+1,J  ,L) = DVM(I+1,J  ,L) + VMUn
  360 DVM(I+1,J-1,L) = DVM(I+1,J-1,L) + VMUs
      If (QNP)  Then
C       J = JM
        VMV  = DT4 *(VO(IM,JM  ,L)+VO(IM,JM-1,L))*MV(IM,JM-1,L)
        VMUn = DT12*(VO(IM,JM-1,L)+VO(1 ,JM  ,L))*MU(IM,JM,L)
        VMUs = DT12*(VO(IM,JM  ,L)+VO(1 ,JM-1,L))*MU(IM,JM,L)
        DVM(IM,JM-1,L) = DVM(IM,JM-1,L) - VMUn - VMV
        DVM(1 ,JM-1,L) = DVM(1 ,JM-1,L) + VMUs
        Do 370 I=1,IM-1
        VMV  = DT4* (VO(I,JM  ,L)+VO(I  ,JM-1,L))*MV(I,JM-1,L)
        VMUn = DT12*(VO(I,JM-1,L)+VO(I+1,JM  ,L))*MU(I,JM,L)
        VMUs = DT12*(VO(I,JM  ,L)+VO(I+1,JM-1,L))*MU(I,JM,L)
        DVM(I  ,JM-1,L) = DVM(I  ,JM-1,L) - VMUn - VMV
  370   DVM(I+1,JM-1,L) = DVM(I+1,JM-1,L) + VMUs  ;  EndIf
C****
C**** Coriolis force and metric term
C****
C**** U component
C     If (QSP)  Then
C       Do 410 I=1,IM-1
C 410   dUM(I,1,L) = dUM(I,1,L) +
C    +    DT2*OMEGA*RADIUS*SINxY(1)*(MV(I,1,L)+MV(I+1,1,L)) +
C    +    TANxY(1)*(UMVw(I,1)+UMVc(I,1)+UMVe(I+1,1))*.5
C       dUM(IM,1,L) = dUM(IM,1,L) +
C    +    DT2*OMEGA*RADIUS*SINxY(1)*(MV(IM,1,L)+MV(1,1,L)) +
C    +    TANxY(1)*(UMVw(IM,1)+UMVc(IM,1)+UMVe(1,1))*.5  !  EndIf
      Do 420 J=J1P,JNP
      dUM(IM,J,L) = dUM(IM,J,L) + DT2*OMEGA*RADIUS*SINxY(J)*
     *    (MV(IM,J-1,L)+MV(1,J-1,L)+MV(IM,J,L)+MV(1,J,L)) +
     +  TANxY(J)*(UMVe(IM,J-1)+UMVc(IM,J-1)+UMVw(1,J-1) +
     +            UMVw(IM,J  )+UMVc(IM,J  )+UMVe(1,J  ))*.5
      Do 420 I=1,IM-1
  420 dUM(I,J,L) = dUM(I,J,L) + DT2*OMEGA*RADIUS*SINxY(J)*
     *    (MV(I,J-1,L)+MV(I+1,J-1,L)+MV(I,J,L)+MV(I+1,J,L)) +
     +  TANxY(J)*(UMVe(I,J-1)+UMVc(I,J-1)+UMVw(I+1,J-1) +
     +            UMVw(I,J  )+UMVc(I,J  )+UMVe(I+1,J  ))*.5
      If (QNP)  Then
        Do 430 I=1,IM-1
  430   dUM(I,JM,L) = dUM(I,JM,L) +
     +    DT2*OMEGA*RADIUS*SINxY(JM)*(MV(I,JM-1,L)+MV(I+1,JM-1,L)) +
     +    TANxY(JM)*(UMVe(I,JM-1)+UMVc(I,JM-1)+UMVw(I+1,JM-1))*.5
        dUM(IM,JM,L) = dUM(IM,JM,L) +
     +    DT2*OMEGA*RADIUS*SINxY(JM)*(MV(IM,JM-1,L)+MV(1,JM-1,L)) +
     +    TANxY(JM)*(UMVe(IM,JM-1)+UMVc(IM,JM-1)+UMVw(1,JM-1))*.5
        EndIf
C**** V component
      Do 450 J=J1,JNP
      Do 440 I=1,IM
      USINYJ(I) =  UO(I,J,L)*SINxY(J) + UO(I,J+1,L)*SINxY(J+1)
  440 UTANUJ(I) = (UO(I,J,L)*TANxY(J) + UO(I,J+1,L)*TANxY(J+1))*
     *            (UO(I,J,L)+UO(I,J+1,L))
      dVM(1,J,L) = dVM(1,J,L) - DT4*DXV(J)*(MO(1,J,L)+MO(1,J+1,L))*
     *  (OMEGA*RADIUS*(USINYJ(IM)+USINYJ(1)) +
     +  .25*(UTANUJ(IM)+UTANUJ(1)) - (TANxY(J)+TANxY(J+1))*
     *    (UO(IM,J,L)-UO(1,J,L))*(UO(IM,J+1,L)-UO(1,J+1,L))/12)
      Do 450 I=2,IM
  450 dVM(I,J,L) = dVM(I,J,L) - DT4*DXV(J)*(MO(I,J,L)+MO(I,J+1,L))*
     *  (OMEGA*RADIUS*(USINYJ(I-1)+USINYJ(I)) +
     +  .25*(UTANUJ(I-1)+UTANUJ(I)) - (TANxY(J)+TANxY(J+1))*
     *    (UO(I-1,J,L)-UO(I,J,L))*(UO(I-1,J+1,L)-UO(I,J+1,L))/12)
  480 Continue
C****
C**** Vertical advection of momentum
C****
C**** U component
C$OMP ParallelDo   Private (I,J,L, FLUX)
      Do 560 J=J1,JN
      If (J==1)   GoTo 520
      If (J==JM)  GoTo 540
      Do 510 L=1,LMO-1
      FLUX = DT4*(MW(IM,J,L)+MW(1,J,L))*(UO(IM,J,L)+UO(IM,J,L+1))
      dUM(IM,J,L)   = dUM(IM,J,L)   - FLUX
      dUM(IM,J,L+1) = dUM(IM,J,L+1) + FLUX
      Do 510 I=1,IM-1
      FLUX = DT4*(MW(I,J,L)+MW(I+1,J,L))*(UO(I,J,L)+UO(I,J,L+1))
      dUM(I,J,L)   = dUM(I,J,L)   - FLUX
  510 dUM(I,J,L+1) = dUM(I,J,L+1) + FLUX
      GoTo 560
  520 Do 530 L=1,LMO-1
      Do 530 I=1,IM
      FLUX = DT2*MW(1,1,L)*(UO(I,1,L)+UO(I,1,L+1))
      dUM(I,1,L)   = dUM(I,1,L)   - FLUX
  530 dUM(I,1,L+1) = dUM(I,1,L+1) + FLUX
      GoTo 560
  540 Do 550 L=1,LMO-1
      Do 550 I=1,IM
      FLUX = DT2*MW(1,JM,L)*(UO(I,JM,L)+UO(I,JM,L+1))
      dUM(I,JM,L)   = dUM(I,JM,L)   - FLUX
  550 dUM(I,JM,L+1) = dUM(I,JM,L+1) + FLUX
  560 Continue
C**** V component
C$OMP ParallelDo   Private (I,J,L, FLUX)
      Do 660 J=J1,JNP
      If (J==1)  GoTo 620
      If (J==JM-1)  GoTo 640
      Do 610 L=1,LMO-1
      Do 610 I=1,IM
      FLUX = DT4*(MW(I,J,L)+MW(I,J+1,L))*(VO(I,J,L)+VO(I,J,L+1))
      dVM(I,J,L)   = dVM(I,J,L)   - FLUX
  610 dVM(I,J,L+1) = dVM(I,J,L+1) + FLUX
      GoTo 660
  620 Do 630 L=1,LMO-1
      Do 630 I=1,IM
      FLUX = DT4*(MW(1,1,L)+MW(I,2,L))*(VO(I,1,L)+VO(I,1,L+1))
      dVM(I,1,L)   = dVM(I,1,L)   - FLUX
  630 dVM(I,1,L+1) = dVM(I,1,L+1) + FLUX
      GoTo 660
  640 Do 650 L=1,LMO-1
      Do 650 I=1,IM
      FLUX = DT4*(MW(I,JM-1,L)+MW(1,JM,L))*(VO(I,JM-1,L)+VO(I,JM-1,L+1))
      dVM(I,JM-1,L)   = dVM(I,JM-1,L)   - FLUX
  650 dVM(I,JM-1,L+1) = dVM(I,JM-1,L+1) + FLUX
  660 Continue
C****
C**** Add changes to momentum
C****
C$OMP ParallelDo   Private (I,J,L, dUMSP,dVMSP,dUMNP,dVMNP)
      Do 730 L=1,LMO
C**** Calculate dUM and dVM at poles, then update UM and VM
C     If (QSP)  Then
C       dUMSP =   Sum (dUM(:,1,L)*COSU(:)) * 2/IM
C       dVMSP = - Sum (dUM(:,1,L)*SINU(:)) * 2/IM
C       UM2(IVSP,1,L) = UM0(IVSP,1,L) + dVMSP
C       UM2(IM  ,1,L) = UM0(IM  ,1,L) + dUMSP  ;  EndIf
      If (QNP)  Then
        dUMNP = Sum (dUM(:,JM,L)*COSU(:)) * 2/IM
        dVMNP = Sum (dUM(:,JM,L)*SINU(:)) * 2/IM
        UM2(IM  ,JM,L) = UM0(IM  ,JM,L) + dUMNP
        UM2(IVNP,JM,L) = UM0(IVNP,JM,L) + dVMNP  ;  EndIf
C**** Update UM and VM away from poles
      Do 710 J=J1P,JNP
  710 UM2(:,J,L) = UM0(:,J,L) + dUM(:,J,L)
      Do 720 J=J1,JNP
  720 VM2(:,J,L) = VM0(:,J,L) + dVM(:,J,L)
  730 Continue
      Return
      EndSubroutine OADVV


      Subroutine OPGF (UM,VM,DT1) 10,12
C****
C**** OPGF adds the pressure gradient force to the momentum
C**** Input: G0M (J), GZM, S0M (kg), SZM, DT (s), MO (kg/m^2)
C**** Output: UM (kg*m/s) = UM - DT*(DH*D(P)+MO*D(PHI))*DYP
C****         VM (kg*m/s) = VM - DT*(DH*D(P)+MO*D(PHI))*DXV
C****
      Use CONSTANT, Only: GRAV
      Use OCEAN, Only: IM,JM,LMO, IVNP,IVSP,
     *                 LMOM=>LMM,LMOU=>LMU,LMOV=>LMV,
     *                 MO, G0M,GZM=>GZMO, S0M,SZM=>SZMO,
     *                 FOCEAN, mZSOLID=>HOCEAN, DXPGF,DYPGF,
     *                 COSI=>COSIC,SINI=>SINIC, OPRESS,OGEOZ,OPBOT
      Use OCEAN_DYN, Only: VBAR,dH, GUP,GDN, SUP,SDN
      Use DOMAIN_DECOMP, Only: GRID, HALO_UPDATE, NORTH
      Implicit None
      Real*8,Parameter :: z12eH=.28867513d0  !  z12eH = 1/SQRT(12)
      Real*8,Intent(Out),
     *  Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO) :: UM,VM
      Real*8,Intent(In) :: DT1
      Real*8,External   :: VOLGSP
C****
      Real*8,Dimension(IM,GRID%J_STRT_HALO:GRID%J_STOP_HALO,LMO) ::
     *          dUM,dVM, P,ZG
      Real*8    PUP(LMO),PDN(LMO), PE,ZGE,VUP,VDN,dZGdP,
     *          dUMNP(LMO),dVMNP(LMO), DT2
      Integer*4 I,J,L,IMAX, J1,JN,J1P,JNP,JNH
      Logical*4 QSP,QNP
C****
C**** Extract domain decomposition band parameters
C**** ASSUME THAT J1 NEVER EQUALS 2 AND JN NEVER EQUALS JM-1
C****                          Band1  Band2  BandM
      J1  = GRID%J_STRT     !    1      5     JM-3   Band minimum
      JN  = GRID%J_STOP     !    4      8     JM     Band maximum
      J1P = Max(J1,2)       !    2      5     JM-3   Exclude SP
      JNP = Min(JN,JM-1)    !    4      8     JM-1   Exclude NP
      JNH = Min(JN+1,JM)    !    5      9     JM     Halo maximum
C     QSP = J1==1           !    T      F      F
      QNP = JN==JM          !    F      F      T
C****
      Call HALO_UPDATE (GRID,OPRESS,FROM=NORTH)
C     Call HALO_UPDATE (GRID,  MO, FROM=NORTH)  !  copied in OFLUX
C     Call HALO_UPDATE (GRID, GUP, FROM=NORTH)  !  recalc in OPGF0
C     Call HALO_UPDATE (GRID, GDN, FROM=NORTH)  !  recalc in OPGF0
C     Call HALO_UPDATE (GRID, SUP, FROM=NORTH)  !  recalc in OPGF0
C     Call HALO_UPDATE (GRID, SDN, FROM=NORTH)  !  recalc in OPGF0
      DT2 = DT1/2
C****
C**** Calculate the mass weighted pressure P (Pa),
C**** geopotential ZG (m^2/s^2), and layer thickness dH (m)
C****
C$OMP ParallelDo   Private (I,J,L,IMAX, PUP,PDN,PE, ZGE,VUP,VDN,dZGdP)
      Do 130 J=J1,JNH
      IMAX=IM  ;  If(J==1.or.J==JM) IMAX=1
      Do 130 I=1,IMAX
      If (FOCEAN(I,J) == 0)  GoTo 130
C**** Calculate pressure by integrating from the top down
      PE = OPRESS(I,J)
      Do 110 L=1,LMOM(I,J)
      P(I,J,L) = PE     + MO(I,J,L)*GRAV*.5
      PUP(L) = P(I,J,L) - MO(I,J,L)*GRAV*z12eH
      PDN(L) = P(I,J,L) + MO(I,J,L)*GRAV*z12eH
  110 PE     = PE       + MO(I,J,L)*GRAV
C**** save bottom pressure diagnostic
      OPBOT(I,J)=PE
C**** Calculate geopotential by integrating from the bottom up
      ZGE = - mZSOLID(I,J)*GRAV
      Do 120 L=LMOM(I,J),1,-1
      VUP = VOLGSP (GUP(I,J,L),SUP(I,J,L),PUP(L))
      VDN = VOLGSP (GDN(I,J,L),SDN(I,J,L),PDN(L))
      dZGdP = VUP*(.5-z12eH) + VDN*(.5+z12eH)
      VBAR(I,J,L) = (VUP + VDN)*.5
      ZG(I,J,L) = MO(I,J,L)*GRAV*.5*dZGdP + ZGE
      dH(I,J,L) = MO(I,J,L)*VBAR(I,J,L)
  120 ZGE = ZGE + dH(I,J,L)*GRAV
      OGEOZ(I,J) = ZGE
  130 Continue
C**** Copy VBAR and DH to all longitudes at north pole
      If (QNP) Then
        Do 140 L=1,LMOM(1,JM)
        VBAR(2:IM,JM,L) = VBAR(1,JM,L)
  140     DH(2:IM,JM,L) =   DH(1,JM,L)
      EndIf
C****
C**** Calculate smoothed East-West Pressure Gradient Force
C****
C$OMP ParallelDo   Private (I,J,L)
      Do 220 J=J1P,JNP
      Do 210 I=1,IM-1
      Do 210 L=1,LMOU(I,J)
  210 dUM(I,J,L) = DT2*DYPGF(J)*
     *             ((dH(I,J,L)+dH(I+1,J,L))*( P(I,J,L)- P(I+1,J,L)) +
     +              (MO(I,J,L)+MO(I+1,J,L))*(ZG(I,J,L)-ZG(I+1,J,L)))
      Do 220 L=1,LMOU(IM,J)
  220 dUM(IM,J,L) = DT2*DYPGF(J)*
     *              ((dH(IM,J,L)+dH(1,J,L))*( P(IM,J,L)- P(1,J,L)) +
     +               (MO(IM,J,L)+MO(1,J,L))*(ZG(IM,J,L)-ZG(1,J,L)))
      Call OPFIL (dUM)
C****
C**** Calculate North-South Pressure Gradient Force
C****
C$OMP ParallelDo   Private (I,J,L)
      Do 340 J=J1,JNP
      If (J==JM-1)  GoTo 320
      Do 310 I=1,IM
      Do 310 L=1,LMOV(I,J)
  310 dVM(I,J,L) = DT2*DXPGF(J)*
     *  ((dH(I,J,L)+dH(I,J+1,L))*( P(I,J,L)- P(I,J+1,L)) +
     +   (MO(I,J,L)+MO(I,J+1,L))*(ZG(I,J,L)-ZG(I,J+1,L)))
      GoTo 340
  320 Do 330 I=1,IM
      Do 330 L=1,LMOV(I,JM-1)
  330 dVM(I,JM-1,L) = DT2*DXPGF(JM-1)*
     *  ((dH(I,JM-1,L)+dH(1,JM,L))*( P(I,JM-1,L)- P(1,JM,L)) +
     +   (MO(I,JM-1,L)+MO(1,JM,L))*(ZG(I,JM-1,L)-ZG(1,JM,L)))
  340 Continue
C****
C**** Pressure Gradient Force at north pole
C****
      If (QNP)  Then
        dUMNP(:) = 0  ;  dVMNP(:) = 0
        Do 510 I=1,IM
        Do 510 L=1,LMOV(I,JM-1)
        dUMNP(L) = dUMNP(L) - SINI(I)*dVM(I,JM-1,L)
  510   dVMNP(L) = dVMNP(L) + COSI(I)*dVM(I,JM-1,L)
        Do 520 L=1,LMOM(1,JM)
        dUMNP(L) = dUMNP(L)*4*DXPGF(JM) / (IM*DXPGF(JM-1))
  520   dVMNP(L) = dVMNP(L)*4*DXPGF(JM) / (IM*DXPGF(JM-1))  ;  EndIf
C****
C**** Add pressure gradient force to momentum
C****
C**** Update UM away from poles
C$OMP ParallelDo   Private (I,J,L)
      Do 630 J=J1,JNP
      Do 620 I=1,IM
      Do 610 L=1,LMOU(I,J)
  610 UM(I,J,L) = UM(I,J,L) + dUM(I,J,L)
C**** Update VM away from poles
      Do 620 L=1,LMOV(I,J)
  620 VM(I,J,L) = VM(I,J,L) + dVM(I,J,L)
  630 Continue
C**** UM and VM at poles
      If (QNP)  Then
        Do 650 L=1,LMOM(1,JM)
        UM(IM  ,JM,L) = UM(IM  ,JM,L) + dUMNP(L)
  650   UM(IVNP,JM,L) = UM(IVNP,JM,L) + dVMNP(L)  ;  EndIf
      Return
      End Subroutine OPGF


      Subroutine OPGF0 4,16
C****
C**** OPGF0 calculates GUP, GDN, SUP and SDN inside each ocean cell,
C**** which will be kept constant during the dynamics of momentum
C****
      Use OCEAN, Only: IM,JM,LMO, LMOM=>LMM,
     *                 G0M,GZM=>GZMO, S0M,SZM=>SZMO
      Use OCEAN_DYN, Only: MMI, GUP,GDN, SUP,SDN
      Use DOMAIN_DECOMP, Only: GRID, HALO_UPDATE, NORTH
      Implicit None
      Real*8,Parameter :: z12eH=.28867513d0  !  z12eH = 1/SQRT(12)
      Integer*4 I,J,L,IMAX, J1,JN,JNH
C****
C**** Extract domain decomposition band parameters
C****                          Band1  Band2  BandM
      J1  = GRID%J_STRT     !    1      5     JM-3   Band minimum
      JN  = GRID%J_STOP     !    4      8     JM     Band maximum
      JNH = Min(JN+1,JM)    !    5      9     JM     Halo maximum
C****
      Call HALO_UPDATE (GRID, MMI, FROM=NORTH)
      Call HALO_UPDATE (GRID, G0M, FROM=NORTH)
      Call HALO_UPDATE (GRID, GZM, FROM=NORTH)
      Call HALO_UPDATE (GRID, S0M, FROM=NORTH)
      Call HALO_UPDATE (GRID, SZM, FROM=NORTH)
C****
C$OMP ParallelDo   Private (I,J,L,IMAX)
      Do 10 J=J1,JNH
      IMAX=IM  ;  If(J==1.or.J==JM) IMAX=1
      Do 10 I=1,IMAX
      Do 10 L=1,LMOM(I,J)
      GUP(I,J,L) = (G0M(I,J,L) - 2*z12eH*GZM(I,J,L)) / MMI(I,J,L)
      GDN(I,J,L) = (G0M(I,J,L) + 2*z12eH*GZM(I,J,L)) / MMI(I,J,L)
      SUP(I,J,L) = (S0M(I,J,L) - 2*z12eH*SZM(I,J,L)) / MMI(I,J,L)
   10 SDN(I,J,L) = (S0M(I,J,L) + 2*z12eH*SZM(I,J,L)) / MMI(I,J,L)
      Return
      End Subroutine OPGF0


      SUBROUTINE OADVT (RM,RX,RY,RZ,DT,QLIMIT, OIJL) 6,18
!@sum  OADVT advects tracers using the linear upstream scheme.
!@auth Gary Russell
!@ver  1.0
C****
C**** Input:  MB (kg) = mass before advection
C****          DT (s) = time step
C****       MU (kg/s) = west to east mass flux
C****       MV (kg/s) = south to north mass flux
C****       MW (kg/s) = downward vertical mass flux
C****          QLIMIT = whether slope limitations should be used
C**** Output: RM (kg) = tracer mass
C****   RX,RY,RZ (kg) = first moments of tracer mass
C****       OIJL (kg) = diagnostic accumulation of tracer mass flux
C****
      USE OCEAN, only : im,jm,lmo
      USE OCEAN_DYN, only : mb=>mmi,smu,smv,smw
      use domain_decomp, only : grid, get

      IMPLICIT NONE
      REAL*8, INTENT(INOUT),     DIMENSION
     *     (IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO) :: RM,RX,RY,RZ
      REAL*8, INTENT(INOUT),
     *  DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO,3) :: OIJL
      REAL*8,
     *  DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO) :: MA
      INTEGER I,J,L, J_0H
      LOGICAL, INTENT(IN) :: QLIMIT
      REAL*8, INTENT(IN) :: DT

      logical :: HAVE_NORTH_POLE  ! ,HAVE_SOUTH_POLE
         call get (grid, J_STRT_HALO=J_0H)
         call get (grid, HAVE_NORTH_POLE=HAVE_NORTH_POLE)
C        call get (grid, HAVE_SOUTH_POLE=HAVE_SOUTH_POLE)

C****
C**** Load mass after advection from mass before advection
C****
      MA = MB
C****
C**** Advect the tracer using the Slopes Scheme
C****
      CALL OADVTX (RM,RX,RY,RZ,MA,SMU,5d-1*DT,QLIMIT,OIJL(1,J_0H,1,1))
      CALL OADVTY (RM,RX,RY,RZ,MA,SMV,     DT,QLIMIT,OIJL(1,J_0H,1,2))
      CALL OADVTZ (RM,RX,RY,RZ,MA,SMW,     DT,QLIMIT,OIJL(1,J_0H,1,3))
      CALL OADVTX (RM,RX,RY,RZ,MA,SMU,5d-1*DT,QLIMIT,OIJL(1,J_0H,1,1))
C**** Fill in values at the poles
      if (HAVE_NORTH_POLE) then
      DO 20 L=1,LMO
      DO 20 I=1,IM
      RM(I,JM,L) = RM(1,JM,L)
   20 RZ(I,JM,L) = RZ(1,JM,L)
      end if
C     if (HAVE_SOUTH_POLE) then
C     DO 30 L=1,LMO
C     DO 30 I=1,IM
C     RM(I, 1,L) = RM(IM,1,L)
C  30 RZ(I, 1,L) = RZ(IM,1,L)
C     end if

      RETURN
      END SUBROUTINE OADVT


      SUBROUTINE OADVTX (RM,RX,RY,RZ,MO,MU,DT,QLIMIT,OIJL) 4,10
!@sum  OADVTX advects tracer in x direction using linear upstream scheme
!@auth Gary Russell
!@ver  1.0
C**** If QLIMIT is true, the gradients are
C**** limited to prevent the mean tracer from becoming negative.
C****
C**** Input: DT (s) = time step
C****     MU (kg/s) = west to east mass flux
C****        QLIMIT = whether slope limitations should be used
C**** Input and Output: RM (kg) = tracer mass
C****             RX,RY,RZ (kg) = first moments of tracer mass
C****                    M (kg) = ocean mass
C****
      USE OCEAN, only : im,jm,lmo,lmm,focean
      use domain_decomp, only : grid, get

      IMPLICIT NONE
      REAL*8, INTENT(INOUT),
     *  DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO) ::
     *  RM,RX,RY,RZ, OIJL, MO
      REAL*8, INTENT(IN),
     *  DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO) :: MU
      LOGICAL*4, INTENT(IN) :: QLIMIT
      REAL*8, INTENT(IN) :: DT
      REAL*8, DIMENSION(IM) :: AM,A,FM,FX,FY,FZ
      REAL*8 RXY
      INTEGER I,J,L,IM1,IP1,ICKERR

      INTEGER :: J_0S,J_1S

      CALL GET(grid, J_STRT_SKP=J_0S,    J_STOP_SKP=J_1S)

C**** Loop over layers and latitudes
      ICKERR=0
!$OMP PARALLEL DO  PRIVATE(I,J,L,IP1,IM1,A,AM,FM,FX,FY,FZ,RXY)
!$OMP&             REDUCTION(+:ICKERR)
      DO 320 L=1,LMO
      DO 320 J=J_0S,J_1S
C****
C**** Calculate FM (kg), FX (kg**2), FY (kg) and FZ (kg)
C****
      I=IM
      DO 140 IP1=1,IM
      AM(I) = DT*MU(I,J,L)
      IF(AM(I)) 110,120,130
C**** Ocean mass flux is negative
  110 A(I)  = AM(I)/MO(IP1,J,L)
      IF(A(I).LT.-1d0)  WRITE (6,*) 'A<-1:',I,J,L,A(I),MO(IP1,J,L)
      FM(I) = A(I)*(RM(IP1,J,L)-(1d0+A(I))*RX(IP1,J,L))
      FX(I) = AM(I)*(A(I)*A(I)*RX(IP1,J,L)-3d0*FM(I))
      FY(I) = A(I)*RY(IP1,J,L)
      FZ(I) = A(I)*RZ(IP1,J,L)
      GO TO 140
C**** Ocean mass flux is zero
  120 A(I)  = 0.
      FM(I) = 0.
      FX(I) = 0.
      FY(I) = 0.
      FZ(I) = 0.
      GO TO 140
C**** Ocean mass flux is positive
  130 A(I)  = AM(I)/MO(I,J,L)
      IF(A(I).GT.1d0)  WRITE (6,*) 'A>1:',I,J,L,A(I),MO(I,J,L)
      FM(I) = A(I)*(RM(I,J,L)+(1d0-A(I))*RX(I,J,L))
      FX(I) = AM(I)*(A(I)*A(I)*RX(I,J,L)-3d0*FM(I))
      FY(I) = A(I)*RY(I,J,L)
      FZ(I) = A(I)*RZ(I,J,L)
  140 I=IP1
C****
C**** Modify the tracer moments so that the tracer mass in each
C**** division is non-negative
C****
      IF(.NOT.QLIMIT)  GO TO 300
      IM1=IM
      DO 290 I=1,IM
      IF(A(IM1).GE.0.)  GO TO 240
C**** Water is leaving through the left edge: 2 or 3 divisions
      IF(FM(IM1).LE.0.)  GO TO 210
C**** Left most division is negative, RML = -FM(I-1) < 0: Case 2 or 4
      RX(I,J,L) = RM(I,J,L)/(1d0+A(IM1))
      FM(IM1) = 0.
      FX(IM1) = AM(IM1)*A(IM1)*A(IM1)*RX(I,J,L)
      IF(A(I).LE.0.)  GO TO 290
      FM(I) = A(I)*(RM(I,J,L)+(1d0-A(I))*RX(I,J,L))
      FX(I) = AM(I)*(A(I)*A(I)*RX(I,J,L)-3d0*FM(I))
      GO TO 290
C**** Left most division is non-negative, RML = -FM(I-() > 0:
C**** Case 1, 3 or 5
  210 IF(A(I).LE.0.)  GO TO 230
C**** Water is leaving through the right edge: 3 divisions
      IF(FM(I).GE.0.)  GO TO 290
C**** Right most division is negative, RMR = FM(I) < 0: Case 3 or 5
  220 RX(I,J,L) = -RM(I,J,L)/(1d0-A(I))
      FM(I) = 0.
      FX(I) = AM(I)*A(I)*A(I)*RX(I,J,L)
      FM(IM1) = A(IM1)*(RM(I,J,L)-(1d0+A(IM1))*RX(I,J,L))
      FX(IM1) = AM(IM1)*(A(IM1)*A(IM1)*RX(I,J,L)-3d0*FM(IM1))
      GO TO 290
C**** No water is leaving through the right edge: 2 divisions
  230 IF(RM(I,J,L)+FM(IM1).GE.0.)  GO TO 290
C**** Right most division is negative, RMR = RM(I,J)+FM(I-1) < 0: Case 3
      RX(I,J,L) = RM(I,J,L)/A(IM1)
      FM(IM1) = -RM(I,J,L)
      FX(IM1) = AM(IM1)*(A(IM1)+3d0)*RM(I,J,L)
      GO TO 290
C**** No water is leaving through the left edge: 1 or 2 divisions
  240 IF(A(I).LE.0.)  GO TO 290
C**** Water is leaving through the right edge: 2 divisions
      IF(FM(I).GE.0.)  GO TO 250
C**** Right most division is negative, RMR = FM(I) < 0: Case 3
      RX(I,J,L) = -RM(I,J,L)/(1d0-A(I))
      FM(I) = 0.
      FX(I) = AM(I)*A(I)*A(I)*RX(I,J,L)
      GO TO 290
C**** Right most division is non-negative, RMR = FM(I) > 0: Case 1 or 2
  250 IF(RM(I,J,L)-FM(I).GE.0.)  GO TO 290
C**** Left most division is negative, RML = RM(I,J)-FM(I) < 0: Case 2
      RX(I,J,L) = RM(I,J,L)/A(I)
      FM(I) = RM(I,J,L)
      FX(I) = AM(I)*(A(I)-3d0)*RM(I,J,L)
C****
  290 IM1=I
C****
C**** Calculate new tracer mass and first moments of tracer mass
C****
  300 IM1=IM
      DO 310 I=1,IM
      IF(L.GT.LMM(I,J))  GO TO 310
      RM(I,J,L) = RM(I,J,L) +  (FM(IM1)-FM(I))
      RX(I,J,L) = (RX(I,J,L)*MO(I,J,L) + (FX(IM1)-FX(I))
     *  + 3d0*((AM(IM1)+AM(I))*RM(I,J,L)-MO(I,J,L)*(FM(IM1)+FM(I))))
     *  / (MO(I,J,L)+AM(IM1)-AM(I))
      RY(I,J,L) = RY(I,J,L) + (FY(IM1)-FY(I))
      RZ(I,J,L) = RZ(I,J,L) + (FZ(IM1)-FZ(I))
C****
      if ( QLIMIT ) then ! limit tracer gradients
        RXY = abs(RX(I,J,L)) + abs(RY(I,J,L))
        if ( RXY > RM(I,J,L) ) then
          RX(I,J,L) = RX(I,J,L)*( RM(I,J,L)/(RXY + tiny(RXY)) )
          RY(I,J,L) = RY(I,J,L)*( RM(I,J,L)/(RXY + tiny(RXY)) )
        end if
        if ( abs(RZ(I,J,L)) > RM(I,J,L) )
     *       RZ(I,J,L) = sign(RM(I,J,L), RZ(I,J,L)+0d0)
      end if
C****
      MO(I,J,L) = MO(I,J,L) +  AM(IM1)-AM(I)
         IF(MO(I,J,L).LE.0.)                ICKERR=ICKERR+1
         IF(QLIMIT .AND. RM(I,J,L).LT.0.)   ICKERR=ICKERR+1
         OIJL(I,J,L) = OIJL(I,J,L) + FM(I)
  310 IM1=I
  320 CONTINUE
!$OMP END PARALLEL DO

C**** IF NO ERROR HAS OCCURRED - RETURN, ELSE STOP
      IF(ICKERR == 0)  RETURN
      DO 440 L=1,LMO
      DO 440 J=J_0S,J_1S
      DO 440 I=1,IM
         IF(FOCEAN(I,J).gt.0 .and. MO(I,J,L).LE.0.)  GO TO 800
         IF(QLIMIT .AND. RM(I,J,L).LT.0.) GO TO 810
  440 CONTINUE
      WRITE(6,*) 'ERROR CHECK INCONSISTENCY: OADVTX ',ICKERR
      call stop_model("OADVTX",255)

  800 WRITE (6,*) 'MO<0 in OADVTX:',I,J,L,MO(I,J,L)
  810 WRITE (6,*) 'RM in OADVTX:',I,J,L,RM(I,J,L)
c      WRITE (6,*) 'A=',(I,A(I),I=1,IM)
      call stop_model("OADVTX",255)
      END SUBROUTINE OADVTX


      subroutine oadvty (rm,rx,ry,rz,mo,mv,dt,qlimit,oijl) 2,14
!@sum  OADVTY advection driver for y-direction
!@auth Gary Russell, modified by T.Clune, R. Ruedy
c****
c**** oadvty advects tracers in the south to north direction using the
c**** linear upstream scheme.  if qlimit is true, the moments are
c**** limited to prevent the mean tracer from becoming negative.
c****
c**** input:
c****     mv (kg/s) = north-south mo flux, positive northward
c****        qlimit = whether slope limitations should be used
c****
c**** input/output:
c****     rm     (kg) = tracer mass
c****   rx,ry,rz (kg) = 1st moments of tracer mass
c****     mo     (kg) = ocean mass
c****
      use DOMAIN_DECOMP, only : grid, get, halo_update
      use DOMAIN_DECOMP, only : halo_update_column
      use DOMAIN_DECOMP, only : NORTH, SOUTH, AM_I_ROOT
      use OCEAN, only : im,jm,lmo,lmm,focean
      implicit none
      REAL*8, dimension(im,grid%j_strt_halo:grid%j_stop_halo,lmo) ::
     &                  rm, rx,ry,rz, mo,mv, oijl
      real*8, intent(in) :: dt
      logical, intent(in) ::  qlimit

      REAL*8, dimension(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO) ::
     &        BM, fm,fx,fy,fz
      integer :: i,j,L,ierr,ICKERR, err_loc(3)
      REAL*8, DIMENSION(LMO) ::
     &     m_np,rm_np,rzm_np ! ,m_sp,rm_sp,rzm_sp

c****Get relevant local distributed parameters
      INTEGER J_0,J_1,J_0H,J_1H,J_1S
      LOGICAL :: HAVE_SOUTH_POLE, HAVE_NORTH_POLE
      CALL GET(grid, J_STRT = J_0,
     &               J_STOP = J_1, J_STOP_SKP=J_1S,
     &               J_STRT_HALO = J_0H,
     &               J_STOP_HALO = J_1H,
     &               HAVE_SOUTH_POLE = HAVE_SOUTH_POLE,
     &               HAVE_NORTH_POLE = HAVE_NORTH_POLE)

c**** loop over layers
      ICKERR=0
!$OMP  PARALLEL DO PRIVATE(I,J,L) DEFAULT(SHARED)
!!! !$OMP* SHARED(JM,im)
      do L=1,lmo

c****   fill in and save polar values
c       if (HAVE_SOUTH_POLE) then
c         m_sp(L) = mo(1,1,L)
c         mo(2:im,1,L) = mo(1,1,L)
c         rm_sp(L) = rm(1,1,L)
c         rm(2:im,1,L) = rm(1,1,L)
c         rzm_sp(L)  = rz(1,1,L)
c         rz(2:im,1,L)  = rz(1,1,L)
c       end if                       !SOUTH POLE

        if (HAVE_NORTH_POLE) then
          m_np(L) = mo(1,jm,L)
          mo(2:im,jm,L) = mo(1,jm,L)
          rm_np(L) = rm(1,jm,l)
          rm(2:im,jm,L) = rm(1,jm,L)
          rzm_np(L)  = rz(1,jm,l)
          rz(2:im,jm,L)  = rz(1,jm,L)
        end if                       !NORTH POLE
c****
c****   convert flux to water mass moving north (if >0)
        do j=j_0,j_1
        do i=1,im
          if(focean(i,j).gt.0. .and. L.le.lmm(i,j)) then
            bm(i,j,L)=dt*mv(i,j,L)
          else
            bm(i,j,L)=0.
            mo(i,j,L)=0.
          end if
        end do
        end do

c****   POLES: set horiz. moments to zero
        IF (HAVE_SOUTH_POLE) THEN
          rx(:,1,L) = 0. ; ry(:,1,L) = 0.
        end if
        IF (HAVE_NORTH_POLE) THEN
          bm(:,jm,L) = 0.
          rx(:,jm,L) = 0. ; ry(:,jm,L) = 0.
        END IF
      end do   ! loop over layers
!$OMP END PARALLEL DO

c****
c**** call 1-d advection routine
c****
        call advec_lin_1D_custom(
     &     rm(1,j_0h,1), rx(1,j_0h,1),ry(1,j_0h,1),rz(1,j_0h,1),
     &     fm(1,j_0h,1), fx(1,j_0h,1),fy(1,j_0h,1),fz(1,j_0h,1),
     &     mo(1,j_0h,1), bm(1,j_0h,1),
     &     qlimit,ierr,err_loc)

        if (ierr.gt.0) then
          write(6,*) "Error in oadvty: i,j,l=",err_loc
          if (ierr == 2) then
ccc         write(0,*) "Error in qlimit: abs(b) > 1"
ccc         call stop_model('Error in qlimit: abs(b) > 1',11)
            ICKERR=ICKERR+1
          endif
        end if
! horizontal moments are zero at pole
c       IF (HAVE_SOUTH_POLE) then
c          rx(:,1, :) = 0  ; ry(:,1, :) = 0
c       end if
        IF (HAVE_NORTH_POLE) then
           rx(:,jm,:) = 0. ; ry(:,jm,:) = 0.
        end if

!$OMP  PARALLEL DO PRIVATE(I,L) DEFAULT(SHARED)
!$OMP&             REDUCTION(+:ICKERR)
!!! !$OMP* SHARED(JM,IM)
      do l=1,lmo

c****   average and update polar boxes
c       if (HAVE_SOUTH_POLE) then
c         mo(:,1 ,l) = (m_sp(l) + sum(mo(:,1 ,l)-m_sp(l)))/im
c         rm(:,1 ,l) = (rm_sp(l) + sum(rm(:,1 ,l)-rm_sp(l)))/im
c         rz(:,1 ,l) = (rzm_sp(l) + sum(rz(:,1 ,l)-rzm_sp(l) ))/im
c       end if   !SOUTH POLE

        if (HAVE_NORTH_POLE .and. L.le.lmm(1,jm)) then
          mo(1,jm,l) = m_np(l)   + sum(bm(:,jm-1,l))/im
          rm(1,jm,l) = rm_np(l)  + sum(fm(:,jm-1,l))/im
          rz(1,jm,l) = rzm_np(l) + sum(fz(:,jm-1,l))/im
          if(mo(1,jm,l)<0. .or. (qlimit.and.rm(1,jm,l)<0.)) then
            ICKERR=ICKERR+1
            write(0,*) 'oadvty: mo or salt<0 at North Pole layer',
     *      L,mo(1,jm,L),rm(1,jm,L)
          endif
          mo(2:im,jm,l)=mo(1,jm,l)
          rm(2:im,jm,l)=rm(1,jm,l)
          rz(2:im,jm,l)=rz(1,jm,l)
        end if  !NORTH POLE

      enddo ! end loop over levels
!$OMP  END PARALLEL DO
c
c**** sum into oijl
c
!$OMP  PARALLEL DO PRIVATE(J,L)
      do l=1,lmo ; do j=J_0,J_1S
          oijl(:,j,l)  = oijl(:,j,l) + fm(:,j,l)
      enddo ; enddo
!$OMP  END PARALLEL DO
C
      IF(ICKERR.GT.0)  CALL stop_model('Stopped in oadvty',11)
C
      return
c****
      end subroutine oadvty


      subroutine advec_lin_1D_custom(s,sx,sy,sz, f,fx,fy,fz, mass,dm, 2,46
     *     qlimit,ierr, err_loc)
!@sum  advec_lin_1d_custom is a parallel variant of adv1d but for a
!@sum  linear upstream scheme.
!@auth T. Clune, R. Ruedy
c--------------------------------------------------------------
c adv1d advects tracers in j-direction using the lin ups scheme
c--------------------------------------------------------------
      use ocean, only: im,jm,lmo,lmm,focean
      USE DOMAIN_DECOMP, only: grid, GET
      USE DOMAIN_DECOMP, only: NORTH, SOUTH
      USE DOMAIN_DECOMP, only: HALO_UPDATE, HALO_UPDATE_COLUMN
      USE DOMAIN_DECOMP, only: CHECKSUM
      implicit none
      !
!@var s mean tracer amount (kg or J)
!@var sx,sy,sz lin tracer moments (kg or J)
!@var f tracer flux (diagnostic output) (kg or J)
!@var fx,fy,fz tracer moment flux (diagnostic output) (kg or J)
!@var mass mass field (kg)
!@var dm mass flux (kg)
!@var qlimit true if negative tracer is to be avoided
!@var ierr, nerr error codes
      logical, intent(in) :: qlimit
      REAL*8, dimension(im, grid%j_strt_halo:grid%j_stop_halo,lmo)
     *  :: s,sx,sy,sz, f,fx,fy,fz, mass,dm
      integer :: n,np1,nm1,nn,ns
      integer,intent(out) :: ierr,err_loc(3) ! 3 dimensions
      REAL*8 :: fracm,frac1,mnew
      INTEGER :: i
      INTEGER :: l
      INTEGER :: J_0, J_1, J_0S, J_1S
      LOGICAL :: HAVE_SOUTH_POLE

      ierr=0

      CALL GET(grid, J_STRT = J_0, J_STOP=J_1,
     & J_STRT_SKP=J_0S, J_STOP_SKP=J_1S,
     & HAVE_SOUTH_POLE=HAVE_SOUTH_POLE)

      CALL HALO_UPDATE(grid, mass, FROM=NORTH)
      CALL HALO_UPDATE(grid, s, FROM=NORTH)
      CALL HALO_UPDATE(grid, sx, FROM=NORTH)
      CALL HALO_UPDATE(grid, sy, FROM=NORTH)
      CALL HALO_UPDATE(grid, sz, FROM=NORTH)

      DO l=1,lmo
        Do i=1, im
          Call calc_tracer_mass_flux() ! f from s,sy,mass,dm
        End Do                         ! temp. store dm/mass in fy
      end do

      CALL HALO_UPDATE(grid, fy, FROM=NORTH+SOUTH) ! still holding dm/m
      ! Limit fluxes to maintain positive mean values?
      If (qlimit) Then

        Call HALO_UPDATE(grid, f, FROM=NORTH+SOUTH)
        DO l=1,lmo
          Do i=1, im
            Call apply_limiter() ! adjusting f,sy
          End Do
        end do
        Call HALO_UPDATE(grid, f, FROM=NORTH+SOUTH)
      End If
c--------------------------------------------------------------------
         ! calculate tracer fluxes of slopes fx,fy,fz
c--------------------------------------------------------------------
      DO l=1,lmo
        Do i=1, im
          Call tracer_slopes()
        end do
      end do
c-------------------------------------------------------------------
c update tracer mass, moments of tracer mass, air mass distribution
c-------------------------------------------------------------------
      CALL HALO_UPDATE(grid, f,  FROM=SOUTH)
      CALL HALO_UPDATE(grid, dm, FROM=SOUTH)
      CALL HALO_UPDATE(grid, fx, FROM=SOUTH)
      CALL HALO_UPDATE(grid, fy, FROM=SOUTH)
      CALL HALO_UPDATE(grid, fz, FROM=SOUTH)

      DO l=1,lmo
        DO i = 1, im
          Call update_tracer_mass() ! s also: sx,sy,sz, mass
        end do
      enddo

      return

      Contains


      Integer Function NeighborByFlux(n, dm) 6,2
        Integer, Intent(In) :: n
        Real*8, Intent(In) :: dm

        Integer :: nn

        If (dm < 0) Then ! air mass flux is negative
          nn=n+1
        else ! air mass flux is positive
          nn=n
        endif
        NeighborByFlux = nn
      End Function NeighborByFlux


      Function FluxFraction(dm) Result(frac) 3,2
        Real*8, Intent(In) :: dm
        Real*8 :: frac

        If (dm < 0 ) Then
          frac = +1.
        Else ! Flux non negative
          frac = -1.
        End If
      End Function FluxFraction


      Function MassFraction(dm, mass) Result (fracm) 3
        Real*8, Intent(In) :: dm
        Real*8, Intent(In) :: mass
        Real*8 :: fracm

        If (mass > 0.0d0) Then
          fracm = dm / mass
        Else
          fracm = 0.d0
        End If
      End Function MassFraction


      Subroutine calc_tracer_mass_flux() 3,9

      Do n = J_0, J_1

        nn = NeighborByFlux(n, dm(i,n,l))
        fracm = MassFraction(dm(i,n,l), mass(i,nn,l))

        frac1 = fracm + FluxFraction(dm(i,n,l))

        f(i,n,l)=fracm*(s(i,nn,l)-frac1*sy(i,nn,l))
      ! temporary storage of fracm in fy, to be used below
        fy(i,n,l)=fracm
      !
      enddo
      End Subroutine calc_tracer_mass_flux


      Subroutine tracer_slopes() 2,2

      Do n = J_0, J_1

        nn = NeighborByFlux(n, dm(i,n,l))
      ! retrieving fracm, which was stored in fy
        fracm=fy(i,n,l)
      !
        fy(i,n,l)=
     &     dm(i,n,l)*(fracm*fracm*sy(i,nn,l)-3.*f(i,n,l))
      ! cross moments
        fx(i,n,l)=fracm*sx(i,nn,l)
        fz(i,n,l)=fracm*sz(i,nn,l)
      enddo
      End Subroutine tracer_slopes


      Subroutine apply_limiter 3,4
      REAL*8 :: an, anm1, fn, fnm1, sn, syn

c     If (HAVE_SOUTH_POLE) Then
c        n = J_0
c        an = fy(i,n,l) ! reading fracm which was stored in fx
c        anm1 = 0
c        fn = f(i,n,l)
c        fnm1 = 0
c        sn = s(i,n,l)
c        syn = sy(i,n,l)
c        call limitq_lin(anm1,an,fnm1,fn,sn,syn)
c        f(i,n,l) = fn
c        sy(i,n,l) = syn
c     End If

      DO n = J_0S, J_1S+1

         an = fy(i,n,l) ! reading fracm which was stored in fy
         anm1 = fy(i,n-1,l)
         fn = f(i,n,l)
         fnm1 = f(i,n-1,l)
         sn = s(i,n,l)
         syn = sy(i,n,l)
         call limitq_lin(anm1,an,fnm1,fn,sn,syn)
         f(i,n,l) = fn
         f(i,n-1,l) = fnm1
         sy(i,n,l) = syn

      enddo
      End Subroutine apply_limiter


      Subroutine update_tracer_mass() ! non-polar only 3
      REAL*8 :: sxy

      Do n=J_0S,J_1S

         if(focean(i,n).le.0. .or. l.gt.lmm(i,n)) cycle
         mnew=mass(i,n,l) + dm(i,n-1,l)-dm(i,n,l)

         s(i,n,l)=s(i,n,l) + (f(i,n-1,l)-f(i,n,l))

         sy(i,n,l)=( sy(i,n,l)*mass(i,n,l) + (fy(i,n-1,l)-fy(i,n,l))
     &              + 3.*( (dm(i,n-1,l)+dm(i,n,l))*s(i,n,l)
     &                     -mass(i,n,l)*(f(i,n-1,l)+f(i,n,l)) ) )/mnew
      ! cross moments
         sx(i,n,l) = sx(i,n,l) + (fx(i,n-1,l)-fx(i,n,l))
         sz(i,n,l) = sz(i,n,l) + (fz(i,n-1,l)-fz(i,n,l))
      !
         if (qlimit) then ! limit tracer gradients
           sxy = abs(sx(i,n,l)) + abs(sy(i,n,l))
           if ( sxy > s(i,n,l) ) then
             sx(i,n,l) = sx(i,n,l)*( s(i,n,l)/(sxy + tiny(sxy)) )
             sy(i,n,l) = sy(i,n,l)*( s(i,n,l)/(sxy + tiny(sxy)) )
           end if
           if ( abs(sz(i,n,l)) > s(i,n,l) )
     *       sz(i,n,l) = sign(s(i,n,l),sz(i,n,l)+0.d0)
         end if
c------------------------------------------------------------------
         mass(i,n,l) = mnew
         if(mass(i,n,l).le.0. .or. (qlimit.and.s(i,n,l).lt.0.)) then
            ierr=2
            err_loc=(/ i, n, l /)
            write(0,*) 'oadvty: mo or salt<0 at',err_loc,mnew,s(i,n,l)
            return
         endif
c-----------------------------------------------------------------

      enddo
      End Subroutine Update_Tracer_Mass

      end subroutine advec_lin_1D_custom


      subroutine limitq_lin(anm1,an,fnm1,fn,sn,sx) 2
!@sum  limitq adjusts moments to maintain non-neg. tracer means/fluxes
!@auth G. Russell, modified by Maxwell Kelley
        implicit none
        REAL*8 :: anm1,an,fnm1,fn,sn,sx
c local variables
        REAL*8 :: sl,sc,sr, frl,frl1, frr,frr1, gamma,g13ab,
     &       fr,fr1, fsign,su,sd
c****
c**** modify the tracer moments so that the tracer mass in each
c**** division is non-negative
c****
c**** no water leaving the box
        if(anm1.ge.0. .and. an.le.0.) return
c**** water is leaving through both the left and right edges
        if(anm1.lt.0. .and. an.gt.0.) then
           sl = -fnm1
           sr = +fn
c**** all divisions are non-negative
           if(sl.ge.0. .and. sr.ge.0.) return
c**** at least one division is negative
           frl = anm1
           frl1 = frl+1.
           frr = an
           frr1 = frr-1.
           if(sl.lt.0.) then           ! leftmost division
              sx = sn/frl1
              sr = frr*(sn-frr1*sx)
              sl = 0.
           else                        ! rightmost division
              sx = sn/frr1
              sl = -frl*(sn-frl1*sx)
              sr = 0.
           endif
           fnm1 = -sl
           fn   = +sr
        else
c**** water is leaving only through one edge
           if(an.gt.0.)  then ! right edge
              fr=an
              sd=fn
              fsign=-1.
           else                  ! left edge
              fr=anm1
              sd=-fnm1
              fsign=1.
           endif
           su = sn-sd
           if(sd.ge.0. .and. su.ge.0.) return
           fr1=fr+fsign
           if(sd.lt.0.)  then
c**** downstream division is negative
              sx = sn/fr1
              su = sn
           else
c**** upstream division is negative
              sx = sn/fr
              su = 0.
           endif
           sd = sn - su
           if(an.gt.0.) then
              fn=sd
           else
              fnm1=-sd
           endif
        endif
        return
      end subroutine limitq_lin


      SUBROUTINE OADVTZ (RM,RX,RY,RZ,MO,MW,DT,QLIMIT,OIJL) 2,10
C****
C**** OADVTZ advects tracers in the vertical direction using the
C**** linear upstream scheme.  If QLIMIT is true, the gradients are
C**** limited to prevent the mean tracer from becoming negative.
C****
C**** Input: DT (s) = time step
C****     MW (kg/s) = downward vertical mass flux
C****        QLIMIT = whether slope limitations should be used
C**** Input and Output: RM (kg) = tracer mass
C****             RX,RY,RZ (kg) = first moments of tracer mass
C****                    M (kg) = ocean mass
C****
      USE OCEAN, only : im,jm,lmo,lmm,focean
      use domain_decomp, only : grid, get

      IMPLICIT NONE
      REAL*8, INTENT(INOUT),
     *  DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO) ::
     *  RM,RX,RY,RZ, OIJL, MO
      REAL*8, INTENT(IN),
     *  DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO-1) :: MW
      LOGICAL*4, INTENT(IN) :: QLIMIT
      REAL*8, INTENT(IN) :: DT
      REAL*8, DIMENSION(0:LMO) :: CM,C,FM,FX,FY,FZ
      INTEGER I,J,L,LMIJ,ICKERR,IMIN,IMAX
      REAL*8 SBMN,SFMN,SFZN,RXY

      INTEGER :: J_0,J_1

      CALL GET(grid, J_STRT=J_0, J_STOP=J_1)

C****
C**** Loop over latitudes and longitudes
      ICKERR=0
!$OMP PARALLEL DO  PRIVATE(I,IMIN,IMAX,J,L,LMIJ, C,CM, FM,FX,FY,FZ,RXY)
!$OMP&             REDUCTION(+:ICKERR)
      DO J=J_0,J_1
        IMIN=1
        IMAX=IM
        IF (J == 1) IMIN=IM
        IF (J == JM) IMAX=1
        DO I=IMIN,IMAX
      CM(0) = 0.
       C(0) = 0.
      FM(0) = 0.
      FX(0) = 0.
      FY(0) = 0.
      FZ(0) = 0.
      LMIJ=LMM(I,J)
      IF(LMIJ.LE.1)  GO TO 330
      CM(LMIJ) = 0.
       C(LMIJ) = 0.
      FM(LMIJ) = 0.
      FX(LMIJ) = 0.
      FY(LMIJ) = 0.
      FZ(LMIJ) = 0.
C****
C**** Calculate FM (kg), FX (kg), FY (kg) and FZ (kg**2)
C****
      DO 120 L=1,LMIJ-1
      CM(L) = DT*MW(I,J,L)
      IF(CM(L).LT.0.)  GO TO 110
C**** Ocean mass flux is positive
      C(L)  = CM(L)/MO(I,J,L)
      IF(C(L).GT.1d0)  WRITE (6,*) 'C>1:',I,L,C(L),MO(I,J,L)
      FM(L) = C(L)*(RM(I,J,L)+(1d0-C(L))*RZ(I,J,L))
      FX(L) = C(L)*RX(I,J,L)
      FY(L) = C(L)*RY(I,J,L)
      FZ(L) = CM(L)*(C(L)*C(L)*RZ(I,J,L)-3d0*FM(L))
      GO TO 120
C**** Ocean mass flux is negative
  110 C(L)  = CM(L)/MO(I,J,L+1)
      IF(C(L).LT.-1d0)  WRITE (6,*) 'C<-1:',I,L,C(L),MO(I,J,L+1)
      FM(L) = C(L)*(RM(I,J,L+1)-(1d0+C(L))*RZ(I,J,L+1))
      FX(L) = C(L)*RX(I,J,L+1)
      FY(L) = C(L)*RY(I,J,L+1)
      FZ(L) = CM(L)*(C(L)*C(L)*RZ(I,J,L+1)-3d0*FM(L))
  120 CONTINUE
C****
C**** Modify the tracer moments so that the tracer mass in each
C**** division is non-negative
C****
      IF(.NOT.QLIMIT)  GO TO 300
      DO 290 L=1,LMIJ
      IF(C(L-1).GE.0.)  GO TO 240
C**** Water is leaving through the bottom edge: 2 or 3 divisions
      IF(FM(L-1).LE.0.)  GO TO 210
C**** Bottom most division is negative, RMB = -FM(L-1) < 0: Case 2 or 4
      RZ(I,J,L) = RM(I,J,L)/(1d0+C(L-1))
      FM(L-1) = 0.
      FZ(L-1) = CM(L-1)*C(L-1)*C(L-1)*RZ(I,J,L)
      IF(C(L).LE.0.)  GO TO 290
      FM(L) = C(L)*(RM(I,J,L)+(1d0-C(L))*RZ(I,J,L))
      FZ(L) = CM(L)*(C(L)*C(L)*RZ(I,J,L)-3d0*FM(L))
      GO TO 290
C**** Bottom most division is non-negative, RMB = -FM(L-1) > 0:
C**** Case 1, 3 or 5
  210 IF(C(L).LE.0.)  GO TO 230
C**** Water is leaving through the top edge: 3 divisions
      IF(FM(L).GE.0.)  GO TO 290
C**** Top most division is negative, RMT = FM(L) < 0: Case 3 or 5
      RZ(I,J,L) = -RM(I,J,L)/(1d0-C(L))
      FM(L) = 0.
      FZ(L) = CM(L)*C(L)*C(L)*RZ(I,J,L)
      FM(L-1) = C(L-1)*(RM(I,J,L)-(1d0+C(L-1))*RZ(I,J,L))
      FZ(L-1) = CM(L-1)*(C(L-1)*C(L-1)*RZ(I,J,L)-3d0*FM(L-1))
      GO TO 290
C**** No water is leaving through the top edge: 2 divisions
  230 IF(RM(I,J,L)+FM(L-1).GE.0.)  GO TO 290
C**** Top most division is negative, RMT = RM(I,J,L)+FM(L-1) < 0: Case 3
      RZ(I,J,L) = RM(I,J,L)/C(L-1)
      FM(L-1) = -RM(I,J,L)
      FZ(L-1) = CM(L-1)*(C(L-1)+3d0)*RM(I,J,L)
      GO TO 290
C**** No water is leaving through the bottom edge: 1 or 2 divisions
  240 IF(C(L).LE.0.)  GO TO 290
C**** Water is leaving through the top edge: 2 divisions
      IF(FM(L).GE.0.)  GO TO 250
C**** Top most division is negative, RMT = FM(L) < 0: Case 3
      RZ(I,J,L) = -RM(I,J,L)/(1d0-C(L))
      FM(L) = 0.
      FZ(L) = CM(L)*C(L)*C(L)*RZ(I,J,L)
      GO TO 290
C**** Top most division is non-negative, RMT = FM(L) > 0: Case 1 or 2
  250 IF(RM(I,J,L)-FM(L).GE.0.)  GO TO 290
C**** Bottom most division is negative, RMB = RM(I,J,L)-FM(L) < 0: Cas 2
      RZ(I,J,L) = RM(I,J,L)/C(L)
      FM(L) = RM(I,J,L)
      FZ(L) = CM(L)*(C(L)-3d0)*RM(I,J,L)
C****
  290 CONTINUE
C****
C**** Calculate new tracer mass and first moments of tracer mass
C****
  300 DO 310 L=1,LMIJ
      RM(I,J,L) = RM(I,J,L) + (FM(L-1)-FM(L))
      RX(I,J,L) = RX(I,J,L) + (FX(L-1)-FX(L))
      RY(I,J,L) = RY(I,J,L) + (FY(L-1)-FY(L))
      RZ(I,J,L) = (RZ(I,J,L)*MO(I,J,L) + (FZ(L-1)-FZ(L))
     *  + 3d0*((CM(L-1)+CM(L))*RM(I,J,L)-MO(I,J,L)*(FM(L-1)+FM(L))))
     *  / (MO(I,J,L)+CM(L-1)-CM(L))
C****
      if ( QLIMIT ) then ! limit tracer gradients
        RXY = abs(RX(I,J,L)) + abs(RY(I,J,L))
        if ( RXY > RM(I,J,L) ) then
          RX(I,J,L) = RX(I,J,L)*( RM(I,J,L)/(RXY + tiny(RXY)) )
          RY(I,J,L) = RY(I,J,L)*( RM(I,J,L)/(RXY + tiny(RXY)) )
        end if
        if ( abs(RZ(I,J,L)) > RM(I,J,L) )
     *       RZ(I,J,L) = sign(RM(I,J,L), RZ(I,J,L)+0d0)
      end if
C****
      MO(I,J,L) = MO(I,J,L) +  CM(L-1)-CM(L)
         IF(MO(I,J,L).LE.0.)              ICKERR=ICKERR+1
         IF(QLIMIT.AND.RM(I,J,L).LT.0.)   ICKERR=ICKERR+1
  310 CONTINUE
         DO 320 L=1,LMIJ-1
  320    OIJL(I,J,L) = OIJL(I,J,L) + FM(L)
  330 CONTINUE
      END DO
      END DO
!$OMP END PARALLEL DO

C**** IF NO ERROR HAS OCCURRED - RETURN, ELSE STOP
      IF(ICKERR == 0)  RETURN
      DO J=J_0,J_1
        IMIN=1
        IMAX=IM
        IF (J == 1) IMIN=IM
        IF (J == JM) IMAX=1
        DO I=IMIN,IMAX
        LMIJ=LMM(I,J)
        DO L=1,LMIJ
          IF(FOCEAN(I,J).gt.0 .and. MO(I,J,L).LE.0.)  GO TO 800
          IF(QLIMIT .AND. RM(I,J,L).LT.0.) GO TO 810
        END DO
      END DO
      END DO
      WRITE(6,*) 'ERROR CHECK INCONSISTENCY: OADVTZ ',ICKERR
      call stop_model("OAVDTZ",255)

  800 WRITE (6,*) 'MO<0 in OADVTZ:',I,J,L,MO(I,J,L)
  810 WRITE (6,*) 'RM in OADVTZ:',I,J,L,RM(I,J,L)
c      WRITE (6,*) 'C=',(L,C(L),L=0,LMIJ)
      call stop_model("OADVTZ",255)
      END SUBROUTINE OADVTZ


      Subroutine OBDRAG 2,12
!@sum  OBDRAG exerts a drag on the Ocean Model's bottom layer
!@auth Gary Russell
!@ver  1.0
      Use OCEAN, Only: im,jm,lmo,IVNP,J1O, mo,uo,vo, lmu,lmv, dts,
     *                 COSI=>COSIC,SINI=>SINIC
      use domain_decomp, only : grid, get, halo_update, north, south

      Implicit None
      REAL*8, PARAMETER :: BDRAGX=1d0, SDRAGX=1d-1
      REAL*8,DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO,LMO):: UT,VT
      Integer*4 I,J,L,Ip1,Im1
      REAL*8 WSQ

      INTEGER :: J_0, J_0S,J_1S  ; logical :: have_north_pole

      CALL GET(grid, J_STRT=J_0, J_STRT_SKP=J_0S, J_STOP_SKP=J_1S,
     *               have_north_pole=have_north_pole)
C****
C**** UO = UO*(1-x/y)  is approximated by  UO*y/(y+x)  for stability
C****
      call halo_update (grid, mo, FROM=NORTH)
      call halo_update (grid, uo, FROM=NORTH)
      call halo_update (grid, vo, FROM=SOUTH)
C**** Save UO,VO into UT,VT which will be unchanged
!$OMP ParallelDo   Private (L)
      DO 10 L=1,LMO
        UT(:,:,L) = UO(:,:,L)
 10     VT(:,:,L) = VO(:,:,L)
!$OMP EndParallelDo
C****
C**** Reduce West-East ocean current
C****
C**** Bottom drag in the interior
!$OMP ParallelDo   Private (I,J,L,Ip1, WSQ)
      DO 120 J=max(J1O,J_0),J_1S
      I=IM
      DO 110 IP1=1,IM
      IF(LMU(I,J) <= 0)  GO TO 110
      L=LMU(I,J)
      WSQ = UT(I,J,L)*UT(I,J,L) + 1d-20 +
     *  .25*(VT(I,J  ,L)*VT(I,J  ,L) + VT(IP1,J  ,L)*VT(IP1,J  ,L)
     *     + VT(I,J-1,L)*VT(I,J-1,L) + VT(IP1,J-1,L)*VT(IP1,J-1,L))
      UO(I,J,L) = UO(I,J,L) * (MO(I,J,L)+MO(IP1,J,L)) /
     *           (MO(I,J,L)+MO(IP1,J,L) + DTS*BDRAGX*SQRT(WSQ)*2d0)
  110 I=IP1
  120 CONTINUE
C**** Bottom drag at the poles
C     IF(LMU(1,1 or JM) <= 0)  GO TO
      if (have_north_pole) then
        L=LMU(1,JM)
        WSQ = UT(IM,JM,L)**2 + UT(IVNP,JM,L)**2 + 1d-20
        UO(IM,JM,L) = UO(IM,JM,L) * MO(1,JM,L) /
     /                           (MO(1,JM,L) + DTS*BDRAGX*Sqrt(WSQ))
        UO(IVNP,JM,L) = UO(IVNP,JM,L) * MO(1,JM,L) /
     /                           (MO(1,JM,L) + DTS*BDRAGX*Sqrt(WSQ))
      end if
C****
C**** Reduce South-North ocean current
C****
!$OMP ParallelDo   Private (I,J,L,Im1, WSQ)
      Do 240 J=max(J1O,J_0),J_1S
      If (J==JM-1)  GoTo 220
C**** Bottom drag away from north pole
      IM1=IM
      DO 210 I=1,IM
      IF(LMV(I,J) <= 0)  GO TO 210
      L=LMV(I,J)
      WSQ = VT(I,J,L)*VT(I,J,L) + 1d-20 +
     *  .25*(UT(IM1,J+1,L)*UT(IM1,J+1,L) + UT(I,J+1,L)*UT(I,J+1,L)
     *     + UT(IM1,J  ,L)*UT(IM1,J  ,L) + UT(I,J  ,L)*UT(I,J  ,L))
      VO(I,J,L) = VO(I,J,L) * (MO(I,J,L)+MO(I,J+1,L)) /
     *           (MO(I,J,L)+MO(I,J+1,L) + DTS*BDRAGX*SQRT(WSQ)*2d0)
  210 IM1=I
      GoTo 240
C**** Bottom drag near north pole
  220 Im1=IM
      Do 230 I=1,IM
      If (LMV(I,JM-1) <= 0)  GoTo 230
      L = LMV(I,JM-1)
      WSQ = VT(I,JM-1,L)*VT(I,JM-1,L) + 1d-20 +
     +   .5*(UT(IM,JM,L)*COSI(I) + UT(IVNP,JM,L)*SINI(I))**2 +
     +  .25*(UT(Im1,JM-1,L)*UT(Im1,JM-1,L) + UT(I,JM-1,L)*UT(I,JM-1,L))
      VO(I,JM-1,L) = VO(I,JM-1,L) * (MO(I,JM-1,L)+MO(1,JM,L)) /
     *              (MO(I,JM-1,L)+MO(1,JM,L) + DTS*BDRAGX*SQRT(WSQ)*2)
  230 Im1=I
  240 Continue
      RETURN
C****
      END Subroutine OBDRAG


      SUBROUTINE OCOAST 2,8
!@sum OCOAST reduces the horizontal perpendicular gradients of tracers
!@sum in coastline ocean grid boxes
!@auth Gary Russell
!@ver  1.0
      USE CONSTANT, only : sday
      USE OCEAN, only : im,jm,dts,lmm,gxmo,gymo,sxmo,symo
#ifdef TRACERS_OCEAN
     *     ,ntm,txmo,tymo
#endif
      use domain_decomp, only : grid, get

      IMPLICIT NONE
      INTEGER I,IM1,IP1,J,LMIN,L,N
      REAL*8 REDUCE

      integer :: J_0S, J_1S

      call get (grid, J_STRT_SKP=J_0S, J_STOP_SKP=J_1S)


      REDUCE = 1d0 - DTS/(SDAY*2d1)
C**** Reduce West-East gradient of tracers
      IM1=IM-1
      I=IM
      DO 120 J=J_0S,J_1S
      DO 120 IP1=1,IM
      LMIN = MIN(LMM(IM1,J),LMM(IP1,J)) + 1
      DO 110 L=LMIN,LMM(I,J)
        GXMO(I,J,L) = GXMO(I,J,L)*REDUCE
        SXMO(I,J,L) = SXMO(I,J,L)*REDUCE
#ifdef TRACERS_OCEAN
        DO N = 1,NTM
          TXMO(I,J,L,N) = TXMO(I,J,L,N) *REDUCE
        END DO
#endif
 110  CONTINUE
      IM1=I
  120 I=IP1
C**** Reduce South-North gradient of tracers
      DO 220 J=J_0S,J_1S
      DO 220 I=1,IM
      LMIN = MIN(LMM(I,J-1),LMM(I,J+1)) + 1
      DO 210 L=LMIN,LMM(I,J)
        GYMO(I,J,L) = GYMO(I,J,L)*REDUCE
        SYMO(I,J,L) = SYMO(I,J,L)*REDUCE
#ifdef TRACERS_OCEAN
        DO N = 1,NTM
          TYMO(I,J,L,N) = TYMO(I,J,L,N) *REDUCE
        END DO
#endif
 210  CONTINUE
  220 CONTINUE
      RETURN
      END SUBROUTINE OCOAST


      SUBROUTINE OSTRES 2,12
!@sum OSTRES applies the atmospheric surface stress over open ocean
!@sum and the sea ice stress to the layer 1 ocean velocities
!@auth Gary Russell
!@ver  1.0
      USE OCEAN, only : im,jm,ivnp,uo,vo,mo,dxyso,dxyno,dxyvo,
     *                  lmu,lmv,cosic,sinic,ratoc
      USE FLUXES, only : dmua,dmva,dmui,dmvi
      use domain_decomp, only : grid, get, halo_update, north

      IMPLICIT NONE
      INTEGER I,J,IP1
C****
C**** All stress now defined for whole box, not just ocn or ice fraction
C**** FLUXCB  DMUA(1)  U momentum downward into open ocean (kg/m*s)
C****         DMVA(1)  V momentum downward into open ocean (kg/m*s)
C****         DMUA(2,JM,1)  polar atmo. mass slowed to zero (kg/m**2)
C****         DMUI     U momentum downward from sea ice (kg/m*s)
C****         DMVI     V momentum downward from sea ice (kg/m*s)

      integer :: J_0, J_1, J_0S, J_1S  ; logical :: have_north_pole

      call get (grid, J_STRT=J_0, J_STOP=J_1,
     *                J_STRT_SKP=J_0S, J_STOP_SKP=J_1S,
     *                have_north_pole=have_north_pole)

C**** Scale stresses for ocean area
      DO J=J_0,J_1
        DO I=1,IM
          DMUA(I,J,1)=RATOC(J)*DMUA(I,J,1)
          DMVA(I,J,1)=RATOC(J)*DMVA(I,J,1)
          DMUI(I,J)=RATOC(J)*DMUI(I,J)
          DMVI(I,J)=RATOC(J)*DMVI(I,J)
        END DO
      END DO
C****
C**** Surface stress is applied to U component
C****
      I=IM
      DO J=J_0S,J_1S
      DO IP1=1,IM
        IF(LMU(I,J).gt.0.)  UO(I,J,1) = UO(I,J,1) +
     *       (DMUA(I,J,1) + DMUA(IP1,J,1) + 2d0*DMUI(I,J)) /
     *       (  MO(I,J,1) +   MO(IP1,J,1))
        I=IP1
      END DO
      END DO
      if (have_north_pole) then
        UO(IM  ,JM,1) = UO(IM  ,JM,1) + DMUA(1,JM,1)/MO(1,JM,1)
        UO(IVNP,JM,1) = UO(IVNP,JM,1) + DMVA(1,JM,1)/MO(1,JM,1)
      end if
C****
C**** Surface stress is applied to V component
C****
      call halo_update(grid, dmva, from=north)
      call halo_update(grid,   mo, from=north)
      DO J=J_0S,min(J_1S,JM-2)
      DO I=1,IM
        IF(LMV(I,J).GT.0.)  VO(I,J,1) = VO(I,J,1) +
     *       (DMVA(I,J  ,1)*DXYNO(J) + DMVA(I,J+1,1)*DXYSO(J+1)
     *       +2d0*DMVI(I,J)*DXYVO(J))
     * / (MO(I,J,1)*DXYNO(J) + MO(I,J+1,1)*DXYSO(J+1))
      END DO
      END DO
C**** Surface stress is applied to V component at the North Pole
      if (have_north_pole) then
      DO I=1,IM
        VO(I,JM-1,1) = VO(I,JM-1,1) +
     *       (DMVA(I,JM-1,1)*DXYNO(JM-1)+
     *       (DMVA(1,JM,1)*COSIC(I) - DMUA(1,JM,1)*SINIC(I))*DXYSO(JM)
     *       + 2d0*DMVI(I,JM-1)*DXYVO(JM-1)) /
     *  (MO(I,JM-1,1)*DXYNO(JM-1) + MO(I,JM,1)*DXYSO(JM))
      END DO
      end if
      RETURN
      END SUBROUTINE OSTRES


      SUBROUTINE GROUND_OC 2,18
!@sum  GROUND_OC adds vertical fluxes into the ocean
!@auth Gary Russell/Gavin Schmidt
!@ver  1.0
      USE CONSTANT, only : grav
      USE GEOM, only : dxyp,bydxyp
      USE OCEAN, only : im,jm,mo,g0m,s0m,focean,gzmo,imaxj,dxypo,bydxypo
     *     ,lmo,lmm,ratoc,rocat,opress
#ifdef TRACERS_OCEAN
     *     ,trmo,ntm
#endif
      USE FLUXES, only : solar,e0,evapor,dmsi,dhsi,dssi,runosi,erunosi
     *     ,flowo,eflowo,srunosi,apress,melti,emelti,smelti
#ifdef TRACERS_OCEAN
#ifdef TRACERS_WATER
     *     ,trflowo,trevapor,trunosi,trmelti
#ifdef TRACERS_DRYDEP
     *     ,trdrydep
#endif
#endif
     *     ,dtrsi
#endif
#ifdef TRACERS_GASEXCH_Natassa
      USE FLUXES, only : TRGASEX
#endif
      USE SEAICE_COM, only : rsi
      use domain_decomp, only : grid, get

      IMPLICIT NONE
      INTEGER I,J
      REAL*8 DXYPJ,BYDXYPJ,RUNO,RUNI,ERUNO,ERUNI,SROX(2),G0ML(LMO)
     *     ,MO1,SO1,ROICE,DMOO,DMOI,DEOO,DEOI,GZML(LMO),SRUNO,SRUNI,DSOO
     *     ,DSOI,POCEAN,POICE
#ifdef TRACERS_OCEAN
      REAL*8, DIMENSION(NTM) :: TRUNO,TRUNI,DTROO,DTROI,TRO1
#endif

      integer ::  J_1, J_0S
      logical :: have_south_pole, have_north_pole

      call get (grid, J_STOP=J_1, J_STRT_SKP=J_0S,
     *                have_north_pole=have_north_pole,
     *                have_south_pole=have_south_pole)

C****
C**** Add surface source of fresh water and heat
C****
      DO J=J_0S,J_1
        DXYPJ=DXYPO(J)
        BYDXYPJ=BYDXYPO(J)
      DO I=1,IMAXJ(J)
      IF(FOCEAN(I,J).gt.0.) THEN
        ROICE = RSI(I,J)
        POCEAN=FOCEAN(I,J)*(1.-ROICE)
        POICE =FOCEAN(I,J)*ROICE
        DXYPJ=DXYPJ*FOCEAN(I,J)     ! adjust areas for completeness
        BYDXYPJ=BYDXYPJ/FOCEAN(I,J) ! no change to results
C**** set mass & energy fluxes (incl. river/sea ice runoff + basal flux)
        RUNO = (FLOWO(I,J)+ MELTI(I,J))/(DXYPJ)-
     *                                   RATOC(J)*EVAPOR(I,J,1)
        RUNI = (FLOWO(I,J)+ MELTI(I,J))/(DXYPJ)+
     *                                   RATOC(J)*RUNOSI(I,J)
        ERUNO=(EFLOWO(I,J)+EMELTI(I,J))/(DXYPJ)+
     *                                   RATOC(J)*E0(I,J,1)
        ERUNI=(EFLOWO(I,J)+EMELTI(I,J))/(DXYPJ)+
     *                                   RATOC(J)*ERUNOSI(I,J)
        SRUNO=SMELTI(I,J)/(DXYPJ)
        SRUNI=SMELTI(I,J)/(DXYPJ)+RATOC(J)*SRUNOSI(I,J)
        G0ML(:) =  G0M(I,J,:)
        GZML(:) = GZMO(I,J,:)
        SROX(1)=SOLAR(1,I,J)*RATOC(J) ! open water
        SROX(2)=SOLAR(3,I,J)*RATOC(J) ! through ice
        MO1 = MO(I,J,1)
        SO1 = S0M(I,J,1)
#ifdef TRACERS_OCEAN
        TRO1(:) = TRMO(I,J,1,:)
#ifdef TRACERS_WATER
        TRUNO(:)=(TRFLOWO(:,I,J)+TRMELTI(:,I,J))/(DXYPJ)-
     *       RATOC(J)*TREVAPOR(:,1,I,J)
#ifdef TRACERS_DRYDEP
     *       + RATOC(J)*trdrydep(:,1,i,j)
#endif
        TRUNI(:)=(TRFLOWO(:,I,J)+TRMELTI(:,I,J))/(DXYPJ)+
     *       RATOC(J)*TRUNOSI(:,I,J)
#else
        TRUNO(:)=0. ; TRUNI(:)=0.
#endif

#ifdef TRACERS_GASEXCH_Natassa
        !note that TRGASEX is positive down i.e. same sign as
        !TRUNO, while TREVAPOR is positive up.
        TRUNO(:)=RATOC(J)*TRGASEX(:,1,I,J)
#endif

#endif

        CALL OSOURC(ROICE,MO1,G0ML,GZML,SO1,DXYPJ,BYDXYPJ,LMM(I,J),RUNO
     *         ,RUNI,ERUNO,ERUNI,SRUNO,SRUNI,SROX,
#ifdef TRACERS_OCEAN
     *         TRO1,TRUNO,TRUNI,DTROO,DTROI,
#endif
     *         DMOO,DEOO,DMOI,DEOI,DSOO,DSOI)

C**** update ocean variables
          MO(I,J,1) = MO1
         S0M(I,J,1) = SO1
         G0M(I,J,:) = G0ML(:)
        GZMO(I,J,:) = GZML(:)

C**** Store mass and energy fluxes for formation of sea ice
        DMSI(1,I,J)=DMOO*ROCAT(J)
        DMSI(2,I,J)=DMOI*ROCAT(J)
        DHSI(1,I,J)=DEOO*ROCAT(J)
        DHSI(2,I,J)=DEOI*ROCAT(J)
        DSSI(1,I,J)=DSOO*ROCAT(J)
        DSSI(2,I,J)=DSOI*ROCAT(J)
#ifdef TRACERS_OCEAN
        TRMO(I,J,1,:) = TRO1(:)
        DTRSI(:,1,I,J)=DTROO(:)*ROCAT(J)
        DTRSI(:,2,I,J)=DTROI(:)*ROCAT(J)
#endif

C**** Calculate pressure anomaly at ocean surface (and scale for areas)
C**** Updated using latest sea ice (this ensures that total column mass
C**** is consistent for OGEOZ calculation).
        OPRESS(I,J) = RATOC(J)*(APRESS(I,J))+GRAV*(
     *       (1.-RSI(I,J))*DMSI(1,I,J) + RSI(I,J)*DMSI(2,I,J))

        END IF
      END DO
      END DO

      if(have_south_pole) OPRESS(2:IM,1)  = OPRESS(1,1)
      if(have_north_pole) OPRESS(2:IM,JM) = OPRESS(1,JM)
C****
      RETURN
      END SUBROUTINE GROUND_OC


      SUBROUTINE OSOURC (ROICE,MO,G0ML,GZML,S0M,DXYPJ,BYDXYPJ,LMIJ,RUNO 3,18
     *     ,RUNI,ERUNO,ERUNI,SRUNO,SRUNI,SROX,
#ifdef TRACERS_OCEAN
     *     TROM,TRUNO,TRUNI,DTROO,DTROI,
#endif
     *     DMOO,DEOO,DMOI,DEOI,DSOO,DSOI)
!@sum  OSOURC applies fluxes to ocean in ice-covered and ice-free areas
!@auth Gary Russell/Gavin Schmidt
!@ver  1.0
      USE CONSTANT, only : shi,lhm
#ifdef TRACERS_OCEAN
      USE TRACER_COM, only : ntm,trname
#endif
      USE SW2OCEAN, only : lsrpd,fsr,fsrz
      USE SEAICE, only : fsss
      IMPLICIT NONE
      REAL*8, INTENT(IN) :: ROICE,DXYPJ,BYDXYPJ,RUNO,RUNI,ERUNO,ERUNI
     *     ,SROX(2),SRUNO,SRUNI
      INTEGER, INTENT(IN) :: LMIJ
      REAL*8, INTENT(INOUT) :: MO,G0ML(LSRPD),GZML(LSRPD),S0M
      REAL*8, INTENT(OUT) :: DMOO,DMOI,DEOO,DEOI,DSOO,DSOI
#ifdef TRACERS_OCEAN
      REAL*8, DIMENSION(NTM), INTENT(INOUT) :: TROM
      REAL*8, DIMENSION(NTM), INTENT(IN) :: TRUNO,TRUNI
      REAL*8, DIMENSION(NTM), INTENT(OUT) :: DTROO,DTROI
      REAL*8, DIMENSION(NTM) :: TMOO,TMOI,FRAC
#ifdef TRACERS_SPECIAL_O18
      REAL*8 fracls
#endif
#endif
      REAL*8 MOO,GOO,GMOO,GMOI,MOI,GOI,SMOO,SMOI,SOO,SOI,GFOO,GFOI,TFOO
     *     ,TFOI,SIOO,SIOI
      REAL*8 GFREZS,TFREZS,TSOL
      INTEGER L,LSR,N

      DMOO=0. ; DEOO=0. ; DMOI=0. ; DEOI=0. ; DSOO=0. ; DSOI=0.
#ifdef TRACERS_OCEAN
      DTROI(:) = 0. ; DTROO(:) = 0.
      do n=1,ntm
#ifdef TRACERS_SPECIAL_O18
        FRAC(n)=fracls(n)
#else
        FRAC(n)=1.
#endif
      end do
#endif

      LSR = MIN(LSRPD,LMIJ)
C****
C**** Open Ocean
C****
      MOO  = MO + RUNO
      GMOO = G0ML(1)*BYDXYPJ + ERUNO
      SMOO = S0M*BYDXYPJ + SRUNO
#ifdef TRACERS_OCEAN
      TMOO(:) = TROM(:)*BYDXYPJ+TRUNO(:)
#endif
      IF (ROICE.lt.1d0) THEN
C**** Remove insolation from layer 1 that goes to lower layers
      IF (LSR.gt.1) GMOO = GMOO - SROX(1)*FSR(2)

      GOO  = GMOO/MOO
      SOO  = SMOO/MOO
      GFOO = GFREZS(SOO)
      IF(GOO.lt.GFOO) THEN
C**** Open ocean is below freezing, calculate
C**** DMOO = mass of ocean that freezes over open fraction from
C**** GOO*MOO = GFOO*(MOO-DMOO) + (TFOO*SHI-LHM*(1-SIOO))*DMOO
        TFOO = TFREZS(SOO)
        SIOO = FSSS*SOO
        DMOO = MOO*(GOO-GFOO)/(TFOO*SHI-LHM*(1.-SIOO)-GFOO)
        DEOO = (TFOO*SHI-LHM*(1.-SIOO))*DMOO
        DSOO = SIOO*DMOO
#ifdef TRACERS_OCEAN
        DTROO(:) = TMOO(:)*FRAC(:)*(DMOO-DSOO)/(MOO-SMOO)
#endif
      END IF
      END IF
C****
C**** Ocean underneath the ice
C****
      MOI  = MO + RUNI
      GMOI = G0ML(1)*BYDXYPJ + ERUNI
      SMOI = S0M*BYDXYPJ + SRUNI
#ifdef TRACERS_OCEAN
      TMOI(:) = TROM(:)*BYDXYPJ+TRUNI(:)
#endif
      IF(ROICE.gt.0.) THEN
C**** Remove insolation from layer 1 that goes to lower layers
        IF (LSR.gt.1) GMOI = GMOI - SROX(2)*FSR(2)

        GOI  = GMOI/MOI
        SOI  = SMOI/MOI
        GFOI = GFREZS(SOI)
        IF(GOI.LT.GFOI) THEN
C**** Ocean underneath the ice is below freezing, calculate
C**** DMOI = mass of ocean that freezes under sea ice fraction from
C**** GOI*MOI = GFOI*(MOI-DMOI) + (TFOI*SHI-LHM*(1-SIOI))*DMOI
          TFOI = TFREZS(SOI)
          SIOI = FSSS*SOI
          DMOI = MOI*(GOI-GFOI)/(TFOI*SHI-LHM*(1.-SIOI)-GFOI)
          DEOI = (TFOI*SHI-LHM*(1.-SIOI))*DMOI
          DSOI = SIOI*DMOI
#ifdef TRACERS_OCEAN
          DTROI(:) = TMOI(:)*FRAC(:)*(DMOI-DSOI)/(MOI-SMOI)
#endif
        END IF
      END IF
C**** Update first layer variables
      MO     =  (MOI-DMOI)*ROICE + (1.-ROICE)*( MOO-DMOO)
      G0ML(1)=((GMOI-DEOI)*ROICE + (1.-ROICE)*(GMOO-DEOO))*DXYPJ
      S0M    =((SMOI-DSOI)*ROICE + (1.-ROICE)*(SMOO-DSOO))*DXYPJ
#ifdef TRACERS_OCEAN
      TROM(:)=((TMOI(:)-DTROI(:))*ROICE + (1.-ROICE)*(TMOO(:)-DTROO(:)))
     *     *DXYPJ
#endif
C**** add insolation to lower layers
      TSOL=(SROX(1)*(1.-ROICE)+SROX(2)*ROICE)*DXYPJ
      DO L=2,LSR-1
        G0ML(L)=G0ML(L)+TSOL*(FSR(L)-FSR(L+1))
        GZML(L)=GZML(L)+TSOL*FSRZ(L)
      END DO
      G0ML(LSR) = G0ML(LSR) + TSOL*FSR (LSR)
      GZML(LSR) = GZML(LSR) + TSOL*FSRZ(LSR)
C****
      RETURN
      END SUBROUTINE OSOURC


      SUBROUTINE PRECIP_OC 1,31
!@sum  PRECIP_OC driver for applying precipitation to ocean fraction
!@auth Gary Russell/Gavin Schmidt
!@ver  1.0
      USE GEOM, only : dxyp
#ifdef TRACERS_OCEAN
      USE TRACER_COM, only : ntm
#ifdef TRACERS_WATER
      USE FLUXES, only : trpreca=>trprec,trunpsia=>trunpsi
#endif
#endif
      USE FLUXES, only : runpsia=>runpsi,srunpsia=>srunpsi,preca=>prec
     *     ,epreca=>eprec
      USE OCEAN, only : im,jm,mo,g0m,s0m,bydxypo,focean,imaxj
#ifdef TRACERS_OCEAN
     *     ,trmo,dxypo
#endif
      USE SEAICE_COM, only : rsia=>rsi
      USE DOMAIN_DECOMP, only : grid,get

      IMPLICIT NONE
      REAL*8, DIMENSION(IM,grid%J_STRT_HALO:grid%J_STOP_HALO) ::
     *   PREC,EPREC,RUNPSI,RSI,SRUNPSI
#ifdef TRACERS_OCEAN
      REAL*8, DIMENSION(NTM,IM,grid%J_STRT_HALO:grid%J_STOP_HALO) ::
     *   trprec,trunpsi
#endif
      INTEGER I,J

      integer :: J_0, J_1

      call get (grid, J_STRT=J_0, J_STOP=J_1)

C**** save surface variables before any fluxes are added
      CALL KVINIT

C**** Convert fluxes on atmospheric grid to oceanic grid
C**** build in enough code to allow a different ocean grid.
C**** Since the geometry differs on B and C grids, some processing
C**** of fluxes is necessary anyway
      DO J=J_0,J_1
        DO I=1,IMAXJ(J)
          PREC   (I,J)=PRECA   (I,J)*DXYP(J)*BYDXYPO(J)  ! kg/m^2
          EPREC  (I,J)=EPRECA  (I,J)*DXYP(J)             ! J
          RUNPSI (I,J)=RUNPSIA (I,J)*DXYP(J)*BYDXYPO(J)  ! kg/m^2
          SRUNPSI(I,J)=SRUNPSIA(I,J)*DXYP(J)             ! kg
          RSI    (I,J)=RSIA    (I,J)
#ifdef TRACERS_OCEAN
#ifdef TRACERS_WATER
          TRPREC(:,I,J)=TRPRECA(:,I,J)                   ! kg
          TRUNPSI(:,I,J)=TRUNPSIA(:,I,J)*DXYP(J)         ! kg
#else
          TRPREC(:,I,J)=0.  ; TRUNPSI(:,I,J)=0.
#endif
#endif
        END DO
      END DO
C****
      DO J=J_0,J_1
        DO I=1,IMAXJ(J)
          IF(FOCEAN(I,J).gt.0. .and. PREC(I,J).gt.0.)  THEN
            MO (I,J,1)= MO(I,J,1) + ((1d0-RSI(I,J))*PREC(I,J) +
     *           RSI(I,J)*RUNPSI(I,J))*FOCEAN(I,J)
            G0M(I,J,1)=G0M(I,J,1)+(1d0-RSI(I,J))*EPREC(I,J)*FOCEAN(I,J)
            S0M(I,J,1)=S0M(I,J,1) + RSI(I,J)*SRUNPSI(I,J)*FOCEAN(I,J)
#ifdef TRACERS_OCEAN
            TRMO(I,J,1,:)=TRMO(I,J,1,:)+((1d0-RSI(I,J))*TRPREC(:,I,J)
     *             +RSI(I,J)*TRUNPSI(:,I,J))*FOCEAN(I,J)
#endif
          END IF
        END DO
      END DO

C**** Convert ocean surface temp to atmospheric SST array
      CALL TOC2SST

      RETURN
      END SUBROUTINE PRECIP_OC


      SUBROUTINE ODIFF (DTDIFF) 2,66
C???? ESMF-exception - ODIFF currently works with global arrays
!@sum  ODIFF applies Wasjowicz horizontal viscosity to velocities
!@auth Gavin Schmidt
!@ver  1.0
C****
C**** ODIFF calculates horizontal Wasjowicz viscosity terms in momentum
C**** equations implicitly using ADI method and assumes no slip/free
C**** slip conditions at the side. K_h (m^2/s) may vary spatially
C**** based on Munk length though must remain isotropic.
C**** (If longitudinal variation is wanted just make K arrays K(I,J))
C**** FSLIP = 0 implies no slip conditions, = 1 implies free slip
C**** Mass variation is included
C****
      USE CONSTANT, only :twopi,rhows,omega,radius
      USE OCEAN, only : im,jm,lmo,mo,uo,vo,
     *  IVNP,UONP,VONP, COSU,SINU, COSI=>COSIC,SINI=>SINIC,
     *  cospo,cosvo,rlat,lmu,lmv,dxpo,dypo,dxvo,dyvo,dxyvo,dxypo,bydxypo
      USE OCEAN_DYN, only : dh
      USE TRIDIAG_MOD, only : tridiag, tridiag_new

      USE DOMAIN_DECOMP, ONLY : grid, GET, AM_I_ROOT
      USE DOMAIN_DECOMP, ONLY : HALO_UPDATE, NORTH, SOUTH, ESMF_BCAST
      USE DOMAIN_DECOMP, ONLY : haveLatitude

      IMPLICIT NONE
      REAL*8, PARAMETER :: AKHMIN=1.5d8, FSLIP=0.

      REAL*8, DIMENSION(grid%j_strt_halo:grid%j_stop_halo) ::
     *     KYPXP,KXPYV,KYVXV,KXVYP
      REAL*8, SAVE, ALLOCATABLE, DIMENSION(:) ::
     *     BYDXYV, KHP,KHV,TANP,TANV,BYDXV,BYDXP,BYDYV,BYDYP
      REAL*8, DIMENSION(IM,grid%j_strt_halo:grid%j_stop_halo,2) ::
     *      DUDX,DUDY,DVDX,DVDY
      REAL*8, DIMENSION(IM,grid%j_strt_halo:grid%j_stop_halo) ::
     *      FUX,FUY,FVX,FVY,BYMU,BYMV

      INTEGER, SAVE :: IFIRST = 1
C**** Local variables

      REAL*8, ALLOCATABLE, DIMENSION(:,:) ::
     *     AU, BU, CU, RU, UU
      REAL*8, ALLOCATABLE, DIMENSION(:,:) ::
     *     AV, BV, CV, RV, UV
      REAL*8, SAVE, ALLOCATABLE, DIMENSION(:,:,:) ::
     *      UXA,UXB,UXC,UYA,UYB,UYC,VXA,VXB,VXC,VYA,VYB,VYC
      REAL*8, SAVE, DIMENSION(LMO) :: UYPB
      REAL*8, SAVE, DIMENSION(IM,LMO) :: UYPA

      REAL*8, INTENT(IN) :: DTDIFF
      REAL*8, SAVE :: BYDXYPJM
      REAL*8 DSV,DSP,VLAT,DLAT,DT2,DTU,DTV,VX,VY,VT,UT,UX,UY
      INTEGER I,J,L,IP1,IM1,II

!     domain decomposition
      INTEGER :: J_0, J_1, J_0S, J_1S, J_0H, J_1H
      LOGICAL :: HAVE_SOUTH_POLE, HAVE_NORTH_POLE

c**** Extract domain decomposition info
      CALL GET(grid, J_STRT = J_0, J_STOP = J_1,
     &               J_STRT_SKP  = J_0S,   J_STOP_SKP  = J_1S,
     &               J_STRT_HALO = J_0H,   J_STOP_HALO = J_1H,
     &               HAVE_SOUTH_POLE = HAVE_SOUTH_POLE,
     &               HAVE_NORTH_POLE = HAVE_NORTH_POLE)

C****

      IF(IFIRST.ne.0)  THEN
      IFIRST = 0

        allocate( BYDXYV(grid%j_strt_halo:grid%j_stop_halo) )
        allocate( KHP   (grid%j_strt_halo:grid%j_stop_halo) )
        allocate( KHV   (grid%j_strt_halo:grid%j_stop_halo) )
        allocate( TANP  (grid%j_strt_halo:grid%j_stop_halo) )
        allocate( TANV  (grid%j_strt_halo:grid%j_stop_halo) )
        allocate( BYDXV (grid%j_strt_halo:grid%j_stop_halo) )
        allocate( BYDXP (grid%j_strt_halo:grid%j_stop_halo) )
        allocate( BYDYV (grid%j_strt_halo:grid%j_stop_halo) )
        allocate( BYDYP (grid%j_strt_halo:grid%j_stop_halo) )

        allocate( UXA(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( UXB(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( UXC(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( UYA(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( UYB(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( UYC(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( VXA(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( VXB(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( VXC(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( VYA(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( VYB(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )
        allocate( VYC(IM,grid%j_strt_halo:grid%j_stop_halo,LMO) )


      DO J=J_0,J_1S
C**** Calculate KH = rho_0 BETA* L_Munk^3 where DX=L_Munk
c      KHP(J)=2d0*RHOWS*OMEGA*COSP(J)*(DXP(J)**3)/RADIUS ! tracer lat
c      KHV(J)=2d0*RHOWS*OMEGA*COSV(J)*(DXV(J)**3)/RADIUS ! (v vel pts)
C**** Calculate KH=rho_0 BETA (sqrt(3) L_Munk/pi)^3, L_Munk=min(DX,DY)
        DSP=MIN(DXPO(J),DYPO(J))*2.*SQRT(3.)/TWOPI  ! tracer lat
        DSV=MIN(DXVO(J),DYVO(J))*2.*SQRT(3.)/TWOPI  ! v vel pts
        KHP(J)=2d0*RHOWS*OMEGA*COSPO(J)*(DSP**3)/RADIUS ! tracer lat
        KHV(J)=2d0*RHOWS*OMEGA*COSVO(J)*(DSV**3)/RADIUS ! (v vel pts)
        KHP(J)=MAX(KHP(J),AKHMIN)
        KHV(J)=MAX(KHV(J),AKHMIN)
        BYDXYV(J)=1D0/DXYVO(J)
        BYDXV(J)=1D0/DXVO(J)
        BYDXP(J)=1D0/DXPO(J)
        BYDYV(J)=1D0/DYVO(J)
        BYDYP(J)=1D0/DYPO(J)
        KYPXP(J)=KHP(J)*DYPO(J)*BYDXP(J)
        KXPYV(J)=KHV(J)*DXPO(J)*BYDYV(J)
        KYVXV(J)=KHV(J)*DYVO(J)*BYDXV(J)
        KXVYP(J)=KHP(J)*DXVO(J)*BYDYP(J)
C**** Discretisation errors need TANP/V to be defined like this
        DLAT = TWOPI*NINT(360d0/(JM-1))/720d0
        TANP(J)=TAN(RLAT(J))*TAN(0.5*DLAT)/(RADIUS*0.5*DLAT)
        VLAT = DLAT*(J+0.5-0.5*(1+JM))
        TANV(J)=TAN(VLAT)*SIN(DLAT)/(DLAT*RADIUS)
      END DO
      !make halo_update if 2 is not present
      !barrier synchoronization is required before sending the message,
      !j=2 is computed before the message is sent
      if( HAVE_SOUTH_POLE ) then
        KHV(1)=KHV(2)
        BYDXV(1)=1D0/DXVO(1)
        BYDYV(1)=1D0/DYVO(1)
        BYDYP(1)=1D0/DYPO(1)
      endif
      if( HAVE_NORTH_POLE ) then
        BYDXP(JM)=1D0/DXPO(JM)
        BYDXYPJM=1D0/(DXYPO(JM)*IM)
        TANP(JM) = 0
      endif

      CALL HALO_UPDATE(grid,TANP (grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=NORTH+SOUTH)
      CALL HALO_UPDATE(grid,BYDXP(grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=NORTH)
      CALL HALO_UPDATE(grid,TANV (grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=SOUTH)
      CALL HALO_UPDATE(grid,KHP (grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=NORTH)
      CALL HALO_UPDATE(grid,KXVYP (grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=SOUTH)
      CALL HALO_UPDATE(grid,DYPO (grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=SOUTH)

C****
C**** Calculate operators fixed in time for U and V equations
C****
      UXA=0. ; UXB=0. ; UXC=0. ; UYA=0. ; UYB=0. ; UYC=0.
      VXA=0. ; VXB=0. ; VXC=0. ; VYA=0. ; VYB=0. ; VYC=0.
      UYPB=0.; UYPA=0.

      DO L=1,LMO
C**** Calculate flux operators
C**** i.e DUDX(1) and DUDX(2) are the coefficients of u1 and u2 for
C**** calculating centered difference K_h du/dx
C**** including metric terms in y derivatives
        DUDX=0.
        DUDY=0.
        DVDX=0.
        DVDY=0.
        DO J=max(2,J_0S-1),J_1S
          I=IM
          DO IP1=1,IM
            IF (L.LE.LMU(IP1,J)) DUDX(IP1,J,1) = KYPXP(J)
            IF (L.LE.LMU(I,J)) THEN
              DUDX(IP1,J,2) = -KYPXP(J)
              IF (L.LE.LMU(I,J+1)) THEN
                DUDY(I,J,1) =  KXPYV(J)*(1. +0.5*TANV(J)*DYVO(J))
                DUDY(I,J,2) = -KXPYV(J)*(1. -0.5*TANV(J)*DYVO(J))
              ELSE
                DUDY(I,J,2) = -(1.-FSLIP)*2d0*KXPYV(J)
              END IF
            ELSE
              IF (L.LE.LMU(I,J+1)) DUDY(I,J,1) = (1.-FSLIP)*2d0*KXPYV(J)
            END IF
            IF (L.LE.LMV(I,J+1)) DVDY(I,J+1,1) = KXVYP(J)*(1. +
     *           0.5*TANP(J)*DYPO(J))
            IF (L.LE.LMV(I,J)) THEN
              DVDY(I,J+1,2) = -KXVYP(J)*(1.-0.5*TANP(J)*DYPO(J))
              IF (L.LE.LMV(IP1,J)) THEN
                DVDX(I,J,1) =  KYVXV(J)
                DVDX(I,J,2) = -KYVXV(J)
              ELSE
                DVDX(I,J,2) = -(1.-FSLIP)*2d0*KYVXV(J)
              END IF
            ELSE
              IF (L.LE.LMV(IP1,J)) DVDX(I,J,1) = (1.-FSLIP)*2d0*KYVXV(J)
            END IF
            I=IP1
          END DO
        END DO
        CALL HALO_UPDATE(grid, DUDY(:,grid%j_strt_halo:grid%j_stop_halo,
     *     :), FROM=SOUTH)
C****
C**** Combine to form tri-diagonal operators including first metric term
C****
        DO J=J_0S,J_1S
          IM1=IM
          DO I=1,IM
            IF(L.LE.LMU(IM1,J)) THEN
              UXA(IM1,J,L) = -DUDX(IM1,J,2)                  *BYDXYPO(J)
              UXB(IM1,J,L) = (DUDX(I  ,J,2) -DUDX(IM1,J  ,1))*BYDXYPO(J)
              UXC(IM1,J,L) =  DUDX(I  ,J,1)                  *BYDXYPO(J)
              UYA(IM1,J,L) = -DUDY(IM1,J-1,2)                *BYDXYPO(J)
     *                     + 0.5*TANP(J)*KHP(J)*BYDYP(J)
              UYB(IM1,J,L) =(DUDY(IM1,J  ,2)-DUDY(IM1,J-1,1))*BYDXYPO(J)
     *                     + TANP(J)*TANP(J)*KHP(J)
              UYC(IM1,J,L) = DUDY(IM1,J  ,1)                 *BYDXYPO(J)
     *                     - 0.5*TANP(J)*KHP(J)*BYDYP(J)
            END IF
            IF (L.LE.LMV(I,J)) THEN
              VXA(I,J,L) = -DVDX(IM1,J,2)                 *BYDXYV(J)
              VXB(I,J,L) = (DVDX(I  ,J,2) - DVDX(IM1,J,1))*BYDXYV(J)
              VXC(I,J,L) =  DVDX(I  ,J,1)                 *BYDXYV(J)
              VYA(I,J,L) = -DVDY(I,J  ,2)                 *BYDXYV(J)
     *                   + 0.5*TANV(J)*KHV(J)*BYDYV(J)
              VYB(I,J,L) = (DVDY(I,J+1,2) - DVDY(I  ,J,1))*BYDXYV(J)
     *                   + TANV(J)*TANV(J)*KHV(J)
              VYC(I,J,L) =  DVDY(I,J+1,1)                 *BYDXYV(J)
     *                   - 0.5*TANV(J)*KHV(J)*BYDYV(J)
            END IF
            IM1=I
          END DO
        END DO
C**** At North Pole
        !following uses jm and jm-1 data, if both are not on same
        !processor (need to check this), halo_date is required.
        IF(HAVE_NORTH_POLE) THEN
          IF(L.LE.LMU(1,JM)) THEN
            DO I=1,IM
              UYPB(L) = UYPB(L) - DUDY(I,JM-1,1)
              UYPA(I,L) = -DUDY(I,JM-1,2)*BYDXYPJM
            END DO
            UYPB(L) = UYPB(L)*BYDXYPJM
          END IF
        END IF
      END DO
      END IF
!**  IFIRST block ends here

C**** End of initialization from first call to ODIFF
C****
C**** Solve diffusion equations semi implicitly
C****
      DT2=DTDIFF*5d-1     ! half time-step
C**** Store North Pole velocity components, they will not be changed
      if(have_north_pole) then
        UONP(:) = UO(IM  ,JM,:)
        VONP(:) = UO(IVNP,JM,:)
      end if

!     also may later need HALO for DXPO(N), DXVO(S)

      CALL HALO_UPDATE(grid,DH,
     *                 FROM=NORTH)
      CALL HALO_UPDATE(grid,MO,
     *                 FROM=NORTH)

      CALL HALO_UPDATE(grid,UO (:,grid%j_strt_halo:grid%j_stop_halo,:) ,
     *                 FROM=NORTH)
      CALL HALO_UPDATE(grid,UO (:,grid%j_strt_halo:grid%j_stop_halo,:) ,
     *                 FROM=SOUTH)
      CALL HALO_UPDATE(grid,VO (:,grid%j_strt_halo:grid%j_stop_halo,:) ,
     *                 FROM=NORTH)
      CALL HALO_UPDATE(grid,VO (:,grid%j_strt_halo:grid%j_stop_halo,:) ,
     *                 FROM=SOUTH)

C****
!$OMP PARALLEL DEFAULT(NONE),
!$OMP&  PRIVATE(AU,AV, BYMU,BYMV,BU,BV, CU,CV, DTU, DTV,
!$OMP&          FUX,FUY,FVX,FVY, I,IP1,IM1, J, L, RU,RV,
!$OMP&          UU,UV,UT,UY,UX, VT,VY,VX),
!$OMP&  SHARED(have_north_pole, UO, VO, UONP, VONP, COSU,SINU,COSI,SINI,
!$OMP&         J_0, J_0S, J_1S, LMU, MO, LMV, TANP, TANV,
!$OMP&         BYDYP, BYDXV, BYDXP, BYDYV, KHV, KHP, grid, DT2, DH,
!$OMP&         UXA, UXB, UXC, UYA, UYB, UYC, VXA, VXB, VXC,
!$OMP&         VYA, VYB, VYC, DYVO, DXVO, DXPO, DYPO, BYDXYPO, BYDXYV)

      allocate( AU(IM,grid%j_strt_halo:grid%j_stop_halo) )
      allocate( BU(IM,grid%j_strt_halo:grid%j_stop_halo) )
      allocate( CU(IM,grid%j_strt_halo:grid%j_stop_halo) )
      allocate( RU(IM,grid%j_strt_halo:grid%j_stop_halo) )
      allocate( UU(IM,grid%j_strt_halo:grid%j_stop_halo) )

      allocate( AV(IM,grid%j_strt_halo:grid%j_stop_halo) )
      allocate( BV(IM,grid%j_strt_halo:grid%j_stop_halo) )
      allocate( CV(IM,grid%j_strt_halo:grid%j_stop_halo) )
      allocate( RV(IM,grid%j_strt_halo:grid%j_stop_halo) )
      allocate( UV(IM,grid%j_strt_halo:grid%j_stop_halo) )

!$OMP DO
      DO L=1,LMO
C**** Calculate rotating polar velocities from UONP and VONP
      if(have_north_pole) then
        UO(:,JM,L) = UONP(L)*COSU(:) + VONP(L)*SINU(:)
        VO(:,JM,L) = VONP(L)*COSI(:) - UONP(L)*SINI(:)
      end if
C**** Save (0.5*) mass reciprical for velocity points
      DO J=J_0S,J_1S
        I=IM
        DO IP1=1,IM
          IF (L.LE.LMU(I,J)) BYMU(I,J) = 1./(MO(I,J,L)+MO(IP1,J,L))
          IF (L.LE.LMV(I,J)) BYMV(I,J) = 1./(MO(I,J,L)+MO(I,J+1,L))
          I=IP1
        END DO
      END DO
      if( HAVE_NORTH_POLE ) then
        IF (L.LE.LMU(1,JM)) BYMU(1,JM) = 1./MO(1,JM,L)
      endif
C**** Calculate Wasjowicz boundary terms
C**** Need dv/dy,tv,dv/dx for u equation, du/dy,tu,du/dx for v equation
      FUX=0             ! flux in U equation at the x_+ boundary
      FUY=0             ! flux in U equation at the y_+ boundary
      FVX=0             ! flux in V equation at the x_+ boundary
      FVY=0             ! flux in V equation at the y_+ boundary
      DO J=J_0, J_1S
        IM1=IM-1
        DO I=1,IM
          UT=0          ! mean u*tan on x_+ boundary for V equation
          UY=0          ! mean du/dx on y_+ boundary for V equation
          UX=0          ! mean du/dy on x_+ boundary for V equation
          IF (L.LE.LMU(I  ,J+1)) THEN
            UT=     UO(I  ,J+1,L)*TANP(J+1)
            UX=     UO(I  ,J+1,L)
            UY=     UO(I  ,J+1,L)
          END IF
          IF (L.LE.LMU(I  ,J  )) THEN
            UT=UT + UO(I  ,J  ,L)*TANP(J  )
            UY=UY - UO(I  ,J  ,L)
          END IF
          IF (L.LE.LMU(IM1,J+1)) UX=UX-    UO(IM1,J+1,L)
          UT=0.5*UT
          UX=UX*BYDXP(J+1)
          UY=UY*BYDYV(J)
C****
          VT=0          ! mean v*tan on x_+ boundary for U equation
          VX=0          ! mean dv/dx on y_+ boundary for U equation
          VY=0          ! mean dv/dy on x_+ boundary for U equation
          IF (L.LE.LMV(I  ,J  )) THEN
            VT=     VO(I  ,J  ,L)*TANV(J  )
            VX=     VO(I  ,J  ,L)
            VY=     VO(I  ,J  ,L)
          END IF
          IF (J.GT.1) THEN
            IF  (L.LE.LMV(I  ,J-1)) THEN
            VT=VT + VO(I  ,J-1,L)*TANV(J-1)
            VY=VY - VO(I  ,J-1,L)
            END IF
          END IF
          IF (L.LE.LMV(IM1,J  )) VX=VX - VO(IM1,J  ,L)
          VT=0.5*VT
          VY=VY*BYDYP(J)
          VX=VX*BYDXV(J)
C**** Calculate fluxes (including FSLIP condition)
          IF (FSLIP == 1.) THEN
            IF (L.LE.LMV(I,J) .AND. L.LE.LMV(IM1,J))
     *           FUY(IM1,J)=KHV(J)*VX
            IF (L.LE.LMU(I,J) .AND. L.LE.LMU(I,J+1))
     *           FVX(I  ,J)=KHV(J)*(UY + UT)
          ELSE
            FUY(IM1,J)=KHV(J)*VX
            FVX(I  ,J)=KHV(J)*(UY + UT)
          END IF
          FUX(IM1,J)=KHP(J)*(VY + VT)
          IF (J.LT.JM-1) FVY(I,J)=KHP(J+1)*UX
          IM1=I
        END DO
      END DO

      CALL HALO_UPDATE(grid,FUY (:,grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=SOUTH)
      CALL HALO_UPDATE(grid,FVY (:,grid%j_strt_halo:grid%j_stop_halo),
     *                 FROM=SOUTH)

C**** Calculate tridiagonal matrix for first semi-implicit step (in x)
C**** Minor complication due to cyclic nature of boundary condition
      AU=0. ; BU=0. ; CU=0. ; RU=0.
      AV=0. ; BV=0. ; CV=0. ; RV=0.

      DO J=J_0S,J_1S
        IM1=IM-1
        I=IM
        DO IP1=1,IM
          BU(I,J) = 1d0
          BV(I,J) = 1d0
          IF (L.LE.LMU(I,J)) THEN
            DTU = DT2*(DH(I,J,L)+DH(IP1,J,L))*BYMU(I,J)
            IF (I.gt.1 ) AU(I,J) =        - DTU*UXA(I,J,L)
                         BU(I,J) = BU(I,J) - DTU*UXB(I,J,L)
            IF (I.lt.IM) CU(I,J) =        - DTU*UXC(I,J,L)
            RU(I,J) = UO(I,J,L) + DTU*(UYA(I,J,L)*UO(I,J-1,L)
     *           +UYB(I,J,L)*UO(I,J,L) + UYC(I,J,L)*UO(I,J+1,L))
C**** Make properly tridiagonal by making explicit cyclic terms
            IF (I == 1 ) RU(I,J)=RU(I,J) + DTU*UXA(I,J,L)*UO(IM,J,L)
            IF (I == IM) RU(I,J)=RU(I,J) + DTU*UXC(I,J,L)*UO(1,J,L)
C**** Add Wasjowicz cross-terms to RU + second metric term
            RU(I,J) = RU(I,J) + DTU*((DYPO(J)*(FUX(IM1,J) - FUX(I,J))
     *           + DXVO(J)*FUY(I,J) - DXVO(J-1)*FUY(I,J-1))*BYDXYPO(J)
     *           - 0.5*(TANV(J-1)*FUY(I,J-1) + TANV(J)*FUY(I,J)))
          END IF
          IF (L.LE.LMV(I,J)) THEN
            DTV = DT2*(DH(I,J,L)+DH(I,J+1,L))*BYMV(I,J)
            IF (I.gt.1 ) AV(I,J) =        - DTV*VXA(I,J,L)
                         BV(I,J) = BV(I,J) - DTV*VXB(I,J,L)
            IF (I.lt.IM) CV(I,J) =        - DTV*VXC(I,J,L)
            RV(I,J) = VO(I,J,L) + DTV*(VYA(I,J,L)*VO(I,J-1,L)
     *           +VYB(I,J,L)*VO(I,J,L) + VYC(I,J,L)*VO(I,J+1,L))
C**** Make properly tridiagonal by making explicit cyclic terms
            IF (I == 1 ) RV(I,J)=RV(I,J) + DTV*VXA(I,J,L)*VO(IM,J,L)
            IF (I == IM) RV(I,J)=RV(I,J) + DTV*VXC(I,J,L)*VO(1,J,L)
C**** Add Wasjowicz cross-terms to RV + second metric term
            RV(I,J) = RV(I,J) + DTV*((DYVO(J)*(FVX(I,J) - FVX(IM1,J))
     *           + DXPO(J)*FVY(I,J-1) - DXPO(J+1)*FVY(I,J))*BYDXYV(J)
     *           + 0.5*(TANP(J-1)*FVY(I,J-1) + TANP(J)*FVY(I,J)))
          END IF
          IM1=I
          I=IP1
        END DO
      END DO
C**** At North Pole (no metric terms)
c     BU(IIP) = 1d0
c     BV(IIP) = 1d0
c     IF (L.LE.LMU(1,JM)) THEN
c     DTU = DT2*DH(1,JM,L)*BYMU(1,JM)
c       RU(IIP) = 0.
c       DO I=1,IM       ! include Wasjowicz cross-terms at North Pole
c         RU(IIP) = RU(IIP) + DTU*(UYPA(I,L)*UO(I,JM-1,L)
c    *                      - DXVO(JM-1)*FUY(I,JM-1)*BYDXYPJM)
c       END DO
c     END IF
C**** Call tridiagonal solver
      DO J = J_0S, J_1S
        CALL TRIDIAG(AU(:,J), BU(:,J), CU(:,J), RU(:,J), UO(:,J,L), IM)
        CALL TRIDIAG(AV(:,J), BV(:,J), CV(:,J), RV(:,J), VO(:,J,L), IM)
      END DO

C****
C**** Now do semi implicit solution in y
C**** Recalculate rotating polar velocities from UONP and VONP
C     UO(:,JM,L) = UONP(L)*COSU(:) + VONP(L)*SINU(:)
C     VO(:,JM,L) = VONP(L)*COSI(:) - UONP(L)*SINI(:)

      CALL HALO_UPDATE(grid,UO (:,grid%j_strt_halo:grid%j_stop_halo,L) ,
     *                 FROM=NORTH)
      CALL HALO_UPDATE(grid,UO (:,grid%j_strt_halo:grid%j_stop_halo,L) ,
     *                 FROM=SOUTH)
      CALL HALO_UPDATE(grid,VO (:,grid%j_strt_halo:grid%j_stop_halo,L) ,
     *                 FROM=NORTH)
      CALL HALO_UPDATE(grid,VO (:,grid%j_strt_halo:grid%j_stop_halo,L) ,
     *                 FROM=SOUTH)

C**** Calc. cross-term fluxes + second metric term (at half time step)
C**** Need dv/dy,tv,dv/dx for u equation, du/dy,tu,du/dx for v equation
      FUX=0             ! flux in U equation at the x_+ boundary
      FUY=0             ! flux in U equation at the y_+ boundary
      FVX=0             ! flux in V equation at the x_+ boundary
      FVY=0             ! flux in V equation at the y_+ boundary
      DO J=J_0,J_1S
        IM1=IM-1
        DO I=1,IM
          UT=0         ! mean u*tan on x_+ boundary for V equation
          UY=0         ! mean du/dx on y_+ boundary for V equation
          UX=0         ! mean du/dy on x_+ boundary for V equation
          IF (L.LE.LMU(I  ,J+1)) THEN
            UT=     UO(I  ,J+1,L)*TANP(J+1)
            UX=     UO(I  ,J+1,L)
            UY=     UO(I  ,J+1,L)
          END IF
          IF (L.LE.LMU(I  ,J  )) THEN
            UT=UT + UO(I  ,J  ,L)*TANP(J  )
            UY=UY - UO(I  ,J  ,L)
          END IF
          IF (L.LE.LMU(IM1,J+1)) UX=UX-    UO(IM1,J+1,L)
          UT=0.5*UT
          UX=UX*BYDXP(J+1)
          UY=UY*BYDYV(J)
C****
          VT=0         ! mean v*tan on x_+ boundary for U equation
          VX=0         ! mean dv/dx on y_+ boundary for U equation
          VY=0         ! mean dv/dy on x_+ boundary for U equation
          IF (L.LE.LMV(I  ,J  )) THEN
            VT=     VO(I  ,J  ,L)*TANV(J  )
            VX=     VO(I  ,J  ,L)
            VY=     VO(I  ,J  ,L)
          END IF
          IF (J.GT.1) THEN
            IF (L.LE.LMV(I  ,J-1)) THEN
            VT=VT + VO(I  ,J-1,L)*TANV(J-1)
            VY=VY - VO(I  ,J-1,L)
          END IF
          END IF
          IF (L.LE.LMV(IM1,J  )) VX=VX - VO(IM1,J  ,L)
          VT=0.5*VT
          VY=VY*BYDYP(J)
          VX=VX*BYDXV(J)
C**** Calculate fluxes (including FSLIP condition)
          IF (FSLIP == 1.) THEN
            IF (L.LE.LMV(I,J) .AND. L.LE.LMV(IM1,J))
     *           FUY(IM1,J)=KHV(J)* VX
            IF (L.LE.LMU(I,J) .AND. L.LE.LMU(I,J+1))
     *           FVX(I,J)=KHV(J)*(UY + UT)
          ELSE
            FUY(IM1,J)=KHV(J)* VX
            FVX(I  ,J)=KHV(J)*(UY + UT)
          END IF
          FUX(IM1,J)=KHP(J)*(VY + VT)
          IF (J.LT.JM-1) FVY(I,J)=KHP(J+1)*UX
          IM1=I
        END DO
      END DO

      CALL HALO_UPDATE(grid,FUY (:,grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=SOUTH)
      CALL HALO_UPDATE(grid,FVY (:,grid%j_strt_halo:grid%j_stop_halo) ,
     *                 FROM=SOUTH)

C**** Calculate tridiagonal matrix for second semi-implicit step (in y)
C**** Minor complication due to singular nature of polar box
      AU=0. ; BU=0. ; CU=0. ; RU=0.; UU=0
      AV=0. ; BV=0. ; CV=0. ; RV=0.; UV=0.

      DO IP1=1,IM
        DO J=J_0S,J_1S
          !put following later into a subroutine
          if(ip1.eq.1) then
            if(J.eq.2) then
              IM1=IM-1; I=IM;
            elseif(J.eq.3) then
              IM1=IM; I=IP1;
            else
              IM1=IP1; I=IP1;
            endif
          endif
          if(ip1.gt.1) then
            if(j.eq.2) then
              IM1=IP1-1; I=IP1-1;
            else
              IM1=IP1; I=IP1;
            endif
          endif

          BU(I,J) = 1d0
          BV(I,J) = 1d0
          IF (L.LE.LMU(I,J)) THEN
            DTU = DT2*(DH(I,J,L)+DH(IP1,J,L))*BYMU(I,J)
            AU(I,J) =        - DTU*UYA(I,J,L)
            BU(I,J) = BU(I,J) - DTU*UYB(I,J,L)
            IF (J.lt.JM-1) CU(I,J) =     - DTU*UYC(I,J,L)
            RU(I,J) = UO(I,J,L) + DTU*(UXA(I,J,L)*UO(IM1,J,L)
     *           +UXB(I,J,L)*UO(I,J,L) + UXC(I,J,L)*UO(IP1,J,L))
c**** Make properly tridiagonal by making explicit polar terms
!mkt  UO(1,JM,L) changed to UO(I,JM,L)
            IF (J == JM-1) RU(I,J)=RU(I,J) + DTU*UYC(I,J,L)*UO(I,JM,L)
C**** Add Wasjowicz cross-terms to RU + second metric term
            RU(I,J) = RU(I,J) + DTU*((DYPO(J)*(FUX(IM1,J) - FUX(I,J))
     *           + DXVO(J)*FUY(I,J) - DXVO(J-1)*FUY(I,J-1))*BYDXYPO(J)
     *           - 0.5*(TANV(J-1)*FUY(I,J-1) + TANV(J)*FUY(I,J)))
          END IF
          IF (L.LE.LMV(I,J)) THEN
            DTV = DT2*(DH(I,J,L)+DH(I,J+1,L))*BYMV(I,J)
            AV(I,J) =        - DTV*VYA(I,J,L)
            BV(I,J) = BV(I,J) - DTV*VYB(I,J,L)
            IF (J.lt.JM-1) CV(I,J) =     - DTV*VYC(I,J,L)
            RV(I,J) = VO(I,J,L) + DTV*(VXA(I,J,L)*VO(IM1,J,L)
     *           +VXB(I,J,L)*VO(I,J,L) + VXC(I,J,L)*VO(IP1,J,L))
c**** Make properly tridiagonal by making explicit polar terms
            IF (J == JM-1) RV(I,J)=RV(I,J) + DTV*VYC(I,J,L)*VO(I,JM,L)
C**** Add Wasjowicz cross-terms to RV + second metric term
            RV(I,J) = RV(I,J) + DTV*((DYVO(J)*(FVX(I,J) - FVX(IM1,J))
     *           + DXPO(J)*FVY(I,J-1) - DXPO(J+1)*FVY(I,J))*BYDXYV(J)
     *           + 0.5*(TANP(J-1)*FVY(I,J-1) + TANP(J)*FVY(I,J)))
          END IF
          IM1=I
          I=IP1
        END DO
      END DO
C**** At North Pole (do partly explicitly) no metric terms
c     BU(IIP) = 1d0
c     BV(IIP) = 1d0
c     IF (L.LE.LMU(1,JM)) THEN
c       DTU = DT2*DH(1,JM,L)*BYMU(1,JM)
c       BU(IIP) = BU(IIP) - DTU*UYPB(L)
c       RU(IIP) = UO(1,JM,L)
c       DO I=1,IM       ! include Wasjowicz cross-terms at North Pole
c         RU(IIP)= RU(IIP) + DTU*(UYPA(I,L)*UO(I,JM-1,L)
c    *         - DXVO(JM-1)*FUY(I,JM-1)*BYDXYPJM)
c       END DO
c     END IF
C**** Call tridiagonal solver

      CALL TRIDIAG_new(AU, BU, CU, RU, UU, grid,J_LOWER=2,J_UPPER=JM-1)
      CALL TRIDIAG_new(AV, BV, CV, RV, UV, grid,J_LOWER=2,J_UPPER=JM-1)

      UO(:,J_0S:J_1S,L)=UU(:,J_0S:J_1S)
      VO(:,J_0S:J_1S,L)=UV(:,J_0S:J_1S)

C****
      END DO
!$OMP END DO
      deallocate(AU, BU, CU, RU, UU)
      deallocate(AV, BV, CV, RV, UV)
!$OMP END PARALLEL

C**** Restore unchanged UONP and VONP into prognostic locations in UO
      if(have_north_pole) then
        UO(IM  ,JM,:) = UONP(:)
        UO(IVNP,JM,:) = VONP(:)
      end if
C****
      RETURN
      END SUBROUTINE ODIFF



      SUBROUTINE TOC2SST 8,20
!@sum  TOC2SST convert ocean surface variables into atmospheric sst
!@auth Gavin Schmidt
!@ver  1.0
      USE CONSTANT, only : byshi,lhm,tf
#ifdef TRACERS_WATER
      USE TRACER_COM, only : trw0
#endif
      USE OCEAN, only : im,jm,IVSP,IVNP, imaxj,dxypo,lmm,
     *     COSU,SINU, COSI=>COSIC,SINI=>SINIC,
     *     focean,g0m,s0m,mo,ogeoz,uo,vo,ogeoz_sv
#ifdef TRACERS_OCEAN
     *     ,trmo
#endif
      USE FLUXES, only : gtemp, sss, mlhc, ogeoza, uosurf, vosurf,
     *      gtempr
#ifdef TRACERS_WATER
     *     ,gtracer
#endif
#ifdef TRACERS_GASEXCH_Natassa
     *     ,GTRACER,TRGASEX
      USE TRACER_COM, only : ntm
#endif

      use domain_decomp, only : grid, get

      IMPLICIT NONE
      INTEGER I,J
      REAL*8 TEMGS,shcgs,GO,SO,GO2,SO2,TO
      integer :: j_0,j_1
#ifdef TRACERS_GASEXCH_Natassa
     .         ,n
#endif
      logical :: HAVE_SOUTH_POLE, HAVE_NORTH_POLE

      call get (grid, j_strt=j_0, j_stop=j_1,
     * HAVE_SOUTH_POLE=HAVE_SOUTH_POLE, HAVE_NORTH_POLE=HAVE_NORTH_POLE)

C****
C**** Note that currently everything is on same grid
C****
      DO J=J_0,J_1
        DO I=1,IMAXJ(J)
          IF (FOCEAN(I,J).gt.0.) THEN
            GO= G0M(I,J,1)/(MO(I,J,1)*DXYPO(J))
            SO= S0M(I,J,1)/(MO(I,J,1)*DXYPO(J))
            TO= TEMGS(GO,SO)
            GTEMP(1,1,I,J)= TO
            GTEMPR(1,I,J) = TO+TF
            SSS(I,J) = 1d3*SO
            MLHC(I,J)= MO(I,J,1)*SHCGS(GO,SO)
            IF (LMM(I,J).gt.1) THEN
              GO2= G0M(I,J,2)/(MO(I,J,2)*DXYPO(J))
              SO2= S0M(I,J,2)/(MO(I,J,2)*DXYPO(J))
              TO= TEMGS(GO2,SO2)
            END IF
            GTEMP(2,1,I,J)= TO
   ! atmospheric grid Ocean height
            OGEOZA(I,J)=0.5*(OGEOZ(I,J)+OGEOZ_SV(I,J))
            UOSURF(I,J)=UO(I,J,1)
            VOSURF(I,J)=VO(I,J,1)

#ifdef TRACERS_WATER
#ifdef TRACERS_OCEAN
            GTRACER(:,1,I,J)=TRMO(I,J,1,:)/(MO(I,J,1)*DXYPO(J)-
     *           S0M(I,J,1))
#else
            GTRACER(:,1,I,J)=trw0(:)
#endif
#endif

#ifdef TRACERS_GASEXCH_Natassa
            GTRACER(:,1,I,J)=TRMO(I,J,1,:)/(MO(I,J,1)*DXYPO(J))
cdiag       do n=1,ntm
cdiag       write(6,'(a,3i5,5e12.4)')'OCNDYN, TRACER  ',
cdiag.           I,J,n,GTRACER(n,1,I,J),TRMO(I,J,1,n),
cdiag.           MO(I,J,n),DXYPO(J),TRGASEX(n,1,I,J)
cdiag       enddo
#endif
          END IF

        END DO
      END DO
C**** do poles
      if (HAVE_NORTH_POLE) then
      IF (FOCEAN(1,JM).gt.0) THEN
        UOSURF(1,JM) = UO(IM,JM,1)*COSU(1) + UO(IVNP,JM,1)*SINU(1)
        VOSURF(1,JM) = UO(IVNP,JM,1)*COSI(1) - UO(IM,JM,1)*SINI(1)
        DO I=2,IM
          GTEMP(:,1,I,JM)=GTEMP(:,1,1,JM)
          GTEMPR(1,I,JM) =GTEMPR(1,1,JM) 
          SSS(I,JM)=SSS(1,JM)
          MLHC(I,JM)=MLHC(1,JM)
          UOSURF(I,JM) = UO(IM,JM,1)*COSU(I) + UO(IVNP,JM,1)*SINU(I)
          VOSURF(I,JM) = UO(IVNP,JM,1)*COSI(I) - UO(IM,JM,1)*SINI(I)
          OGEOZA(I,JM)=OGEOZA(1,JM)
#if (defined TRACERS_WATER) || (defined TRACERS_GASEXCH_Natassa)
          GTRACER(:,1,I,JM)=GTRACER(:,1,1,JM)
#endif
        END DO
      END IF
      end if

      if (HAVE_SOUTH_POLE) then
      IF (FOCEAN(1,1).gt.0) THEN
        DO I=2,IM
          GTEMP(:,1,I,1)=GTEMP(:,1,1,1)
          GTEMPR(1,I,1) =GTEMPR(1,1,1)
          SSS(I,1)=SSS(1,1)
          MLHC(I,1)=MLHC(1,1)
          UOSURF(I,1) = UO(IM,1,1)*COSU(1) - UO(IVSP,1,1)*SINU(1)
          VOSURF(I,0) = UO(IVSP,1,1)*COSI(I) - UO(IM,1,1)*SINI(I)
          OGEOZA(I,1)=OGEOZA(1,1)
#if (defined TRACERS_WATER) || (defined TRACERS_GASEXCH_Natassa)
          GTRACER(:,1,I,1)=GTRACER(:,1,1,1)
#endif
        END DO
      END IF
      end if

      RETURN
C****
      END SUBROUTINE TOC2SST


      SUBROUTINE io_oda(kunit,it,iaction,ioerr) 2,8
!@sum  io_oda dummy routine for consistency with uncoupled model
!@auth Gavin Schmidt
!@ver  1.0
      RETURN
      END SUBROUTINE io_oda


      SUBROUTINE ADVSI_DIAG 1,12
!@sum ADVSI_DIAG dummy routine for consistency with qflux model
      RETURN
      END SUBROUTINE ADVSI_DIAG


      SUBROUTINE AT2OT(FIELDA,FIELDO,NF,QCONSERV),6
!@sum  AT2OT interpolates Atm Tracer grid to Ocean Tracer grid
!@auth Gavin Schmidt
!@ver  1.0
c GISS-ESMF EXCEPTIONAL CASE - AT2OT not needed yet - nothing done yet
      USE MODEL_COM, only : ima=>im,jma=>jm
      USE OCEAN, only : imo=>im,jmo=>jm,imaxj,ratoc
      use domain_decomp, only : grid,get

      IMPLICIT NONE
!@var QCONSERV true if integrated field must be conserved
      LOGICAL, INTENT(IN) :: QCONSERV
!@var N number of fields
      INTEGER, INTENT(IN) :: NF
!@var FIELDA array on atmospheric tracer grid
      REAL*8, INTENT(IN), DIMENSION(NF,IMA,JMA) :: FIELDA
!@var FIELDO array on oceanic tracer grid
      REAL*8, INTENT(OUT), DIMENSION(NF,IMO,JMO) :: FIELDO
      INTEGER I,J

C**** currently no need for interpolation,
C**** just scaling due to area differences for fluxes
      IF (QCONSERV) THEN
        DO J=1,JMO
          DO I=1,IMAXJ(J)
            FIELDO(:,I,J) = FIELDA(:,I,J)*RATOC(J)
          END DO
        END DO
        DO I=2,IMO
          FIELDO(:,I,JMO)=FIELDO(:,1,JMO)
          FIELDO(:,I,1  )=FIELDO(:,1,  1)
        END DO
      ELSE
        FIELDO = FIELDA
      END IF
C****
      RETURN
      END SUBROUTINE AT2OT


      SUBROUTINE OT2AT(FIELDO,FIELDA,NF,QCONSERV),6
c GISS-ESMF EXCEPTIONAL CASE - OT2AT not needed yet - nothing done yet
!@sum  OT2AT interpolates Ocean Tracer grid to Atm Tracer grid
!@auth Gavin Schmidt
!@ver  1.0
      USE MODEL_COM, only : ima=>im,jma=>jm
      USE GEOM, only : imaxj
      USE OCEAN, only : imo=>im,jmo=>jm,rocat
      IMPLICIT NONE
!@var QCONSERV true if integrated field must be conserved
      LOGICAL, INTENT(IN) :: QCONSERV
!@var N number of fields
      INTEGER, INTENT(IN) :: NF
!@var FIELDA array on atmospheric tracer grid
      REAL*8, INTENT(OUT), DIMENSION(NF,IMA,JMA) :: FIELDA
!@var FIELDO array on oceanic tracer grid
      REAL*8, INTENT(IN), DIMENSION(NF,IMO,JMO) :: FIELDO
      INTEGER I,J

C**** currently no need for interpolation,
C**** just scaling due to area differences for fluxes
      IF (QCONSERV) THEN
        DO J=1,JMA
          DO I=1,IMAXJ(J)
            FIELDA(:,I,J) = FIELDO(:,I,J)*ROCAT(J)
          END DO
        END DO
        DO I=2,IMA
          FIELDA(:,I,JMA)=FIELDA(:,1,JMA)
          FIELDA(:,I,1  )=FIELDA(:,1,  1)
        END DO
      ELSE
        FIELDA = FIELDO
      END IF
C****
      RETURN
      END SUBROUTINE OT2AT


      SUBROUTINE AT2OV(FIELDA,FIELDO,NF,QCONSERV,QU),6
!@sum  AT2OV interpolates Atm Tracer grid to Ocean Velocity grid
!@auth Gavin Schmidt
!@ver  1.0
c GISS-ESMF EXCEPTIONAL CASE - AT2OV not needed yet - nothing done yet
      USE MODEL_COM, only : ima=>im,jma=>jm
      USE GEOM, only : imaxj
      USE OCEAN, only : imo=>im,jmo=>jm,ramvn,ramvs,ratoc
      IMPLICIT NONE
!@var QCONSERV true if integrated field must be conserved
!@var QU true if u-velocity pts. are wanted (false for v velocity pts.)
      LOGICAL, INTENT(IN) :: QCONSERV, QU
!@var N number of fields
      INTEGER, INTENT(IN) :: NF
!@var FIELDA array on atmospheric tracer grid
      REAL*8, INTENT(IN), DIMENSION(NF,IMA,JMA) :: FIELDA
!@var FIELDO array on oceanic tracer grid
      REAL*8, INTENT(OUT), DIMENSION(NF,IMO,JMO) :: FIELDO
      INTEGER I,J,IP1

C**** Ocean velocities are on C grid
      IF (QU) THEN  ! interpolate onto U points
        IF (QCONSERV) THEN
          DO J=1,JMO-1
            I=IMO
            DO IP1=1,IMO
              FIELDO(:,I,J)=0.5*RATOC(J)*(FIELDA(:,I,J)+FIELDA(:,IP1,J))
              I=IP1
            END DO
          END DO
C**** do poles
          DO I=1,IMO
            FIELDO(:,I,JMO) = FIELDA(:,1,JMO)*RATOC(JMO)
            FIELDO(:,I,  1) = FIELDA(:,1,  1)*RATOC(JMO)
          END DO
        ELSE   ! no area weighting
          DO J=1,JMO-1
            I=IMO
            DO IP1=1,IMO
              FIELDO(:,I,J) = 0.5*(FIELDA(:,I,J)+FIELDA(:,IP1,J))
              I=IP1
            END DO
          END DO
C**** do poles
          DO I=1,IMO
            FIELDO(:,I,JMO) = FIELDO(:,1,JMO)
            FIELDO(:,I,  1) = FIELDO(:,1,  1)
          END DO
        END IF
      ELSE                      ! interpolate onto V points
        IF (QCONSERV) THEN
          DO J=1,JMO-1
            DO I=1,IMO
              FIELDO(:,I,J) = RATOC(J)*RAMVN(J)*FIELDA(:,I,J)+
     *             RATOC(J+1)*RAMVS(J+1)*FIELDA(:,I,J+1)
            END DO
          END DO
        ELSE
          DO J=1,JMO-1
            DO I=1,IMO
              FIELDO(:,I,J) = 0.5*(FIELDA(:,I,J)+FIELDA(:,I,J+1))
            END DO
          END DO
        END IF
        FIELDO(:,:,JMO) = 0.
      END IF
C****
      RETURN
      END SUBROUTINE AT2OV


      SUBROUTINE GLMELT(DT) 2,10
!@sum  GLMELT adds glacial melt around Greenland and Antarctica to ocean
!@auth Sukeshi Sheth/Gavin Schmidt
!@ver  1.0
      USE MODEL_COM, only : dtsrc
      USE OCEAN, only : imaxj,jm,g0m,mo,ze,focean,dxypo
#ifdef TRACERS_OCEAN
     *     ,trmo
#endif
      USE FLUXES, only : gmelt,egmelt
#ifdef TRACERS_WATER  /* TNL: inserted */
#ifdef TRACERS_OCEAN
     *     ,trgmelt
#endif
#endif               /* TNL: inserted */
      use domain_decomp, only : grid,get

      IMPLICIT NONE
!@var MAXL number of ocean levels over which GMELT is applied
      INTEGER, PARAMETER :: MAXL=5
      REAL*8, INTENT(IN) :: DT  !@var DT timestep for GLMELT call
      REAL*8 DZ
      INTEGER I,J,L

      integer :: j_0,j_1

      call get (grid, J_STRT=j_0, J_STOP=j_1)

      DO L=1,MAXL
C**** divide over depth and scale for time step
        DZ=DT*(ZE(L)-ZE(L-1))/(DTsrc*ZE(MAXL))
        DO J=j_0,j_1
          DO I=1,IMAXJ(J)
            IF (FOCEAN(I,J).GT.0. .and. GMELT(I,J).gt.0) THEN
              MO(I,J,L) = MO(I,J,L)+GMELT(I,J)*DZ/(DXYPO(J)*FOCEAN(I,J))
              G0M(I,J,L)=G0M(I,J,L)+EGMELT(I,J)*DZ
#ifdef TRACERS_WATER  /* TNL: inserted */
#ifdef TRACERS_OCEAN
              TRMO(I,J,L,:)=TRMO(I,J,L,:)+TRGMELT(:,I,J)*DZ
#endif
#endif               /* TNL: inserted */
            END IF
          END DO
        END DO
      END DO
C****
      RETURN
      END SUBROUTINE GLMELT



      SUBROUTINE ADJUST_MEAN_SALT 2,44
!@sum  ADJUST_MEAN_SALT sets the global mean salinity in the ocean
!@auth Gavin Schmidt
!@ver  1.0
      USE GEOM, only : dxyp,imaxj
      USE CONSTANT, only : grav
      USE OCEAN, only : im,jm,lmo,focean,lmm,mo,s0m,sxmo
     *     ,symo,szmo,dxypo,oc_salt_mean,g0m
      USE STRAITS, only : s0mst,sxmst,szmst,nmst,lmst,g0mst,mmst,dist
     *     ,wist
      use DOMAIN_DECOMP, only: grid, GLOBALSUM, get, AM_I_ROOT,
     *     ESMF_BCAST
      IMPLICIT NONE
      REAL*8 :: totalSalt,totalMass

      REAL*8, DIMENSION(grid%J_STRT_HALO:grid%J_STOP_HALO) ::OSALTJ
     *     ,OMASSJ
      REAL*8 mean_S,frac_inc,T_ORIG,T_NEW,temgsp,shcgs,pres,g,s
      INTEGER I,J,L,N,J_0,J_1

      CALL GET(grid, J_STRT = J_0, J_STOP = J_1)

      call conserv_OSL(OSALTJ)
      call conserv_OMS(OMASSJ)
      OSALTJ(:)=OSALTJ(:)*DXYP(:)
      OMASSJ(:)=OMASSJ(:)*DXYP(:)

      CALL GLOBALSUM(grid, OSALTJ, totalSalt, ALL=.true.)
      CALL GLOBALSUM(grid, OMASSJ, totalMass, ALL=.true.)

      if (AM_I_ROOT()) then
        mean_S=1000*totalSalt/totalMass  ! psu
        frac_inc=oc_salt_mean/mean_S
        write(6,*) "Changing ocean salinity: ",mean_S,frac_inc
      end if
      call ESMF_BCAST(grid,  frac_inc)

C**** adjust open ocean salinity
      DO J=J_0,J_1
        DO I=1,IMAXJ(J)
          PRES=0
          DO L=1,LMM(I,J)
            PRES=PRES+MO(I,J,L)*GRAV*.5
            G=G0M(I,J,L)/(MO(I,J,L)*DXYPO(J))
            S=S0M(I,J,L)/(MO(I,J,L)*DXYPO(J))
            T_ORIG=TEMGSP (G,S,PRES)
            S0M(I,J,L)=frac_inc*S0M(I,J,L)
            if (frac_inc.lt.1.) then
              SXMO(I,J,L)=frac_inc*SXMO(I,J,L)
              SYMO(I,J,L)=frac_inc*SYMO(I,J,L)
              SZMO(I,J,L)=frac_inc*SZMO(I,J,L)
            end if
            S=S0M(I,J,L)/(MO(I,J,L)*DXYPO(J))
            T_NEW=TEMGSP (G,S,PRES)
C**** approximately adjust enthalpy to restore temperature
            G0M(I,J,L)=G0M(I,J,L)+(T_ORIG-T_NEW)*SHCGS(G,S)*MO(I,J,L)
     *           *DXYPO(J)
            G=G0M(I,J,L)/(MO(I,J,L)*DXYPO(J))
            IF (L.lt.LMM(I,J)) PRES=PRES+MO(I,J,L)*GRAV*.5
          END DO
        END DO
      END DO

C**** adjust strait salinity
      if (am_I_root()) then
        DO N=1,NMST
          PRES=0
          DO L=1,LMST(N)
            PRES=PRES+MMST(L,N)*GRAV*.5/(DIST(N)*WIST(N))
            G=G0MST(L,N)/MMST(L,N)
            S=S0MST(L,N)/MMST(L,N)
            T_ORIG=TEMGSP (G,S,PRES)
            S0MST(L,N)=frac_inc*S0MST(L,N)
            if (frac_inc.lt.1) then
              SXMST(L,N)=frac_inc*SXMST(L,N)
              SZMST(L,N)=frac_inc*SZMST(L,N)
            end if
            S=S0MST(L,N)/MMST(L,N)
            T_NEW=TEMGSP (G,S,PRES)
C**** approximately adjust enthalpy to restore temperature
            G0MST(L,N)=G0MST(L,N)+(T_ORIG-T_NEW)*SHCGS(G,S)*MMST(L,N)
            G=G0MST(L,N)/MMST(L,N)
            IF (L.lt.LMST(N)) PRES=PRES+MMST(L,N)*GRAV*.5/(DIST(N)
     *           *WIST(N))
          END DO
        END DO
      end if
      CALL ESMF_BCAST(grid, S0MST)
      CALL ESMF_BCAST(grid, SXMST)
      CALL ESMF_BCAST(grid, SZMST)

C**** Check
      call conserv_OSL(OSALTJ)
      OSALTJ(:)=OSALTJ(:)*DXYP(:)
      CALL GLOBALSUM(grid, OSALTJ, totalSalt, ALL=.true.)

      if (AM_I_ROOT()) then
        mean_S=1000*totalSalt/totalMass  ! psu
        write(6,*) "New ocean salinity: ",mean_S,oc_salt_mean
      end if

      RETURN
      END SUBROUTINE ADJUST_MEAN_SALT