Flow-aware harmonic / biharmonic viscosity for the ocean dyn-core:
per-face ah_face_* (m^2/s) and nu4_face_* (m^4/s), recomputed
each step from the local flow and read by the horizontal-viscosity
kernel in place of the scalar nu_h/nu_4 (closure LMIX_NONE
⇒ scalar fallback ⇒ bit-identical). The coastal path uses
Smagorinsky in rdb_ml_horizontal_viscosity; the ocean path
defaults to Leith, which scales with vorticity gradient and avoids
over-damping coherent eddies. Leith (1968); Smagorinsky (1963);
Fox-Kemper & Menemenlis (2008); Griffies & Hallberg (2000).
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | public, | parameter | :: | LMIX_BIHARMONIC | = | 3 |
Constant-coefficient biharmonic. |
| integer, | public, | parameter | :: | LMIX_INVALID | = | -1 |
Sentinel for an unrecognised namelist string; aborts loudly at configure rather than silently falling back to background-only. |
| integer, | public, | parameter | :: | LMIX_LEITH | = | 1 |
Leith vorticity-gradient closure (default eddy-resolving). |
| integer, | public, | parameter | :: | LMIX_LEITH_BIHARM | = | 4 |
Leith-scaled biharmonic (Griffies & Hallberg 2000). |
| integer, | public, | parameter | :: | LMIX_NONE | = | 0 |
No flow-aware closure — falls back to the scalar |
| integer, | public, | parameter | :: | LMIX_SMAGORINSKY | = | 2 |
Smagorinsky strain-rate closure. |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | public | :: | ah_bg | = | 0.0_wp |
Background harmonic viscosity (m^2/s), floored beneath the closure to avoid zero damping in laminar patches. |
|
| real(kind=wp), | public, | allocatable | :: | ah_face_x(:,:,:) |
Harmonic viscosity at east faces (m^2/s), shape
|
||
| real(kind=wp), | public, | allocatable | :: | ah_face_y(:,:,:) |
Harmonic viscosity at north faces (m^2/s), shape
|
||
| real(kind=wp), | public | :: | ah_max | = | 1.0e4_wp |
Upper clip on harmonic viscosity (m^2/s). Caps Leith spikes and enforces the viscous-CFL bound (nu·dt/dx² ≤ 0.5). |
|
| real(kind=wp), | public | :: | c_leith | = | 1.0_wp |
Leith dimensionless coefficient. Typical 1.0–2.0. |
|
| real(kind=wp), | public | :: | c_leith_bi | = | 0.0_wp |
Nondimensional biharmonic Leith constant for
|
|
| real(kind=wp), | public | :: | c_smag | = | 0.15_wp |
Smagorinsky dimensionless coefficient (fallback). |
|
| integer, | public | :: | closure | = | LMIX_NONE |
Active closure tag. Default |
|
| logical, | public | :: | is_init | = | .false. |
True between |
|
| real(kind=wp), | public | :: | kh_vel_scale_live | = | 0.0_wp |
Live velocity-scale viscosity coefficient (m/s). When
positive, |
|
| logical, | public | :: | no_slip | = | .false. |
Lateral BC at coasts (shared with Coriolis). |
|
| real(kind=wp), | public | :: | nu4_bg | = | 0.0_wp |
Background biharmonic viscosity floor (m⁴/s). |
|
| real(kind=wp), | public, | allocatable | :: | nu4_face_x(:,:,:) |
Biharmonic viscosity at east faces (m⁴/s), shape
|
||
| real(kind=wp), | public, | allocatable | :: | nu4_face_y(:,:,:) |
Biharmonic viscosity at north faces (m⁴/s), shape
|
||
| real(kind=wp), | public | :: | nu4_max | = | 1.0e12_wp |
Static upper clip on biharmonic viscosity (m⁴/s) applied when
filling |
|
| integer, | public | :: | nx_total | = | 0 | ||
| integer, | public | :: | ny_total | = | 0 | ||
| integer, | public | :: | nz_ml | = | 0 | ||
| logical, | public | :: | resoln_scaled_visc | = | .false. |
Hallberg (2013) resolution scaling. When |
|
| logical, | public | :: | smag_ah_active | = | .false. |
When true, |
|
| real(kind=wp), | public | :: | smag_bi_const | = | 0.06_wp |
Nondimensional biharmonic Smagorinsky constant (typical 0.015–0.06). |
|
| type(scratch_3d_buffer_t), | public | :: | vort_corner |
Relative vorticity at C-grid corners. |
| procedure, public, non_overridable :: bytes => ocean_lateral_mix_bytes | |
| procedure, public, non_overridable :: destroy => ocean_lateral_mix_destroy | |
| procedure, public, non_overridable :: enter_data => ocean_lateral_mix_enter_data | |
| procedure, public, non_overridable :: exit_data => ocean_lateral_mix_exit_data | |
| procedure, public, non_overridable :: init => ocean_lateral_mix_init |
.true. iff the configured biharmonic dispatch will produce a
STRICTLY POSITIVE dissipation coefficient somewhere. Drives the
configure-time fail-loud guard requiring a biharmonic backstop when
MEKE backscatter is on — the negative harmonic backscatter feeds a
grid-scale mode that only a positive biharmonic can dissipate.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | nu_4 | |||
| logical, | intent(in) | :: | smag_ah | |||
| real(kind=wp), | intent(in) | :: | smag_bi_const | |||
| integer, | intent(in) | :: | code | |||
| real(kind=wp), | intent(in) | :: | c_leith_bi | |||
| real(kind=wp), | intent(in) | :: | nu_4_bg |
.true. iff the closure is LMIX_LEITH_BIHARM and smag_ah is
on — both would fill nu4_face_* and smag_ah runs last, so it
would silently overwrite the Leith-biharmonic fill (we do not
max-combine biharmonic closures). Drives a fail-loud guard.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | code | |||
| logical, | intent(in) | :: | smag_ah |
.true. iff the closure code has a working dispatcher path.
Drives the configure-time fail-loud guard. Keep in lock-step
with the select case in ocean_lateral_mix_compute.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | code |
.true. iff the Leith-biharmonic closure is selected but its
coefficient is <= 0 (PR-6 fail-loud). ν₄ is linear in
c_leith_bi, so c_leith_bi <= 0 makes the whole closure a
provable no-op — the user asked for biharmonic dissipation and
got none. Returns .false. for any other closure (they do not
read c_leith_bi, so the guard must not fire on them). Promotes
the previous configure-time warning to an abort.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | code | |||
| real(kind=wp), | intent(in) | :: | c_leith_bi |
Translate a namelist string into an LMIX_* code. Returns
LMIX_INVALID on an unrecognised value (fail loud at configure);
only “none”/”off”/”” map to LMIX_NONE.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | name |
Counted allocatable footprint of the lateral viscosity slot (0 when unallocated). One arr_bytes term per array — add a term here when a new allocatable joins the type.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_lateral_mix_t), | intent(in) | :: | this |
Dispatcher — runs the compute kernel for the active closure tag.
LMIX_LEITH/LMIX_SMAGORINSKY → harmonic ah_face_*;
LMIX_LEITH_BIHARM → biharmonic nu4_face_*; LMIX_BIHARMONIC
→ scalar nu_4 in the apply step (no per-face fill); LMIX_NONE
→ no-op. The independent smag_ah_active switch separately
fills nu4_face_* from the strain rate. this is optional so
the driver can call unconditionally. res_fn_u/v (optional
VarMix resolution-function fields): when supplied AND
resoln_scaled_visc, scale the dynamic coefficients before the
clamps; absent ⇒ unscaled ⇒ bit-identical.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics | |||
| type(ocean_lateral_mix_t), | intent(inout), | optional | :: | this | ||
| type(multilayer_state_t), | intent(in) | :: | ms | |||
| real(kind=wp), | intent(in), | optional | :: | res_fn_u(grid%nx_total+1,grid%ny_total) | ||
| real(kind=wp), | intent(in), | optional | :: | res_fn_v(grid%nx_total,grid%ny_total+1) |
Public only for the unit-test suite; ignore in production code.
Populate ah_face_x/ah_face_y (m^2/s) with the Leith viscosity
A_h(face) = max(ah_bg, min(ah_max, (C_L · dx)^3 · |∇ζ|))
where ζ is relative vorticity at C-grid corners (pass 1) and |∇ζ|
the 2D gradient magnitude at each face (pass 2). Wall faces get
the background viscosity.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics | |||
| type(ocean_lateral_mix_t), | intent(inout) | :: | this | |||
| type(multilayer_state_t), | intent(in) | :: | ms | |||
| real(kind=wp), | intent(in), | optional | :: | res_fn_u(grid%nx_total+1,grid%ny_total) |
VarMix resolution function at u-faces (nondim, [0,1]). When
present AND |
|
| real(kind=wp), | intent(in), | optional | :: | res_fn_v(grid%nx_total,grid%ny_total+1) |
VarMix resolution function at v-faces. |
Public only for the unit-test suite; ignore in production code.
Populate nu4_face_x/nu4_face_y (m⁴/s) with the 2-D Leith
biharmonic viscosity
A_4(face) = clamp(C_lb · grid_sp⁶ · inv_PI6 · |∇²ζ|,
nu4_bg, nu4_max)
where ζ is C-grid corner relative vorticity, ∇²ζ its 5-point
corner Laplacian, grid_sp⁶ = grid_sp_h2³, and inv_PI6 = (1/π)⁶.
Per-face |∇²ζ| is the mean of the two adjacent corner Laplacians.
Wall faces get nu4_bg. Leith (1968); Griffies & Hallberg (2000).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics | |||
| type(ocean_lateral_mix_t), | intent(inout) | :: | this | |||
| type(multilayer_state_t), | intent(in) | :: | ms |
Populate ah_face_x/ah_face_y (m^2/s) with the Smagorinsky
Laplacian viscosity
A_h(face) = max(ah_bg, min(ah_max, (C_S · dx)^2 · |D|))
where |D| = sqrt(D_T^2 + D_S^2) is the deformation-tensor
magnitude — tension D_T = ∂u/∂x − ∂v/∂y (cell centred) and
shear D_S = ∂v/∂x + ∂u/∂y (corner) — averaged onto the face.
Wall faces get the background viscosity (wall-adjacent rows
re-use the next interior row). Smagorinsky (1963); C_S ≈ 0.15–0.2.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics | |||
| type(ocean_lateral_mix_t), | intent(inout) | :: | this | |||
| type(multilayer_state_t), | intent(in) | :: | ms | |||
| real(kind=wp), | intent(in), | optional | :: | res_fn_u(grid%nx_total+1,grid%ny_total) |
VarMix resolution function at u-faces (nondim, [0,1]). When
present AND |
|
| real(kind=wp), | intent(in), | optional | :: | res_fn_v(grid%nx_total,grid%ny_total+1) |
VarMix resolution function at v-faces. |
Public only for the unit-test suite; ignore in production code.
Populate nu4_face_x/nu4_face_y (m⁴/s) with the biharmonic
Smagorinsky viscosity
A_4(face) = clamp(C_b · L⁴ · |D|, nu4_bg, nu4_max)
where L² = 2·dx²·dy²/(dx²+dy²) (harmonic mean of dx²,dy²) and
|D| is the strain-rate magnitude from compute_smag. Wall
faces get nu4_bg. SMAG_BI_CONST ≈ 0.015–0.06.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics | |||
| type(ocean_lateral_mix_t), | intent(inout) | :: | this | |||
| type(multilayer_state_t), | intent(in) | :: | ms |
Public only for the unit-test suite; ignore in production code.
Live velocity-scale viscosity (MOM6 KH_VEL_SCALE, Kh = U·Δ):
per face A_vel = kh_vel_scale_live · L_grid · |u_face|
(L_grid = sqrt(dxT·dyT)), max-combined into ah_face_* so it
floors — never reduces — the active closure. seed_bg = .true.
first fills every face with ah_bg (used when no closure ran);
.false. only raises faces where A_vel exceeds the closure.
No-op when kh_vel_scale_live <= 0.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics | |||
| type(ocean_lateral_mix_t), | intent(inout) | :: | this | |||
| type(multilayer_state_t), | intent(in) | :: | ms | |||
| logical, | intent(in) | :: | seed_bg |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_lateral_mix_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_lateral_mix_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(ocean_lateral_mix_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_lateral_mix_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(ocean_lateral_mix_t), | intent(inout) | :: | this |
Allocate the face viscosity coefficients + corner-vorticity
scratch. Default nz_ml = 1 preserves the barotropic-only
constructor; pass nz_ml = ms%nz_ml for the multilayer driver.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_lateral_mix_t), | intent(inout) | :: | this | |||
| type(hgrid_t), | intent(in) | :: | grid | |||
| integer, | intent(in), | optional | :: | nz_ml |