pure subroutine drain_swept_flux_x(nx, ny, nz, areaT, uhh, hprev, aL, aR, a6, F)
!! MOM6 swept-average CW parabola flux for the zonal faces.
!! Face i, donor = cell (i-1) if uhh>0 else cell (i). Per-pass
!! Courant CFL = |uhh| / (areaT·hprev) on the donor, clamped [0,1].
!! uhh >= 0: F = uhh·( aR − 0.5·CFL·((aR−aL) − a6·(1 − ⅔·CFL)) )
!! uhh < 0: F = uhh·( aL + 0.5·CFL·((aR−aL) + a6·(1 − ⅔·CFL)) )
integer, intent(in) :: nx, ny, nz
real(wp), intent(in) :: areaT(nx, ny)
real(wp), intent(in) :: uhh(nx + 1, ny, nz)
real(wp), intent(in) :: hprev(nx, ny, nz)
real(wp), intent(in) :: aL(nx, ny, nz), aR(nx, ny, nz), a6(nx, ny, nz)
real(wp), intent(inout) :: F(nx + 1, ny, nz)
integer :: i, j, k
real(wp) :: u, cfl, vol, conc
! Array-edge faces (1, nx+1) have no donor cell -- the u>0 donor at
! face 1 is cell 0 and the u<0 donor at face nx+1 is cell nx+1, both
! off the [1,nx] cell arrays. They feed only ghost cells the caller
! re-wraps, so set them to zero. Explicit do concurrent (not the
! F(1,:,:)=0 array-section assignment, which does not reliably
! offload under stdpar).
do concurrent(k=1:nz, j=1:ny)
F(1, j, k) = 0.0_wp
F(nx + 1, j, k) = 0.0_wp
end do
! Interior faces 2..nx: the u>0 donor i-1 >= 1 and the u<0 donor i
! <= nx are both in range. (Was i=1:nx+1, which read cell 0 / nx+1
! at the edge faces -- a latent OOB that only faults once the array
! is page-aligned at large grids.)
do concurrent(k=1:nz, j=1:ny, i=2:nx) local(u, cfl, vol, conc)
u = uhh(i, j, k)
if (u > 0.0_wp) then
vol = max(areaT(i - 1, j)*hprev(i - 1, j, k), DRAIN_MIN_VOL)
cfl = min(u/vol, 1.0_wp)
conc = aR(i - 1, j, k) - 0.5_wp*cfl* &
((aR(i - 1, j, k) - aL(i - 1, j, k)) &
- a6(i - 1, j, k)*(1.0_wp - (2.0_wp/3.0_wp)*cfl))
F(i, j, k) = u*conc
else if (u < 0.0_wp) then
vol = max(areaT(i, j)*hprev(i, j, k), DRAIN_MIN_VOL)
cfl = min(-u/vol, 1.0_wp)
conc = aL(i, j, k) + 0.5_wp*cfl* &
((aR(i, j, k) - aL(i, j, k)) &
+ a6(i, j, k)*(1.0_wp - (2.0_wp/3.0_wp)*cfl))
F(i, j, k) = u*conc
else
F(i, j, k) = 0.0_wp
end if
end do
end subroutine drain_swept_flux_x