ocean_seam_refresh_surface_stress Subroutine

public subroutine ocean_seam_refresh_surface_stress(ss, grid, bc, device_resident)

Make the surface-stress pair valid in every ghost cell, then re-derive stress_mag from it.

Why this exists. tau_x/tau_y are C-grid face fields that several kernels read ONE CELL BEYOND the cell they write:

  • ocean_surfstress_derived_impl averages tau_x(i)+tau_x(i+1) into the cell-centred stress_mag, which feeds KPP/EPBL u_*.
  • mle_face_ustar_x/y (Fox-Kemper / Bodner) take a 4-point corner average reaching tau_y(i-1, ·) / tau_x(·, j-1).

At an MPI seam those reads land in ghost cells that belong to the neighbour rank, so they MUST come from an exchange. Nothing may extrapolate them: a zero-gradient / edge-copy fill silently substitutes this rank’s edge value for the neighbour’s real data, which is decomposition-dependent and therefore invisible to any single-rank test. (This mirrors the convention in MOM6, which halo-exchanges the stress pair and never extrapolates forcing.)

Order is load-bearing and matches the prognostic-state path in ocean_dyn_step_split: exchange, THEN periodic wrap on any axis the exchange did not own, THEN the north fold. The periodic kernels are skipped on a decomposed axis because the halo already filled those ghosts — running both would overwrite correct neighbour data with a local wrap (see rdb_ocean_periodic).

stress_mag needs no wrap or fold of its own: it is recomputed LAST, over the full array, from a tau pair whose ghosts are by then already valid, so its ghosts come out right for free.

Safe to call before ocean_state_enter_data with device_resident = .false. (the configure-time seed path).

Arguments

Type IntentOptional Attributes Name
type(ocean_surface_stress_t), intent(inout) :: ss

Stress slot whose tau_x/tau_y ghosts are to be filled and whose stress_mag is then refreshed.

type(hgrid_t), intent(in) :: grid
type(ocean_bc_state_t), intent(in) :: bc

Supplies periodic_x/periodic_y/north_fold.

logical, intent(in), optional :: device_resident

Forwarded to the halo primitives; .false. for host-side configure-time calls.


Calls

proc~~ocean_seam_refresh_surface_stress~~CallsGraph proc~ocean_seam_refresh_surface_stress ocean_seam_refresh_surface_stress interface~ocean_halo_face_x ocean_halo_face_x proc~ocean_seam_refresh_surface_stress->interface~ocean_halo_face_x interface~ocean_halo_face_y ocean_halo_face_y proc~ocean_seam_refresh_surface_stress->interface~ocean_halo_face_y proc~ocean_fold_wrap_stress ocean_fold_wrap_stress proc~ocean_seam_refresh_surface_stress->proc~ocean_fold_wrap_stress proc~ocean_halo_is_decomposed_x ocean_halo_is_decomposed_x proc~ocean_seam_refresh_surface_stress->proc~ocean_halo_is_decomposed_x proc~ocean_halo_is_decomposed_y ocean_halo_is_decomposed_y proc~ocean_seam_refresh_surface_stress->proc~ocean_halo_is_decomposed_y proc~ocean_periodic_wrap_face_x_2d ocean_periodic_wrap_face_x_2d proc~ocean_seam_refresh_surface_stress->proc~ocean_periodic_wrap_face_x_2d proc~ocean_periodic_wrap_face_y_2d ocean_periodic_wrap_face_y_2d proc~ocean_seam_refresh_surface_stress->proc~ocean_periodic_wrap_face_y_2d proc~ocean_surface_stress_set_derived ocean_surface_stress_set_derived proc~ocean_seam_refresh_surface_stress->proc~ocean_surface_stress_set_derived proc~oh_count_suppress_off oh_count_suppress_off proc~ocean_seam_refresh_surface_stress->proc~oh_count_suppress_off proc~oh_count_suppress_on oh_count_suppress_on proc~ocean_seam_refresh_surface_stress->proc~oh_count_suppress_on proc~profiler_start profiler_start proc~ocean_seam_refresh_surface_stress->proc~profiler_start proc~profiler_stop profiler_stop proc~ocean_seam_refresh_surface_stress->proc~profiler_stop proc~ocean_halo_face_x_2d ocean_halo_face_x_2d interface~ocean_halo_face_x->proc~ocean_halo_face_x_2d proc~ocean_halo_face_x_3d ocean_halo_face_x_3d interface~ocean_halo_face_x->proc~ocean_halo_face_x_3d proc~ocean_halo_face_y_2d ocean_halo_face_y_2d interface~ocean_halo_face_y->proc~ocean_halo_face_y_2d proc~ocean_halo_face_y_3d ocean_halo_face_y_3d interface~ocean_halo_face_y->proc~ocean_halo_face_y_3d interface~fold_north_u_face fold_north_u_face proc~ocean_fold_wrap_stress->interface~fold_north_u_face interface~fold_north_v_face fold_north_v_face proc~ocean_fold_wrap_stress->interface~fold_north_v_face interface~ocean_fold_pack ocean_fold_pack proc~ocean_fold_wrap_stress->interface~ocean_fold_pack interface~ocean_fold_unpack ocean_fold_unpack proc~ocean_fold_wrap_stress->interface~ocean_fold_unpack proc~ocean_fold_begin ocean_fold_begin proc~ocean_fold_wrap_stress->proc~ocean_fold_begin proc~ocean_fold_end ocean_fold_end proc~ocean_fold_wrap_stress->proc~ocean_fold_end proc~ocean_fold_exchange ocean_fold_exchange proc~ocean_fold_wrap_stress->proc~ocean_fold_exchange proc~ocean_fold_is_distributed ocean_fold_is_distributed proc~ocean_fold_wrap_stress->proc~ocean_fold_is_distributed proc~ocean_surface_stress_apply_cover ocean_surface_stress_apply_cover proc~ocean_surface_stress_set_derived->proc~ocean_surface_stress_apply_cover proc~ocean_surface_stress_refresh_mag ocean_surface_stress_refresh_mag proc~ocean_surface_stress_set_derived->proc~ocean_surface_stress_refresh_mag proc~find_or_create_region find_or_create_region proc~profiler_start->proc~find_or_create_region proc~get_wall_time get_wall_time proc~profiler_start->proc~get_wall_time proc~nvtx_range_push nvtx_range_push proc~profiler_start->proc~nvtx_range_push proc~profiler_stop->proc~get_wall_time proc~nvtx_range_pop nvtx_range_pop proc~profiler_stop->proc~nvtx_range_pop proc~fold_north_u_face_2d fold_north_u_face_2d interface~fold_north_u_face->proc~fold_north_u_face_2d proc~fold_north_u_face_3d fold_north_u_face_3d interface~fold_north_u_face->proc~fold_north_u_face_3d proc~fold_north_v_face_2d fold_north_v_face_2d interface~fold_north_v_face->proc~fold_north_v_face_2d proc~fold_north_v_face_3d fold_north_v_face_3d interface~fold_north_v_face->proc~fold_north_v_face_3d proc~ocean_fold_pack_2d ocean_fold_pack_2d interface~ocean_fold_pack->proc~ocean_fold_pack_2d proc~ocean_fold_pack_3d ocean_fold_pack_3d interface~ocean_fold_pack->proc~ocean_fold_pack_3d proc~ocean_fold_unpack_2d ocean_fold_unpack_2d interface~ocean_fold_unpack->proc~ocean_fold_unpack_2d proc~ocean_fold_unpack_3d ocean_fold_unpack_3d interface~ocean_fold_unpack->proc~ocean_fold_unpack_3d proc~grow_buffers grow_buffers proc~ocean_fold_begin->proc~grow_buffers to_string to_string proc~ocean_fold_begin->to_string warning warning proc~ocean_fold_begin->warning comm_irecv_real_sp_array_n comm_irecv_real_sp_array_n proc~ocean_fold_exchange->comm_irecv_real_sp_array_n comm_isend_real_sp_array_n comm_isend_real_sp_array_n proc~ocean_fold_exchange->comm_isend_real_sp_array_n proc~comm_env_compute_comm comm_env_compute_comm proc~ocean_fold_exchange->proc~comm_env_compute_comm waitall waitall proc~ocean_fold_exchange->waitall proc~ocean_halo_face_x_2d_impl ocean_halo_face_x_2d_impl proc~ocean_halo_face_x_2d->proc~ocean_halo_face_x_2d_impl proc~oh_count_face_x_2d oh_count_face_x_2d proc~ocean_halo_face_x_2d->proc~oh_count_face_x_2d proc~ocean_halo_face_x_3d->comm_irecv_real_sp_array_n proc~ocean_halo_face_x_3d->comm_isend_real_sp_array_n proc~ocean_halo_face_x_3d->proc~comm_env_compute_comm proc~ew_rank_east ew_rank_east proc~ocean_halo_face_x_3d->proc~ew_rank_east proc~ew_rank_west ew_rank_west proc~ocean_halo_face_x_3d->proc~ew_rank_west proc~needs_flags needs_flags proc~ocean_halo_face_x_3d->proc~needs_flags proc~ns_rank_north ns_rank_north proc~ocean_halo_face_x_3d->proc~ns_rank_north proc~ns_rank_south ns_rank_south proc~ocean_halo_face_x_3d->proc~ns_rank_south proc~ocean_halo_buffers_ensure_nz ocean_halo_buffers_ensure_nz proc~ocean_halo_face_x_3d->proc~ocean_halo_buffers_ensure_nz proc~ocean_periodic_wrap_face_x_3d ocean_periodic_wrap_face_x_3d proc~ocean_halo_face_x_3d->proc~ocean_periodic_wrap_face_x_3d proc~oh_count_face_x_3d oh_count_face_x_3d proc~ocean_halo_face_x_3d->proc~oh_count_face_x_3d proc~oh_count_msgs oh_count_msgs proc~ocean_halo_face_x_3d->proc~oh_count_msgs proc~ocean_halo_face_x_3d->waitall proc~ocean_halo_face_y_2d_impl ocean_halo_face_y_2d_impl proc~ocean_halo_face_y_2d->proc~ocean_halo_face_y_2d_impl proc~oh_count_face_y_2d oh_count_face_y_2d proc~ocean_halo_face_y_2d->proc~oh_count_face_y_2d proc~ocean_halo_face_y_3d->comm_irecv_real_sp_array_n proc~ocean_halo_face_y_3d->comm_isend_real_sp_array_n proc~ocean_halo_face_y_3d->proc~comm_env_compute_comm proc~ocean_halo_face_y_3d->proc~ew_rank_east proc~ocean_halo_face_y_3d->proc~ew_rank_west proc~ocean_halo_face_y_3d->proc~needs_flags proc~ocean_halo_face_y_3d->proc~ns_rank_north proc~ocean_halo_face_y_3d->proc~ns_rank_south proc~ocean_halo_face_y_3d->proc~ocean_halo_buffers_ensure_nz proc~ocean_periodic_wrap_face_y_3d ocean_periodic_wrap_face_y_3d proc~ocean_halo_face_y_3d->proc~ocean_periodic_wrap_face_y_3d proc~oh_count_face_y_3d oh_count_face_y_3d proc~ocean_halo_face_y_3d->proc~oh_count_face_y_3d proc~ocean_halo_face_y_3d->proc~oh_count_msgs proc~ocean_halo_face_y_3d->waitall proc~ocean_surface_stress_apply_cover->proc~ocean_surface_stress_refresh_mag proc~ocean_surfstress_cover_impl ocean_surfstress_cover_impl proc~ocean_surface_stress_apply_cover->proc~ocean_surfstress_cover_impl proc~ocean_surfstress_derived_impl ocean_surfstress_derived_impl proc~ocean_surface_stress_refresh_mag->proc~ocean_surfstress_derived_impl comm_world comm_world proc~comm_env_compute_comm->comm_world proc~decomp_rank_from_coords decomp_rank_from_coords proc~ew_rank_east->proc~decomp_rank_from_coords proc~ew_rank_west->proc~decomp_rank_from_coords local local proc~fold_north_v_face_2d->local proc~fold_north_v_face_3d->local proc~ns_rank_north->proc~decomp_rank_from_coords proc~ns_rank_south->proc~decomp_rank_from_coords proc~ocean_fold_pack_2d->proc~ocean_fold_pack_3d proc~fold_stagger_family fold_stagger_family proc~ocean_fold_pack_3d->proc~fold_stagger_family proc~fold_stagger_nrows fold_stagger_nrows proc~ocean_fold_pack_3d->proc~fold_stagger_nrows proc~ocean_fold_unpack_2d->proc~ocean_fold_unpack_3d proc~ocean_fold_unpack_3d->proc~fold_stagger_family proc~ocean_fold_unpack_3d->proc~fold_stagger_nrows proc~ocean_halo_buffers_ensure_nz->to_string proc~ocean_halo_buffers_ensure_nz->warning proc~ocean_halo_face_x_2d_impl->proc~ocean_periodic_wrap_face_x_2d proc~ocean_halo_face_x_2d_impl->comm_irecv_real_sp_array_n proc~ocean_halo_face_x_2d_impl->comm_isend_real_sp_array_n proc~ocean_halo_face_x_2d_impl->proc~comm_env_compute_comm proc~ocean_halo_face_x_2d_impl->proc~ew_rank_east proc~ocean_halo_face_x_2d_impl->proc~ew_rank_west proc~ocean_halo_face_x_2d_impl->proc~needs_flags proc~ocean_halo_face_x_2d_impl->proc~ns_rank_north proc~ocean_halo_face_x_2d_impl->proc~ns_rank_south proc~ocean_halo_face_x_2d_impl->proc~oh_count_msgs proc~ocean_halo_face_x_2d_impl->waitall proc~ocean_halo_face_y_2d_impl->proc~ocean_periodic_wrap_face_y_2d proc~ocean_halo_face_y_2d_impl->comm_irecv_real_sp_array_n proc~ocean_halo_face_y_2d_impl->comm_isend_real_sp_array_n proc~ocean_halo_face_y_2d_impl->proc~comm_env_compute_comm proc~ocean_halo_face_y_2d_impl->proc~ew_rank_east proc~ocean_halo_face_y_2d_impl->proc~ew_rank_west proc~ocean_halo_face_y_2d_impl->proc~needs_flags proc~ocean_halo_face_y_2d_impl->proc~ns_rank_north proc~ocean_halo_face_y_2d_impl->proc~ns_rank_south proc~ocean_halo_face_y_2d_impl->proc~oh_count_msgs proc~ocean_halo_face_y_2d_impl->waitall proc~ocean_surfstress_cover_impl->local proc~ocean_surfstress_derived_impl->local

Called by

proc~~ocean_seam_refresh_surface_stress~~CalledByGraph proc~ocean_seam_refresh_surface_stress ocean_seam_refresh_surface_stress proc~configure_ocean_forcing configure_ocean_forcing proc~configure_ocean_forcing->proc~ocean_seam_refresh_surface_stress proc~ice_ocean_stress_flux ice_ocean_stress_flux proc~ice_ocean_stress_flux->proc~ocean_seam_refresh_surface_stress proc~ocean_data_forcing_apply ocean_data_forcing_apply proc~ocean_data_forcing_apply->proc~ocean_seam_refresh_surface_stress proc~ocean_data_forcing_configure ocean_data_forcing_configure proc~ocean_data_forcing_configure->proc~ocean_seam_refresh_surface_stress proc~engine_setup engine_setup proc~engine_setup->proc~configure_ocean_forcing proc~engine_setup->proc~ocean_data_forcing_configure proc~engine_step engine_step proc~engine_step->proc~ocean_data_forcing_apply proc~engine_step_ice engine_step_ice proc~engine_step_ice->proc~ice_ocean_stress_flux proc~complete_ocean_create complete_ocean_create proc~complete_ocean_create->proc~engine_setup proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_setup proc~driver_run_ocean->proc~engine_step proc~driver_run_ocean->proc~engine_step_ice proc~driver_validate driver_validate proc~driver_validate->proc~engine_setup proc~rdb_ocean_step rdb_ocean_step proc~rdb_ocean_step->proc~engine_step proc~rdb_ocean_step->proc~engine_step_ice proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean proc~rdb_ocean_create_finalize rdb_ocean_create_finalize proc~rdb_ocean_create_finalize->proc~complete_ocean_create proc~rdb_ocean_create_from_string rdb_ocean_create_from_string proc~rdb_ocean_create_from_string->proc~complete_ocean_create

Variables

Type Visibility Attributes Name Initial
integer, private :: ng
integer, private :: nxp
integer, private :: nxt
integer, private :: nyp
integer, private :: nyt
logical, private :: wrap_x
logical, private :: wrap_y

Source Code

   subroutine ocean_seam_refresh_surface_stress(ss, grid, bc, device_resident)
      !! Make the surface-stress pair valid in every ghost cell, then
      !! re-derive `stress_mag` from it.
      !!
      !! **Why this exists.**  `tau_x`/`tau_y` are C-grid face fields that
      !! several kernels read ONE CELL BEYOND the cell they write:
      !!
      !!   * `ocean_surfstress_derived_impl` averages `tau_x(i)`+`tau_x(i+1)`
      !!     into the cell-centred `stress_mag`, which feeds KPP/EPBL `u_*`.
      !!   * `mle_face_ustar_x/y` (Fox-Kemper / Bodner) take a 4-point
      !!     corner average reaching `tau_y(i-1, ·)` / `tau_x(·, j-1)`.
      !!
      !! At an MPI seam those reads land in ghost cells that belong to the
      !! neighbour rank, so they MUST come from an exchange.  Nothing may
      !! extrapolate them: a zero-gradient / edge-copy fill silently
      !! substitutes this rank's edge value for the neighbour's real data,
      !! which is decomposition-dependent and therefore invisible to any
      !! single-rank test.  (This mirrors the convention in MOM6, which
      !! halo-exchanges the stress pair and never extrapolates forcing.)
      !!
      !! **Order is load-bearing** and matches the prognostic-state path in
      !! `ocean_dyn_step_split`: exchange, THEN periodic wrap on any axis
      !! the exchange did not own, THEN the north fold.  The periodic
      !! kernels are skipped on a decomposed axis because the halo already
      !! filled those ghosts — running both would overwrite correct
      !! neighbour data with a local wrap (see `rdb_ocean_periodic`).
      !!
      !! `stress_mag` needs no wrap or fold of its own: it is recomputed
      !! LAST, over the full array, from a `tau` pair whose ghosts are by
      !! then already valid, so its ghosts come out right for free.
      !!
      !! Safe to call before `ocean_state_enter_data` with
      !! `device_resident = .false.` (the configure-time seed path).
      type(ocean_surface_stress_t), intent(inout) :: ss
         !! Stress slot whose `tau_x`/`tau_y` ghosts are to be filled and
         !! whose `stress_mag` is then refreshed.
      type(hgrid_t), intent(in) :: grid
      type(ocean_bc_state_t), intent(in) :: bc
         !! Supplies `periodic_x`/`periodic_y`/`north_fold`.
      logical, intent(in), optional :: device_resident
         !! Forwarded to the halo primitives; `.false.` for host-side
         !! configure-time calls.

      integer :: nxt, nyt, nxp, nyp, ng
      logical :: wrap_x, wrap_y

      if (.not. allocated(ss%tau_x) .or. .not. allocated(ss%tau_y)) return

      nxt = grid%nx_total
      nyt = grid%ny_total
      nxp = grid%nx_phys
      nyp = grid%ny_phys
      ng = grid%nghost

      call profiler_start("ocean_comms_stress")
      call oh_count_suppress_on()
      call ocean_halo_face_x(ss%tau_x, device_resident)
      call ocean_halo_face_y(ss%tau_y, device_resident)
      call oh_count_suppress_off()
      call profiler_stop("ocean_comms_stress")

      ! Local periodic wrap only on an axis the halo did NOT own.
      wrap_x = bc%periodic_x .and. (.not. ocean_halo_is_decomposed_x())
      wrap_y = bc%periodic_y .and. (.not. ocean_halo_is_decomposed_y())
      if (wrap_x .or. wrap_y) then
         call ocean_periodic_wrap_face_x_2d(ss%tau_x, nxt + 1, nyt, &
                                            nxp, nyp, ng, wrap_x, wrap_y)
         call ocean_periodic_wrap_face_y_2d(ss%tau_y, nxt, nyt + 1, &
                                            nxp, nyp, ng, wrap_x, wrap_y)
      end if

      ! Tripolar north fold.  `tau_x`/`tau_y` are TRUE VECTOR components,
      ! so the sign-flipping u/v-face variants are the correct ones (the
      ! scalar-copy duplicates in `rdb_ocean_metrics` exist precisely
      ! because those are NOT vectors).  px = 1: the local kernels; px > 1:
      ! one owner-routed exchange group (`ocean_fold_wrap_stress`).
      if (bc%north_fold) call ocean_fold_wrap_stress(grid, bc, ss%tau_x, ss%tau_y, device_resident)

      call ocean_surface_stress_set_derived(grid, ss)
   end subroutine ocean_seam_refresh_surface_stress