ocean_halo_exchange_ice_state Subroutine

public subroutine ocean_halo_exchange_ice_state(ice, grid, bc, device_resident)

Make the sea-ice CATEGORY state valid in every ghost cell (X1 of the sea-ice MPI plan): part_size, m_ice, m_snow, enth_ice, sal_ice, enth_snow, one two-pass centre exchange each, all categories (and ice layers) in one message per direction, THEN the tripolar north fold of each (added with the fold-seam fix below).

Why. Nothing inside the ice step refreshes these ghosts — the column thermodynamics, ITD and (PR 4b) transport compress write PHYSICAL cells only — yet three consumers read them one cell into the halo: the EVP’s category gather (mis/mice/ci over the full array, then strength, face mass and corner ratios), and the stress coupler’s face concentration a_u = (ci(i-1)+ci(i))/2 at the west/south-most owned face. On one rank with a periodic axis the primitives’ local wrap closes the seam the same way (the pre-existing “D7” stale-ghost note in rdb_ice_evp).

The fold. On a north = 'tripolar_fold' grid the MPI/periodic exchange above fills every ghost EXCEPT the north cap: the fold seam needs its own 180-degree-rotated mirror (rdb_ocean_fold), which ocean_halo_centre knows nothing about. Before this fix the north-fold ghost band of every one of these fields was stale (uninitialised / previous-step), so the ITD/transport readers one cell into it on the fold-seam row effectively saw an unrelated cell — the root cause of unbounded ice growth on the seam row (a mass source with no physical origin). Every field here is a per-category SCALAR (mass, enthalpy, salinity, fractional area), never a vector, so the fold is always the negate=.false. copy contract (ocean_fold_wrap_centre_flat).

When. At the end of every thermo block (the category state changes only there) and once at cold-start configure, host-side, BEFORE enter_data and NEVER on a warm restart (the checkpoint carries the writer’s ghosts; re-deriving them resumed a different state, 8e1931f20). Single-rank non-periodic: a no-op. Requires ocean_halo_init; the caller skips it otherwise. The fold itself is gated independently on bc%north_fold and no-ops on any non-tripolar grid.

The rank-4 enth_ice/sal_ice/enth_snow and the 0:ncat part_size are contiguous and go out as one flat nz each, through an explicit-shape seam (ice_halo_centre_flat), never the aggregate ice (CLAUDE.md: component arrays only).

Arguments

Type IntentOptional Attributes Name
type(ocean_sea_ice_t), intent(inout) :: ice

Live sea-ice slot (ice%is_init); a no-op otherwise.

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

Supplies north_fold + the grid metrics the fold kernels need.

logical, intent(in), optional :: device_resident

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


Calls

proc~~ocean_halo_exchange_ice_state~~CallsGraph proc~ocean_halo_exchange_ice_state ocean_halo_exchange_ice_state proc~ice_halo_centre_flat ice_halo_centre_flat proc~ocean_halo_exchange_ice_state->proc~ice_halo_centre_flat proc~ocean_fold_wrap_centre_flat ocean_fold_wrap_centre_flat proc~ocean_halo_exchange_ice_state->proc~ocean_fold_wrap_centre_flat proc~oh_count_suppress_off oh_count_suppress_off proc~ocean_halo_exchange_ice_state->proc~oh_count_suppress_off proc~oh_count_suppress_on oh_count_suppress_on proc~ocean_halo_exchange_ice_state->proc~oh_count_suppress_on proc~profiler_start profiler_start proc~ocean_halo_exchange_ice_state->proc~profiler_start proc~profiler_stop profiler_stop proc~ocean_halo_exchange_ice_state->proc~profiler_stop interface~ocean_halo_centre ocean_halo_centre proc~ice_halo_centre_flat->interface~ocean_halo_centre interface~fold_north_centre fold_north_centre proc~ocean_fold_wrap_centre_flat->interface~fold_north_centre interface~ocean_fold_pack ocean_fold_pack proc~ocean_fold_wrap_centre_flat->interface~ocean_fold_pack interface~ocean_fold_unpack ocean_fold_unpack proc~ocean_fold_wrap_centre_flat->interface~ocean_fold_unpack proc~ocean_fold_begin ocean_fold_begin proc~ocean_fold_wrap_centre_flat->proc~ocean_fold_begin proc~ocean_fold_end ocean_fold_end proc~ocean_fold_wrap_centre_flat->proc~ocean_fold_end proc~ocean_fold_exchange ocean_fold_exchange proc~ocean_fold_wrap_centre_flat->proc~ocean_fold_exchange proc~ocean_fold_is_distributed ocean_fold_is_distributed proc~ocean_fold_wrap_centre_flat->proc~ocean_fold_is_distributed 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_centre_2d fold_north_centre_2d interface~fold_north_centre->proc~fold_north_centre_2d proc~fold_north_centre_3d fold_north_centre_3d interface~fold_north_centre->proc~fold_north_centre_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~ocean_halo_centre_2d ocean_halo_centre_2d interface~ocean_halo_centre->proc~ocean_halo_centre_2d proc~ocean_halo_centre_3d ocean_halo_centre_3d interface~ocean_halo_centre->proc~ocean_halo_centre_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 comm_world comm_world proc~comm_env_compute_comm->comm_world 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_centre_2d_impl ocean_halo_centre_2d_impl proc~ocean_halo_centre_2d->proc~ocean_halo_centre_2d_impl proc~oh_count_centre_2d oh_count_centre_2d proc~ocean_halo_centre_2d->proc~oh_count_centre_2d proc~ocean_halo_centre_3d->comm_irecv_real_sp_array_n proc~ocean_halo_centre_3d->comm_isend_real_sp_array_n proc~ocean_halo_centre_3d->proc~comm_env_compute_comm proc~ocean_halo_centre_3d->waitall proc~ew_rank_east ew_rank_east proc~ocean_halo_centre_3d->proc~ew_rank_east proc~ew_rank_west ew_rank_west proc~ocean_halo_centre_3d->proc~ew_rank_west proc~needs_flags needs_flags proc~ocean_halo_centre_3d->proc~needs_flags proc~ns_rank_north ns_rank_north proc~ocean_halo_centre_3d->proc~ns_rank_north proc~ns_rank_south ns_rank_south proc~ocean_halo_centre_3d->proc~ns_rank_south proc~ocean_halo_buffers_ensure_nz ocean_halo_buffers_ensure_nz proc~ocean_halo_centre_3d->proc~ocean_halo_buffers_ensure_nz proc~ocean_periodic_wrap_centre_3d ocean_periodic_wrap_centre_3d proc~ocean_halo_centre_3d->proc~ocean_periodic_wrap_centre_3d proc~oh_count_centre_3d oh_count_centre_3d proc~ocean_halo_centre_3d->proc~oh_count_centre_3d proc~oh_count_msgs oh_count_msgs proc~ocean_halo_centre_3d->proc~oh_count_msgs 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~ns_rank_north->proc~decomp_rank_from_coords proc~ns_rank_south->proc~decomp_rank_from_coords proc~ocean_halo_buffers_ensure_nz->to_string proc~ocean_halo_buffers_ensure_nz->warning proc~ocean_halo_centre_2d_impl->comm_irecv_real_sp_array_n proc~ocean_halo_centre_2d_impl->comm_isend_real_sp_array_n proc~ocean_halo_centre_2d_impl->proc~comm_env_compute_comm proc~ocean_halo_centre_2d_impl->waitall proc~ocean_halo_centre_2d_impl->proc~ew_rank_east proc~ocean_halo_centre_2d_impl->proc~ew_rank_west proc~ocean_halo_centre_2d_impl->proc~needs_flags proc~ocean_halo_centre_2d_impl->proc~ns_rank_north proc~ocean_halo_centre_2d_impl->proc~ns_rank_south proc~ocean_halo_centre_2d_impl->proc~oh_count_msgs proc~ocean_periodic_wrap_centre_2d ocean_periodic_wrap_centre_2d proc~ocean_halo_centre_2d_impl->proc~ocean_periodic_wrap_centre_2d

Called by

proc~~ocean_halo_exchange_ice_state~~CalledByGraph proc~ocean_halo_exchange_ice_state ocean_halo_exchange_ice_state proc~engine_setup engine_setup proc~engine_setup->proc~ocean_halo_exchange_ice_state proc~engine_step_ice engine_step_ice proc~engine_step_ice->proc~ocean_halo_exchange_ice_state 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_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_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 :: nxt
integer, private :: nyt

Source Code

   subroutine ocean_halo_exchange_ice_state(ice, grid, bc, device_resident)
      !! Make the sea-ice CATEGORY state valid in every ghost cell (X1 of
      !! the sea-ice MPI plan): `part_size`, `m_ice`, `m_snow`,
      !! `enth_ice`, `sal_ice`, `enth_snow`, one two-pass centre exchange
      !! each, all categories (and ice layers) in one message per
      !! direction, THEN the tripolar north fold of each (added with the
      !! fold-seam fix below).
      !!
      !! **Why.**  Nothing inside the ice step refreshes these ghosts — the
      !! column thermodynamics, ITD and (PR 4b) transport compress write
      !! PHYSICAL cells only — yet three consumers read them one cell into
      !! the halo: the EVP's category gather (`mis`/`mice`/`ci` over the
      !! full array, then strength, face mass and corner ratios), and the
      !! stress coupler's face concentration `a_u = (ci(i-1)+ci(i))/2` at
      !! the west/south-most owned face.  On one rank with a periodic axis
      !! the primitives' local wrap closes the seam the same way (the
      !! pre-existing "D7" stale-ghost note in `rdb_ice_evp`).
      !!
      !! **The fold.**  On a `north = 'tripolar_fold'` grid the MPI/periodic
      !! exchange above fills every ghost EXCEPT the north cap: the fold
      !! seam needs its own 180-degree-rotated mirror (`rdb_ocean_fold`),
      !! which `ocean_halo_centre` knows nothing about.  Before this fix
      !! the north-fold ghost band of every one of these fields was stale
      !! (uninitialised / previous-step), so the ITD/transport readers one
      !! cell into it on the fold-seam row effectively saw an unrelated
      !! cell — the root cause of unbounded ice growth on the seam row
      !! (a mass source with no physical origin). Every field here is a
      !! per-category SCALAR (mass, enthalpy, salinity, fractional area),
      !! never a vector, so the fold is always the `negate=.false.` copy
      !! contract (`ocean_fold_wrap_centre_flat`).
      !!
      !! **When.**  At the end of every thermo block (the category state
      !! changes only there) and once at cold-start configure, host-side,
      !! BEFORE `enter_data` and NEVER on a warm restart (the checkpoint
      !! carries the writer's ghosts; re-deriving them resumed a different
      !! state, `8e1931f20`).  Single-rank non-periodic: a no-op.  Requires
      !! `ocean_halo_init`; the caller skips it otherwise.  The fold itself
      !! is gated independently on `bc%north_fold` and no-ops on any
      !! non-tripolar grid.
      !!
      !! The rank-4 `enth_ice`/`sal_ice`/`enth_snow` and the `0:ncat`
      !! `part_size` are contiguous and go out as one flat `nz` each,
      !! through an explicit-shape seam (`ice_halo_centre_flat`), never
      !! the aggregate `ice` (CLAUDE.md: component arrays only).
      type(ocean_sea_ice_t), intent(inout) :: ice
         !! Live sea-ice slot (`ice%is_init`); a no-op otherwise.
      type(hgrid_t), intent(in) :: grid
      type(ocean_bc_state_t), intent(in) :: bc
         !! Supplies `north_fold` + the grid metrics the fold kernels need.
      logical, intent(in), optional :: device_resident
         !! Forwarded to the halo primitives; `.false.` for the host-side
         !! configure-time call.

      integer :: nxt, nyt

      if (.not. ice%is_init) return
      nxt = ice%nx_total
      nyt = ice%ny_total

      call profiler_start("ice_comms_state")
      call oh_count_suppress_on()
      call ice_halo_centre_flat(ice%part_size, nxt, nyt, ice%ncat + 1, device_resident)
      call ice_halo_centre_flat(ice%m_ice, nxt, nyt, ice%ncat, device_resident)
      call ice_halo_centre_flat(ice%m_snow, nxt, nyt, ice%ncat, device_resident)
      call ice_halo_centre_flat(ice%enth_ice, nxt, nyt, ice%ncat*ice%nk_ice, device_resident)
      call ice_halo_centre_flat(ice%sal_ice, nxt, nyt, ice%ncat*ice%nk_ice, device_resident)
      call ice_halo_centre_flat(ice%enth_snow, nxt, nyt, ice%ncat, device_resident)
      call oh_count_suppress_off()
      call profiler_stop("ice_comms_state")

      ! Tripolar north fold.  Every field above is a per-category
      ! SCALAR (mass / enthalpy / salinity / fractional area), never a
      ! vector, so each fold is the plain copy (`negate=.false.`)
      ! contract — see `ocean_fold_wrap_centre_flat`.  No-op off a
      ! tripolar grid (`bc%north_fold = .false.`).
      if (bc%north_fold) then
         call ocean_fold_wrap_centre_flat(grid, bc, ice%part_size, nxt, nyt, &
                                          ice%ncat + 1, device_resident)
         call ocean_fold_wrap_centre_flat(grid, bc, ice%m_ice, nxt, nyt, &
                                          ice%ncat, device_resident)
         call ocean_fold_wrap_centre_flat(grid, bc, ice%m_snow, nxt, nyt, &
                                          ice%ncat, device_resident)
         call ocean_fold_wrap_centre_flat(grid, bc, ice%enth_ice, nxt, nyt, &
                                          ice%ncat*ice%nk_ice, device_resident)
         call ocean_fold_wrap_centre_flat(grid, bc, ice%sal_ice, nxt, nyt, &
                                          ice%ncat*ice%nk_ice, device_resident)
         call ocean_fold_wrap_centre_flat(grid, bc, ice%enth_snow, nxt, nyt, &
                                          ice%ncat, device_resident)
      end if
   end subroutine ocean_halo_exchange_ice_state