vmix_apply_in_stage Subroutine

private subroutine vmix_apply_in_stage(grid, dyn, vmix, vd, ss, bd, ms, dt, stage, sf, epbl, kshear, vmix_tidal, bt_work, lambda_top_u, lambda_top_v, cover_u, cover_v, apply_tracers, metrics, dt_remnant, bc)

Bundle the per-stage vmix closure / KPP overlay / KV_ML_INVZ2 / assembly gate / vdiff dispatch into one routine so the run_stage drivers can call vmix_apply_in_stage(grid, dyn, vmix, vd, ss, ms, dt, sf) instead of carrying 30 lines of nested if-branching.

Closure chain (all upstream CONTRIBUTORS into kv/kt): PP81 interior → KPP overlay XOR EPBL merge → kappa-shear additive merge → tidal-mixing additive merge → KV_ML_INVZ2 surface band → convective adjustment (Brunt-Vaisala trigger, interior-only, masked below the active KPP/EPBL boundary layer) → vmix_split_kd_heat_salt (derives ks from kt; NOT a contributor, must stay last) → vmix_assemble (the single downstream gate: background floors, kv_max/kd_max ceilings, optional smoothing, optional guard — applies to kv, kt, AND ks) → vdiff.

Logic preserved verbatim from the prior in-driver dispatch: - When vmix%use_closure is true: optional PP81 / KPP overlay populate vmix%kv/vmix%kt, then vdiff reads them; KPP non-local γ is applied after the tracer vdiff solve when thermodynamics are active. - When vmix%use_closure is false: vdiff uses its scalar K_v_* defaults — kv_source not passed. - Tracer vdiff + KPP non-local fire only when enable_thermodynamics .and. is_thermo_step().

Profiler regions are emitted from here uniformly (the prior run_stage path had no profiler regions around vmix; the split path did — now both share the same labels).

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_dyn_t), intent(in) :: dyn
type(ocean_vmix_t), intent(inout) :: vmix
type(ocean_vdiff_t), intent(inout) :: vd
type(ocean_surface_stress_t), intent(in) :: ss
type(ocean_bottom_drag_t), intent(in) :: bd

Bottom-drag slot — supplies the bed-layer Rayleigh-rate field lambda_bot_u/v for the implicit-drag vdiff fold.

type(multilayer_state_t), intent(inout) :: ms
real(kind=wp), intent(in) :: dt
integer, intent(in) :: stage

RK2 stage (1 or 2): the EPBL/kappa-shear COLUMN SOLVES fire only at stage 1 of a thermo step (their kd is stage-invariant by design — recomputing at stage 2 doubled the closure cost for no accuracy: 19.6%% of GPU time was kappa-shear at 2x cadence). The merges still run EVERY stage (PP81 rewrites kv/kt per stage).

type(ocean_surface_flux_t), intent(in), optional :: sf
type(ocean_epbl_t), intent(inout), optional :: epbl

EPBL slot. When present and enabled, replaces the KPP overlay (configure enforces the mutual exclusion): epbl_compute refreshes kd_int at thermo cadence and the merge into vmix%kv / vmix%kt runs every stage (PP81 rewrites those arrays each stage).

type(ocean_kappa_shear_t), intent(inout), optional :: kshear

Kappa-shear interior closure slot. When present and enabled, kappa_shear_compute refreshes kd_int at thermo cadence and the additive merge into vmix%kv / vmix%kt runs every stage. Coexists with KPP / EPBL (no mutual exclusion).

type(ocean_tidal_mixing_t), intent(inout), optional :: vmix_tidal

St-Laurent/Simmons tidal-mixing interior closure slot. When present and enabled, tidal_mixing_compute refreshes kd_int at thermo cadence and the additive merge into vmix%kv / vmix%kt runs every stage. Coexists with KPP / EPBL / kappa-shear (no mutual exclusion).

type(barotropic_workstate_t), intent(inout), optional :: bt_work

The BT-corrector workstate — supplies visc_rem_u/v as the vdiff kernel’s OUTPUT. Present only from run_stage_split (the unsplit path has no BT correction to consume it). When present AND bt_work%bt_visc_rem_producer, the viscous remnant γ is (re)computed here, at step 9 of the CURRENT stage — the next stage’s forcing/renorm/bt_rem_from consumers read it, a one-stage (Δt/2) lag (see the step-9 call site below). Absent, or no consumer on, ⇒ no remnant work ⇒ bit-identical.

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 (1/s) at u / v faces — the ocean_top_drag_t slot’s lambda_top_u/v, forwarded verbatim to vdiff_apply_momentum’s k = nz diagonal fold. Passed as ARRAYS rather than the slot itself because the slot is optional one level up: forwarding an absent optional ARRAY dummy on to another optional dummy is legal Fortran, whereas dereferencing an absent derived-type dummy is not. Absent, or vd%implicit_top_drag off ⇒ bit-identical.

EXPLICIT SHAPE, not (:, :): the attribute has to hold on EVERY frame that forwards the optional, or gfortran reinstates the speculative pack (and its uninitialised packing flag) in whichever frame still hands an assumed-shape actual down. The whole argument is written out on vdiff_apply_momentum.

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 (the OR of the two abutting cells). Present together with lambda_top_*; used to mask the wind RHS off on covered faces. Explicit-shape for the same reason.

logical, intent(in), optional :: apply_tracers

.false. = momentum-only: skip the tracer vdiff + KPP non-local applies regardless of the thermo gate. The pred_corr PREDICTOR passes this — MOM6’s predictor applies vertvisc(up, dt_pred) to the provisional velocity (so the spurious grounded-layer accelerations are absorbed BEFORE continuity forms u_av) but never touches tracers. Default .true. = historical.

type(ocean_metrics_t), intent(in), optional :: metrics

Metrics slot — supplies the halo-valid wet masks (wet_T/wet_u/wet_v) the kappa-shear VERTEX form’s corner gather needs, and metrics%geolatT — the C7 Henyey latitude factor’s only spatial input, forwarded to vmix_assemble (unread when bkgnd_henyey is off). Optional so the split_rk2/legacy call shapes stay valid; both consumers fail loud when their knob is on and the slot is absent — kappa_shear_compute for at_vertex, and vmix_assemble for bkgnd_henyey.

real(kind=wp), intent(in), optional :: dt_remnant

dt (the pred_corr PREDICTOR, where this routine is called with dt_vel = pc_be·dt), the visc_rem PRODUCER is split out of the velocity solve and re-run as its own remnant-only call at dt_remnant — matching MOM6’s VISC_REM_TIMESTEP_BUG = .false. default (vertvisc_remnant always at the outer step’s dt), never at dt_pred. Absent ⇒ the historical fused behaviour (remnant built from the SAME matrix as the velocity solve, at dt). See bt_forcing_visc_rem’s docstring in rdb_barotropic_workstate for the full call-point mapping.

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

Open-boundary / periodic / tripolar-fold state — forwarded ONLY so the visc_rem halo refresh (visc_rem_halo_refresh) can re-wrap bt_work%visc_rem_u/v’s ghosts after production. Unread when do_remnant is false.


Calls

proc~~vmix_apply_in_stage~~CallsGraph proc~vmix_apply_in_stage vmix_apply_in_stage error error proc~vmix_apply_in_stage->error proc~epbl_compute epbl_compute proc~vmix_apply_in_stage->proc~epbl_compute proc~epbl_merge_into_kv_kt epbl_merge_into_kv_kt proc~vmix_apply_in_stage->proc~epbl_merge_into_kv_kt proc~kappa_shear_compute kappa_shear_compute proc~vmix_apply_in_stage->proc~kappa_shear_compute proc~kappa_shear_merge_into_kv_kt kappa_shear_merge_into_kv_kt proc~vmix_apply_in_stage->proc~kappa_shear_merge_into_kv_kt proc~ocean_dyn_is_thermo_step ocean_dyn_t%ocean_dyn_is_thermo_step proc~vmix_apply_in_stage->proc~ocean_dyn_is_thermo_step proc~ocean_dyn_therm_dt ocean_dyn_t%ocean_dyn_therm_dt proc~vmix_apply_in_stage->proc~ocean_dyn_therm_dt proc~profiler_start profiler_start proc~vmix_apply_in_stage->proc~profiler_start proc~profiler_stop profiler_stop proc~vmix_apply_in_stage->proc~profiler_stop proc~tidal_mixing_compute tidal_mixing_compute proc~vmix_apply_in_stage->proc~tidal_mixing_compute proc~tidal_mixing_merge_into_kt tidal_mixing_merge_into_kt proc~vmix_apply_in_stage->proc~tidal_mixing_merge_into_kt proc~vdiff_apply_momentum vdiff_apply_momentum proc~vmix_apply_in_stage->proc~vdiff_apply_momentum proc~vdiff_apply_tracers vdiff_apply_tracers proc~vmix_apply_in_stage->proc~vdiff_apply_tracers proc~visc_rem_halo_refresh visc_rem_halo_refresh proc~vmix_apply_in_stage->proc~visc_rem_halo_refresh proc~visc_rem_precompute visc_rem_precompute proc~vmix_apply_in_stage->proc~visc_rem_precompute proc~vmix_add_kv_ml_invz2 vmix_add_kv_ml_invz2 proc~vmix_apply_in_stage->proc~vmix_add_kv_ml_invz2 proc~vmix_apply_convection vmix_apply_convection proc~vmix_apply_in_stage->proc~vmix_apply_convection proc~vmix_apply_kpp_overlay vmix_apply_kpp_overlay proc~vmix_apply_in_stage->proc~vmix_apply_kpp_overlay proc~vmix_apply_nonlocal_tendencies vmix_apply_nonlocal_tendencies proc~vmix_apply_in_stage->proc~vmix_apply_nonlocal_tendencies proc~vmix_assemble vmix_assemble proc~vmix_apply_in_stage->proc~vmix_assemble proc~vmix_compute_pp81 vmix_compute_pp81 proc~vmix_apply_in_stage->proc~vmix_compute_pp81 proc~vmix_split_kd_heat_salt vmix_split_kd_heat_salt proc~vmix_apply_in_stage->proc~vmix_split_kd_heat_salt proc~epbl_column_kernel epbl_column_kernel proc~epbl_compute->proc~epbl_column_kernel proc~kappa_shear_column_kernel kappa_shear_column_kernel proc~kappa_shear_compute->proc~kappa_shear_column_kernel proc~kappa_shear_vertex_kernel kappa_shear_vertex_kernel proc~kappa_shear_compute->proc~kappa_shear_vertex_kernel proc~kappa_shear_vertex_scatter kappa_shear_vertex_scatter proc~kappa_shear_compute->proc~kappa_shear_vertex_scatter 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~tidal_mixing_column_kernel tidal_mixing_column_kernel proc~tidal_mixing_compute->proc~tidal_mixing_column_kernel 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 proc~vdiff_apply_tracers->error proc~apply_factored_tracer apply_factored_tracer proc~vdiff_apply_tracers->proc~apply_factored_tracer proc~build_factorize_tracer_matrix build_factorize_tracer_matrix proc~vdiff_apply_tracers->proc~build_factorize_tracer_matrix proc~vdiff_apply_tracers->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~visc_rem_precompute->proc~vdiff_apply_momentum proc~visc_rem_precompute->proc~visc_rem_halo_refresh local local proc~vmix_add_kv_ml_invz2->local proc~vmix_convection_impl vmix_convection_impl proc~vmix_apply_convection->proc~vmix_convection_impl proc~vmix_kpp_overlay_impl vmix_kpp_overlay_impl proc~vmix_apply_kpp_overlay->proc~vmix_kpp_overlay_impl proc~vmix_assemble_clip_henyey_impl vmix_assemble_clip_henyey_impl proc~vmix_assemble->proc~vmix_assemble_clip_henyey_impl proc~vmix_assemble_clip_impl vmix_assemble_clip_impl proc~vmix_assemble->proc~vmix_assemble_clip_impl proc~vmix_assemble_clip_profile_impl vmix_assemble_clip_profile_impl proc~vmix_assemble->proc~vmix_assemble_clip_profile_impl proc~vmix_bkgnd_fill_impl vmix_bkgnd_fill_impl proc~vmix_assemble->proc~vmix_bkgnd_fill_impl proc~vmix_guard_impl vmix_guard_impl proc~vmix_assemble->proc~vmix_guard_impl proc~vmix_resolve_kd_min vmix_resolve_kd_min proc~vmix_assemble->proc~vmix_resolve_kd_min proc~vmix_smooth_121_impl vmix_smooth_121_impl proc~vmix_assemble->proc~vmix_smooth_121_impl proc~vmix_compute_pp81->local proc~vmix_split_ddiff_eos_impl vmix_split_ddiff_eos_impl proc~vmix_split_kd_heat_salt->proc~vmix_split_ddiff_eos_impl proc~vmix_split_ddiff_impl vmix_split_ddiff_impl proc~vmix_split_kd_heat_salt->proc~vmix_split_ddiff_impl proc~vmix_split_kd_heat_salt_impl vmix_split_kd_heat_salt_impl proc~vmix_split_kd_heat_salt->proc~vmix_split_kd_heat_salt_impl 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 proc~apply_factored_tracer->local proc~build_factorize_tracer_matrix->local proc~face_thick face_thick proc~build_factorize_tracer_matrix->proc~face_thick proc~diffuse_velocity_columns_impl->local proc~diffuse_velocity_columns_impl->proc~face_thick proc~epbl_column_kernel->local proc~eos_specvol_derivs eos_specvol_derivs proc~epbl_column_kernel->proc~eos_specvol_derivs proc~epbl_find_mstar epbl_find_mstar proc~epbl_column_kernel->proc~epbl_find_mstar proc~epbl_lf17_la epbl_lf17_la proc~epbl_column_kernel->proc~epbl_lf17_la proc~epbl_lf17_wave_state epbl_lf17_wave_state proc~epbl_column_kernel->proc~epbl_lf17_wave_state proc~epbl_lt_enhance epbl_lt_enhance proc~epbl_column_kernel->proc~epbl_lt_enhance proc~epbl_mixlen_shape epbl_mixlen_shape proc~epbl_column_kernel->proc~epbl_mixlen_shape proc~sw_pe_cost_shape sw_pe_cost_shape proc~epbl_column_kernel->proc~sw_pe_cost_shape proc~kappa_shear_column_kernel->local proc~ks_precompute ks_precompute proc~kappa_shear_column_kernel->proc~ks_precompute proc~ks_solve_column ks_solve_column proc~kappa_shear_column_kernel->proc~ks_solve_column proc~massless_build_maps massless_build_maps proc~kappa_shear_column_kernel->proc~massless_build_maps proc~massless_interp_back massless_interp_back proc~kappa_shear_column_kernel->proc~massless_interp_back proc~massless_merge_fields massless_merge_fields proc~kappa_shear_column_kernel->proc~massless_merge_fields proc~kappa_shear_vertex_kernel->local proc~ks_gather_corner ks_gather_corner proc~kappa_shear_vertex_kernel->proc~ks_gather_corner proc~kappa_shear_vertex_kernel->proc~ks_precompute proc~kappa_shear_vertex_kernel->proc~ks_solve_column proc~kappa_shear_vertex_kernel->proc~massless_build_maps proc~kappa_shear_vertex_kernel->proc~massless_interp_back proc~kappa_shear_vertex_kernel->proc~massless_merge_fields proc~kappa_shear_vertex_scatter->local 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~tidal_mixing_column_kernel->local proc~tidal_mixing_column_kernel->proc~eos_specvol_derivs proc~vmix_assemble_clip_henyey_impl->local proc~henyey_lat_factor_impl henyey_lat_factor_impl proc~vmix_assemble_clip_henyey_impl->proc~henyey_lat_factor_impl proc~vmix_bkgnd_fill_impl->local proc~vmix_convection_impl->local reduce reduce proc~vmix_guard_impl->reduce proc~vmix_kpp_overlay_impl->local proc~eos_buoyancy_coeffs eos_buoyancy_coeffs proc~vmix_kpp_overlay_impl->proc~eos_buoyancy_coeffs proc~kpp_surface_buoyancy_flux kpp_surface_buoyancy_flux proc~vmix_kpp_overlay_impl->proc~kpp_surface_buoyancy_flux proc~sw_transmission sw_transmission proc~vmix_kpp_overlay_impl->proc~sw_transmission proc~vmix_smooth_121_impl->local proc~vmix_split_ddiff_eos_impl->local proc~vmix_split_ddiff_eos_impl->proc~eos_buoyancy_coeffs proc~vmix_split_ddiff_impl->local 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~roquet_spv_point roquet_spv_point proc~eos_buoyancy_coeffs->proc~roquet_spv_point proc~eos_specvol_derivs->proc~roquet_spv_point proc~one_m_exp_x one_m_exp_x proc~epbl_lf17_la->proc~one_m_exp_x proc~ks_solve_column->proc~eos_specvol_derivs proc~ks_adaptive_dt ks_adaptive_dt proc~ks_solve_column->proc~ks_adaptive_dt proc~ks_find_kappa_tke ks_find_kappa_tke proc~ks_solve_column->proc~ks_find_kappa_tke proc~ks_projected_state ks_projected_state proc~ks_solve_column->proc~ks_projected_state proc~ks_src_func ks_src_func proc~ks_solve_column->proc~ks_src_func 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~ks_adaptive_dt->proc~ks_projected_state proc~ks_adaptive_dt->proc~ks_src_func proc~ks_find_kappa_tke->proc~ks_src_func 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~~vmix_apply_in_stage~~CalledByGraph proc~vmix_apply_in_stage vmix_apply_in_stage proc~run_stage run_stage proc~run_stage->proc~vmix_apply_in_stage proc~run_stage_split run_stage_split proc~run_stage_split->proc~vmix_apply_in_stage proc~ocean_dyn_step ocean_dyn_step proc~ocean_dyn_step->proc~run_stage proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~run_stage_split proc~engine_step engine_step proc~engine_step->proc~ocean_dyn_step proc~engine_step->proc~ocean_dyn_step_split 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 :: do_remnant
logical, private :: do_tracers
logical, private :: epbl_active
logical, private :: kshear_active
logical, private :: request_remnant
logical, private :: split_remnant
logical, private :: tidal_active
logical, private :: vertex_kv

Source Code

   subroutine vmix_apply_in_stage(grid, dyn, vmix, vd, ss, bd, ms, dt, stage, sf, epbl, kshear, vmix_tidal, bt_work, &
                                  lambda_top_u, lambda_top_v, cover_u, cover_v, &
                                  apply_tracers, metrics, dt_remnant, bc)
      !! Bundle the per-stage vmix closure / KPP overlay / KV_ML_INVZ2 /
      !! assembly gate / vdiff dispatch into one routine so the run_stage
      !! drivers can call `vmix_apply_in_stage(grid, dyn, vmix, vd, ss,
      !! ms, dt, sf)` instead of carrying 30 lines of nested if-branching.
      !!
      !! Closure chain (all upstream CONTRIBUTORS into kv/kt):
      !!   PP81 interior → KPP overlay XOR EPBL merge → kappa-shear
      !!   additive merge → tidal-mixing additive merge → KV_ML_INVZ2
      !!   surface band → convective adjustment (Brunt-Vaisala trigger,
      !!   interior-only, masked below the active KPP/EPBL boundary
      !!   layer) → **vmix_split_kd_heat_salt** (derives ks from kt; NOT a
      !!   contributor, must stay last) → **vmix_assemble** (the single
      !!   downstream gate: background floors, kv_max/kd_max ceilings,
      !!   optional smoothing, optional guard — applies to kv, kt, AND ks)
      !!   → vdiff.
      !!
      !! Logic preserved verbatim from the prior in-driver dispatch:
      !!   - When `vmix%use_closure` is true: optional PP81 / KPP
      !!     overlay populate `vmix%kv`/`vmix%kt`, then vdiff reads
      !!     them; KPP non-local γ is applied after the tracer vdiff
      !!     solve when thermodynamics are active.
      !!   - When `vmix%use_closure` is false: vdiff uses its scalar
      !!     `K_v_*` defaults — kv_source not passed.
      !!   - Tracer vdiff + KPP non-local fire only when
      !!     `enable_thermodynamics .and. is_thermo_step()`.
      !!
      !! Profiler regions are emitted from here uniformly (the prior
      !! run_stage path had no profiler regions around vmix; the
      !! split path did — now both share the same labels).
      type(hgrid_t), intent(in) :: grid
      type(ocean_dyn_t), intent(in) :: dyn
      type(ocean_vmix_t), intent(inout) :: vmix
      type(ocean_vdiff_t), intent(inout) :: vd
      type(ocean_surface_stress_t), intent(in) :: ss
      type(ocean_bottom_drag_t), intent(in) :: bd
         !! Bottom-drag slot — supplies the bed-layer Rayleigh-rate field
         !! `lambda_bot_u/v` for the implicit-drag vdiff fold.
      type(multilayer_state_t), intent(inout) :: ms
      real(wp), intent(in) :: dt
      integer, intent(in) :: stage
         !! RK2 stage (1 or 2): the EPBL/kappa-shear COLUMN SOLVES fire
         !! only at stage 1 of a thermo step (their kd is
         !! stage-invariant by design — recomputing at stage 2 doubled
         !! the closure cost for no accuracy: 19.6%% of GPU time was
         !! kappa-shear at 2x cadence).  The merges still run EVERY
         !! stage (PP81 rewrites kv/kt per stage).
      type(ocean_surface_flux_t), intent(in), optional :: sf
      type(ocean_epbl_t), intent(inout), optional :: epbl
         !! EPBL slot.  When present and enabled, replaces the KPP
         !! overlay (configure enforces the mutual exclusion):
         !! `epbl_compute` refreshes `kd_int` at thermo cadence and
         !! the merge into `vmix%kv` / `vmix%kt` runs every stage
         !! (PP81 rewrites those arrays each stage).
      type(ocean_kappa_shear_t), intent(inout), optional :: kshear
         !! Kappa-shear interior closure slot.  When present and enabled,
         !! `kappa_shear_compute` refreshes `kd_int` at thermo cadence and
         !! the additive merge into `vmix%kv` / `vmix%kt` runs every stage.
         !! Coexists with KPP / EPBL (no mutual exclusion).
      type(ocean_tidal_mixing_t), intent(inout), optional :: vmix_tidal
         !! St-Laurent/Simmons tidal-mixing interior closure slot.  When
         !! present and enabled, `tidal_mixing_compute` refreshes `kd_int`
         !! at thermo cadence and the additive merge into `vmix%kv` /
         !! `vmix%kt` runs every stage.  Coexists with KPP / EPBL /
         !! kappa-shear (no mutual exclusion).
      type(barotropic_workstate_t), intent(inout), optional :: bt_work
         !! The BT-corrector workstate — supplies `visc_rem_u/v` as the
         !! vdiff kernel's OUTPUT.  Present only from `run_stage_split`
         !! (the unsplit path has no BT correction to consume it).  When
         !! present AND `bt_work%bt_visc_rem_producer`, the viscous
         !! remnant γ is (re)computed here, at step 9 of the CURRENT
         !! stage — the next stage's forcing/renorm/bt_rem_from consumers
         !! read it, a one-stage (Δt/2) lag (see the step-9 call site
         !! below).  Absent, or no consumer on, ⇒ no remnant work ⇒
         !! bit-identical.

      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 (1/s) at u / v faces — the
         !! `ocean_top_drag_t` slot's `lambda_top_u/v`, forwarded
         !! verbatim to `vdiff_apply_momentum`'s `k = nz` diagonal fold.
         !! Passed as ARRAYS rather than the slot itself because the slot
         !! is optional one level up: forwarding an absent optional
         !! ARRAY dummy on to another optional dummy is legal Fortran,
         !! whereas dereferencing an absent derived-type dummy is not.
         !! Absent, or `vd%implicit_top_drag` off ⇒ bit-identical.
         !!
         !! EXPLICIT SHAPE, not `(:, :)`: the attribute has to hold on
         !! EVERY frame that forwards the optional, or gfortran reinstates
         !! the speculative pack (and its uninitialised packing flag) in
         !! whichever frame still hands an assumed-shape actual down.  The
         !! whole argument is written out on `vdiff_apply_momentum`.
      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 (the OR of the two abutting cells).
         !! Present together with `lambda_top_*`; used to mask the wind
         !! RHS off on covered faces.  Explicit-shape for the same reason.
      logical, intent(in), optional :: apply_tracers
         !! `.false.` = momentum-only: skip the tracer vdiff + KPP
         !! non-local applies regardless of the thermo gate.  The pred_corr
         !! PREDICTOR passes this — MOM6's predictor applies
         !! `vertvisc(up, dt_pred)` to the provisional velocity (so the
         !! spurious grounded-layer accelerations are absorbed BEFORE
         !! continuity forms `u_av`) but never touches tracers.
         !! Default `.true.` = historical.
      type(ocean_metrics_t), intent(in), optional :: metrics
         !! Metrics slot — supplies the halo-valid wet masks
         !! (`wet_T`/`wet_u`/`wet_v`) the kappa-shear VERTEX form's
         !! corner gather needs, and `metrics%geolatT` — the C7 Henyey
         !! latitude factor's only spatial input, forwarded to
         !! `vmix_assemble` (unread when `bkgnd_henyey` is off).
         !! Optional so the split_rk2/legacy call shapes stay valid; both
         !! consumers fail loud when their knob is on and the slot is
         !! absent — `kappa_shear_compute` for `at_vertex`, and
         !! `vmix_assemble` for `bkgnd_henyey`.
      real(wp), intent(in), optional :: dt_remnant
         !! PR-1: when present AND different from the velocity-apply
         !! `dt` (the `pred_corr` PREDICTOR, where this routine is called
         !! with `dt_vel = pc_be·dt`), the visc_rem PRODUCER is split out
         !! of the velocity solve and re-run as its own remnant-only call
         !! at `dt_remnant` — matching MOM6's `VISC_REM_TIMESTEP_BUG =
         !! .false.` default (`vertvisc_remnant` always at the outer
         !! step's `dt`), never at
         !! `dt_pred`.  Absent ⇒ the historical fused behaviour (remnant
         !! built from the SAME matrix as the velocity solve, at `dt`).
         !! See `bt_forcing_visc_rem`'s docstring in
         !! `rdb_barotropic_workstate` for the full call-point mapping.
      type(ocean_bc_state_t), intent(in), optional :: bc
         !! Open-boundary / periodic / tripolar-fold state — forwarded
         !! ONLY so the visc_rem halo refresh (`visc_rem_halo_refresh`)
         !! can re-wrap `bt_work%visc_rem_u/v`'s ghosts after production.
         !! Unread when `do_remnant` is false.

      logical :: epbl_active, kshear_active, tidal_active, do_remnant
      logical :: do_tracers, vertex_kv, split_remnant, request_remnant

      do_tracers = .true.
      if (present(apply_tracers)) do_tracers = apply_tracers
      epbl_active = .false.
      if (present(epbl)) epbl_active = epbl%enable
      kshear_active = .false.
      if (present(kshear)) kshear_active = kshear%enable
      ! Vertex kappa-shear routes its viscosity corner->face: the
      ! momentum vdiff gets `kd_corner` as `kv_corner_source` (scaled by
      ! prandtl_turb) and the cell-centred kv merge is suppressed inside
      ! `kappa_shear_merge_into_kv_kt` (MOM6 zeroes the tracer-point
      ! Kv_shear when VERTEX_SHEAR is on — no double-count).
      vertex_kv = .false.
      if (present(kshear)) vertex_kv = kshear_active .and. kshear%at_vertex
      tidal_active = .false.
      if (present(vmix_tidal)) tidal_active = vmix_tidal%enable
      ! D1 follow-up: the producer must run whenever ANY consumer needs
      ! visc_rem_u/v fresh, not only the (retired) weighted BT-correction
      ! fold — read the decoupled `bt_visc_rem_producer` gate (set by
      ! `configure_ocean_bt` to the OR of forcing/renorm/bt_rem_from and
      ! the legacy correction flag), not `bt_correction_visc_rem` alone.
      do_remnant = .false.
      if (present(bt_work)) do_remnant = bt_work%bt_visc_rem_producer
      ! PR-1 VISC_REM_TIMESTEP_BUG fix: at the pred_corr PREDICTOR this
      ! routine is called with `dt_vel = pc_be·dt` (the provisional
      ! velocity's own apply dt), but MOM6's default (non-buggy) remnant
      ! is always built at the OUTER step's `dt`.  Since the remnant
      ! matrix depends only on {dt, h, kv, drag} — never on velocity — it
      ! cannot be produced correctly by fusing it into a dt_vel-based
      ! velocity solve; `split_remnant` routes it to a SEPARATE
      ! remnant-only call at `dt_remnant` instead (`visc_rem_precompute`),
      ! run AFTER the (remnant-free) velocity solve below.  `request_remnant`
      ! is what actually reaches `vdiff_apply_momentum` this call.
      split_remnant = .false.
      if (do_remnant .and. present(dt_remnant)) split_remnant = (dt_remnant /= dt)
      request_remnant = do_remnant .and. .not. split_remnant

      if (vmix%use_closure) then
         call profiler_start("ocean_vmix_compute")
         if (vmix%interior_closure == VMIX_INTERIOR_PP81) then
            call vmix_compute_pp81(grid, vmix, ms)
         else
            ! PR-6 fail-loud: VMIX_INTERIOR_LARGE94 / VMIX_INTERIOR_CVMIX are
            ! reserved-but-unwired (no kernel).  Selecting one would leave
            ! kv/kt with NO interior mixing.  There is no namelist key for
            ! interior_closure today, so this is defence-in-depth for the
            ! next code/config consumer that sets it.
            call logger%error("vmix_apply_in_stage: the selected interior closure "// &
                              "has no kernel (only VMIX_INTERIOR_PP81 is implemented; "// &
                              "VMIX_INTERIOR_LARGE94/CVMIX are reserved-but-unwired) — "// &
                              "running it would leave kv/kt with no interior mixing")
            error stop "vmix_apply_in_stage: unimplemented vmix interior closure"
         end if
         if (vmix%use_kpp .and. .not. epbl_active) then
            if (.not. present(sf)) then
               call logger%error("vmix_apply_in_stage: KPP requires the surface-flux slot (sf)")
               error stop "vmix_apply_in_stage: use_kpp requires the surface-flux slot (sf)"
            end if
            call vmix_apply_kpp_overlay(grid, vmix, ms, ss, sf)
         end if
         if (epbl_active) then
            if (.not. present(sf)) then
               call logger%error("vmix_apply_in_stage: EPBL requires the surface-flux slot (sf)")
               error stop "vmix_apply_in_stage: EPBL requires the surface-flux slot (sf)"
            end if
            if (dyn%enable_thermodynamics .and. dyn%is_thermo_step() .and. stage == 1) then
               call epbl_compute(grid, epbl, ms, ss, dyn%therm_dt(dt), sf)
            end if
            call epbl_merge_into_kv_kt(epbl, size(vmix%kt, 1), size(vmix%kt, 2), &
                                       size(vmix%kt, 3), vmix%kv, vmix%kt)
         end if
         if (kshear_active) then
            if (dyn%enable_thermodynamics .and. dyn%is_thermo_step() .and. stage == 1) then
               if (kshear%at_vertex) then
                  ! Vertex form needs the halo-valid wet masks for the
                  ! corner gather; fail-loud inside if metrics is absent.
                  if (.not. present(metrics)) then
                     call logger%error("vmix_apply_in_stage: kappa-shear at_vertex "// &
                                       "requires the metrics slot (wet masks)")
                     error stop "vmix_apply_in_stage: at_vertex needs metrics"
                  end if
                  call kappa_shear_compute(grid, kshear, ms, dyn%therm_dt(dt), &
                                           wet_t=metrics%wet_T, wet_u=metrics%wet_u, &
                                           wet_v=metrics%wet_v)
               else
                  call kappa_shear_compute(grid, kshear, ms, dyn%therm_dt(dt))
               end if
            end if
            call kappa_shear_merge_into_kv_kt(kshear, size(vmix%kt, 1), size(vmix%kt, 2), &
                                              size(vmix%kt, 3), vmix%kv, vmix%kt)
         end if
         if (tidal_active) then
            if (dyn%enable_thermodynamics .and. dyn%is_thermo_step() .and. stage == 1) then
               call tidal_mixing_compute(grid, vmix_tidal, ms, dyn%therm_dt(dt))
            end if
            call tidal_mixing_merge_into_kt(vmix_tidal, size(vmix%kt, 1), size(vmix%kt, 2), &
                                            size(vmix%kt, 3), vmix%kv, vmix%kt)
         end if
         call vmix_add_kv_ml_invz2(grid, vmix, ms)
         if (vmix%conv_enable) then
            ! Brunt-Vaisala-triggered convective adjustment.  A CONTRIBUTOR
            ! (max() floor), so it must run BEFORE vmix_assemble -- see the
            ! rdb_ocean_vmix module docstring for the D1-D4 divergences from
            ! MOM6's MOM_CVMix_conv.  Masks against the live BL depth of
            ! whichever surface scheme is active this stage.
            if (epbl_active) then
               call vmix_apply_convection(grid, vmix, ms, epbl%mld)
            else
               call vmix_apply_convection(grid, vmix, ms, vmix%bl_depth)
            end if
         end if
         ! `vmix_split_kd_heat_salt` is NOT a contributor -- it must be
         ! the LAST statement before `vmix_assemble`, always.  Any future
         ! PR adding a kv/kt contributor (e.g. convective adjustment)
         ! inserts ABOVE this line, never below it -- ks is derived from
         ! whatever kt holds at this point, so a contributor placed after
         ! the split silently never reaches ks.
         call vmix_split_kd_heat_salt(grid, vmix, ms)
         ! Single downstream assembly gate: background floors, kv_max/kd_max
         ! ceilings, optional 1-2-1 smoothing, optional negative/NaN guard.
         ! Defaults reproduce the pre-assembly chain bit-for-bit.  Gates kv,
         ! kt, AND ks.  `geolat` rides on the optional metrics slot (the C7
         ! Henyey latitude factor's only spatial input) — forwarded when the
         ! caller threaded metrics through, omitted otherwise.  Omitting it
         ! is safe: vmix_assemble fails loud if `bkgnd_henyey` is on and
         ! geolat is absent, so a caller that forgot cannot silently lose
         ! the latitude factor.
         if (present(metrics)) then
            call vmix_assemble(grid, vmix, ms, geolat=metrics%geolatT)
         else
            call vmix_assemble(grid, vmix, ms)
         end if
         call profiler_stop("ocean_vmix_compute")
         call profiler_start("ocean_vdiff_apply")
         if (vertex_kv) then
            ! Corner Kv seam: same calls + the corner viscosity source.
            if (request_remnant) 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, &
                                         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, &
                                         kv_corner_source=kshear%kd_corner, &
                                         kv_corner_prandtl=kshear%prandtl_turb)
            end if
         else if (request_remnant) 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)
         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)
         end if
         if (do_tracers .and. dyn%enable_thermodynamics .and. dyn%is_thermo_step()) then
            call vdiff_apply_tracers(grid, vd, ms, dyn%therm_dt(dt), &
                                     kt_source=vmix%kt, ks_source=vmix%ks)
            if (vmix%use_kpp) then
               call vmix_apply_nonlocal_tendencies(grid, vmix, ms, dyn%therm_dt(dt))
            end if
         end if
         call profiler_stop("ocean_vdiff_apply")
      else
         call profiler_start("ocean_vdiff_apply")
         if (request_remnant) then
            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)
         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)
         end if
         if (do_tracers .and. dyn%enable_thermodynamics .and. dyn%is_thermo_step()) then
            call vdiff_apply_tracers(grid, vd, ms, dyn%therm_dt(dt))
         end if
         call profiler_stop("ocean_vdiff_apply")
      end if
      ! PR-1: the split-dt remnant refresh (predictor stage, see
      ! `split_remnant` above) runs AFTER the velocity solve above, at
      ! `dt_remnant` — `visc_rem_precompute` does its own halo/periodic/
      ! fold refresh at the end, so nothing further is needed here.  The
      ! FUSED path (every other call site) must still get its own halo
      ! refresh — MOM6's `pass_visc_rem` group pass runs after EVERY
      ! `vertvisc_remnant` call, not just the split one.
      if (split_remnant) then
         call visc_rem_precompute(grid, bt_work, vmix, vd, ss, bd, ms, dt_remnant, kshear=kshear, &
                                  lambda_top_u=lambda_top_u, lambda_top_v=lambda_top_v, &
                                  cover_u=cover_u, cover_v=cover_v, bc=bc)
      else if (request_remnant) then
         call visc_rem_halo_refresh(grid, bt_work, bc)
      end if
   end subroutine vmix_apply_in_stage