tracer_advect_one_impl Subroutine

private pure subroutine tracer_advect_one_impl(nx, ny, nz, dt, iareaT, wet_T, h, hTr, mass_flux_x, mass_flux_y, Tr_face_left_x, Tr_face_right_x, Tr_face_left_y, Tr_face_right_y)

One-tracer PPM advection. Flat-impl: takes bare 3D arrays (no derived-type deref inside do-concurrent), so NVHPC stdpar handles it cleanly even for tracers stored in an array-of-derived-types registry.

Three passes per direction: 1. PPM reconstruction on Tr = hTr/h (Colella-Woodward monotonic limiter) — same helpers as continuity-PPM. Stores left/right face states in the scratch buffers. 2. Upwind pick using mass_flux sign — overwrites the same scratch buffer with mass_flux * Tr_face (the tracer mass flux at each face). 3. Flux divergence + forward-Euler update of hTr.

Wall faces are gated by mass_flux (= 0 at walls from continuity), so tracer flux through walls is identically zero — no special wall handling needed in this kernel.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: iareaT(nx,ny)
real(kind=wp), intent(in) :: wet_T(nx,ny)
real(kind=wp), intent(in) :: h(nx,ny,nz)
real(kind=wp), intent(inout) :: hTr(nx,ny,nz)
real(kind=wp), intent(in) :: mass_flux_x(nx+1,ny,nz)
real(kind=wp), intent(in) :: mass_flux_y(nx,ny+1,nz)
real(kind=wp), intent(inout) :: Tr_face_left_x(nx+1,ny,nz)
real(kind=wp), intent(inout) :: Tr_face_right_x(nx+1,ny,nz)
real(kind=wp), intent(inout) :: Tr_face_left_y(nx,ny+1,nz)
real(kind=wp), intent(inout) :: Tr_face_right_y(nx,ny+1,nz)

Calls

proc~~tracer_advect_one_impl~~CallsGraph proc~tracer_advect_one_impl tracer_advect_one_impl local local proc~tracer_advect_one_impl->local proc~ppm_cell_limiter ppm_cell_limiter proc~tracer_advect_one_impl->proc~ppm_cell_limiter proc~ppm_limited_slope ppm_limited_slope proc~tracer_advect_one_impl->proc~ppm_limited_slope proc~ppm_mirror_h ppm_mirror_h proc~tracer_advect_one_impl->proc~ppm_mirror_h

Called by

proc~~tracer_advect_one_impl~~CalledByGraph proc~tracer_advect_one_impl tracer_advect_one_impl proc~tracer_advect tracer_advect proc~tracer_advect->proc~tracer_advect_one_impl

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private :: Tr_0
real(kind=wp), private :: Tr_face
real(kind=wp), private :: Tr_left
real(kind=wp), private :: Tr_m1
real(kind=wp), private :: Tr_m2
real(kind=wp), private :: Tr_p1
real(kind=wp), private :: Tr_p2
real(kind=wp), private :: Tr_right
real(kind=wp), private :: dh_0
real(kind=wp), private :: dh_m1
real(kind=wp), private :: dh_p1
integer, private :: i
integer, private :: j
integer, private :: k
real(kind=wp), private :: mass_x
real(kind=wp), private :: mass_y

Source Code

   pure subroutine tracer_advect_one_impl(nx, ny, nz, dt, iareaT, wet_T, h, hTr, &
                                          mass_flux_x, mass_flux_y, &
                                          Tr_face_left_x, Tr_face_right_x, &
                                          Tr_face_left_y, Tr_face_right_y)
      !! One-tracer PPM advection.  Flat-impl: takes bare 3D arrays
      !! (no derived-type deref inside do-concurrent), so NVHPC
      !! stdpar handles it cleanly even for tracers stored in an
      !! array-of-derived-types registry.
      !!
      !! Three passes per direction:
      !!   1. PPM reconstruction on Tr = hTr/h (Colella-Woodward
      !!      monotonic limiter) — same helpers as continuity-PPM.
      !!      Stores left/right face states in the scratch buffers.
      !!   2. Upwind pick using mass_flux sign — overwrites the same
      !!      scratch buffer with mass_flux * Tr_face (the tracer
      !!      mass flux at each face).
      !!   3. Flux divergence + forward-Euler update of hTr.
      !!
      !! Wall faces are gated by mass_flux (= 0 at walls from
      !! continuity), so tracer flux through walls is identically
      !! zero — no special wall handling needed in this kernel.
      integer, intent(in) :: nx, ny, nz
      real(wp), intent(in) :: dt
      real(wp), intent(in) :: iareaT(nx, ny)
      real(wp), intent(in) :: wet_T(nx, ny)
      real(wp), intent(in) :: h(nx, ny, nz)
      real(wp), intent(inout) :: hTr(nx, ny, nz)
      real(wp), intent(in) :: mass_flux_x(nx + 1, ny, nz)
      real(wp), intent(in) :: mass_flux_y(nx, ny + 1, nz)
      real(wp), intent(inout) :: Tr_face_left_x(nx + 1, ny, nz)
      real(wp), intent(inout) :: Tr_face_right_x(nx + 1, ny, nz)
      real(wp), intent(inout) :: Tr_face_left_y(nx, ny + 1, nz)
      real(wp), intent(inout) :: Tr_face_right_y(nx, ny + 1, nz)

      integer :: i, j, k
      real(wp) :: Tr_m2, Tr_m1, Tr_0, Tr_p1, Tr_p2
      real(wp) :: dh_m1, dh_0, dh_p1, Tr_left, Tr_right
      real(wp) :: mass_x, mass_y, Tr_face

      ! ===== X-direction PPM reconstruction =====
      ! Interior cells with full 5-point stencil.  Mirror-T at land
      ! neighbours (C2); bit-identical for all-wet.
      do concurrent(k=1:nz, j=1:ny, i=3:nx - 2) &
         local(Tr_m2, Tr_m1, Tr_0, Tr_p1, Tr_p2, &
               dh_m1, dh_0, dh_p1, Tr_left, Tr_right)
         Tr_0 = hTr(i, j, k)/h(i, j, k)
         Tr_m1 = ppm_mirror_h(hTr(i - 1, j, k)/h(i - 1, j, k), Tr_0, wet_T(i - 1, j))
         Tr_p1 = ppm_mirror_h(hTr(i + 1, j, k)/h(i + 1, j, k), Tr_0, wet_T(i + 1, j))
         Tr_m2 = ppm_mirror_h(hTr(i - 2, j, k)/h(i - 2, j, k), Tr_m1, wet_T(i - 2, j))
         Tr_p2 = ppm_mirror_h(hTr(i + 2, j, k)/h(i + 2, j, k), Tr_p1, wet_T(i + 2, j))
         call ppm_limited_slope(Tr_m2, Tr_m1, Tr_0, dh_m1)
         call ppm_limited_slope(Tr_m1, Tr_0, Tr_p1, dh_0)
         call ppm_limited_slope(Tr_0, Tr_p1, Tr_p2, dh_p1)
         dh_0 = dh_0*wet_T(i - 1, j)*wet_T(i, j)*wet_T(i + 1, j)
         Tr_left = 0.5_wp*(Tr_m1 + Tr_0) - (dh_0 - dh_m1)/6.0_wp
         Tr_right = 0.5_wp*(Tr_0 + Tr_p1) - (dh_p1 - dh_0)/6.0_wp
         call ppm_cell_limiter(Tr_0, Tr_left, Tr_right)
         Tr_face_right_x(i, j, k) = Tr_left
         Tr_face_left_x(i + 1, j, k) = Tr_right
      end do
      ! Boundary cells (i=1, 2, nx-1, nx): 1st-order fallback (face
      ! value = abutting cell's concentration).  Mass flux is zero
      ! at i=1 and i=nx+1 walls anyway, so the wall faces' Tr value
      ! doesn't enter the divergence.
      do concurrent(k=1:nz, j=1:ny)
         Tr_face_left_x(1, j, k) = hTr(1, j, k)/h(1, j, k)
         Tr_face_right_x(1, j, k) = hTr(1, j, k)/h(1, j, k)
         Tr_face_left_x(2, j, k) = hTr(1, j, k)/h(1, j, k)
         Tr_face_right_x(2, j, k) = hTr(2, j, k)/h(2, j, k)
         ! Face 3: cell 2's 5-point stencil is short, so its right
         ! edge falls back to 1st order — match continuity's fix.
         Tr_face_left_x(3, j, k) = hTr(2, j, k)/h(2, j, k)
         Tr_face_right_x(3, j, k) = hTr(2, j, k)/h(2, j, k)
         ! Face nx-1: mirror of face 3.
         Tr_face_right_x(nx - 1, j, k) = hTr(nx - 1, j, k)/h(nx - 1, j, k)
         Tr_face_left_x(nx, j, k) = hTr(nx - 1, j, k)/h(nx - 1, j, k)
         Tr_face_right_x(nx, j, k) = hTr(nx, j, k)/h(nx, j, k)
         Tr_face_left_x(nx + 1, j, k) = hTr(nx, j, k)/h(nx, j, k)
         Tr_face_right_x(nx + 1, j, k) = hTr(nx, j, k)/h(nx, j, k)
      end do

      ! Upwind pick at east faces -> overwrite Tr_face_left_x with
      ! the per-face tracer mass flux (mass_flux_x * Tr_face_upwind).
      do concurrent(k=1:nz, j=1:ny, i=1:nx + 1) local(mass_x, Tr_face)
         mass_x = mass_flux_x(i, j, k)
         if (mass_x >= 0.0_wp) then
            Tr_face = Tr_face_left_x(i, j, k)
         else
            Tr_face = Tr_face_right_x(i, j, k)
         end if
         Tr_face_left_x(i, j, k) = mass_x*Tr_face
      end do

      ! ===== Y-direction PPM reconstruction =====
      do concurrent(k=1:nz, j=3:ny - 2, i=1:nx) &
         local(Tr_m2, Tr_m1, Tr_0, Tr_p1, Tr_p2, &
               dh_m1, dh_0, dh_p1, Tr_left, Tr_right)
         Tr_0 = hTr(i, j, k)/h(i, j, k)
         Tr_m1 = ppm_mirror_h(hTr(i, j - 1, k)/h(i, j - 1, k), Tr_0, wet_T(i, j - 1))
         Tr_p1 = ppm_mirror_h(hTr(i, j + 1, k)/h(i, j + 1, k), Tr_0, wet_T(i, j + 1))
         Tr_m2 = ppm_mirror_h(hTr(i, j - 2, k)/h(i, j - 2, k), Tr_m1, wet_T(i, j - 2))
         Tr_p2 = ppm_mirror_h(hTr(i, j + 2, k)/h(i, j + 2, k), Tr_p1, wet_T(i, j + 2))
         call ppm_limited_slope(Tr_m2, Tr_m1, Tr_0, dh_m1)
         call ppm_limited_slope(Tr_m1, Tr_0, Tr_p1, dh_0)
         call ppm_limited_slope(Tr_0, Tr_p1, Tr_p2, dh_p1)
         dh_0 = dh_0*wet_T(i, j - 1)*wet_T(i, j)*wet_T(i, j + 1)
         Tr_left = 0.5_wp*(Tr_m1 + Tr_0) - (dh_0 - dh_m1)/6.0_wp
         Tr_right = 0.5_wp*(Tr_0 + Tr_p1) - (dh_p1 - dh_0)/6.0_wp
         call ppm_cell_limiter(Tr_0, Tr_left, Tr_right)
         Tr_face_right_y(i, j, k) = Tr_left
         Tr_face_left_y(i, j + 1, k) = Tr_right
      end do
      do concurrent(k=1:nz, i=1:nx)
         Tr_face_left_y(i, 1, k) = hTr(i, 1, k)/h(i, 1, k)
         Tr_face_right_y(i, 1, k) = hTr(i, 1, k)/h(i, 1, k)
         Tr_face_left_y(i, 2, k) = hTr(i, 1, k)/h(i, 1, k)
         Tr_face_right_y(i, 2, k) = hTr(i, 2, k)/h(i, 2, k)
         Tr_face_left_y(i, 3, k) = hTr(i, 2, k)/h(i, 2, k)
         Tr_face_right_y(i, 3, k) = hTr(i, 2, k)/h(i, 2, k)
         Tr_face_right_y(i, ny - 1, k) = hTr(i, ny - 1, k)/h(i, ny - 1, k)
         Tr_face_left_y(i, ny, k) = hTr(i, ny - 1, k)/h(i, ny - 1, k)
         Tr_face_right_y(i, ny, k) = hTr(i, ny, k)/h(i, ny, k)
         Tr_face_left_y(i, ny + 1, k) = hTr(i, ny, k)/h(i, ny, k)
         Tr_face_right_y(i, ny + 1, k) = hTr(i, ny, k)/h(i, ny, k)
      end do

      ! Upwind pick at north faces
      do concurrent(k=1:nz, j=1:ny + 1, i=1:nx) local(mass_y, Tr_face)
         mass_y = mass_flux_y(i, j, k)
         if (mass_y >= 0.0_wp) then
            Tr_face = Tr_face_left_y(i, j, k)
         else
            Tr_face = Tr_face_right_y(i, j, k)
         end if
         Tr_face_left_y(i, j, k) = mass_y*Tr_face
      end do

      ! ===== Apply: forward-Euler hTr update (transport div · iareaT) =====
      do concurrent(k=1:nz, j=1:ny, i=1:nx)
         hTr(i, j, k) = hTr(i, j, k) - dt*( &
                        (Tr_face_left_x(i + 1, j, k) - Tr_face_left_x(i, j, k)) + &
                        (Tr_face_left_y(i, j + 1, k) - Tr_face_left_y(i, j, k)))*iareaT(i, j)
      end do
   end subroutine tracer_advect_one_impl