configure_ocean_lateral Subroutine

public subroutine configure_ocean_lateral(cfg, ocean_state, grid, compute_rank, ierr)

Flow-aware lateral-viscosity closure (Leith / Smagorinsky + biharmonic Smagorinsky_AH), the vertical coordinate (VCOORD_* code + z_fixed reference depth), and the PP81/KPP vertical-mixing switches — with their rank-0 log lines.

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
integer, intent(out), optional :: ierr

Non-zero when any of the sub-closures configured here (EPBL/kappa-shear/tidal-mixing/conv/ddiff/wavespeed/Fox-Kemper) rejects the resolved configuration, when present; absent behaves as today (error stop).


Calls

proc~~configure_ocean_lateral~~CallsGraph proc~configure_ocean_lateral configure_ocean_lateral info info proc~configure_ocean_lateral->info proc~configure_ocean_conv configure_ocean_conv proc~configure_ocean_lateral->proc~configure_ocean_conv proc~configure_ocean_ddiff configure_ocean_ddiff proc~configure_ocean_lateral->proc~configure_ocean_ddiff proc~configure_ocean_epbl configure_ocean_epbl proc~configure_ocean_lateral->proc~configure_ocean_epbl proc~configure_ocean_foxkemper configure_ocean_foxkemper proc~configure_ocean_lateral->proc~configure_ocean_foxkemper proc~configure_ocean_kappa_shear configure_ocean_kappa_shear proc~configure_ocean_lateral->proc~configure_ocean_kappa_shear proc~configure_ocean_meke configure_ocean_meke proc~configure_ocean_lateral->proc~configure_ocean_meke proc~configure_ocean_tidal_mixing configure_ocean_tidal_mixing proc~configure_ocean_lateral->proc~configure_ocean_tidal_mixing proc~configure_ocean_varmix configure_ocean_varmix proc~configure_ocean_lateral->proc~configure_ocean_varmix proc~configure_ocean_wavespeed configure_ocean_wavespeed proc~configure_ocean_lateral->proc~configure_ocean_wavespeed proc~configure_ocean_z_fixed_profile configure_ocean_z_fixed_profile proc~configure_ocean_lateral->proc~configure_ocean_z_fixed_profile proc~configure_rho_target configure_rho_target proc~configure_ocean_lateral->proc~configure_rho_target proc~fail fail proc~configure_ocean_lateral->proc~fail proc~hycom_top_dz hycom_top_dz proc~configure_ocean_lateral->proc~hycom_top_dz proc~metrics_dx_min metrics_dx_min proc~configure_ocean_lateral->proc~metrics_dx_min proc~parse_buoyancy_coeffs parse_buoyancy_coeffs proc~configure_ocean_lateral->proc~parse_buoyancy_coeffs proc~parse_kpp_sw_method parse_kpp_sw_method proc~configure_ocean_lateral->proc~parse_kpp_sw_method proc~parse_lateral_closure parse_lateral_closure proc~configure_ocean_lateral->proc~parse_lateral_closure proc~parse_ocean_vcoord_type parse_ocean_vcoord_type proc~configure_ocean_lateral->proc~parse_ocean_vcoord_type proc~parse_remap_method parse_remap_method proc~configure_ocean_lateral->proc~parse_remap_method proc~vmix_seed_backgrounds ocean_vmix_t%vmix_seed_backgrounds proc~configure_ocean_lateral->proc~vmix_seed_backgrounds to_string to_string proc~configure_ocean_lateral->to_string proc~configure_ocean_conv->info proc~configure_ocean_conv->proc~fail proc~configure_ocean_conv->to_string proc~configure_ocean_ddiff->info proc~configure_ocean_ddiff->proc~fail proc~configure_ocean_ddiff->to_string proc~configure_ocean_epbl->info proc~configure_ocean_epbl->proc~fail proc~configure_ocean_epbl->to_string proc~fill_coriolis_centre fill_coriolis_centre proc~configure_ocean_epbl->proc~fill_coriolis_centre proc~parse_epbl_combine parse_epbl_combine proc~configure_ocean_epbl->proc~parse_epbl_combine proc~parse_epbl_lt_scheme parse_epbl_lt_scheme proc~configure_ocean_epbl->proc~parse_epbl_lt_scheme proc~parse_epbl_mstar_scheme parse_epbl_mstar_scheme proc~configure_ocean_epbl->proc~parse_epbl_mstar_scheme proc~parse_epbl_vstar_scheme parse_epbl_vstar_scheme proc~configure_ocean_epbl->proc~parse_epbl_vstar_scheme proc~configure_ocean_foxkemper->info proc~configure_ocean_foxkemper->proc~fail proc~configure_ocean_foxkemper->to_string proc~configure_ocean_kappa_shear->info proc~configure_ocean_kappa_shear->proc~fail proc~configure_ocean_kappa_shear->to_string proc~configure_ocean_kappa_shear->proc~fill_coriolis_centre proc~fill_coriolis_corner fill_coriolis_corner proc~configure_ocean_kappa_shear->proc~fill_coriolis_corner proc~ocean_kappa_shear_init_vertex ocean_kappa_shear_t%ocean_kappa_shear_init_vertex proc~configure_ocean_kappa_shear->proc~ocean_kappa_shear_init_vertex warning warning proc~configure_ocean_kappa_shear->warning proc~configure_ocean_meke->proc~fill_coriolis_centre proc~ocean_meke_set_f_centre ocean_meke_t%ocean_meke_set_f_centre proc~configure_ocean_meke->proc~ocean_meke_set_f_centre proc~configure_ocean_tidal_mixing->info proc~configure_ocean_tidal_mixing->proc~fail proc~configure_ocean_tidal_mixing->to_string proc~ocean_tidal_mixing_set_e_uniform ocean_tidal_mixing_t%ocean_tidal_mixing_set_e_uniform proc~configure_ocean_tidal_mixing->proc~ocean_tidal_mixing_set_e_uniform proc~configure_ocean_varmix->proc~fill_coriolis_centre proc~ocean_varmix_build_static ocean_varmix_t%ocean_varmix_build_static proc~configure_ocean_varmix->proc~ocean_varmix_build_static proc~configure_ocean_wavespeed->proc~fail proc~configure_ocean_wavespeed->proc~fill_coriolis_centre proc~ocean_wave_speed_build_static ocean_wave_speed_t%ocean_wave_speed_build_static proc~configure_ocean_wavespeed->proc~ocean_wave_speed_build_static proc~configure_ocean_z_fixed_profile->info proc~configure_ocean_z_fixed_profile->to_string proc~ocean_vcoord_set_z_fixed_profile ocean_vcoord_set_z_fixed_profile proc~configure_ocean_z_fixed_profile->proc~ocean_vcoord_set_z_fixed_profile proc~parse_z_fixed_profile parse_z_fixed_profile proc~configure_ocean_z_fixed_profile->proc~parse_z_fixed_profile proc~z_fixed_nominal_dz z_fixed_nominal_dz proc~configure_ocean_z_fixed_profile->proc~z_fixed_nominal_dz error error proc~fail->error proc~error_ring_push error_ring_push proc~fail->proc~error_ring_push proc~parse_vcoord_type parse_vcoord_type proc~parse_ocean_vcoord_type->proc~parse_vcoord_type proc~metrics_fill_coriolis metrics_fill_coriolis proc~fill_coriolis_centre->proc~metrics_fill_coriolis proc~parse_coriolis_scheme parse_coriolis_scheme proc~fill_coriolis_centre->proc~parse_coriolis_scheme proc~fill_coriolis_corner->proc~metrics_fill_coriolis proc~fill_coriolis_corner->proc~parse_coriolis_scheme proc~varmix_dfdx_vface varmix_dfdx_vface proc~ocean_varmix_build_static->proc~varmix_dfdx_vface proc~varmix_dfdy_uface varmix_dfdy_uface proc~ocean_varmix_build_static->proc~varmix_dfdy_uface

Called by

proc~~configure_ocean_lateral~~CalledByGraph proc~configure_ocean_lateral configure_ocean_lateral proc~engine_setup engine_setup proc~engine_setup->proc~configure_ocean_lateral 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
integer, private :: local_ierr

Source Code

   subroutine configure_ocean_lateral(cfg, ocean_state, grid, compute_rank, ierr)
      !! Flow-aware lateral-viscosity closure (Leith / Smagorinsky + biharmonic
      !! Smagorinsky_AH), the vertical coordinate (VCOORD_* code + z_fixed
      !! reference depth), and the PP81/KPP vertical-mixing switches — with
      !! their rank-0 log lines.
      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
      integer, intent(out), optional :: ierr
         !! Non-zero when any of the sub-closures configured here
         !! (EPBL/kappa-shear/tidal-mixing/conv/ddiff/wavespeed/Fox-Kemper)
         !! rejects the resolved configuration, when present; absent
         !! behaves as today (`error stop`).
      integer :: local_ierr

      ! Lateral closure + background/cap coefficients.  ah_bg defaults to the
      ! MOM6 floor max(nu_h, kh_vel_scale·dx) when cfg leaves it negative.
      ocean_state%lateral_mix%closure = parse_lateral_closure(cfg%ocean%hvisc%lateral_closure)
      ocean_state%lateral_mix%no_slip = cfg%ocean%hvisc%no_slip
      ocean_state%lateral_mix%c_smag = cfg%ocean%hvisc%c_smag
      ocean_state%lateral_mix%c_leith = cfg%ocean%hvisc%c_leith
      if (cfg%ocean%hvisc%ah_bg >= 0.0_wp) then
         ocean_state%lateral_mix%ah_bg = cfg%ocean%hvisc%ah_bg
      else
         ocean_state%lateral_mix%ah_bg = &
            max(cfg%ocean%hvisc%nu_h, cfg%ocean%hvisc%kh_vel_scale* &
                metrics_dx_min(ocean_state%metrics, grid))
      end if
      ocean_state%lateral_mix%ah_max = cfg%ocean%hvisc%ah_max
      ocean_state%lateral_mix%kh_vel_scale_live = cfg%ocean%hvisc%kh_vel_scale_live
      ocean_state%lateral_mix%smag_ah_active = cfg%ocean%hvisc%smag_ah
      ocean_state%lateral_mix%smag_bi_const = cfg%ocean%hvisc%smag_bi_const
      ocean_state%lateral_mix%c_leith_bi = cfg%ocean%hvisc%c_leith_bi
      ocean_state%lateral_mix%nu4_bg = cfg%ocean%hvisc%nu_4_bg
      ocean_state%lateral_mix%nu4_max = cfg%ocean%hvisc%nu_4_max
      ocean_state%lateral_mix%resoln_scaled_visc = cfg%ocean%hvisc%resoln_scaled_visc
      if (compute_rank == 0) then
         select case (ocean_state%lateral_mix%closure)
         case (LMIX_NONE)
            call logger%info("Lateral closure:  none (scalar ocean_nu_h)")
         case (LMIX_LEITH)
            call logger%info("Lateral closure:  Leith  C_L="//to_string(cfg%ocean%hvisc%c_leith)// &
                             "  ah_bg="//to_string(ocean_state%lateral_mix%ah_bg)// &
                             "  ah_max="//to_string(cfg%ocean%hvisc%ah_max)//" m²/s")
         case (LMIX_SMAGORINSKY)
            call logger%info("Lateral closure:  Smagorinsky  C_S="//to_string(cfg%ocean%hvisc%c_smag)// &
                             "  ah_bg="//to_string(ocean_state%lateral_mix%ah_bg)// &
                             "  ah_max="//to_string(cfg%ocean%hvisc%ah_max)//" m²/s")
         case (LMIX_LEITH_BIHARM)
            call logger%info("Lateral closure:  Leith-biharmonic  C_lb="//to_string(cfg%ocean%hvisc%c_leith_bi)// &
                             "  nu4_bg="//to_string(ocean_state%lateral_mix%nu4_bg)// &
                             "  nu4_max="//to_string(cfg%ocean%hvisc%nu_4_max)//" m⁴/s")
            ! PR-6: the c_leith_bi<=0 "inert" case is now a configure-time
            ! ABORT in validate_config (leith_biharm_is_inert), so setup is
            ! never reached with an inert leith_biharm — the warning here
            ! would be dead code.
         case (LMIX_BIHARMONIC)
            call logger%info("Lateral closure:  constant biharmonic  nu_4="//to_string(cfg%ocean%hvisc%nu_4)//" m⁴/s")
         case default
            ! LMIX_NONE handled above; any other code is rejected at
            ! configure (validate_config), so this is unreachable.
            call logger%info("Lateral closure:  "//trim(cfg%ocean%hvisc%lateral_closure))
         end select
         if (ocean_state%lateral_mix%smag_ah_active) then
            call logger%info("Biharmonic closure: Smagorinsky_AH  C_b="// &
                             to_string(cfg%ocean%hvisc%smag_bi_const)// &
                             "  nu4_bg="//to_string(cfg%ocean%hvisc%nu_4_bg)// &
                             "  nu4_max="//to_string(cfg%ocean%hvisc%nu_4_max)//" m⁴/s")
         end if
      end if

      ! Vertical coordinate: namelist string → VCOORD_* code; z_fixed needs a
      ! reference total depth.  Without this every vcoord_type silently ran
      ! Eulerian-z (no ALE remap).
      ocean_state%vcoord%coord_type = parse_ocean_vcoord_type(cfg%vcoord_type)
      ocean_state%vcoord%remap_method = parse_remap_method(cfg%remap_method)
      call configure_ocean_z_fixed_profile(cfg, ocean_state, compute_rank, log_it=.false.)
      ! Isopycnal (VCOORD_RHO) target densities: a uniform light->dense
      ! linspace from rho_target_light/dense (MOM6 ALE_COORDINATE_CONFIG=
      ! UNIFORM analogue).  `rho_target(0)` is the lightest (surface)
      ! interface, `rho_target(nz)` the densest (bed).
      ocean_state%vcoord%rho_ref_pressure = cfg%rho_ref_pressure
      if (allocated(ocean_state%vcoord%rho_target)) then
         call configure_rho_target(cfg, ocean_state%vcoord%rho_target, ocean_state%vcoord%nz_ml)
      end if
      ! ALE-regrid refinements (all default-off ⇒ bit-identical).
      ocean_state%vcoord%regrid_time_scale = cfg%regrid_time_scale
      ocean_state%vcoord%remap_vel_conserve_ke = cfg%remap_vel_conserve_ke
      ocean_state%vcoord%remap_boundary_extrap = cfg%remap_boundary_extrap
      ocean_state%vcoord%remap_nonuniform_weights = cfg%remap_nonuniform_weights
      ocean_state%vcoord%remap_check_preconditions = cfg%remap_check_preconditions
      ocean_state%vcoord%check_vanished_content = cfg%check_vanished_content
      if (compute_rank == 0) then
         if (ocean_state%vcoord%coord_type == VCOORD_EULERIAN_Z) then
            call logger%info("Vertical coord:  "//trim(cfg%vcoord_type)// &
                             " → EULERIAN_Z (no ALE remap)")
         else if (ocean_state%vcoord%coord_type == VCOORD_RHO) then
            ! RHO is state-dependent: the regrid silently falls back to a
            ! geometric grid if S/T or the EOS handle are unavailable at
            ! remap time.  Make activation + its requirement visible here
            ! (a hard configure-time S/T check is a deferred follow-up —
            ! S/T are always registered on the default ocean path).
            call logger%info("Vertical coord:  rho (isopycnal, ALE remap; "// &
                             "validation-grade — needs S+T + EOS; rho_ref_p="// &
                             to_string(cfg%rho_ref_pressure)//" Pa, targets "// &
                             to_string(ocean_state%vcoord%rho_target(0))//" .. "// &
                             to_string(ocean_state%vcoord%rho_target( &
                                       ocean_state%vcoord%nz_ml))//" kg/m^3 ("// &
                             trim(cfg%rho_target_profile)//"))")
         else if (ocean_state%vcoord%coord_type == VCOORD_HYCOM) then
            ! HYCOM = the RHO density-space inversion + a z* nominal-floor
            ! sweep (fixed-resolution surface band, isopycnal interior).
            ! Reuses the RHO rho_target + EOS; the floor is the z* nominal
            ! profile in metres (z_fixed_profile).  Needs S+T.
            call logger%info("Vertical coord:  hycom (hybrid z*/isopycnal, ALE "// &
                             "remap; needs S+T + EOS; rho_ref_p="// &
                             to_string(cfg%rho_ref_pressure)//" Pa, targets "// &
                             to_string(ocean_state%vcoord%rho_target(0))//" .. "// &
                             to_string(ocean_state%vcoord%rho_target( &
                                       ocean_state%vcoord%nz_ml))//" kg/m^3 ("// &
                             trim(cfg%rho_target_profile)//"); z* floor "// &
                             trim(cfg%z_fixed_profile)//", top dz "// &
                             to_string(hycom_top_dz(ocean_state%vcoord))//" m)")
         else
            call logger%info("Vertical coord:  "//trim(cfg%vcoord_type)// &
                             " (code "//to_string(ocean_state%vcoord%coord_type)// &
                             ", ALE remap enabled)  h_surf_target="// &
                             to_string(cfg%zstar_h_surf_target)//" m")
         end if
         ! Echo the IC thickness seed only when it is NOT the default, so the
         ! banner stays unchanged for every existing config.  Worth surfacing:
         ! "uniform_z" changes the resting isopycnal geometry, not just a
         ! tolerance, and is otherwise invisible after t=0.
         if (trim(cfg%thickness_config) /= "sigma") then
            call logger%info("Initial thickness: "//trim(cfg%thickness_config)// &
                             " (MOM6 uniform-z interfaces over max_depth="// &
                             to_string(cfg%ocean%topo%max_depth)// &
                             " m, clipped to bathymetry; flat resting isopycnals)")
         end if
      end if

      ! Vertical mixing: PP81 interior + KPP boundary-layer overlay.
      ocean_state%vmix%use_closure = cfg%ocean%vmix%use_closure
      ocean_state%vmix%use_kpp = cfg%ocean%vmix%use_kpp
      ! Shared EOS handle: KPP B0 reads the surface α/β from here so
      ! it tracks the dyn-core EOS (was a private never-refreshed copy).
      ocean_state%vmix%eos = ocean_state%eos

      ! PP81 interior closure + KPP BL-depth constants (`&ocean_vmix_nml
      ! pp81_*`/`kpp_*`).  Each default equals the ocean_vmix_t field
      ! default, so an nml that never mentions these keys is bit-identical.
      ocean_state%vmix%pp81_nu0 = cfg%ocean%vmix%pp81_nu0
      ocean_state%vmix%pp81_nu_bg = cfg%ocean%vmix%pp81_nu_bg
      ocean_state%vmix%pp81_kappa_bg = cfg%ocean%vmix%pp81_kappa_bg
      ocean_state%vmix%pp81_alpha = cfg%ocean%vmix%pp81_alpha
      ocean_state%vmix%shear2_floor = cfg%ocean%vmix%shear2_floor
      ocean_state%vmix%ri_crit = cfg%ocean%vmix%kpp_ri_crit
      ocean_state%vmix%cs_nonlocal = cfg%ocean%vmix%kpp_cs_nonlocal
      ocean_state%vmix%c_vt2 = cfg%ocean%vmix%kpp_c_vt2
      ! (PR-21) KPP shortwave-in-BL method (&ocean_thermo_nml
      ! kpp_sw_method).  validate_config already rejected unknown strings.
      ocean_state%vmix%kpp_sw_method = &
         parse_kpp_sw_method(trim(cfg%ocean%thermo%kpp_sw_method))
      ! (E4) Source of the α/β pair behind the KPP `B_0` and the
      ! double-diffusion density ratio (`&ocean_vmix_nml
      ! buoyancy_coeffs`).  `nml_enum allowed=` already rejected an
      ! unknown spelling at parse time; the INVALID arm here is the
      ! belt-and-braces guard that keeps the schema list and
      ! `parse_buoyancy_coeffs` from drifting apart silently (a wrong α is
      ! a physics change with no symptom, so never a fallback).  Both this
      ! and the `p_top` gate below are plain host scalars latched BEFORE
      ! `ocean_state_enter_data`'s `copyin`, which is the same contract
      ! `rho0` and the `pp81_*` scalars beside them rely on.
      ocean_state%vmix%buoyancy_coeffs = &
         parse_buoyancy_coeffs(trim(cfg%ocean%vmix%buoyancy_coeffs))
      if (ocean_state%vmix%buoyancy_coeffs == BUOY_COEFFS_INVALID) then
         call fail("ocean_vmix_nml: buoyancy_coeffs='"// &
                   trim(cfg%ocean%vmix%buoyancy_coeffs)// &
                   "' is not a known source; use 'constant' or 'eos'", &
                   ierr, OCEAN_STATUS_ERR_SETUP)
         return
      end if
      ! Mirror of the E3 `&ocean_psurf_nml in_eos` seam gate, taken from
      ! the SAME knob EPBL's `in_eos` takes so the two boundary-layer
      ! schemes measure their in-situ pressures from the same origin.
      ! NOT redundant with `p_top` being zero — a cavity fills `p_top`
      ! with the ice load whether or not `in_eos` is set.
      ocean_state%vmix%p_top_in_eos = cfg%ocean%psurf%in_eos
      ! MANDATORY re-derive: kv_bg/kt_bg/ks_bg and the kt/ks/kd_bg arrays
      ! were set from the TYPE-DEFAULT pp81_* at `init` time (before this
      ! configure call runs); without this, `vmix_assemble`'s background
      ! floor would silently clamp against the old default even though
      ! the user just set a new pp81_nu_bg/pp81_kappa_bg, and a
      ! Bryan-Lewis/Henyey background config would lose `kd_bg` entirely
      ! on every warm restart (`kd_bg` is set ONLY here) — see
      ! `vmix_seed_backgrounds`'s docstring.  `kv` alone is skipped when
      ! `engine_setup`'s restart read actually found it in the checkpoint
      ! (PR-2, bt-rem-from-av-rem: `vmix%kv_from_restart`, set by
      ! `ocean_state_restart_read`) -- it is the one CARRIED field here
      ! (`visc_rem_precompute` reads the PREVIOUS stage's `kv`), so an
      ! unconditional reseed would stomp the just-restored value back to
      ! the cold background on every resume; a cold start or an older
      ! checkpoint without `vmix_kv` leaves `kv_from_restart = .false.`
      ! and `kv` still reseeds normally.  Runs before
      ! `ocean_state_enter_data` (driver), so no `!$acc update` is needed
      ! either way -- the host array this leaves in place (restored or
      ! freshly seeded) is what the later `copyin` maps.
      call ocean_state%vmix%seed_backgrounds(skip_kv=ocean_state%vmix%kv_from_restart)

      ! EPBL — energetics-based PBL.  Replaces the KPP overlay when
      ! enabled (mutually exclusive surface schemes); the PP81
      ! interior + background continue underneath.
      if (present(ierr)) then
         call configure_ocean_epbl(cfg, ocean_state, grid, compute_rank, ierr=local_ierr)
         if (local_ierr /= 0) then
            ierr = local_ierr
            return
         end if
      else
         call configure_ocean_epbl(cfg, ocean_state, grid, compute_rank)
      end if

      ! Kappa-shear — JHL08 shear-driven INTERIOR closure.  Coexists
      ! with KPP and EPBL (no mutual exclusion); its kappa is added to
      ! the interior diffusivities.
      if (present(ierr)) then
         call configure_ocean_kappa_shear(cfg, ocean_state, grid, compute_rank, ierr=local_ierr)
         if (local_ierr /= 0) then
            ierr = local_ierr
            return
         end if
      else
         call configure_ocean_kappa_shear(cfg, ocean_state, grid, compute_rank)
      end if

      ! Tidal mixing — St-Laurent/Simmons internal-tide INTERIOR closure.
      ! Coexists with KPP/EPBL and PP81/background/kappa-shear (no mutual
      ! exclusion); its Kd is added to the interior diffusivities.
      if (present(ierr)) then
         call configure_ocean_tidal_mixing(cfg, ocean_state, compute_rank, ierr=local_ierr)
         if (local_ierr /= 0) then
            ierr = local_ierr
            return
         end if
      else
         call configure_ocean_tidal_mixing(cfg, ocean_state, compute_rank)
      end if

      ! Convective adjustment — Brunt-Vaisala-triggered INTERIOR closure
      ! CONTRIBUTOR (max() floor on kv/kt).  Coexists with KPP/EPBL (masks
      ! against whichever BL depth is live); no mutual exclusion.
      if (present(ierr)) then
         call configure_ocean_conv(cfg, ocean_state, compute_rank, ierr=local_ierr)
         if (local_ierr /= 0) then
            ierr = local_ierr
            return
         end if
      else
         call configure_ocean_conv(cfg, ocean_state, compute_rank)
      end if

      ! Double diffusion — salt fingering + diffusive convection, folded
      ! INTO the heat/salt split (asymmetric ks vs kt).  Reads the mirrored
      ! vmix%eos coefficients set above.
      if (present(ierr)) then
         call configure_ocean_ddiff(cfg, ocean_state, compute_rank, ierr=local_ierr)
         if (local_ierr /= 0) then
            ierr = local_ierr
            return
         end if
      else
         call configure_ocean_ddiff(cfg, ocean_state, compute_rank)
      end if

      ! Wave speed — B1 first-baroclinic cg1 + Rd diagnostic.
      if (present(ierr)) then
         call configure_ocean_wavespeed(cfg, ocean_state, grid, ierr=local_ierr)
         if (local_ierr /= 0) then
            ierr = local_ierr
            return
         end if
      else
         call configure_ocean_wavespeed(cfg, ocean_state, grid)
      end if
      ! Fox-Kemper MLE restratification (B5) — reads epbl%mld; configure
      ! after EPBL so the enable check sees the resolved EPBL state.
      if (present(ierr)) then
         call configure_ocean_foxkemper(cfg, ocean_state, compute_rank, ierr=local_ierr)
         if (local_ierr /= 0) then
            ierr = local_ierr
            return
         end if
      else
         call configure_ocean_foxkemper(cfg, ocean_state, compute_rank)
      end if

      ! VarMix (capability [4]) — build the STATIC f2_dx2/beta_dx2/L2 face
      ! terms once from the (filled) metrics + cell-centre Coriolis.  Runs
      ! after wavespeed (cg1) + metrics; the per-step Res_fn/SN/assembly
      ! fire at thermo cadence in the dyn step.  No-op when disabled.
      call configure_ocean_varmix(cfg, ocean_state, grid)

      ! MEKE (capability [5]) — fill the cell-centre |f| from the same
      ! Coriolis path VarMix/EPBL use, so the Rhines length is live when
      ! alpha_rhines > 0.  No-op when MEKE is disabled.  Scalar knobs are
      ! copied earlier by `ocean_state_copy_config`.
      call configure_ocean_meke(cfg, ocean_state, grid)

      if (compute_rank == 0) then
         if (ocean_state%epbl%enable) then
            call logger%info("Vertical mixing: PP81 interior + EPBL boundary layer")
         else if (ocean_state%vmix%use_closure .and. ocean_state%vmix%use_kpp) then
            call logger%info("Vertical mixing: PP81 interior + KPP overlay enabled")
         else if (ocean_state%vmix%use_closure) then
            call logger%info("Vertical mixing: PP81 interior only (KPP off)")
         else
            call logger%info("Vertical mixing: scalar K_v fallback (closure disabled)")
         end if
         if (cfg%ocean%vmix%pp81_nu0 /= 1.0e-2_wp .or. cfg%ocean%vmix%pp81_nu_bg /= 1.0e-4_wp .or. &
             cfg%ocean%vmix%pp81_kappa_bg /= 1.0e-5_wp .or. cfg%ocean%vmix%pp81_alpha /= 5.0_wp .or. &
             cfg%ocean%vmix%shear2_floor /= 1.0e-10_wp) then
            call logger%info("PP81 closure:     nu0="//to_string(cfg%ocean%vmix%pp81_nu0)// &
                             "  nu_bg="//to_string(cfg%ocean%vmix%pp81_nu_bg)// &
                             "  kappa_bg="//to_string(cfg%ocean%vmix%pp81_kappa_bg)// &
                             "  alpha="//to_string(cfg%ocean%vmix%pp81_alpha)// &
                             "  shear2_floor="//to_string(cfg%ocean%vmix%shear2_floor))
         end if
         if (cfg%ocean%vmix%kpp_ri_crit /= 0.3_wp .or. cfg%ocean%vmix%kpp_cs_nonlocal /= 6.3_wp .or. &
             cfg%ocean%vmix%kpp_c_vt2 /= 1.8_wp) then
            call logger%info("KPP constants:    ri_crit="//to_string(cfg%ocean%vmix%kpp_ri_crit)// &
                             "  cs_nonlocal="//to_string(cfg%ocean%vmix%kpp_cs_nonlocal)// &
                             "  c_vt2="//to_string(cfg%ocean%vmix%kpp_c_vt2))
         end if
      end if
      if (present(ierr)) ierr = OCEAN_STATUS_OK
   end subroutine configure_ocean_lateral