!======================================================================
! CROCO is a branch of ROMS developped at IRD, INRIA, 
! Ifremer, CNRS and Univ. Toulouse III  in France
! The two other branches from UCLA (Shchepetkin et al)
! and Rutgers University (Arango et al) are under MIT/X style license.
! CROCO specific routines (nesting) are under CeCILL-C license.
!
! CROCO website : http://www.croco-ocean.org
!======================================================================
!
#ifndef CHILD
!
# include "cppdefs.h"
# ifdef SOLVE3D
      subroutine v3dbc_tile (Istr,Iend,Jstr,Jend,grad)
#  ifdef AGRIF
      use AGRIF_Util
      integer Istr,Iend,Jstr,Jend
      real grad(PRIVATE_2D_SCRATCH_ARRAY)
      if (AGRIF_Root()) then
        call v3dbc_parent_tile (Istr,Iend,Jstr,Jend,grad)
      else
        call v3dbc_child_tile (Istr,Iend,Jstr,Jend,grad)
c        call v3dbc_interp_tile(Istr,Iend,Jstr,Jend)
      endif
      return
      end
!
! PARENT
!
      subroutine v3dbc_parent_tile (Istr,Iend,Jstr,Jend,grad)
#  endif
!
! Set lateral boundary conditions for ETA-component velocity
! v(:,:,:,nnew) for the parent grid.
!
# endif /* SOLVE3D */
#else
# ifdef SOLVE3D
!
! CHILD
!
      subroutine v3dbc_child_tile (Istr,Iend,Jstr,Jend,grad)
!
! Set lateral boundary conditions for ETA-component velocity
! v(:,:,:,nnew) for the child grid.
!
# endif /* SOLVE3D */
#endif /* CHILD */
#ifdef SOLVE3D
!
! Common Code
!
# include "set_obc_definitions.h"
!
      implicit none
# include "param.h"
# include "grid.h"
# include "ocean3d.h"
# include "climat.h"
# include "scalars.h"
# if defined MRL_WCI || defined OW_COUPLING
#  include "forces.h"
# endif
# include "boundary.h"
      integer Istr,Iend,Jstr,Jend, i,j,k
      real grad(PRIVATE_2D_SCRATCH_ARRAY), cff,eps,
     &      cx,cy, dft,dfx,dfy, tau,tau_in,tau_out
      parameter (eps=1.E-20)
!
# include "compute_auxiliary_bounds.h"
!
! Interpolations of the parent values to get vbry_east or vclm
!
# ifdef CHILD
      call v3dbc_interp_tile(Istr,Iend,Jstr,Jend)
# endif
!
# if defined M3_FRC_BRY || defined M3CLIMATOLOGY
      tau_in=dt*tauM_in
      tau_out=dt*tauM_out
# endif
!
!$acc kernels if(compute_on_device) default(present)
# ifndef NS_COM_PERIODIC
!
!====================================================================
!                            SOUTHERN BC
!====================================================================
      if (SOUTHERN_EDGE) then
#  ifdef OBC_COM_SOUTH
#   if defined OBC_COM_M3SPECIFIED || defined OBC_COM_M3SPECIFIED_SOUTH
!                                           Southern edge Specified BC
!                                           ======== ==== ========= ==
        do k=1,N
          do i=Istr,Iend
#    ifdef M3_FRC_BRY
            v(i,Jstr,k,nnew)=vbry_south(i,k)        ! specified
#    else
            v(i,Jstr,k,nnew)=vclm(i,Jstr,k)
#    endif
#    ifdef MASKING
     &                       *vmask(i,Jstr)
#    endif
#    ifdef WET_DRY
     &                       *vmask_wet(i,Jstr)
#    endif
          enddo
        enddo
!
#   elif defined OBC_COM_M3ORLANSKI
        do k=1,N                            ! Southern edge radiation
          do i=Istr,Iend+1                  ! ======== ==== =========
            grad(i,Jstr  )=(v(i,Jstr  ,k,nstp)-v(i-1,Jstr  ,k,nstp))
#    ifdef MASKING
     &                                                *pmask(i,Jstr)
#    endif
            grad(i,Jstr+1)=(v(i,Jstr+1,k,nstp)-v(i-1,Jstr+1,k,nstp))
#    ifdef MASKING
     &                                              *pmask(i,Jstr+1)
#    endif
          enddo
          do i=Istr,Iend
            dft=v(i,Jstr+1,k,nstp)-v(i,Jstr+1,k,nnew)
            dfx=v(i,Jstr+1,k,nnew)-v(i,Jstr+2,k,nnew)
            if (dfx*dft .lt. 0.) then
              dft=0.                      ! <-- cancel cx, if inflow
#    if defined M3_FRC_BRY || defined M3CLIMATOLOGY
              tau=tau_in
            else
              tau=tau_out
#    endif
            endif
            if (dft*(grad(i,Jstr+1)+grad(i+1,Jstr+1)) .gt. 0.) then
              dfy=grad(i,Jstr+1)
            else
              dfy=grad(i+1,Jstr+1)
            endif
            cff=max(dfx*dfx+dfy*dfy, eps)
            cx=dft*dfx
            cy=min(cff,max(dft*dfy,-cff))
            v(i,Jstr,k,nnew)=(  cff*v(i,Jstr,k,nstp)
     &                        +cx*v(i,Jstr+1,k,nnew)
     &                    -max(cy,0.)*grad(i  ,Jstr)
     &                    -min(cy,0.)*grad(i+1,Jstr)
     &                                    )/(cff+cx)
#    if defined M3_FRC_BRY  || defined M3CLIMATOLOGY
            v(i,Jstr,k,nnew)=(1.-tau)*v(i,Jstr,k,nnew)+tau*
#     ifdef M3_FRC_BRY
     &                                    vbry_south(i,k)
#     else
     &                                     vclm(i,Jstr,k)
#     endif
#    endif
#    ifdef MASKING
            v(i,Jstr,k,nnew)=v(i,Jstr,k,nnew)*vmask(i,Jstr)
#    endif
#    ifdef WET_DRY
            v(i,Jstr,k,nnew)=v(i,Jstr,k,nnew)*vmask_wet(i,Jstr)
#    endif
          enddo
        enddo
#   else
!                                           Southern edge gradient BC
!                                           ======== ==== ======== ==
        do k=1,N
          do i=Istr,Iend
            v(i,Jstr,k,nnew)=v(i,Jstr+1,k,nnew)  ! gradient (default)
#    ifdef MASKING
     &                       *vmask(i,Jstr)
#    endif
#    ifdef WET_DRY
     &                       *vmask_wet(i,Jstr)
#    endif
          enddo
        enddo
#   endif

#  else
        do k=1,N                               ! Southern edge closed
          do i=Istr,Iend                       ! ======== ==== ======
#   ifdef MRL_WCI
            v(i,Jstr,k,nnew)=-vst(i,Jstr,k)    ! no Lagrangian flux
#    ifdef MASKING
     &                       *vmask(i,Jstr)
#    endif
#   else
            v(i,Jstr,k,nnew)=0.                !  (no-flux: default)
#   endif
          enddo
        enddo
#  endif              /* OBC_COM_SOUTH */
      endif         !<-- SOUTHERN_EDGE
!
!====================================================================
!                            NORTHERN BC
!====================================================================
      if (NORTHERN_EDGE) then
#  ifdef OBC_COM_NORTH
#   if defined OBC_COM_M3SPECIFIED || defined OBC_COM_M3SPECIFIED_NORTH
!                                           Northern edge Specified BC
!                                           ======== ==== ========= ==
        do k=1,N
          do i=Istr,Iend
#    ifdef M3_FRC_BRY
            v(i,Jend+1,k,nnew)=vbry_north(i,k)      ! specified
#    else
            v(i,Jend+1,k,nnew)=vclm(i,Jend+1,k)
#    endif
#    ifdef MASKING
     &                         *vmask(i,Jend+1)
#    endif
#    ifdef WET_DRY
     &                         *vmask_wet(i,Jend+1)
#    endif
          enddo
        enddo
!
#   elif defined OBC_COM_M3ORLANSKI
        do k=1,N                            ! Northern edge radiation
          do i=Istr,Iend+1                  ! ======== ==== =========
            grad(i,Jend  )=(v(i,Jend  ,k,nstp)-v(i-1,Jend  ,k,nstp))
#    ifdef MASKING
     &                                                *pmask(i,Jend)
#    endif
            grad(i,Jend+1)=(v(i,Jend+1,k,nstp)-v(i-1,Jend+1,k,nstp))
#    ifdef MASKING
     &                                              *pmask(i,Jend+1)
#    endif
          enddo
          do i=Istr,Iend
            dft=v(i,Jend,k,nstp)-v(i,Jend  ,k,nnew)
            dfx=v(i,Jend,k,nnew)-v(i,Jend-1,k,nnew)
            if (dfx*dft .lt. 0.) then
              dft=0.                       ! <-- cancel cx, if inflow
#    if defined M3_FRC_BRY || defined M3CLIMATOLOGY
              tau=tau_in
            else
              tau=tau_out
#    endif
            endif
            if (dft*(grad(i,Jend)+grad(i+1,Jend)) .gt. 0.) then
              dfy=grad(i,Jend)
            else
              dfy=grad(i+1,Jend)
            endif
            cff=max(dfx*dfx+dfy*dfy, eps)
            cx=dft*dfx
            cy=min(cff,max(dft*dfy,-cff))
            v(i,Jend+1,k,nnew)=(  cff*v(i,Jend+1,k,nstp)
     &                              +cx*v(i,Jend,k,nnew)
     &                      -max(cy,0.)*grad(i  ,Jend+1)
     &                      -min(cy,0.)*grad(i+1,Jend+1)
     &                                        )/(cff+cx)
#    if defined M3_FRC_BRY  || defined M3CLIMATOLOGY
            v(i,Jend+1,k,nnew)=(1.-tau)*v(i,Jend+1,k,nnew)+tau*
#     ifdef M3_FRC_BRY
     &                                         vbry_north(i,k)
#     else
     &                                        vclm(i,Jend+1,k)
#     endif
#    endif
#    ifdef MASKING
            v(i,Jend+1,k,nnew)=v(i,Jend+1,k,nnew)*vmask(i,Jend+1)
#    endif
#    ifdef WET_DRY
            v(i,Jend+1,k,nnew)=v(i,Jend+1,k,nnew)*vmask_wet(i,Jend+1)
#    endif
          enddo
        enddo
!
#   else
        do k=1,N
          do i=Istr,Iend
!                                           Northern edge gradient BC
!                                           ======== ==== ======== ==
            v(i,Jend+1,k,nnew)=v(i,Jend,k,nnew)  ! gradient (default)
#    ifdef MASKING
     &                         *vmask(i,Jend+1)
#    endif
#    ifdef WET_DRY
     &                         *vmask_wet(i,Jend+1)
#    endif
          enddo
        enddo
#   endif

#  else
        do k=1,N                                   ! Northern edge closed
          do i=Istr,Iend                           ! ======== ==== ======
#   ifdef MRL_WCI
            v(i,Jend+1,k,nnew)=-vst(i,Jend+1,k)    ! no Lagrangian flux
#    ifdef MASKING
     &                         *vmask(i,Jend+1)
#    endif
#   else
            v(i,Jend+1,k,nnew)=0.                  !  (no-flux: default)
#   endif
          enddo
        enddo
#  endif
      endif     !<--  NORTHERN_EDGE
# endif          /* !NS_COM_PERIODIC */

# ifndef EW_COM_PERIODIC
!
!====================================================================
!                            WESTERN BC
!====================================================================
      if (WESTERN_EDGE) then
#  ifdef OBC_COM_WEST
#   if defined OBC_COM_M3SPECIFIED || defined OBC_COM_M3SPECIFIED_WEST
!                                            Western edge Specified BC
!                                            ======= ==== ========= ==
        do k=1,N
          do j=JstrV,Jend
#    ifdef M3_FRC_BRY
            v(Istr-1,j,k,nnew)=vbry_west(j,k)       ! specified
#    else
            v(Istr-1,j,k,nnew)=vclm(Istr-1,j,k)
#    endif
#    ifdef MASKING
     &                         *vmask(Istr-1,j)
#    endif
#    ifdef WET_DRY
     &                         *vmask_wet(Istr-1,j)
#    endif
          enddo
        enddo
!
#   elif defined OBC_COM_M3ORLANSKI
        do k=1,N                             ! Western edge radiation
          do j=JstrV-1,Jend                  ! ======= ==== =========
            grad(Istr-1,j)=v(Istr-1,j+1,k,nstp)-v(Istr-1,j,k,nstp)
            grad(Istr  ,j)=v(Istr  ,j+1,k,nstp)-v(Istr  ,j,k,nstp)
          enddo
          do j=JstrV,Jend
            dft=v(Istr,j,k,nstp)-v(Istr  ,j,k,nnew)
            dfx=v(Istr,j,k,nnew)-v(Istr+1,j,k,nnew)
            if (dfx*dft .lt. 0.) then
              dft=0.                       ! <-- cancel cx, if inflow
#    if defined M3_FRC_BRY || defined M3CLIMATOLOGY
              tau=tau_in
            else
              tau=tau_out
#    endif
            endif
            if (dft*(grad(Istr,j-1)+grad(Istr,j)) .gt. 0.) then
              dfy=grad(Istr,j-1)
            else
              dfy=grad(Istr,j  )
            endif
            cff=max(dfx*dfx+dfy*dfy, eps)
            cx=dft*dfx
            cy=min(cff,max(dft*dfy,-cff))
            v(Istr-1,j,k,nnew)=(  cff*v(Istr-1,j,k,nstp)
     &                              +cx*v(Istr,j,k,nnew)
     &                      -max(cy,0.)*grad(Istr-1,j-1)
     &                      -min(cy,0.)*grad(Istr-1,j  )
     &                                        )/(cff+cx)
#    if defined M3_FRC_BRY  || defined M3CLIMATOLOGY
            v(Istr-1,j,k,nnew)=(1.-tau)*v(Istr-1,j,k,nnew)
#     ifdef M3_FRC_BRY
     &                                 +tau*vbry_west(j,k)
#     else
     &                               +tau*vclm(Istr-1,j,k)
#     endif
#    endif
#    ifdef MASKING
            v(Istr-1,j,k,nnew)=v(Istr-1,j,k,nnew)*vmask(Istr-1,j)
#    endif
#    ifdef WET_DRY
            v(Istr-1,j,k,nnew)=v(Istr-1,j,k,nnew)*vmask_wet(Istr-1,j)
#    endif
          enddo
        enddo
!
#   else
!                                            Western edge gradient BC
!                                            ======= ==== ======== ==
        do k=1,N
          do j=JstrV,Jend
            v(Istr-1,j,k,nnew)=v(Istr,j,k,nnew)  ! gradient (default)
#    ifdef MASKING
     &                         *vmask(Istr-1,j)
#    endif
#    ifdef WET_DRY
     &                         *vmask_wet(Istr-1,j)
#    endif
          enddo
        enddo
#   endif
#  else
#   ifdef NS_COM_PERIODIC
#    define J_RANGE JstrV,Jend
#   else
#    define J_RANGE Jstr,JendR
#   endif
        do k=1,N                        ! Wall: free-slip (gamma2=+1)
          do j=J_RANGE                  ! =====   no-slip (gamma2=-1)
            v(Istr-1,j,k,nnew)=gamma2*v(Istr,j,k,nnew)
#   ifdef MASKING
     &                                *vmask(Istr-1,j)
#   endif
          enddo
        enddo
#   undef J_RANGE
#  endif
      endif          !<-- WESTERN_EDGE
!
!====================================================================
!                            EASTERN BC
!====================================================================
      if (EASTERN_EDGE) then
#  ifdef OBC_COM_EAST
#   if defined OBC_COM_M3SPECIFIED || defined OBC_COM_M3SPECIFIED_EAST
!                                            Eastern edge Specified BC
!                                            ======= ==== ========= ==
        do k=1,N
          do j=Jstr,Jend
#    ifdef M3_FRC_BRY
            v(Iend+1,j,k,nnew)=vbry_east(j,k)       ! specified
#    else
            v(Iend+1,j,k,nnew)=vclm(Iend+1,j,k)
#    endif
#    ifdef MASKING
     &                         *vmask(Iend+1,j)
#    endif
#    ifdef WET_DRY
     &                         *vmask_wet(Iend+1,j)
#    endif
          enddo
        enddo
!
#   elif defined OBC_COM_M3ORLANSKI
        do k=1,N                             ! Eastern edge radiation
          do j=JstrV-1,Jend                  ! ======= ==== =========
            grad(Iend  ,j)=v(Iend  ,j+1,k,nstp)-v(Iend  ,j,k,nstp)
            grad(Iend+1,j)=v(Iend+1,j+1,k,nstp)-v(Iend+1,j,k,nstp)
          enddo
          do j=JstrV,Jend
            dft=v(Iend,j,k,nstp)-v(Iend  ,j,k,nnew)
            dfx=v(Iend,j,k,nnew)-v(Iend-1,j,k,nnew)
            if (dfx*dft .lt. 0.) then
              dft=0.                       ! <-- cancel cx, if inflow
#    if defined M3_FRC_BRY || defined M3CLIMATOLOGY
              tau=tau_in
            else
              tau=tau_out
#    endif
            endif
            if (dft*(grad(Iend,j-1)+grad(Iend,j)) .gt. 0.) then
              dfy=grad(Iend,j-1)
            else
              dfy=grad(Iend,j  )
            endif
            cff=max(dfx*dfx+dfy*dfy, eps)
            cx=dft*dfx
            cy=min(cff,max(dft*dfy,-cff))
            v(Iend+1,j,k,nnew)=(  cff*v(Iend+1,j,k,nstp)
     &                              +cx*v(Iend,j,k,nnew)
     &                      -max(cy,0.)*grad(Iend+1,j-1)
     &                      -min(cy,0.)*grad(Iend+1,j  )
     &                                        )/(cff+cx)
#    if defined M3_FRC_BRY  || defined M3CLIMATOLOGY
            v(Iend+1,j,k,nnew)=(1.-tau)*v(Iend+1,j,k,nnew)
#     ifdef M3_FRC_BRY
     &                                 +tau*vbry_east(j,k)
#     else
     &                               +tau*vclm(Iend+1,j,k)
#     endif
#    endif
#    ifdef MASKING
            v(Iend+1,j,k,nnew)=v(Iend+1,j,k,nnew)*vmask(Iend+1,j)
#    endif
#    ifdef WET_DRY
            v(Iend+1,j,k,nnew)=v(Iend+1,j,k,nnew)*vmask_wet(Iend+1,j)
#    endif
          enddo
        enddo
!
#   else
!                                            Eastern edge gradient BC
!                                            ======= ==== ======== ==
        do k=1,N
          do j=Jstr,Jend
            v(Iend+1,j,k,nnew)=v(Iend,j,k,nnew)  ! gradient (default)
#    ifdef MASKING
     &                         *vmask(Iend+1,j)
#    endif
#    ifdef WET_DRY
     &                         *vmask_wet(Iend+1,j)
#    endif
          enddo
        enddo
#   endif
#  else
#   ifdef NS_COM_PERIODIC
#    define J_RANGE JstrV,Jend
#   else
#    define J_RANGE Jstr,JendR
#   endif
        do k=1,N                        ! Wall: free-slip (gamma2=+1)
          do j=J_RANGE                  ! ====    no-slip (gamma2=-1)
            v(Iend+1,j,k,nnew)=gamma2*v(Iend,j,k,nnew)
#   ifdef MASKING
     &                                *vmask(Iend+1,j)
#   endif
          enddo
        enddo
#   undef J_RANGE
#  endif
      endif     !<-- EASTERN_EDGE
# endif          /* !EW_COM_PERIODIC */

                           ! Corners between adjacent open boundaries
                           ! ======= ======= ======== ==== ==========

# if defined OBC_COM_SOUTH && defined OBC_COM_WEST
      if (WESTERN_EDGE .and. SOUTHERN_EDGE) then
        do k=1,N
          v(Istr-1,Jstr,k,nnew)=0.5*( v(Istr-1,Jstr+1,k,nnew)
     &                               +v(Istr  ,Jstr  ,k,nnew))
#  ifdef MASKING
     &                          *vmask(Istr-1,Jstr)
#  endif
        enddo
      endif
# endif
# if defined OBC_COM_SOUTH && defined OBC_COM_EAST
      if (EASTERN_EDGE .and. SOUTHERN_EDGE) then
        do k=1,N
          v(Iend+1,Jstr,k,nnew)=0.5*( v(Iend+1,Jstr+1,k,nnew)
     &                               +v(Iend  ,Jstr  ,k,nnew))
#  ifdef MASKING
     &                          *vmask(Iend+1,Jstr)
#  endif
        enddo
      endif
# endif
# if defined OBC_COM_NORTH && defined OBC_COM_WEST
      if (WESTERN_EDGE .and. NORTHERN_EDGE) then
        do k=1,N
          v(Istr-1,Jend+1,k,nnew)=0.5*( v(Istr-1,Jend,k,nnew)
     &                                 +v(Istr,Jend+1,k,nnew))
#  ifdef MASKING
     &                            *vmask(Istr-1,Jend+1)
#  endif
        enddo
      endif
# endif
# if defined OBC_COM_NORTH && defined OBC_COM_EAST
      if (EASTERN_EDGE .and. NORTHERN_EDGE) then
        do k=1,N
          v(Iend+1,Jend+1,k,nnew)=0.5*( v(Iend+1,Jend,k,nnew)
     &                                 +v(Iend,Jend+1,k,nnew))
#  ifdef MASKING
     &                            *vmask(Iend+1,Jend+1)
#  endif
        enddo
      endif
# endif
!$acc end kernels
      return
      end
#else
      subroutine v3dbc_empty
      end
#endif /* SOLVE3D */
#ifndef CHILD
# define CHILD
# ifdef AGRIF
#  include "v3dbc.F"
# endif
# undef CHILD
#endif  /* !CHILD */

