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 | Intent | Optional | 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 |
||
| logical, | intent(in) | :: | poison_w |
Poison west ghost band. |
||
| logical, | intent(in) | :: | poison_e |
Poison east ghost band. |
||
| logical, | intent(in) | :: | poison_s |
Poison south ghost band. |
||
| logical, | intent(in) | :: | poison_n |
Poison north ghost band. |
| 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 |
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