ice_ocean_stress_flux Subroutine

public subroutine ice_ocean_stress_flux(metrics, stress, ice, grid, bc)

Ice->ocean momentum-mediation blend (PR 5), the momentum mirror of ice_ocean_brine_flux: FULL overwrite each outer step from the pristine wind snapshot + the lagged EVP drag, weighted by ice concentration at each face. a_u(i,j) = 0.5(ci(i-1,j) + ci(i,j)) tau_x(i,j) = (1-a_u)tau_a_x(i,j) + a_u*fxoc(i,j) ditto y (a_v(i,j) = 0.5*(ci(i,j-1)+ci(i,j))). ci is re-gathered via ice_cell_concentration_impl (shared with rdb_ice_evp, so this module never depends on the EVP kernel).

Because this is a full overwrite of tau_x/tau_y, it also OWNS the DERIVED stress_mag (cell-centred |tau|) that KPP and EPBL take u_* from — refreshed on the device immediately after the blend, so the boundary-layer schemes see the ice-mediated stress rather than the configure-time wind. Any future writer of the tau pair inherits the same obligation.

Momentum-budget caveat (F5, D7). This blend gives the ocean (1-a)*tau_a + a*fxoc per face. With &ocean_ice_nml a_face_stress=.false. (default, legacy) the ice absorbs the FULL wind stress tau_a and sheds the FULL drag fxoc — so at fractional cover a in (0,1) the coupled system sees a spurious net input (1-a)*(tau_a - fxoc) per face. It is exact only at a in {0,1} and at steady free drift (fxoc == tau_a); a generic transient at fractional ci (ITD) leaks momentum. &ocean_ice_nml a_face_stress=.true. (PR 62, evp_u_momentum_impl/evp_v_momentum_impl in rdb_ice_evp) CLOSES this identically, for every a and in every transient, by weighting BOTH the wind AND the ice-ocean drag by the same a_u inside the ice momentum balance (this blend needs no code change — it already matches SIS2’s set_ocean_top_stress_Cgrid exactly). Weighting the wind alone — the naive reading of “weight the wind the ICE feels by a_face” — is WORSE than doing nothing: it converts this leak (zero at steady free drift) into a PERMANENT one, -(1-a)*a*tau_a, nonzero at steady state forever. Do not implement that half-measure.

Seam ghosts (X5). With grid + bc present (the engine always passes them) the blended pair goes through ocean_seam_refresh_surface_stress — halo exchange, local periodic wrap on an axis the halo does not own, fold — which also re-derives stress_mag. The blend itself is correct only on the faces this rank owns: a ghost face blends against fxoc = 0 (the EVP momentum writes physical faces only), yet the ocean’s KPP/EPBL/MLE read tau one cell into the halo. The mirror into tau_ocn_x/y is taken AFTER the refresh so a restart resumes the exchanged ghosts the uninterrupted run stepped on. Absent (the single-tile unit-test seam): the legacy stress_mag-only refresh.

Arguments

Type IntentOptional Attributes Name
type(ocean_metrics_t), intent(in) :: metrics
type(ocean_surface_stress_t), intent(inout) :: stress
type(ocean_sea_ice_t), intent(inout) :: ice
type(hgrid_t), intent(in), optional :: grid
type(ocean_bc_state_t), intent(in), optional :: bc

Calls

proc~~ice_ocean_stress_flux~~CallsGraph proc~ice_ocean_stress_flux ice_ocean_stress_flux proc~ice_cell_concentration_impl ice_cell_concentration_impl proc~ice_ocean_stress_flux->proc~ice_cell_concentration_impl proc~ice_ocean_stress_flux_impl ice_ocean_stress_flux_impl proc~ice_ocean_stress_flux->proc~ice_ocean_stress_flux_impl proc~ice_tau_mirror_impl ice_tau_mirror_impl proc~ice_ocean_stress_flux->proc~ice_tau_mirror_impl proc~ocean_seam_refresh_surface_stress ocean_seam_refresh_surface_stress proc~ice_ocean_stress_flux->proc~ocean_seam_refresh_surface_stress proc~ocean_surface_stress_refresh_mag ocean_surface_stress_refresh_mag proc~ice_ocean_stress_flux->proc~ocean_surface_stress_refresh_mag proc~stress_scratch_ensure stress_scratch_ensure proc~ice_ocean_stress_flux->proc~stress_scratch_ensure local local proc~ice_cell_concentration_impl->local proc~ice_ocean_stress_flux_impl->local 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_surfstress_derived_impl ocean_surfstress_derived_impl proc~ocean_surface_stress_refresh_mag->proc~ocean_surfstress_derived_impl proc~ice_ocean_stress_cleanup ice_ocean_stress_cleanup proc~stress_scratch_ensure->proc~ice_ocean_stress_cleanup 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_set_derived->proc~ocean_surface_stress_refresh_mag 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_surfstress_derived_impl->local 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 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 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

Called by

proc~~ice_ocean_stress_flux~~CalledByGraph proc~ice_ocean_stress_flux ice_ocean_stress_flux proc~engine_step_ice engine_step_ice proc~engine_step_ice->proc~ice_ocean_stress_flux proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_step_ice proc~rdb_ocean_step rdb_ocean_step proc~rdb_ocean_step->proc~engine_step_ice proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean

Source Code

   subroutine ice_ocean_stress_flux(metrics, stress, ice, grid, bc)
      !! Ice->ocean momentum-mediation blend (PR 5), the momentum mirror
      !! of `ice_ocean_brine_flux`: FULL overwrite each outer step from
      !! the pristine wind snapshot + the lagged EVP drag, weighted by
      !! ice concentration at each face.
      !!   a_u(i,j) = 0.5*(ci(i-1,j) + ci(i,j))
      !!   tau_x(i,j) = (1-a_u)*tau_a_x(i,j) + a_u*fxoc(i,j)
      !! ditto y (`a_v(i,j) = 0.5*(ci(i,j-1)+ci(i,j))`).  `ci` is
      !! re-gathered via `ice_cell_concentration_impl` (shared with
      !! `rdb_ice_evp`, so this module never depends on the EVP kernel).
      !!
      !! Because this is a full overwrite of `tau_x`/`tau_y`, it also OWNS
      !! the DERIVED `stress_mag` (cell-centred `|tau|`) that KPP and EPBL
      !! take `u_*` from — refreshed on the device immediately after the
      !! blend, so the boundary-layer schemes see the ice-mediated stress
      !! rather than the configure-time wind.  Any future writer of the
      !! `tau` pair inherits the same obligation.
      !!
      !! **Momentum-budget caveat (F5, D7).**  This blend gives the ocean
      !! `(1-a)*tau_a + a*fxoc` per face.  With `&ocean_ice_nml
      !! a_face_stress=.false.` (default, legacy) the ice absorbs the FULL
      !! wind stress `tau_a` and sheds the FULL drag `fxoc` — so at
      !! fractional cover `a in (0,1)` the coupled system sees a spurious
      !! net input `(1-a)*(tau_a - fxoc)` per face.  It is exact only at
      !! `a in {0,1}` and at steady free drift (`fxoc == tau_a`); a
      !! generic transient at fractional `ci` (ITD) leaks momentum.
      !! `&ocean_ice_nml a_face_stress=.true.` (PR 62,
      !! `evp_u_momentum_impl`/`evp_v_momentum_impl` in `rdb_ice_evp`)
      !! CLOSES this identically, for every `a` and in every transient, by
      !! weighting BOTH the wind AND the ice-ocean drag by the same `a_u`
      !! *inside the ice momentum balance* (this blend needs no code
      !! change — it already matches SIS2's `set_ocean_top_stress_Cgrid`
      !! exactly).  Weighting the wind alone — the naive reading of "weight
      !! the wind the ICE feels by `a_face`" — is WORSE than doing nothing:
      !! it converts this leak (zero at steady free drift) into a
      !! PERMANENT one, `-(1-a)*a*tau_a`, nonzero at steady state forever.
      !! Do not implement that half-measure.
      !!
      !! **Seam ghosts (X5).**  With `grid` + `bc` present (the engine
      !! always passes them) the blended pair goes through
      !! `ocean_seam_refresh_surface_stress` — halo exchange, local
      !! periodic wrap on an axis the halo does not own, fold — which also
      !! re-derives `stress_mag`.  The blend itself is correct only on the
      !! faces this rank owns: a ghost face blends against `fxoc = 0`
      !! (the EVP momentum writes physical faces only), yet the ocean's
      !! KPP/EPBL/MLE read `tau` one cell into the halo.  The mirror into
      !! `tau_ocn_x/y` is taken AFTER the refresh so a restart resumes the
      !! exchanged ghosts the uninterrupted run stepped on.  Absent (the
      !! single-tile unit-test seam): the legacy `stress_mag`-only refresh.
      type(ocean_metrics_t), intent(in) :: metrics
      type(ocean_surface_stress_t), intent(inout) :: stress
      type(ocean_sea_ice_t), intent(inout) :: ice
      type(hgrid_t), intent(in), optional :: grid
      type(ocean_bc_state_t), intent(in), optional :: bc

      call stress_scratch_ensure(ice%nx_total, ice%ny_total)
      call ice_cell_concentration_impl(metrics%wet_T, ice%part_size, ice%m_ice, ice%m_snow, &
                                       mis_scratch, mice_scratch, ci_scratch, ice%ncat, &
                                       ice%nx_total, ice%ny_total)
      call ice_ocean_stress_flux_impl(stress%tau_x, stress%tau_y, ice%tau_a_x, ice%tau_a_y, &
                                      ice%fxoc, ice%fyoc, ci_scratch, ice%nx_total, ice%ny_total)
      ! The blend above OVERWROTE `tau_x`/`tau_y`, so the cell-centred
      ! `|tau|` the boundary-layer schemes take `u_* = sqrt(|tau|/rho0)`
      ! from is stale until it is re-derived from the new pair.  KPP
      ! (`rdb_ocean_vmix`) and EPBL (`rdb_ocean_epbl`) read
      ! `stress%stress_mag` and NOTHING else, and no other per-step path
      ! refreshes it (the wind setters run at configure; the data-override
      ! seam refresh runs only under `&ocean_dataovr_nml`), so without this
      ! both schemes mix on the configure-time WIND under ice — identically
      ! zero, and therefore `u_* == 0`, in a windless ice-covered run.
      ! Device-resident and allocation-free.  Reached only through this
      ! routine, which the engine calls only when `&ocean_ice_nml enable`
      ! AND `dynamics` are both on — an ice-free run never executes it and
      ! stays bit-identical.  Gate: `test_ocean_ice_stress_mag`.
      if (present(grid) .and. present(bc)) then
         ! X5: seam ghosts first (exchange -> wrap -> fold), then
         ! `stress_mag` over the full array from the refreshed pair.
         call ocean_seam_refresh_surface_stress(stress, grid, bc)
      else
         call ocean_surface_stress_refresh_mag(stress)
      end if
      ! PR 63: mirror the exact blended value into the restart-carried
      ! tau_ocn_x/y (device copy — ice_tau_mirror_impl is NOT part of the
      ! blend math above, so ice_ocean_stress_flux_impl stays byte-identical
      ! by inspection and the tau_coupling gate is unaffected).  After the
      ! seam refresh, so the checkpoint carries the exchanged ghosts.  The
      ! host scalar write needs no signature change: this routine is
      ! already non-pure (stress_scratch_ensure) with ice intent(inout).
      call ice_tau_mirror_impl(ice%tau_ocn_x, ice%tau_ocn_y, stress%tau_x, stress%tau_y, &
                               ice%nx_total, ice%ny_total)
      ice%tau_ocn_valid = 1.0_wp
   end subroutine ice_ocean_stress_flux