Copy the &ocean_kappa_shear_nml knobs onto the kappa-shear slot
— every field, end to end (don’t repeat the KPP dead-config
gap). Fills f_centre from the same beta-plane parameters the
Coriolis slot uses, copies the EOS hookup, and validates. No
mutual exclusion: kappa-shear is an interior closure that
coexists with KPP / EPBL and PP81/background.
| 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 on a kappa-shear configuration conflict when present;
absent behaves as today ( |
subroutine configure_ocean_kappa_shear(cfg, ocean_state, grid, compute_rank, ierr) !! Copy the `&ocean_kappa_shear_nml` knobs onto the kappa-shear slot !! — every field, end to end (don't repeat the KPP dead-config !! gap). Fills `f_centre` from the same beta-plane parameters the !! Coriolis slot uses, copies the EOS hookup, and validates. No !! mutual exclusion: kappa-shear is an interior closure that !! coexists with KPP / EPBL and PP81/background. 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 on a kappa-shear configuration conflict when present; !! absent behaves as today (`error stop`). associate (ks => ocean_state%kshear, kcfg => cfg%ocean%kshear) ! `enable` is flipped LAST, only once every validation below has ! passed (P0.1 review F9 — `configure_ocean_porous` is the ! pattern): setting it up front left a half-configured slot ! flagged enabled on any of this routine's many failure returns. if (.not. kcfg%enable) then ks%enable = .false. if (present(ierr)) ierr = OCEAN_STATUS_OK return end if ! Fail loud at configure (wavespeed precedent): both kappa-shear ! kernels gather into fixed-size NZ_STACK_MAX column arrays, so ! nz > NZ_STACK_MAX would silently overrun per-thread stack. if (ocean_state%multilayer%nz_ml > NZ_STACK_MAX) then call fail("ocean_kappa_shear_nml: nz_layers exceeds "// & "NZ_STACK_MAX (raise NZ_STACK_MAX in rdb_constants)", ierr, OCEAN_STATUS_ERR_SETUP) return end if ! Every JHL08 knob (no dead-config gaps). ks%ri_crit = kcfg%ri_crit ks%shearmix_rate = kcfg%shearmix_rate ks%fri_curvature = kcfg%fri_curvature ks%c_n = kcfg%c_n ks%c_s = kcfg%c_s ks%lambda = kcfg%lambda ks%lz_rescale = kcfg%lz_rescale ks%kappa_0 = kcfg%kappa_0 ks%kappa_seed = kcfg%kappa_seed ks%kappa_trunc = kcfg%kappa_trunc ks%tke_bg = kcfg%tke_bg ks%tol_err = kcfg%tol_err ks%max_inner_it = kcfg%max_inner_it ks%max_substep_it = kcfg%max_substep_it ks%src_max_chg = kcfg%src_max_chg ks%prandtl_turb = kcfg%prandtl_turb ks%vel_underflow = kcfg%vel_underflow ks%massless_merge = kcfg%massless_merge ks%vertex_geometric_mean = kcfg%vertex_geometric_mean ks%vertex_geomean_kdmin = kcfg%vertex_geomean_kdmin ! EOS hookup: the buoyancy derivatives use the SAME EOS handle ! the dyn-core runs (shared flat-POD copy — one source of ! truth, no scalar copies that can drift). ks%eos = ocean_state%eos ks%rho0 = ocean_state%eos%rho0 ! |f| at cell centres, same scheme the Coriolis slot uses (D7). call fill_coriolis_centre(cfg, ocean_state%metrics, grid, ks%f_centre) ! Vertex form (MOM6 VERTEX_SHEAR): allocate the corner fields ! (deliberately not in `init` — the corner carrier is nz+1 full ! planes, paid only when selected) and fill the SIGNED corner ! f via the same generator-driven scheme (the kernel squares ! it). Runs before `ocean_state_enter_data`, so the new ! allocatables map with the slot. if (kcfg%at_vertex) then ! nghost >= 2 fail-loud (pv_adv_required_nghost precedent): ! corners are solved on [2,nx]x[2,ny] only, so the outermost ! array ring averages with never-solved zero ring corners. ! With nghost >= 2 that ring is entirely ghost cells and the ! owned-cell answer is exact; with nghost < 2 the depressed ! first-ring kt could leak into owned cells via smoothing. if (grid%nghost < 2) then call fail("ocean_kappa_shear_nml: at_vertex requires "// & "nghost >= 2 (corner solve covers interior "// & "corners only; got nghost="//to_string(grid%nghost)//")", ierr, OCEAN_STATUS_ERR_SETUP) return end if call ks%init_vertex(grid, ocean_state%multilayer%nz_ml) call fill_coriolis_corner(cfg, ocean_state%metrics, grid, ks%f_corner) end if ! Validation. if (.not. cfg%ocean%vmix%use_closure) then call fail("ocean_kappa_shear_nml: enable=.true. requires "// & "ocean_vmix_nml use_closure=.true. (kappa-shear "// & "folds into the interior closure's kv/kt)", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (.not. cfg%ocean%thermo%enable_thermodynamics) then call fail("ocean_kappa_shear_nml: enable=.true. requires "// & "active thermodynamics (kappa-shear reads T, S)", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (ks%ri_crit <= 0.0_wp) then call fail("ocean_kappa_shear_nml: ri_crit must be > 0", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (ks%max_inner_it < 1) then call fail("ocean_kappa_shear_nml: max_inner_it must be >= 1", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (ks%max_substep_it < 1) then call fail("ocean_kappa_shear_nml: max_substep_it must be >= 1", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (ks%kappa_0 <= 0.0_wp) then call fail("ocean_kappa_shear_nml: kappa_0 must be > 0", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (ks%tol_err <= 0.0_wp) then call fail("ocean_kappa_shear_nml: tol_err must be > 0", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (ks%prandtl_turb <= 0.0_wp) then call fail("ocean_kappa_shear_nml: prandtl_turb must be > 0", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (ks%vertex_geomean_kdmin < 0.0_wp) then call fail("ocean_kappa_shear_nml: vertex_geomean_kdmin must be >= 0", ierr, OCEAN_STATUS_ERR_SETUP) return end if if (compute_rank == 0) then ! Advisory warnings (MOM6-parity semantics, not errors). if (kcfg%vertex_geomean_kdmin > 0.0_wp .and. & .not. kcfg%vertex_geometric_mean) then call logger%warning("ocean_kappa_shear_nml: vertex_geomean_kdmin "// & "is inert without vertex_geometric_mean") end if if (kcfg%at_vertex .and. kcfg%vertex_geometric_mean .and. & kcfg%vertex_geomean_kdmin == 0.0_wp) then call logger%warning("ocean_kappa_shear_nml: geometric mean with "// & "kdmin=0 hard-zeros Kd wherever ANY corner is 0 "// & "(every shear-zone edge); production configs "// & "use vertex_geomean_kdmin=1e-9") end if end if if (compute_rank == 0) then call logger%info("Kappa-shear: interior closure enabled "// & "(ri_crit="//to_string(kcfg%ri_crit)// & " prandtl_turb="//to_string(kcfg%prandtl_turb)// & trim(merge(" at_vertex", " ", kcfg%at_vertex))//")") end if ks%enable = .true. end associate if (present(ierr)) ierr = OCEAN_STATUS_OK end subroutine configure_ocean_kappa_shear