configure_ocean_land_mask Subroutine

public subroutine configure_ocean_land_mask(cfg, ocean_state, grid, compute_rank, warm_restart)

Derive the static C-grid land masks from the seeded T-cell wet_mask and zero the 6 face metrics at land faces (metrics_apply_land_mask). Run AFTER configure_ocean_metrics (the metrics + inverses must exist) AND configure_ocean_bc (the periodic / fold flags drive the halo-aware mask derivation), but BEFORE ocean_state_enter_data (the host edit is what the GPU copyin captures).

For a domain with no land (wet_mask≡1, the flat-bottom / analytical path) every mask is 1.0 and the metric multiply is a literal no-op ⇒ bit-identical to a no-mask build — except that a face of ZERO width (dy_cu/dx_cv = 0, a node-aligned tripolar cap’s pole columns) is closed even between wet cells. This call is the only place that closure happens, so every geometry path must run it.

Arguments

Type IntentOptional Attributes Name
type(config_t), intent(in) :: cfg
type(ocean_state_t), intent(inout) :: ocean_state
type(hgrid_t), intent(in) :: grid
integer, intent(in) :: compute_rank
logical, intent(in), optional :: warm_restart

.true. when the prognostic state was just read from a checkpoint: the land-state seed below is then SKIPPED (the masks and metrics are still derived). Absent ⇒ cold start.


Calls

proc~~configure_ocean_land_mask~~CallsGraph proc~configure_ocean_land_mask configure_ocean_land_mask interface~ocean_halo_centre ocean_halo_centre proc~configure_ocean_land_mask->interface~ocean_halo_centre proc~metrics_apply_land_mask metrics_apply_land_mask proc~configure_ocean_land_mask->proc~metrics_apply_land_mask proc~ocean_bc_outer_face_tag ocean_bc_outer_face_tag proc~configure_ocean_land_mask->proc~ocean_bc_outer_face_tag proc~ocean_fold_is_distributed ocean_fold_is_distributed proc~configure_ocean_land_mask->proc~ocean_fold_is_distributed proc~ocean_fold_wrap_eta_2d ocean_fold_wrap_eta_2d proc~configure_ocean_land_mask->proc~ocean_fold_wrap_eta_2d proc~ocean_halo_is_decomposed ocean_halo_is_decomposed proc~configure_ocean_land_mask->proc~ocean_halo_is_decomposed proc~ocean_halo_is_decomposed_x ocean_halo_is_decomposed_x proc~configure_ocean_land_mask->proc~ocean_halo_is_decomposed_x proc~ocean_halo_is_decomposed_y ocean_halo_is_decomposed_y proc~configure_ocean_land_mask->proc~ocean_halo_is_decomposed_y proc~ocean_state_seed_land_cells ocean_state_seed_land_cells proc~configure_ocean_land_mask->proc~ocean_state_seed_land_cells 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 interface~fold_north_centre fold_north_centre proc~metrics_apply_land_mask->interface~fold_north_centre proc~metrics_periodic_x_2d metrics_periodic_x_2d proc~metrics_apply_land_mask->proc~metrics_periodic_x_2d proc~metrics_periodic_y_2d metrics_periodic_y_2d proc~metrics_apply_land_mask->proc~metrics_periodic_y_2d proc~ocean_fold_wrap_eta_2d->proc~ocean_fold_is_distributed proc~ocean_fold_wrap_eta_2d->interface~fold_north_centre interface~ocean_fold_pack ocean_fold_pack proc~ocean_fold_wrap_eta_2d->interface~ocean_fold_pack interface~ocean_fold_unpack ocean_fold_unpack proc~ocean_fold_wrap_eta_2d->interface~ocean_fold_unpack proc~ocean_fold_begin ocean_fold_begin proc~ocean_fold_wrap_eta_2d->proc~ocean_fold_begin proc~ocean_fold_end ocean_fold_end proc~ocean_fold_wrap_eta_2d->proc~ocean_fold_end proc~ocean_fold_exchange ocean_fold_exchange proc~ocean_fold_wrap_eta_2d->proc~ocean_fold_exchange proc~multilayer_enforce_vanished_content_host multilayer_state_t%multilayer_enforce_vanished_content_host proc~ocean_state_seed_land_cells->proc~multilayer_enforce_vanished_content_host proc~seed_land_face_vel_2d_impl seed_land_face_vel_2d_impl proc~ocean_state_seed_land_cells->proc~seed_land_face_vel_2d_impl proc~seed_land_face_vel_impl seed_land_face_vel_impl proc~ocean_state_seed_land_cells->proc~seed_land_face_vel_impl proc~seed_land_h_floor_impl seed_land_h_floor_impl proc~ocean_state_seed_land_cells->proc~seed_land_h_floor_impl proc~seed_land_tracer_hold_impl seed_land_tracer_hold_impl proc~ocean_state_seed_land_cells->proc~seed_land_tracer_hold_impl 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 rdb_vl_merge_content rdb_vl_merge_content proc~multilayer_enforce_vanished_content_host->rdb_vl_merge_content 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_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~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~ocean_halo_centre_3d->waitall 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~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_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->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_halo_centre_2d_impl->waitall 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~~configure_ocean_land_mask~~CalledByGraph proc~configure_ocean_land_mask configure_ocean_land_mask proc~engine_setup engine_setup proc~engine_setup->proc~configure_ocean_land_mask 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_validate driver_validate proc~driver_validate->proc~engine_setup 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
logical, private :: seed_land

Source Code

   subroutine configure_ocean_land_mask(cfg, ocean_state, grid, compute_rank, warm_restart)
      !! Derive the static C-grid land masks from the seeded T-cell
      !! `wet_mask` and zero the 6 face metrics at land faces
      !! (`metrics_apply_land_mask`).  Run AFTER `configure_ocean_metrics`
      !! (the metrics + inverses must exist) AND `configure_ocean_bc` (the
      !! periodic / fold flags drive the halo-aware mask derivation), but
      !! BEFORE `ocean_state_enter_data` (the host edit is what the GPU
      !! copyin captures).
      !!
      !! For a domain with no land (`wet_mask≡1`, the flat-bottom /
      !! analytical path) every mask is 1.0 and the metric multiply is a
      !! literal no-op ⇒ bit-identical to a no-mask build — except that a
      !! face of ZERO width (`dy_cu`/`dx_cv = 0`, a node-aligned tripolar
      !! cap's pole columns) is closed even between wet cells.  This call
      !! is the only place that closure happens, so every geometry path
      !! must run it.
      type(config_t), intent(in) :: cfg
      type(ocean_state_t), intent(inout) :: ocean_state
      type(hgrid_t), intent(in) :: grid
      integer, intent(in) :: compute_rank
      logical, intent(in), optional :: warm_restart
         !! `.true.` when the prognostic state was just read from a
         !! checkpoint: the land-state seed below is then SKIPPED (the
         !! masks and metrics are still derived).  Absent ⇒ cold start.

      logical :: seed_land

      ! Multi-rank seam ghost fill of wet_mask (O3 land x decomp): a subdomain
      ! seam that bisects a continent needs the NEIGHBOUR rank's wet_T in the
      ! seam ghost columns so the C-grid face/corner masks derived inside
      ! metrics_apply_land_mask close the seam faces correctly.  Done HERE (the
      ! caller) rather than inside metrics_apply_land_mask because importing the
      ! comm-layer rdb_ocean_halo into the low-level rdb_ocean_metrics leaf
      ! creates an NVFORTRAN USE cycle (config -> lateral_mix -> metrics).
      ! ocean_halo_init has already run (driver orders it before this call).
      ! Gated on ocean_halo_is_DECOMPOSED (px>1 or py>1) — a genuine
      ! cross-rank seam is the ONLY case wet_mask needs a message: the
      ! periodic ghost wrap is done locally inside metrics_apply_land_mask,
      ! and a single-rank non-periodic run's ghosts are domain-boundary
      ! walls.  Gating on the seam (not periodicity / not merely is_init)
      ! also keeps the MPI runtime untouched in non-MPI ocean unit tests
      ! that init the halo via this setup path but never call MPI_Init
      ! (those are px=py=1 ⇒ skip).  Runs host-side before enter_data; a
      ! GPU+MPI init-time host-halo variant is an O3+ follow-up.
      if (ocean_halo_is_decomposed()) then
         call ocean_halo_centre(ocean_state%multilayer%wet_mask, device_resident=.false.)
      end if
      ! Distributed tripolar fold (px > 1, plan site S2):
      ! `metrics_apply_land_mask` folds its working copy with the LOCAL
      ! kernel, which is exact only on a tile holding the whole fold row.
      ! Fold the STORED wet mask here instead, through the owner-routed
      ! exchange (collective over the north rank row, host mode), and tell
      ! the land mask not to fold again.  Folding the stored mask, not a
      ! copy, also closes the seed-time gap for it: the seed derives
      ! `wet_mask` elementwise from the water column, whose north ghosts
      ! the single-rank seed folded first (`seed_wrap_static_2d`) and the
      ! px > 1 seed could not, so the stored ghosts now equal the serial
      ! run's bit for bit (an elementwise map commutes with the fold copy).
      if (ocean_fold_is_distributed()) then
         call ocean_fold_wrap_eta_2d(grid, ocean_state%bc, ocean_state%multilayer%wet_mask, &
                                     device_resident=.false.)
      end if

      ! Solid-wall velocity masking (&ocean_bc_nml mask_wall_velocity):
      ! thread the per-edge WALL flags so only genuine solid walls get their
      ! ghost wet_T zeroed (periodic + open/OBC edges keep their ghosts).
      !
      ! The `has_*` conjunct is load-bearing under MPI.  `bc_type` is the
      ! GLOBAL edge tag, identical on every rank; `has_*` is the per-rank
      ! "I actually sit on that domain edge" flag (set from decomp).
      ! Without the AND, every rank zeroed its own west/east/south/north
      ! ghost band, so an interior MPI SEAM was masked as land: wet_u at
      ! the seam face went to 0 and the six masked face metrics with it.
      ! Measured on double_gyre_mom6 (44x40, 10 days, wall basin): px=1
      ! gave En 3.036E-03 / MaxCFL 0.01000, px=2 gave 2.998E-03 / 0.01197
      ! — and MaxCFL is a max reduction, which is exactly order-invariant,
      ! so that spread could not have been reduction roundoff.  With the
      ! conjunct, px=1 and px=2 agree.
      ! The periodic flags select a LOCAL wrap of the working wet mask's
      ! ghosts, which is right only on an axis this rank holds whole: on a
      ! decomposed periodic axis the wrap ghosts are an MPI seam the
      ! exchange above already filled, and a local wrap overwrote them with
      ! the tile's own opposite edge (every rank of a px >= 3 periodic run
      ! with land near a seam then masked the wrong ghost faces; the
      ! decomposed-vs-serial bit-identity test caught it on 4x1).  The wall
      ! flags below carry the edge tags, so nothing else reads these two.
      call metrics_apply_land_mask(ocean_state%metrics, &
                                   ocean_state%multilayer%wet_mask, grid, &
                                   ocean_state%bc%periodic_x .and. .not. ocean_halo_is_decomposed_x(), &
                                   ocean_state%bc%periodic_y .and. .not. ocean_halo_is_decomposed_y(), &
                                   ocean_state%bc%north_fold .and. .not. ocean_fold_is_distributed(), &
                                   mask_wall_velocity=cfg%ocean%bc%mask_wall_velocity, &
                                   wall_west=(ocean_bc_outer_face_tag(ocean_state%bc%west%bc_type) == OBC_WALL &
                                              .and. ocean_state%bc%has_west), &
                                   wall_east=(ocean_bc_outer_face_tag(ocean_state%bc%east%bc_type) == OBC_WALL &
                                              .and. ocean_state%bc%has_east), &
                                   wall_south=(ocean_bc_outer_face_tag(ocean_state%bc%south%bc_type) == OBC_WALL &
                                               .and. ocean_state%bc%has_south), &
                                   wall_north=(ocean_bc_outer_face_tag(ocean_state%bc%north%bc_type) == OBC_WALL &
                                               .and. ocean_state%bc%has_north))

      ! Hold land T-cells at finite reference values so masked arithmetic
      ! never multiplies 0 by a NaN (0*NaN = NaN).  Land h_layer is floored
      ! to H_VANISHED (never 0), tracers held at the IC reference, layer +
      ! face velocities zeroed on land.
      !
      ! Cold start only.  The seed is the land state's INITIAL value, not an
      ! invariant the step maintains: the ALE remap regrids a land column
      ! like any other (a `z_fixed` land column leaves step 1 as `nz-1`
      ! `zstar_h_min` fillers over a bed cell holding the rest of its
      ! `nz·H_VANISHED`), and the checkpoint saves that state verbatim.
      ! Re-seeding after a restart read rewrote every land column back to
      ! uniform `H_VANISHED` -- column total unchanged, layout not -- so the
      ! resumed run was not the run that wrote the file (1/4-degree Southern
      ! Ocean, 2026-10-01: 56 046 land columns x 50 layers per rank).
      seed_land = .true.
      if (present(warm_restart)) seed_land = .not. warm_restart
      if (seed_land) call ocean_state_seed_land_cells(ocean_state, grid)

      if (compute_rank == 0) then
         associate (unused => cfg%ocean%grid%grid_config)
         end associate
      end if
   end subroutine configure_ocean_land_mask