rdb_ocean_kappa_shear Module

Prognostic interior shear-mixing closure: shear-driven turbulence is modelled as a diffusivity field kappa(z) and a TKE field Q(z) at layer interfaces, coupled through two steady-state vertical diffusion-reaction equations solved per column, iteratively to convergence, with internal adaptive time-substepping as the column re-stratifies within one model step. Unlike algebraic Richardson-number schemes (PP81, LMD94) the diffusivity diffuses in z with a stratification/rotation/boundary-limited decay length, so resolved shear layers entrain at the right rate even when the Richardson number is marginal; the closure is self-limiting and relaxes the column toward Ri >~ Ri_c.

Reference: Jackson, Hallberg & Legg (2008), “A Parameterization of Shear-Driven Turbulence for Ocean Climate Models”, J. Phys. Oceanogr. 38, 1033-1053. Knob table: docs/generated_nml_knobs.md.

Place in the stack: an INTERIOR closure. It coexists with the surface boundary-layer schemes (KPP or EPBL) and with PP81/background; its kappa is ADDED to the other interior diffusivities (kt += kd_int) and its viscosity (prandtl_turb * kd_int) added to kv. Not mutually exclusive with anything. Runs at thermo cadence (kappa_shear_compute); the merge into vmix%kv / vmix%kt runs every RK2 stage (kappa_shear_merge_into_kv_kt).

Interface convention (same as rdb_ocean_vmix / EPBL): kd_int(:,:,K) lives at the bottom interface of layer K; global kd_int(:,:,1) is the bed and kd_int(:,:,nz+1) the free surface, both forced to exactly 0. Layers are bottom-up: k=1 bed, k=nz surface. The column solver itself runs surface-down (local k=1 = surface); the gather/scatter loops carry the index flip (global K = nz+2 - K_local).


Uses

  • module~~rdb_ocean_kappa_shear~~UsesGraph module~rdb_ocean_kappa_shear rdb_ocean_kappa_shear ieee_arithmetic ieee_arithmetic module~rdb_ocean_kappa_shear->ieee_arithmetic iso_fortran_env iso_fortran_env module~rdb_ocean_kappa_shear->iso_fortran_env module~rdb_constants rdb_constants module~rdb_ocean_kappa_shear->module~rdb_constants module~rdb_eos rdb_eos module~rdb_ocean_kappa_shear->module~rdb_eos module~rdb_grid rdb_grid module~rdb_ocean_kappa_shear->module~rdb_grid module~rdb_massless rdb_massless module~rdb_ocean_kappa_shear->module~rdb_massless module~rdb_mem_report rdb_mem_report module~rdb_ocean_kappa_shear->module~rdb_mem_report module~rdb_multilayer_state rdb_multilayer_state module~rdb_ocean_kappa_shear->module~rdb_multilayer_state pic_types pic_types module~rdb_constants->pic_types module~rdb_eos->module~rdb_constants module~rdb_eos->module~rdb_grid module~rdb_grid->module~rdb_constants module~rdb_massless->module~rdb_constants module~rdb_mem_report->iso_fortran_env module~rdb_mem_report->module~rdb_constants pic_logger pic_logger module~rdb_mem_report->pic_logger pic_strings pic_strings module~rdb_mem_report->pic_strings module~rdb_multilayer_state->iso_fortran_env module~rdb_multilayer_state->module~rdb_constants module~rdb_multilayer_state->module~rdb_grid module~rdb_multilayer_state->module~rdb_mem_report module~rdb_efp rdb_efp module~rdb_multilayer_state->module~rdb_efp module~rdb_error_ring rdb_error_ring module~rdb_multilayer_state->module~rdb_error_ring module~rdb_tracer rdb_tracer module~rdb_multilayer_state->module~rdb_tracer module~rdb_multilayer_state->pic_logger module~rdb_efp->ieee_arithmetic module~rdb_efp->iso_fortran_env module~rdb_error_ring->pic_logger module~rdb_tracer->iso_fortran_env module~rdb_tracer->module~rdb_constants module~rdb_tracer->module~rdb_grid module~rdb_tracer->module~rdb_mem_report

Used by

  • module~~rdb_ocean_kappa_shear~~UsedByGraph module~rdb_ocean_kappa_shear rdb_ocean_kappa_shear module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_ocean_kappa_shear module~rdb_ocean_state rdb_ocean_state module~rdb_ocean_state->module~rdb_ocean_kappa_shear module~rdb_ocean_state->module~rdb_ocean_dyn module~rdb_driver rdb_driver module~rdb_driver->module~rdb_ocean_dyn module~rdb_driver->module~rdb_ocean_state module~rdb_ocean_engine rdb_ocean_engine module~rdb_driver->module~rdb_ocean_engine module~rdb_handle rdb_handle module~rdb_handle->module~rdb_ocean_state module~rdb_handle->module~rdb_ocean_engine module~rdb_ocean_api rdb_ocean_api module~rdb_ocean_api->module~rdb_ocean_dyn module~rdb_ocean_api->module~rdb_handle module~rdb_ocean_diag_derived rdb_ocean_diag_derived module~rdb_ocean_api->module~rdb_ocean_diag_derived module~rdb_ocean_diag_fills rdb_ocean_diag_fills module~rdb_ocean_api->module~rdb_ocean_diag_fills module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_ocean_diag_derived->module~rdb_ocean_state module~rdb_ocean_diag_derived->module~rdb_ocean_diag_fills module~rdb_ocean_diag_fills->module~rdb_ocean_state module~rdb_ocean_engine->module~rdb_ocean_dyn module~rdb_ocean_engine->module~rdb_ocean_state module~rdb_ocean_engine->module~rdb_ocean_diag_derived module~rdb_ocean_engine->module~rdb_ocean_diag_fills module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_setup module~rdb_ocean_setup->module~rdb_ocean_dyn module~rdb_ocean_setup->module~rdb_ocean_state

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private, parameter :: H_TINY_CORNER = 0.5_wp*H_DIV_EPS

Sub-roundoff thickness added to the 2-point thickness-weight denominator of the corner u/v average (pure 1/0 armour).

real(kind=wp), private, parameter :: MASK_SUM_EPS = 1.0e-36_wp

Armour added to a MASK sum (a nondimensional wet-cell count), so an all-land denominator gives 0/1e-36 = 0 rather than 0/0.

integer, private, parameter :: NZL = NZ_STACK_MAX

Maximum number of layers a single column kernel can solve.

integer, private, parameter :: NZLI = NZ_STACK_MAX+1

Interface array dimension (= NZL + 1).


Derived Types

type, public ::  ocean_kappa_shear_t

Components

Type Visibility Attributes Name Initial
logical, public :: at_vertex = .false.

Solve the JHL08 columns at C-grid CORNERS (vorticity points) instead of tracer points, then average the corner Kd back to tracer points (MOM6 VERTEX_SHEAR; the OM5-class production setting). The corner column sees the native face velocities without the u_h/v_h centre average, so resolved shear is not damped before the solve. Default off — bit-identical. Kd: corner solve -> Pass-C average -> kd_int at tracer points. Kv: routed corner->face (MOM6 Kv_shear_Bu consumed in vertvisc) — kd_corner feeds vdiff_apply_momentum’s kv_corner_source seam scaled by prandtl_turb, and the cell-centred kv merge is suppressed (no corner->centre->face smoothing, no double-count). Corner TKE is not carried (tke_int is zeroed in vertex mode).

real(kind=wp), public :: c_n = 0.24_wp

TKE decay vs N (TKE_N_DECAY_CONST).

real(kind=wp), public :: c_s = 0.14_wp

TKE decay vs shear (TKE_SHEAR_DECAY_CONST).

logical, public :: enable = .false.

Master switch. Default off — existing namelists and tests stay bit-identical. Requires vmix%use_closure + thermodynamics (validated at configure).

type(eos_t), public :: eos
real(kind=wp), public, allocatable :: f_centre(:,:)

|f| at cell centres (1/s); filled by set_f_centre.

real(kind=wp), public, allocatable :: f_corner(:,:)

SIGNED Coriolis f at C-grid corners (1/s), (nx+1,ny+1); corner (i,j) is the SW corner of cell (i,j). The kernel squares it (MOM6 vertex form takes f^2 straight at the corner, no 4-point average). Filled by set_f_corner (beta-plane) or fill_coriolis_corner at configure.

real(kind=wp), public :: fri_curvature = -0.97_wp

Ri-function curvature (FRI_CURVATURE).

logical, public :: is_init = .false.

True between init and destroy.

real(kind=wp), public :: kappa_0 = 1.0e-7_wp

Background diffusivity (m^2/s); also the pre-step kappa.

real(kind=wp), public :: kappa_seed = 1.0_wp

Iteration seed diffusivity (m^2/s).

real(kind=wp), public :: kappa_trunc = 1.0e-9_wp

Diffusivity below this -> 0 (m^2/s).

real(kind=wp), public, allocatable :: kd_corner(:,:,:)

Corner diffusivity at interfaces (m^2/s), (nx+1,ny+1,nz+1), global bottom-up. The Pass-B -> Pass-C carrier (MOM6 kappa_vertex): the corner solve writes it, the scatter kernel averages it to tracer points, and the momentum vdiff reads it as the corner Kv source (kv_corner_source, scaled by prandtl_turb — the corner->face viscosity seam, MOM6 Kv_shear_Bu). A REQUIRED snapshot — fusing solve+scatter would be a read-neighbour/write-own do concurrent race. Ring corners (ic=1, ic=nx+1, jc=1, jc=ny+1) are never solved and stay exactly 0.

real(kind=wp), public, allocatable :: kd_int(:,:,:)

Kappa-shear diffusivity at interfaces (m^2/s), (nx,ny,nz+1), global bottom-up: zero at bed (K=1) and surface (K=nz+1).

real(kind=wp), public :: lambda = 0.82_wp

Buoyancy length-scale coefficient (KAPPA_BUOY_SCALE_COEF).

real(kind=wp), public :: lz_rescale = 1.0_wp

Boundary-distance length-scale rescale (LZ_RESCALE).

logical, public :: massless_merge = .false.

is merged onto its massive sub-grid (rdb_massless), solved on nzc <= nz layers, and the kappa/TKE interface fields are interpolated back — replacing the blunt max(h, H_VANISHED) gather floor. Default off (bit-identical). A per-column any(h < H_VANISHED) precheck makes healthy columns bypass the merge machinery entirely, so knob-on stays bit-identical on healthy envelopes (invariant I1).

integer, public :: max_inner_it = 50

Inner Picard iteration cap (MAX_RINO_IT).

integer, public :: max_substep_it = 13

Outer adaptive substep cap (MAX_KAPPA_SHEAR_IT).

real(kind=wp), public :: prandtl_turb = 1.0_wp

Kv = prandtl_turb * Kd into the momentum solve.

real(kind=wp), public :: rho0 = 1035.0_wp

Boussinesq reference density (kg/m^3).

real(kind=wp), public :: ri_crit = 0.25_wp

Critical Richardson number (MOM6 RINO_CRIT).

real(kind=wp), public :: shearmix_rate = 0.089_wp

Source-rate coefficient (SHEARMIX_RATE).

real(kind=wp), public :: src_max_chg = 10.0_wp

Adaptive-dt source-change tolerance band.

real(kind=wp), public :: tke_bg = 0.0_wp

Background TKE (m^2/s^2); Q is a denominator, floored.

real(kind=wp), public, allocatable :: tke_int(:,:,:)

Time-mean TKE at interfaces (m^2/s^2), same shape/convention — diagnostic (currently filled to 0; reserved for the TKE budget diag). Vertex mode zeroes it (corner TKE not carried).

real(kind=wp), public :: tol_err = 0.1_wp

Picard convergence tolerance (KAPPA_SHEAR_TOL_ERR).

real(kind=wp), public :: vel_underflow = 0.0_wp

Velocity snap-to-zero magnitude (m/s) in the projection.

real(kind=wp), public :: vertex_geomean_kdmin = 0.0_wp

Floor (m^2/s) applied to each corner Kd BEFORE the geometric mean (MOM6 VERTEX_SHEAR_GEOMETRIC_MEAN_KDMIN; inert unless vertex_geometric_mean). With 0 the geometric mean hard- zeros Kd at every shear-zone edge; OM5 configs use 1e-9.

logical, public :: vertex_geometric_mean = .false.

Corner->centre averaging: geometric mean of the 4 corner Kd (MOM6 VERTEX_SHEAR_GEOMETRIC_MEAN) instead of the plain arithmetic mean. A geometric mean is 0 if ANY corner is 0 — pair with vertex_geomean_kdmin (see below).

Type-Bound Procedures

procedure, public, non_overridable :: bytes => ocean_kappa_shear_bytes
procedure, public, non_overridable :: destroy => ocean_kappa_shear_destroy
procedure, public, non_overridable :: enter_data => ocean_kappa_shear_enter_data
procedure, public, non_overridable :: exit_data => ocean_kappa_shear_exit_data
procedure, public, non_overridable :: init => ocean_kappa_shear_init
procedure, public, non_overridable :: init_vertex => ocean_kappa_shear_init_vertex
procedure, public, non_overridable :: set_f_centre => ocean_kappa_shear_set_f_centre
procedure, public, non_overridable :: set_f_corner => ocean_kappa_shear_set_f_corner

Functions

private pure function ks_adaptive_dt(nz, dt_rem, itt_outer, max_substep_it, ri_crit, shearmix_rate, fri_curvature, src_max_chg, tol_err, vel_underflow, dbuoy_t, dbuoy_s, h_s, u_cur, v_cur, t_cur, s_cur, kappa_out_s, kappa_src_s, local_src_s, local_src_avg_s, ks_kap, ke_kap, idz_int_s) result(dt_now_r)

Largest dt_test <= dt_rem such that, after mixing for 0.5*dt_test with kappa_out_s, the regenerated source stays within the tolerance bands of the accepted-state source (design doc section 5.3): a halving pass followed by a 5-step refinement pass.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt_rem
integer, intent(in) :: itt_outer
integer, intent(in) :: max_substep_it
real(kind=wp), intent(in) :: ri_crit
real(kind=wp), intent(in) :: shearmix_rate
real(kind=wp), intent(in) :: fri_curvature
real(kind=wp), intent(in) :: src_max_chg
real(kind=wp), intent(in) :: tol_err
real(kind=wp), intent(in) :: vel_underflow
real(kind=wp), intent(in) :: dbuoy_t(NZLI)
real(kind=wp), intent(in) :: dbuoy_s(NZLI)
real(kind=wp), intent(in) :: h_s(NZL)
real(kind=wp), intent(in) :: u_cur(NZL)
real(kind=wp), intent(in) :: v_cur(NZL)
real(kind=wp), intent(in) :: t_cur(NZL)
real(kind=wp), intent(in) :: s_cur(NZL)
real(kind=wp), intent(in) :: kappa_out_s(NZLI)
real(kind=wp), intent(in) :: kappa_src_s(NZLI)
real(kind=wp), intent(in) :: local_src_s(NZLI)
real(kind=wp), intent(in) :: local_src_avg_s(NZLI)
integer, intent(in) :: ks_kap
integer, intent(in) :: ke_kap
real(kind=wp), intent(in) :: idz_int_s(NZLI)

Return Value real(kind=wp)

private pure function ks_src_func(ri_crit, shearmix_rate, fri_curvature, n2, s2) result(ksrc)

Shear-source function K_src at one interface (JHL08 eq. for the source term): nonzero only where N^2 < Ri_c * S^2.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: ri_crit
real(kind=wp), intent(in) :: shearmix_rate
real(kind=wp), intent(in) :: fri_curvature
real(kind=wp), intent(in) :: n2
real(kind=wp), intent(in) :: s2

Return Value real(kind=wp)

private pure function ocean_kappa_shear_bytes(this) result(nbytes)

Counted allocatable footprint of the kappa-shear slot (0 when unallocated). One arr_bytes term per array — add a term here when a new allocatable joins the type.

Arguments

Type IntentOptional Attributes Name
class(ocean_kappa_shear_t), intent(in) :: this

Return Value integer(kind=int64)


Subroutines

public pure subroutine kappa_shear_compute(grid, this, ms, dt, wet_t, wet_u, wet_v)

Run kappa-shear over the domain: fill this%kd_int (interface diffusivity). Call at thermo cadence with the thermo dt. Outer shim: dereferences the tracer-registry hTr arrays on the host (the array-of-DT indirection blocks NVHPC device codegen), then forwards to the column kernel. Velocities reach the kernel as the C-grid face arrays, face-averaged to tracer points inside (same source as the PP81 interior shear).

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_kappa_shear_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in), optional :: wet_t(:,:)

Halo-valid tracer-cell wet mask (metrics%wet_T), (nx,ny).

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

u-face open mask (metrics%wet_u), (nx+1,ny).

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

v-face open mask (metrics%wet_v), (nx,ny+1).

public pure subroutine kappa_shear_merge_into_kv_kt(this, nx, ny, nzp1, kv, kt)

Fold the kappa-shear diffusivity into the vmix interface fields, ADDITIVELY (MOM6 interior-diffusivity semantics). Called EVERY stage (the interior closure rewrites kv/kt each stage; kd_int itself refreshes at thermo cadence). Interior interfaces only — K=1 (bed) and K=nz+1 (surface) stay zero in both source and target.

Read more…

Arguments

Type IntentOptional Attributes Name
type(ocean_kappa_shear_t), intent(in) :: this
integer, intent(in) :: nx

Interface-field extents (explicit shape: assumed-shape dummies in a do concurrent kernel make NVHPC walk the descriptor with per-launch memcpys — this runs every stage).

integer, intent(in) :: ny

Interface-field extents (explicit shape: assumed-shape dummies in a do concurrent kernel make NVHPC walk the descriptor with per-launch memcpys — this runs every stage).

integer, intent(in) :: nzp1

Interface-field extents (explicit shape: assumed-shape dummies in a do concurrent kernel make NVHPC walk the descriptor with per-launch memcpys — this runs every stage).

real(kind=wp), intent(inout) :: kv(nx,ny,nzp1)

Momentum viscosity at interfaces; gets prandtl_turb*kd (column mode) or nothing (vertex mode — see above).

real(kind=wp), intent(inout) :: kt(nx,ny,nzp1)

Tracer diffusivity at interfaces; gets kd.

public pure subroutine kappa_shear_vertex_scatter(nx, ny, nzp1, geometric, kdmin, wet_t, kd_corner, kd_int, tke_int)

Corner -> tracer-point averaging (MOM6 vertex form, Pass C). Cell (i,j) reads its four corners SW=(i,j), SE=(i+1,j), NW=(i,j+1), NE=(i+1,j+1) — a read-only corner stencil into an own-cell write, safe as its own do concurrent but NEVER fusable with the corner solve (kd_corner is the required snapshot). Two modes: arithmetic (default): 0.25 * ((SW+NE) + (NW+SE)) geometric: 4th root of the product of the four corner values, each floored at kdmin first — a geometric mean is 0 if ANY factor is 0, which would otherwise blank Kd along every shear-zone edge. The floor applies to the CORNER values only, never the output: a land cell still gets exactly 0 via the wet_t multiply. Endpoints (bed K=1, surface K=nzp1) are forced to exactly 0. tke_int is zeroed — the vertex form does not carry a cell-centred TKE (corner TKE deliberately not materialised). Bracketing is reproducible-sum ordering; keep literal.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nzp1
logical, intent(in) :: geometric
real(kind=wp), intent(in) :: kdmin
real(kind=wp), intent(in) :: wet_t(nx,ny)
real(kind=wp), intent(in) :: kd_corner(nx+1,ny+1,nzp1)
real(kind=wp), intent(out) :: kd_int(nx,ny,nzp1)
real(kind=wp), intent(out) :: tke_int(nx,ny,nzp1)

public pure subroutine ks_gather_corner(nx, ny, nz, ic, jc, h_layer, u_face, v_face, hT, hS, wet_t, wet_u, wet_v, h_sd, u_sd, v_sd, t_sd, s_sd)

Assemble the surface-down column at corner (ic,jc) from the 2x2 cell patch + 4 adjacent faces (JHL08 vertex form; the interpolation recipes of the reference implementation): u,v : 2-point THICKNESS-weighted average across the corner, with the face thickness itself a mask-weighted 2-cell average (recomputed inline — deterministic, so the repeated evaluation is bitwise identical to MOM6’s precomputed h_at_u/h_at_v Pass A, non-OBC-bug form). T,S : 4-cell mask-AND-thickness-weighted average. The registry stores hTr = hT, which is exactly the weighted quantity, so we sum wethTr directly. h : 4-cell mask-weighted average (no thickness weight — it IS the thickness). Returns RAW h (no floor) — the caller decides floor vs massless-merge exactly as the column path does. The deliberate (SW+NE)+(SE+NW) bracketing is reproducible-sum ordering — do not reassociate.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
integer, intent(in) :: ic
integer, intent(in) :: jc
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: u_face(nx+1,ny,nz)
real(kind=wp), intent(in) :: v_face(nx,ny+1,nz)
real(kind=wp), intent(in) :: hT(nx,ny,nz)
real(kind=wp), intent(in) :: hS(nx,ny,nz)
real(kind=wp), intent(in) :: wet_t(nx,ny)
real(kind=wp), intent(in) :: wet_u(nx+1,ny)
real(kind=wp), intent(in) :: wet_v(nx,ny+1)
real(kind=wp), intent(out) :: h_sd(NZL)
real(kind=wp), intent(out) :: u_sd(NZL)
real(kind=wp), intent(out) :: v_sd(NZL)
real(kind=wp), intent(out) :: t_sd(NZL)
real(kind=wp), intent(out) :: s_sd(NZL)

private pure subroutine kappa_shear_column_kernel(grid, this, ms, hT, hS, dt)

Per-column JHL08 solve. One do concurrent (j, i) over owned cells; every column is solved serially in surface-down order. Per-thread work is fixed-size local() arrays (L1 layout — all live in GPU registers, no shared memory); the column solve bodies are the same-module pure !$acc routine seq helpers below.

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_kappa_shear_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in) :: hT(:,:,:)

Temperature tracer hTr (degC*m), host-dereferenced.

real(kind=wp), intent(in) :: hS(:,:,:)

Salinity tracer hTr (PSU*m), host-dereferenced.

real(kind=wp), intent(in) :: dt

private pure subroutine kappa_shear_vertex_kernel(this, nx, ny, nz, dt, h_layer, u_face, v_face, hT, hS, wet_t, wet_u, wet_v)

Per-CORNER JHL08 solve (MOM6 vertex form, Pass B). One do concurrent (jc, ic) over the interior corners [2,nx]x[2,ny] — the set whose full 2x2 cell patch exists in-array, which covers every corner any owned tracer cell needs (nghost >= 1). Ring corners stay 0.

Read more…

Arguments

Type IntentOptional Attributes Name
type(ocean_kappa_shear_t), intent(inout) :: this
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: u_face(nx+1,ny,nz)
real(kind=wp), intent(in) :: v_face(nx,ny+1,nz)
real(kind=wp), intent(in) :: hT(nx,ny,nz)
real(kind=wp), intent(in) :: hS(nx,ny,nz)
real(kind=wp), intent(in) :: wet_t(nx,ny)
real(kind=wp), intent(in) :: wet_u(nx+1,ny)
real(kind=wp), intent(in) :: wet_v(nx,ny+1)

private pure subroutine ks_find_kappa_tke(nz, tke_min, f2_val, ri_crit, shearmix_rate, fri_curvature, c_n2, c_s2, ilambda2, kappa_0, kappa_trunc, tke_bg, tol_err, max_inner_it, n2_in, s2_in, kappa_seed, k_q_io, idz_s, hint_s, il2_s, e1_s, tke_o, kappa_o, ksrc_sc, tkedec_sc, aq_sc, dq_sc, cq_sc, dk_sc, ck_sc, ild2_sc)

The inner Picard solve (design doc section 5.4): alternate a TKE tridiagonal sweep (Dirichlet surface, e1 tail below the deepest active interface) with a kappa tridiagonal sweep (smooth truncation ramp + active-range tracking) until the Picard increment converges. Scratch arrays are supplied by the caller to avoid double-allocating per-thread stack.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
real(kind=wp), intent(in) :: tke_min
real(kind=wp), intent(in) :: f2_val
real(kind=wp), intent(in) :: ri_crit
real(kind=wp), intent(in) :: shearmix_rate
real(kind=wp), intent(in) :: fri_curvature
real(kind=wp), intent(in) :: c_n2
real(kind=wp), intent(in) :: c_s2
real(kind=wp), intent(in) :: ilambda2
real(kind=wp), intent(in) :: kappa_0
real(kind=wp), intent(in) :: kappa_trunc
real(kind=wp), intent(in) :: tke_bg
real(kind=wp), intent(in) :: tol_err
integer, intent(in) :: max_inner_it
real(kind=wp), intent(in) :: n2_in(NZLI)
real(kind=wp), intent(in) :: s2_in(NZLI)
real(kind=wp), intent(in) :: kappa_seed(NZLI)
real(kind=wp), intent(inout) :: k_q_io(NZLI)
real(kind=wp), intent(in) :: idz_s(NZL)
real(kind=wp), intent(in) :: hint_s(NZLI)
real(kind=wp), intent(in) :: il2_s(NZLI)
real(kind=wp), intent(in) :: e1_s(NZLI)
real(kind=wp), intent(out) :: tke_o(NZLI)
real(kind=wp), intent(out) :: kappa_o(NZLI)
real(kind=wp), intent(inout) :: ksrc_sc(NZLI)
real(kind=wp), intent(inout) :: tkedec_sc(NZLI)
real(kind=wp), intent(inout) :: aq_sc(NZL)
real(kind=wp), intent(inout) :: dq_sc(NZLI)
real(kind=wp), intent(inout) :: cq_sc(NZLI)
real(kind=wp), intent(inout) :: dk_sc(NZLI)
real(kind=wp), intent(inout) :: ck_sc(NZLI)
real(kind=wp), intent(inout) :: ild2_sc(NZLI)

private pure subroutine ks_precompute(nz, lz_rescale, h_sd, idz_o, idz_int_o, hint_o, il2_o)

Build the thickness-derived interface grids that the iteration reuses: 1/h, the interface 1/dz, the harmonic-mean interface FV cell thicknesses h_Int (Sum h_Int = Sum h), and the inverse boundary length scale squared (design doc section 5.1).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
real(kind=wp), intent(in) :: lz_rescale
real(kind=wp), intent(in) :: h_sd(NZL)
real(kind=wp), intent(out) :: idz_o(NZL)
real(kind=wp), intent(out) :: idz_int_o(NZLI)
real(kind=wp), intent(out) :: hint_o(NZLI)
real(kind=wp), intent(out) :: il2_o(NZLI)

private pure subroutine ks_projected_state(nz, dt_now, ks, ke, vel_underflow, dbuoy_t, dbuoy_s, h_sd, idz_int_s, u0, v0, t0, s0, kappa_ps, u_o, v_o, t_o, s_o, c1_o, n2_o, s2_o)

Mix (u0,v0,T0,S0) implicitly with kappa_ps over dt_now, restricted to the layer band [ks,ke], and recompute N^2/S^2 at interfaces (band edges blend mixed inside / original outside). Backward-Euler tridiagonal; no-slip for u,v iff the band reaches the bed (ke==nz), insulating T,S. N^2 floored at 0 (design doc section 5.5).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt_now
integer, intent(in) :: ks
integer, intent(in) :: ke
real(kind=wp), intent(in) :: vel_underflow
real(kind=wp), intent(in) :: dbuoy_t(NZLI)
real(kind=wp), intent(in) :: dbuoy_s(NZLI)
real(kind=wp), intent(in) :: h_sd(NZL)
real(kind=wp), intent(in) :: idz_int_s(NZLI)
real(kind=wp), intent(in) :: u0(NZL)
real(kind=wp), intent(in) :: v0(NZL)
real(kind=wp), intent(in) :: t0(NZL)
real(kind=wp), intent(in) :: s0(NZL)
real(kind=wp), intent(in) :: kappa_ps(NZLI)
real(kind=wp), intent(out) :: u_o(NZL)
real(kind=wp), intent(out) :: v_o(NZL)
real(kind=wp), intent(out) :: t_o(NZL)
real(kind=wp), intent(out) :: s_o(NZL)
real(kind=wp), intent(out) :: c1_o(NZLI)
real(kind=wp), intent(out) :: n2_o(NZLI)
real(kind=wp), intent(out) :: s2_o(NZLI)

private pure subroutine ks_solve_column(nz, dt, f2_val, rho0, ri_crit, shearmix_rate, fri_curvature, c_n, c_s, lambda, kappa_0, kappa_seed_in, kappa_trunc, tke_bg, tol_err, max_inner_it, max_substep_it, src_max_chg, vel_underflow, eos, h_sd, u_sd, v_sd, t_sd, s_sd, idz_s, idz_int_s, hint_s, il2_s, kappa_avg_sd, tke_avg_sd)

Full JHL08 column solve in surface-down indices (design doc section 5.1-5.2): background kappa_0 pre-step (no-slip bed for u,v; insulating T,S), frozen interface buoyancy derivatives, e1 tail recursion, then the adaptive predictor-corrector outer loop driving the Picard inner solve. Returns the time-mean diffusivity kappa_avg_sd and TKE tke_avg_sd over dt.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: f2_val
real(kind=wp), intent(in) :: rho0
real(kind=wp), intent(in) :: ri_crit
real(kind=wp), intent(in) :: shearmix_rate
real(kind=wp), intent(in) :: fri_curvature
real(kind=wp), intent(in) :: c_n
real(kind=wp), intent(in) :: c_s
real(kind=wp), intent(in) :: lambda
real(kind=wp), intent(in) :: kappa_0
real(kind=wp), intent(in) :: kappa_seed_in
real(kind=wp), intent(in) :: kappa_trunc
real(kind=wp), intent(in) :: tke_bg
real(kind=wp), intent(in) :: tol_err
integer, intent(in) :: max_inner_it
integer, intent(in) :: max_substep_it
real(kind=wp), intent(in) :: src_max_chg
real(kind=wp), intent(in) :: vel_underflow
type(eos_t), intent(in) :: eos

Shared EOS handle (by value) for the buoyancy derivatives.

real(kind=wp), intent(in) :: h_sd(NZL)
real(kind=wp), intent(in) :: u_sd(NZL)
real(kind=wp), intent(in) :: v_sd(NZL)
real(kind=wp), intent(in) :: t_sd(NZL)
real(kind=wp), intent(in) :: s_sd(NZL)
real(kind=wp), intent(in) :: idz_s(NZL)
real(kind=wp), intent(in) :: idz_int_s(NZLI)
real(kind=wp), intent(in) :: hint_s(NZLI)
real(kind=wp), intent(in) :: il2_s(NZLI)
real(kind=wp), intent(out) :: kappa_avg_sd(NZLI)
real(kind=wp), intent(out) :: tke_avg_sd(NZLI)

private subroutine ocean_kappa_shear_destroy(this)

Arguments

Type IntentOptional Attributes Name
class(ocean_kappa_shear_t), intent(inout) :: this

private subroutine ocean_kappa_shear_enter_data(this)

Arguments

Type IntentOptional Attributes Name
class(ocean_kappa_shear_t), intent(inout) :: this

private subroutine ocean_kappa_shear_enter_data_impl(this)

Arguments

Type IntentOptional Attributes Name
type(ocean_kappa_shear_t), intent(inout) :: this

private subroutine ocean_kappa_shear_exit_data(this)

Arguments

Type IntentOptional Attributes Name
class(ocean_kappa_shear_t), intent(inout) :: this

private subroutine ocean_kappa_shear_exit_data_impl(this)

Arguments

Type IntentOptional Attributes Name
type(ocean_kappa_shear_t), intent(inout) :: this

private subroutine ocean_kappa_shear_init(this, grid, nz_ml)

Allocate the persistent fields. Always allocates (configure runs after init, so enable is not known yet); the off-cost is the f_centre + two interface fields.

Arguments

Type IntentOptional Attributes Name
class(ocean_kappa_shear_t), intent(inout) :: this
type(hgrid_t), intent(in) :: grid
integer, intent(in), optional :: nz_ml

private subroutine ocean_kappa_shear_init_vertex(this, grid, nz_ml)

Allocate the vertex-mode corner fields and set at_vertex. Called at CONFIGURE time (after init, before enter_data) — deliberately NOT from init, so the (nx+1,ny+1,nz+1) corner carrier is only ever allocated when the vertex form is actually selected (~327 MB at 1000x800x50).

Arguments

Type IntentOptional Attributes Name
class(ocean_kappa_shear_t), intent(inout) :: this
type(hgrid_t), intent(in) :: grid
integer, intent(in) :: nz_ml

private subroutine ocean_kappa_shear_set_f_centre(this, grid, f_0, beta, y_ref)

Fill f_centre with the beta-plane Coriolis magnitude at cell centres: |f_0 + beta*(y - y_ref)|. Mirrors EPBL’s set_f_centre. Call after init, before enter_data.

Arguments

Type IntentOptional Attributes Name
class(ocean_kappa_shear_t), intent(inout) :: this
type(hgrid_t), intent(in) :: grid
real(kind=wp), intent(in) :: f_0
real(kind=wp), intent(in) :: beta
real(kind=wp), intent(in) :: y_ref

private subroutine ocean_kappa_shear_set_f_corner(this, grid, f_0, beta, y_ref)

Fill f_corner with the SIGNED beta-plane Coriolis at C-grid corners: f_0 + beta(y - y_ref), corner row j at y = (j-1-nghost)dy (half a cell below centre row j — corner (i,j) is the SW corner of cell (i,j)). Bit-identical to metrics_fill_coriolis’s beta-plane corner fill. Call after init_vertex, before enter_data.

Arguments

Type IntentOptional Attributes Name
class(ocean_kappa_shear_t), intent(inout) :: this
type(hgrid_t), intent(in) :: grid
real(kind=wp), intent(in) :: f_0
real(kind=wp), intent(in) :: beta
real(kind=wp), intent(in) :: y_ref