visc_rem_precompute Subroutine

private subroutine visc_rem_precompute(grid, bt_work, vmix, vd, ss, bd, ms, dt, kshear, lambda_top_u, lambda_top_v, cover_u, cover_v, bc)

Refresh bt_work%visc_rem_u/v from the CURRENT stage state BEFORE the barotropic forcing assembly (PGF_BUG.md §9) — the MOM6-order parity (vertvisc_coef runs before btstep every stage). The stage-end producer alone leaves visc_rem at its init value (≡ 1) for the whole first stage, so the rem-weighted F_bt degenerates to the plain mean exactly when the spurious grounded-layer PGF is at its ballistic worst, and the Δu corrector then deposits the spurious column-mean into wet layers. Remnant-only vdiff call: builds the same matrix the stage-end solve will build (kv one stage stale — benign; the thickness field, which the BBL glue keys on, is current) and does NOT touch the velocities. Vertex kappa-shear: the corner Kv source enters this matrix too (same operator as the stage-end momentum solve — a remnant built without it would weight the BT corrector with a different friction operator than the one actually applied).

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(barotropic_workstate_t), intent(inout) :: bt_work
type(ocean_vmix_t), intent(in) :: vmix
type(ocean_vdiff_t), intent(inout) :: vd
type(ocean_surface_stress_t), intent(in) :: ss
type(ocean_bottom_drag_t), intent(in) :: bd
type(multilayer_state_t), intent(inout) :: ms
real(kind=wp), intent(in) :: dt
type(ocean_kappa_shear_t), intent(in), optional :: kshear

Kappa-shear slot; only read when enabled + vertex mode (supplies the corner Kv source).

real(kind=wp), intent(in), optional :: lambda_top_u(grid%nx_total+1,grid%ny_total)
real(kind=wp), intent(in), optional :: lambda_top_v(grid%nx_total,grid%ny_total+1)

Ice-shelf top-drag Rayleigh rate — forwarded so the REMNANT is built from the same operator the stage-end momentum solve will build. A remnant built without a sink the solve has would weight the barotropic corrector with a friction operator that is not the one applied. EXPLICIT SHAPE for the reason spelled out on vmix_apply_in_stage’s twin dummies.

real(kind=wp), intent(in), optional :: cover_u(grid%nx_total+1,grid%ny_total)
real(kind=wp), intent(in), optional :: cover_v(grid%nx_total,grid%ny_total+1)

Face ice-cover masks, same reason.

type(ocean_bc_state_t), intent(in), optional :: bc

Forwarded ONLY for the post-production halo/periodic/fold refresh of visc_rem_u/v (visc_rem_halo_refresh, PR-1) — MOM6’s pass_visc_rem group pass, run after every vertvisc_remnant call.


Calls

proc~~visc_rem_precompute~~CallsGraph proc~visc_rem_precompute visc_rem_precompute proc~vdiff_apply_momentum vdiff_apply_momentum proc~visc_rem_precompute->proc~vdiff_apply_momentum proc~visc_rem_halo_refresh visc_rem_halo_refresh proc~visc_rem_precompute->proc~visc_rem_halo_refresh proc~diffuse_velocity_columns_impl diffuse_velocity_columns_impl proc~vdiff_apply_momentum->proc~diffuse_velocity_columns_impl proc~fill_bbl_constants fill_bbl_constants proc~vdiff_apply_momentum->proc~fill_bbl_constants proc~fill_kv_scalar_buf fill_kv_scalar_buf proc~vdiff_apply_momentum->proc~fill_kv_scalar_buf interface~ocean_halo_face_x ocean_halo_face_x proc~visc_rem_halo_refresh->interface~ocean_halo_face_x interface~ocean_halo_face_y ocean_halo_face_y proc~visc_rem_halo_refresh->interface~ocean_halo_face_y proc~ocean_fold_wrap_visc_rem ocean_fold_wrap_visc_rem proc~visc_rem_halo_refresh->proc~ocean_fold_wrap_visc_rem proc~ocean_halo_is_decomposed_x ocean_halo_is_decomposed_x proc~visc_rem_halo_refresh->proc~ocean_halo_is_decomposed_x proc~ocean_halo_is_decomposed_y ocean_halo_is_decomposed_y proc~visc_rem_halo_refresh->proc~ocean_halo_is_decomposed_y proc~ocean_periodic_wrap_face_x_3d ocean_periodic_wrap_face_x_3d proc~visc_rem_halo_refresh->proc~ocean_periodic_wrap_face_x_3d proc~ocean_periodic_wrap_face_y_3d ocean_periodic_wrap_face_y_3d proc~visc_rem_halo_refresh->proc~ocean_periodic_wrap_face_y_3d 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 local local proc~diffuse_velocity_columns_impl->local proc~face_thick face_thick proc~diffuse_velocity_columns_impl->proc~face_thick interface~fold_north_u_face fold_north_u_face proc~ocean_fold_wrap_visc_rem->interface~fold_north_u_face interface~fold_north_v_face fold_north_v_face proc~ocean_fold_wrap_visc_rem->interface~fold_north_v_face interface~ocean_fold_pack ocean_fold_pack proc~ocean_fold_wrap_visc_rem->interface~ocean_fold_pack interface~ocean_fold_unpack ocean_fold_unpack proc~ocean_fold_wrap_visc_rem->interface~ocean_fold_unpack proc~ocean_fold_begin ocean_fold_begin proc~ocean_fold_wrap_visc_rem->proc~ocean_fold_begin proc~ocean_fold_end ocean_fold_end proc~ocean_fold_wrap_visc_rem->proc~ocean_fold_end proc~ocean_fold_exchange ocean_fold_exchange proc~ocean_fold_wrap_visc_rem->proc~ocean_fold_exchange proc~ocean_fold_is_distributed ocean_fold_is_distributed proc~ocean_fold_wrap_visc_rem->proc~ocean_fold_is_distributed 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->proc~ocean_periodic_wrap_face_x_3d 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~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->proc~ocean_periodic_wrap_face_y_3d 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~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 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->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_periodic_wrap_face_x_2d ocean_periodic_wrap_face_x_2d proc~ocean_halo_face_x_2d_impl->proc~ocean_periodic_wrap_face_x_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_periodic_wrap_face_y_2d ocean_periodic_wrap_face_y_2d proc~ocean_halo_face_y_2d_impl->proc~ocean_periodic_wrap_face_y_2d

Called by

proc~~visc_rem_precompute~~CalledByGraph proc~visc_rem_precompute visc_rem_precompute proc~run_stage_split run_stage_split proc~run_stage_split->proc~visc_rem_precompute proc~vmix_apply_in_stage vmix_apply_in_stage proc~run_stage_split->proc~vmix_apply_in_stage proc~vmix_apply_in_stage->proc~visc_rem_precompute proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~run_stage_split proc~run_stage run_stage proc~run_stage->proc~vmix_apply_in_stage proc~engine_step engine_step proc~engine_step->proc~ocean_dyn_step_split proc~ocean_dyn_step ocean_dyn_step proc~engine_step->proc~ocean_dyn_step proc~ocean_dyn_step->proc~run_stage proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_step proc~rdb_ocean_step rdb_ocean_step proc~rdb_ocean_step->proc~engine_step proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean

Variables

Type Visibility Attributes Name Initial
logical, private :: vertex_kv

Source Code

   subroutine visc_rem_precompute(grid, bt_work, vmix, vd, ss, bd, ms, dt, kshear, &
                                  lambda_top_u, lambda_top_v, cover_u, cover_v, bc)
      !! Refresh `bt_work%visc_rem_u/v` from the CURRENT stage state
      !! BEFORE the barotropic forcing assembly (PGF_BUG.md §9) — the
      !! MOM6-order parity (`vertvisc_coef` runs before `btstep` every
      !! stage).  The stage-end producer alone leaves visc_rem at its
      !! init value (≡ 1) for the whole first stage, so the rem-weighted
      !! `F_bt` degenerates to the plain mean exactly when the spurious
      !! grounded-layer PGF is at its ballistic worst, and the Δu
      !! corrector then deposits the spurious column-mean into wet
      !! layers.  Remnant-only vdiff call: builds the same matrix the
      !! stage-end solve will build (kv one stage stale — benign; the
      !! thickness field, which the BBL glue keys on, is current) and
      !! does NOT touch the velocities.  Vertex kappa-shear: the corner
      !! Kv source enters this matrix too (same operator as the
      !! stage-end momentum solve — a remnant built without it would
      !! weight the BT corrector with a different friction operator
      !! than the one actually applied).
      type(hgrid_t), intent(in) :: grid
      type(barotropic_workstate_t), intent(inout) :: bt_work
      type(ocean_vmix_t), intent(in) :: vmix
      type(ocean_vdiff_t), intent(inout) :: vd
      type(ocean_surface_stress_t), intent(in) :: ss
      type(ocean_bottom_drag_t), intent(in) :: bd
      type(multilayer_state_t), intent(inout) :: ms
      real(wp), intent(in) :: dt
      type(ocean_kappa_shear_t), intent(in), optional :: kshear
         !! Kappa-shear slot; only read when enabled + vertex mode
         !! (supplies the corner Kv source).
      real(wp), intent(in), optional :: lambda_top_u(grid%nx_total + 1, grid%ny_total)
      real(wp), intent(in), optional :: lambda_top_v(grid%nx_total, grid%ny_total + 1)
         !! Ice-shelf top-drag Rayleigh rate — forwarded so the REMNANT
         !! is built from the same operator the stage-end momentum solve
         !! will build.  A remnant built without a sink the solve has
         !! would weight the barotropic corrector with a friction
         !! operator that is not the one applied.  EXPLICIT SHAPE for the
         !! reason spelled out on `vmix_apply_in_stage`'s twin dummies.
      real(wp), intent(in), optional :: cover_u(grid%nx_total + 1, grid%ny_total)
      real(wp), intent(in), optional :: cover_v(grid%nx_total, grid%ny_total + 1)
         !! Face ice-cover masks, same reason.
      type(ocean_bc_state_t), intent(in), optional :: bc
         !! Forwarded ONLY for the post-production halo/periodic/fold
         !! refresh of `visc_rem_u/v` (`visc_rem_halo_refresh`, PR-1) —
         !! MOM6's `pass_visc_rem` group pass, run after every
         !! `vertvisc_remnant` call.

      logical :: vertex_kv

      vertex_kv = .false.
      if (present(kshear)) vertex_kv = kshear%enable .and. kshear%at_vertex

      if (vmix%use_closure) then
         if (vertex_kv) then
            call vdiff_apply_momentum(grid, vd, ms, dt, kv_source=vmix%kv, &
                                      tau_u=ss%tau_x, tau_v=ss%tau_y, &
                                      lambda_bot_u=bd%lambda_bot_u, &
                                      lambda_bot_v=bd%lambda_bot_v, rho0=ss%rho0, &
                                      lambda_top_u=lambda_top_u, lambda_top_v=lambda_top_v, &
                                      cover_u=cover_u, cover_v=cover_v, &
                                      visc_rem_u=bt_work%visc_rem_u, &
                                      visc_rem_v=bt_work%visc_rem_v, &
                                      remnant_only=.true., &
                                      kv_corner_source=kshear%kd_corner, &
                                      kv_corner_prandtl=kshear%prandtl_turb)
         else
            call vdiff_apply_momentum(grid, vd, ms, dt, kv_source=vmix%kv, &
                                      tau_u=ss%tau_x, tau_v=ss%tau_y, &
                                      lambda_bot_u=bd%lambda_bot_u, &
                                      lambda_bot_v=bd%lambda_bot_v, rho0=ss%rho0, &
                                      lambda_top_u=lambda_top_u, lambda_top_v=lambda_top_v, &
                                      cover_u=cover_u, cover_v=cover_v, &
                                      visc_rem_u=bt_work%visc_rem_u, &
                                      visc_rem_v=bt_work%visc_rem_v, &
                                      remnant_only=.true.)
         end if
      else
         call vdiff_apply_momentum(grid, vd, ms, dt, &
                                   tau_u=ss%tau_x, tau_v=ss%tau_y, &
                                   lambda_bot_u=bd%lambda_bot_u, &
                                   lambda_bot_v=bd%lambda_bot_v, rho0=ss%rho0, &
                                   lambda_top_u=lambda_top_u, lambda_top_v=lambda_top_v, &
                                   cover_u=cover_u, cover_v=cover_v, &
                                   visc_rem_u=bt_work%visc_rem_u, &
                                   visc_rem_v=bt_work%visc_rem_v, &
                                   remnant_only=.true.)
      end if
      ! PR-2 root cause (replaces the PR-1 consumer-flag gate that used to
      ! sit here): `is_pc` alone (every pred_corr stage, default knobs)
      ! already called this routine before PR-1 (MOM6-order parity,
      ! PGF_BUG.md §9) even though the output went completely unread with
      ! every consumer off.  PR-1 found that making the new halo refresh
      ! UNCONDITIONAL broke `rdb_test_ocean_dyn_mpi_4rank`'s hand-derived
      ! `check_exchange_counts` canary (`tests/mpi/test_ocean_dyn_mpi.F90`)
      ! -- `face_x_3d`/`face_y_3d` came back exactly DOUBLE the expected
      ! count, and (at nprocs=2) `msgs` came back exactly
      ! `+4*N_STEPS` (wall) / `+8*N_STEPS` (periodic, 2 active x-dirs).
      ! That is the EXACT signature of the new, legitimate
      ! visc_rem_halo_refresh traffic (one face_x_3d + one face_y_3d per
      ! pred_corr stage, same `is_pc .and. decomposed` gating as the
      ! existing u_av/v_av seam fill) -- i.e. the counters were telling
      ! the truth and the test's hand-derived formula was stale, not
      ! reporting a defect.  Verified directly: `visc_rem_u/v` starts at
      ! 1.0 everywhere (`barotropic_workstate_init`) and the exchange is a
      ! same-shape, blocking, self-contained isend/irecv/waitall pair
      ! (`ocean_halo_face_x_3d`/`_y_3d`) identical in structure to every
      ! other 3D face exchange in this module -- there is no unpaired
      ! request, no tag collision (each call posts and waits inside the
      ! same subroutine invocation, no module-level async state survives
      ! past the `waitall`), and no shape mismatch (`visc_rem_u/v` are
      ! allocated `(nx+1,ny,nz)`/`(nx,ny+1,nz)` off the SAME `nz_ml` as
      ! every other layered field).  Running this routine's gate removed
      ! at 1/2/4 ranks for 100 steps on the wall/island/periodic/
      ! poisoned-wall/poisoned-periodic legs produces IDENTICAL mass/KE/
      ! salt/heat agreement to round-off (~1e-16) in every leg; the only
      ! failures were the stale counter formula (fixed in
      ! `check_exchange_counts`, `tests/mpi/test_ocean_dyn_mpi.F90`). The
      ! pre-existing `[nan-catch]` BT-correction messages some legs print
      ! are `apply_bt_correction`'s own defensive non-finite counter
      ! (unrelated to visc_rem -- it already fires in the GATED baseline,
      ! just later/rarer); it recovers the finite answer either way and
      ! is not evidence of poisoning.
      !
      ! MOM6 semantics settle it anyway: `pass_visc_rem` runs
      ! UNCONDITIONALLY after every `vertvisc_remnant` call, with no
      ! consumer gate -- a face field
      ! that starts at 1.0 and is only ever READ by an opt-in consumer
      ! cannot be "poisoned" by being exchanged.  So: drop the gate, keep
      ! the halo refresh unconditional, exactly like MOM6.
      call visc_rem_halo_refresh(grid, bt_work, bc)
   end subroutine visc_rem_precompute