ocean_poison_ghost_bands Subroutine

public subroutine ocean_poison_ghost_bands(grid, ms, bt_work, ss, poison_w, poison_e, poison_s, poison_n)

Sentinel-NaN the exchange-covered ghost bands of all multilayer prognostic fields and the BT workstate fields. Called at the TOP of each outer step (before any exchange or kernel) so that any kernel consuming an unexchanged ghost produces a loud NaN at the offending step.

poison_w/e/s/n encode the band-selection rule: pass (.not. bc%has_*) .or. bc%periodic_*. When all four are .false. (e.g. a single-rank all-wall run with no periodic axes) this routine is a no-op — every ghost is BC-owned. Do not allocate anything: device-resident state arrays are written via plain do concurrent band-fill kernels, identical to the periodic-wrap pattern.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid

Horizontal grid (carries nx_total, ny_total, nx_phys, ny_phys, nghost).

type(multilayer_state_t), intent(inout) :: ms

Multilayer C-grid state whose exchange-covered ghost bands are poisoned.

type(barotropic_workstate_t), intent(inout) :: bt_work

BT workstate whose exchange-covered ghost bands are poisoned.

type(ocean_surface_stress_t), intent(inout) :: ss

Surface-stress slot. Its tau_x/tau_y bands became exchange-owned with the forcing-halo fix, so they are poisoned here too — see the block at the end of the body.

logical, intent(in) :: poison_w

Poison west ghost band. .true. iff the west edge is exchange-owned (MPI seam or periodic-x).

logical, intent(in) :: poison_e

Poison east ghost band. .true. iff the east edge is exchange-owned.

logical, intent(in) :: poison_s

Poison south ghost band. .true. iff the south edge is exchange-owned.

logical, intent(in) :: poison_n

Poison north ghost band. .true. iff the north edge is exchange-owned.


Calls

proc~~ocean_poison_ghost_bands~~CallsGraph proc~ocean_poison_ghost_bands ocean_poison_ghost_bands proc~poison_centre_2d poison_centre_2d proc~ocean_poison_ghost_bands->proc~poison_centre_2d proc~poison_centre_3d poison_centre_3d proc~ocean_poison_ghost_bands->proc~poison_centre_3d proc~poison_face_x_2d poison_face_x_2d proc~ocean_poison_ghost_bands->proc~poison_face_x_2d proc~poison_face_x_3d poison_face_x_3d proc~ocean_poison_ghost_bands->proc~poison_face_x_3d proc~poison_face_y_2d poison_face_y_2d proc~ocean_poison_ghost_bands->proc~poison_face_y_2d proc~poison_face_y_3d poison_face_y_3d proc~ocean_poison_ghost_bands->proc~poison_face_y_3d

Called by

proc~~ocean_poison_ghost_bands~~CalledByGraph proc~ocean_poison_ghost_bands ocean_poison_ghost_bands proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~ocean_poison_ghost_bands proc~engine_step engine_step proc~engine_step->proc~ocean_dyn_step_split proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_step proc~rdb_ocean_step rdb_ocean_step proc~rdb_ocean_step->proc~engine_step proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean

Variables

Type Visibility Attributes Name Initial
integer, private :: it
integer, private :: ng
integer, private :: nxl
integer, private :: nyl
integer, private :: nzt
real(kind=wp), private :: qnan

Source Code

   subroutine ocean_poison_ghost_bands(grid, ms, bt_work, ss, &
                                       poison_w, poison_e, poison_s, poison_n)
      !! Sentinel-NaN the exchange-covered ghost bands of all multilayer
      !! prognostic fields and the BT workstate fields.  Called at the TOP
      !! of each outer step (before any exchange or kernel) so that any
      !! kernel consuming an unexchanged ghost produces a loud NaN at the
      !! offending step.
      !!
      !! `poison_w/e/s/n` encode the band-selection rule: pass
      !! `(.not. bc%has_*) .or. bc%periodic_*`.
      !! When all four are `.false.` (e.g. a single-rank all-wall run with no
      !! periodic axes) this routine is a no-op — every ghost is BC-owned.
      !! Do not allocate anything: device-resident state arrays are written
      !! via plain `do concurrent` band-fill kernels, identical to the
      !! periodic-wrap pattern.
      type(hgrid_t), intent(in) :: grid
         !! Horizontal grid (carries nx_total, ny_total, nx_phys, ny_phys, nghost).
      type(multilayer_state_t), intent(inout) :: ms
         !! Multilayer C-grid state whose exchange-covered ghost bands are poisoned.
      type(barotropic_workstate_t), intent(inout) :: bt_work
         !! BT workstate whose exchange-covered ghost bands are poisoned.
      type(ocean_surface_stress_t), intent(inout) :: ss
         !! Surface-stress slot.  Its `tau_x`/`tau_y` bands became
         !! exchange-owned with the forcing-halo fix, so they are poisoned
         !! here too — see the block at the end of the body.
      logical, intent(in) :: poison_w
         !! Poison west ghost band.  `.true.` iff the west edge is exchange-owned
         !! (MPI seam or periodic-x).
      logical, intent(in) :: poison_e
         !! Poison east ghost band.  `.true.` iff the east edge is exchange-owned.
      logical, intent(in) :: poison_s
         !! Poison south ghost band.  `.true.` iff the south edge is exchange-owned.
      logical, intent(in) :: poison_n
         !! Poison north ghost band.  `.true.` iff the north edge is exchange-owned.

      integer :: nxl, nyl, ng, nzt, it
      real(wp) :: qnan

      nxl = grid%nx_phys
      nyl = grid%ny_phys
      ng = grid%nghost
      nzt = ms%nz_ml

      ! Compute the sentinel value on the host — ieee_value is a host intrinsic.
      ! Pass the scalar into the do-concurrent kernels as a local value (device-safe:
      ! plain IEEE representation, no intrinsic call inside the DC body).
      qnan = ieee_value(0.0_wp, ieee_quiet_nan)

      ! ------------------------------------------------------------------
      ! ML h_layer: centre (nx_total, ny_total, nz_ml)
      ! ------------------------------------------------------------------
      call poison_centre_3d(ms%h_layer, nxl, nyl, ng, nzt, qnan, &
                            poison_w, poison_e, poison_s, poison_n)

      ! ------------------------------------------------------------------
      ! ML u_face_x_layer: face-x (nx_total+1, ny_total, nz_ml)
      ! ------------------------------------------------------------------
      call poison_face_x_3d(ms%u_face_x_layer, nxl, nyl, ng, nzt, qnan, &
                            poison_w, poison_e, poison_s, poison_n)

      ! ------------------------------------------------------------------
      ! ML v_face_y_layer: face-y (nx_total, ny_total+1, nz_ml)
      ! ------------------------------------------------------------------
      call poison_face_y_3d(ms%v_face_y_layer, nxl, nyl, ng, nzt, qnan, &
                            poison_w, poison_e, poison_s, poison_n)

      ! ------------------------------------------------------------------
      ! Tracers: per-tracer hTr — centre (nx_total, ny_total, nz_ml).
      ! Outer-shim rule: array-of-DT cannot be dereferenced on-device;
      ! the loop is on the HOST, each element passed by explicit-shape helper.
      ! ------------------------------------------------------------------
      if (allocated(ms%tracers)) then
         do it = 1, size(ms%tracers)
            if (allocated(ms%tracers(it)%hTr)) then
               call poison_centre_3d(ms%tracers(it)%hTr, nxl, nyl, ng, nzt, qnan, &
                                     poison_w, poison_e, poison_s, poison_n)
            end if
         end do
      end if

      ! ------------------------------------------------------------------
      ! BT workstate: bt_eta (centre), bt_ubt (face-x), bt_vbt (face-y).
      ! These are re-derived/exchanged early in each stage.  Poisoning
      ! them proves the derive/coupling+exchange chain covers the bands.
      ! Guard with allocated() — the BT workstate is only allocated when
      ! ocean_dyn_init has been called with nz_ml present (split path).
      ! ------------------------------------------------------------------
      if (allocated(bt_work%bt_eta)) then
         call poison_centre_2d(bt_work%bt_eta, nxl, nyl, ng, qnan, &
                               poison_w, poison_e, poison_s, poison_n)
      end if
      if (allocated(bt_work%bt_ubt)) then
         call poison_face_x_2d(bt_work%bt_ubt, nxl, nyl, ng, qnan, &
                               poison_w, poison_e, poison_s, poison_n)
      end if
      if (allocated(bt_work%bt_vbt)) then
         call poison_face_y_2d(bt_work%bt_vbt, nxl, nyl, ng, qnan, &
                               poison_w, poison_e, poison_s, poison_n)
      end if

      ! ------------------------------------------------------------------
      ! Surface stress (face-x / face-y).  Exchange-owned as of the
      ! forcing-halo fix: `ocean_seam_refresh_surface_stress` fills these
      ! bands at configure and after every file blend.  Poisoning them is
      ! what proves that — several kernels read one cell beyond their own
      ! (`stress_mag` face-to-centre average feeding KPP/EPBL, and the MLE
      ! corner average), so an unexchanged band shows up as NaN rather
      ! than as a quiet decomposition-dependent answer.
      ! ------------------------------------------------------------------
      if (allocated(ss%tau_x)) then
         call poison_face_x_2d(ss%tau_x, nxl, nyl, ng, qnan, &
                               poison_w, poison_e, poison_s, poison_n)
      end if
      if (allocated(ss%tau_y)) then
         call poison_face_y_2d(ss%tau_y, nxl, nyl, ng, qnan, &
                               poison_w, poison_e, poison_s, poison_n)
      end if

   end subroutine ocean_poison_ghost_bands