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 | Intent | Optional | 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 ( |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | private | :: | local_ierr |
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