rdb_ocean_horizontal_viscosity Module

Carries the closure parameters and per-step tendency workspace for the Laplacian horizontal-momentum-viscosity kernel. Sits alongside rdb_ocean_lateral_mix (which holds the variable-coefficient state for Leith/Smag closures): this module is the kernel, that one is the closure. Phase Tier-1 ships the constant-nu_h variant; later phases will read coefficients out of ocean_lateral_mix_t%ah_face_* and replace the scalar.

Each compute pass writes a per-face tendency (du_visc, dv_visc); the apply step adds dt * tendency onto u_face_x_layer / v_face_y_layer. The two-step pattern matches Coriolis and PGF — additive tendency buffers keep the SSP-RK2 driver simple (order between applies doesn’t matter).

Wall handling: at the four C-grid wall faces the tendency is forced to zero. Interior faces use the standard 5-point Laplacian stencil on the face velocity itself (no thickness weighting — Phase 5+ adds the h * A * grad u flux-form once variable-thickness conservation matters).

MOM6 stress-divergence path (stress_tensor = .true., spec PR2): instead of the velocity Laplacian the kernel assembles a thickness-weighted stress and takes its divergence: tension str_xx = A_T·(du/dx − dv/dy)·h_T (T-cell) shear str_xy = A_q·(dv/dx + du/dy)·h_q·slip (Bu corner) diffu = (1/(h_u + h_neglect))·iareaCu·∂(str), h_neglect an H_VANISHED-class floor. Momentum-conserving and down-weights vanishing layers. A per-cell CFL viscosity limiter (MOM6 BOUND_KH) clamps A from the actual discrete stencil + dt, replacing the global ah_max cap; wet_u, wet_v, wet_q mask the stress so momentum is not diffused across coastlines. On a uniform-grid + uniform-h + all-wet column the cross terms cancel discretely and the operator reduces to A·∇²u to round-off.

Biharmonic composition: the constant-nu_4 / flow-aware (smag_ah, leith_biharm) biharmonic add-on composes with all three harmonic operators — scalar Laplacian, face (Leith/Smagorinsky) Laplacian, and the stress-divergence path — matching MOM6 (BIHARMONIC “may be used with LAPLACIAN”, default .true.). kh_aniso therefore also composes with the biharmonic; it is no longer mutually exclusive. The composition happens at the tendency level, not the stress level: under stress_tensor, the harmonic part is momentum-conserving and coast-masked while the velocity-form biharmonic part is neither (a documented fidelity divergence from MOM6, which sums both into one stress tensor before differencing — porting the biharmonic into the stress tensor is a follow-on PR, not this module today).


Uses

  • module~~rdb_ocean_horizontal_viscosity~~UsesGraph module~rdb_ocean_horizontal_viscosity rdb_ocean_horizontal_viscosity iso_fortran_env iso_fortran_env module~rdb_ocean_horizontal_viscosity->iso_fortran_env module~rdb_constants rdb_constants module~rdb_ocean_horizontal_viscosity->module~rdb_constants module~rdb_grid rdb_grid module~rdb_ocean_horizontal_viscosity->module~rdb_grid module~rdb_mem_report rdb_mem_report module~rdb_ocean_horizontal_viscosity->module~rdb_mem_report module~rdb_multilayer_state rdb_multilayer_state module~rdb_ocean_horizontal_viscosity->module~rdb_multilayer_state module~rdb_ocean_lateral_mix rdb_ocean_lateral_mix module~rdb_ocean_horizontal_viscosity->module~rdb_ocean_lateral_mix module~rdb_ocean_metrics rdb_ocean_metrics module~rdb_ocean_horizontal_viscosity->module~rdb_ocean_metrics module~rdb_scratch_3d rdb_scratch_3d module~rdb_ocean_horizontal_viscosity->module~rdb_scratch_3d pic_types pic_types module~rdb_constants->pic_types module~rdb_grid->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_ocean_lateral_mix->iso_fortran_env module~rdb_ocean_lateral_mix->module~rdb_constants module~rdb_ocean_lateral_mix->module~rdb_grid module~rdb_ocean_lateral_mix->module~rdb_mem_report module~rdb_ocean_lateral_mix->module~rdb_multilayer_state module~rdb_ocean_lateral_mix->module~rdb_ocean_metrics module~rdb_ocean_lateral_mix->module~rdb_scratch_3d module~rdb_ocean_metrics->iso_fortran_env module~rdb_ocean_metrics->module~rdb_constants module~rdb_ocean_metrics->module~rdb_grid module~rdb_ocean_metrics->module~rdb_mem_report module~rdb_ocean_metrics->module~rdb_error_ring module~rdb_io_netcdf rdb_io_netcdf module~rdb_ocean_metrics->module~rdb_io_netcdf module~rdb_ocean_bipolar rdb_ocean_bipolar module~rdb_ocean_metrics->module~rdb_ocean_bipolar module~rdb_ocean_fold rdb_ocean_fold module~rdb_ocean_metrics->module~rdb_ocean_fold module~rdb_ocean_status rdb_ocean_status module~rdb_ocean_metrics->module~rdb_ocean_status netcdf netcdf module~rdb_ocean_metrics->netcdf module~rdb_ocean_metrics->pic_logger module~rdb_ocean_metrics->pic_strings module~rdb_scratch_3d->iso_fortran_env module~rdb_scratch_3d->module~rdb_constants module~rdb_scratch_3d->module~rdb_mem_report module~rdb_efp->iso_fortran_env ieee_arithmetic ieee_arithmetic module~rdb_efp->ieee_arithmetic module~rdb_error_ring->pic_logger module~rdb_io_netcdf->iso_fortran_env module~rdb_io_netcdf->module~rdb_constants module~rdb_io_netcdf->module~rdb_error_ring module~rdb_io_netcdf->netcdf module~rdb_io_netcdf->pic_logger module~rdb_io_netcdf->pic_strings iso_c_binding iso_c_binding module~rdb_io_netcdf->iso_c_binding module~rdb_ocean_bipolar->module~rdb_constants module~rdb_ocean_fold->module~rdb_constants 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_horizontal_viscosity~~UsedByGraph module~rdb_ocean_horizontal_viscosity rdb_ocean_horizontal_viscosity module~rdb_barotropic_coupling rdb_barotropic_coupling module~rdb_barotropic_coupling->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_bt_budget_probe rdb_ocean_bt_budget_probe module~rdb_ocean_bt_budget_probe->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_dyn->module~rdb_barotropic_coupling module~rdb_ocean_dyn->module~rdb_ocean_bt_budget_probe module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_setup->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_setup->module~rdb_ocean_dyn module~rdb_ocean_state rdb_ocean_state module~rdb_ocean_setup->module~rdb_ocean_state module~rdb_ocean_state->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_state->module~rdb_ocean_dyn proc~validate_config validate_config proc~validate_config->module~rdb_ocean_horizontal_viscosity 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_setup 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

Derived Types

type, public ::  ocean_horizontal_viscosity_t

Components

Type Visibility Attributes Name Initial
type(scratch_3d_buffer_t), public :: ah_q

Harmonic viscosity averaged onto Bu corners (m²/s), shape (nx+1, ny+1, nz). CFL-clamped per corner.

type(scratch_3d_buffer_t), public :: ah_t

Harmonic viscosity averaged onto T-cell centres (m²/s), shape (nx, ny, nz). CFL-clamped per cell.

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

Precomputed direction-tensor factor (n1²−n2²)/(n1²+n2²) (MOM6 n1n1_m_n2n2). Default (1,0) ⇒ 1.

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

Precomputed direction-tensor factor 2·n1·n2/(n1²+n2²) (MOM6 n1n2). Set by ocean_hvisc_set_aniso_direction from the (n1,n2) direction vector. Default (1,0) = grid-i ⇒ n1n2 = 0 (cross terms vanish; the operator just adds kh_aniso to the tension coefficient).

real(kind=wp), public :: bound_coef = 0.8_wp

CFL safety coefficient for the per-cell viscosity limiter (MOM6 HORVISC_BOUND_COEF). Consulted on the stress_tensor path and, when bound_kh is set, on the velocity-Laplacian paths too.

logical, public :: bound_kh = .false.

MOM6 BOUND_KH analogue for the velocity-Laplacian paths (scalar nu_h and the flow-aware per-face closure). When .true., the per-face harmonic viscosity is clamped to bound_coef·0.125/(dt·(idx²+idy²)) — ~0.25× the forward- Euler stability limit, matching MOM6’s Kh_Max_xx margin. Load-bearing beyond simple FE stability: the barotropic mode receives the depth-mean viscous force FROZEN over the outer step (via F_bt), and for grid-scale gravity modes with ω·dt ≳ 1 an unbounded λ·dt = ν·k²·dt ≳ 0.6 frozen force is applied with reversed phase — ANTI-damping — which exponentially pumps rim-trapped barotropic modes through the Δu corrector (the 600² Lagrangian double-gyre h-guard blow-up; e-fold ~10 outer steps). The clamp keeps λ·dt ≤ 0.5·bound_coef at every face so the corrector loop stays damped at any resolution. Default .false. ⇒ bit-identical.

logical, public :: compute_ke_diss = .false.

When set (by configure when MEKE’s frictional source is on), the apply step fills ke_diss with the lateral-viscosity KE dissipation rate. Default off ⇒ no extra work, bit-identical.

type(scratch_3d_buffer_t), public :: du_visc

Per-step viscous tendency at east faces, shape (nx+1, ny, nz). Filled by the compute step, consumed by the apply step.

type(scratch_3d_buffer_t), public :: dv_visc

Per-step viscous tendency at north faces, shape (nx, ny+1, nz).

logical, public :: is_init = .false.

True between init and destroy. Tracks GPU device attachment too — prefer this to allocated(...).

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

Depth-integrated KE dissipation rate by the lateral viscosity, Σ_k ρ_k h_k (u·du_visc + v·dv_visc) (kg/s³, ≤0), at T-cell centres (nx, ny). MEKE consumes it as -frcoeff·i_mass·ke_diss (the mean→eddy frictional source). Holds the most recent stage’s rate (the quantity MEKE needs is a rate, so no accumulation).

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

Anisotropic Laplacian viscosity magnitude (m²/s). When positive, a two-coefficient direction tensor splits the harmonic viscosity into along- and cross-direction parts. Only consulted on the stress_tensor path. Default 0 ⇒ isotropic, bit-identical. MOM6 KH_ANISO analogue (Smith & McWilliams 2003, “Anisotropic horizontal viscosity for ocean models”, Ocean Modelling 5(2), §2). Composes with the biharmonic add-on (nu_4 / smag_ah / leith_biharm) — the two are independent linear operators that sum, matching MOM6’s composition (kh_aniso inside the harmonic block, biharmonic added to the same tensor).

type(scratch_3d_buffer_t), public :: lap_u

First-pass Laplacian buffer used by the biharmonic path — holds ∇²u_face so the second Laplacian pass (∇²(∇²u)) can read it. Same shape as du_visc. Allocated unconditionally; sits inert when nu_4 = 0.

type(scratch_3d_buffer_t), public :: lap_v

v-face counterpart of lap_u.

logical, public :: no_slip = .false.

Coastal lateral BC selector (shared with the lateral-mix / Coriolis kernels). .false. (free-slip) masks the corner shear stress by wet_q; .true. (no-slip) by 2 - wet_q — on the stress_tensor path AND on the velocity-Laplacian paths (scalar nu_h and the flow-aware face closures), whose corner (shear) fluxes carry the same factor. The biharmonic add-on is always free-slip (wet_q; MOM6 refuses NOSLIP with BIHARMONIC). All-wet ⇒ factor ≡ 1 ⇒ bit-identical.

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

Constant biharmonic horizontal viscosity (m^4/s). Scale- selective damping for stratified closed-basin runs: damps proportional to ν₄·k⁴, so it kills grid-scale baroclinic noise without bleeding into resolved scales the way a large Laplacian ν_h would. Required to keep stratified Tasman-class runs bounded past day ~10 (Laplacian-only configurations grow exponentially via parametric amplification of roundoff seeds). Numerical-stability cap: ν₄ · dt · (1/dx² + 1/dy²)² <= 1/16. Typical 2 km resolution value ~1e10 m^4/s. Composes with all of the harmonic dispatch arms, including stress_tensor — it is no longer silently disabled by it.

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

Constant Laplacian horizontal viscosity (m^2/s). Phase Tier-1 default is zero (kernel becomes a no-op); set positive to enable damping. Numerical-stability cap: nu_h * dt * (1/dx^2 + 1/dy^2) <= 0.5.

type(scratch_3d_buffer_t), public :: str_xx

Thickness-weighted tension stress A_T·(du/dx−dv/dy)·h_T at T-cell centres, shape (nx, ny, nz).

type(scratch_3d_buffer_t), public :: str_xy

Thickness-weighted shear stress A_q·(dv/dx+du/dy)·h_q·slip at Bu corners, shape (nx+1, ny+1, nz).

logical, public :: stress_tensor = .false.

When true, the compute step uses the MOM6-faithful thickness-weighted stress-divergence operator diffu = (1/(h_u+h_neglect))·∇·(h·A·∇u) with a per-cell CFL viscosity limiter and wet_* coast-masking instead of the velocity Laplacian A·∇²u. Default false ⇒ bit-identical to the historical kernel. See the module header for the operator form.

Type-Bound Procedures

procedure, public, non_overridable :: bytes => ocean_horizontal_viscosity_bytes
procedure, public, non_overridable :: destroy => ocean_hvisc_destroy
procedure, public, non_overridable :: enter_data => ocean_hvisc_enter_data
procedure, public, non_overridable :: exit_data => ocean_hvisc_exit_data
procedure, public, non_overridable :: init => ocean_hvisc_init

Functions

public pure function aniso_mode_is_implemented(mode) result(ok)

.true. iff the anisotropy-direction mode has an implemented direction tensor. Only mode 0 (grid-relative (n1,n2) = aniso_dir, a constant tensor) is built; the MOM6 flow-aligned modes are not ported. Drives the configure-time fail-loud guard in validate_config so a requested-but-unimplemented mode aborts the run instead of silently falling back to the grid-i default.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: mode

Return Value logical

private pure function hvisc_kh_cfl_bound(idx, idy, bound_coef, idt) result(kh_max_cfl)

Per-face harmonic-viscosity ceiling for the velocity-Laplacian paths (MOM6 Kh_Max_xx analogue, uniform-grid reduction): ν_max = bound_coef · 0.125 / (dt · (idx² + idy²)) — one quarter of the forward-Euler stability limit ν·dt·4·(idx²+idy²) ≤ 2, the same margin MOM6’s harmonic bound uses (“avoid overshoots when bound_coef < 1”). Keeps the FROZEN depth-mean viscous forcing on the barotropic mode (F_bt) out of the phase-reversed anti-damping regime for grid-scale gravity modes (see the bound_kh docstring). Returns huge (no clamp) for a fully-masked face. !$acc routine seq — called from the Laplacian do concurrent.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: idx

Metric inverses 1/dx, 1/dy at the face.

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

Metric inverses 1/dx, 1/dy at the face.

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

CFL safety coefficient (this%bound_coef).

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

Reciprocal time step 1/dt.

Return Value real(kind=wp)

private pure function hvisc_nu4_cfl_bound(idx, idy, bound_coef, idt) result(nu4_max_cfl)

Per-face explicit-biharmonic CFL ceiling on the biharmonic viscosity ν₄ (m⁴/s). Forward-Euler stability for −ν₄·∇⁴u on a local cell of spacing (dx, dy) requires (established project constant) ν₄ · dt · ((π/dx)² + (π/dy)²)² ≤ 2, so the per-face bound is ν₄_max = bound_coef · 2 / (dt · ((π·idx)² + (π·idy)²)²) where idx = 1/dx, idy = 1/dy are the metric inverses at the face (idxCu/idyCu at u-faces, idxCv/idyCv at v-faces). bound_coef (MOM6 HORVISC_BOUND_COEF, default 0.8) is the CFL safety margin shared with the harmonic hvisc_clamp_A. Returns a huge value (no clamp) when the metric inverses are both zero (a fully-masked land face) so the caller’s min leaves the coefficient untouched there. !$acc routine seq — called from the biharmonic do concurrent.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: idx

Metric inverses 1/dx, 1/dy at the face.

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

Metric inverses 1/dx, 1/dy at the face.

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

CFL safety coefficient (this%bound_coef).

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

Reciprocal time step 1/dt.

Return Value real(kind=wp)

private pure function ocean_horizontal_viscosity_bytes(this) result(nbytes)

Counted allocatable footprint of the horizontal viscosity 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_horizontal_viscosity_t), intent(in) :: this

Return Value integer(kind=int64)

private pure function raw_sh_xx(u_face, v_face, idxCu, idyCu, idxCv, dy_dxT, dx_dyT, i, j, k, nx, ny, nz) result(sh_xx)

Raw tension strain sh_xx = du/dx − dv/dy at T-cell (i,j), mirroring Phase-1’s gradient form (unmasked — used only by the anisotropic cross term where the all-wet reduction is exact). !$acc routine seq so the stress-assembly do concurrent can call it on-device.

Arguments

Type IntentOptional Attributes Name
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) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCu(nx+1,ny)
real(kind=wp), intent(in) :: idxCv(nx,ny+1)
real(kind=wp), intent(in) :: dy_dxT(nx,ny)
real(kind=wp), intent(in) :: dx_dyT(nx,ny)
integer, intent(in) :: i
integer, intent(in) :: j
integer, intent(in) :: k
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

Return Value real(kind=wp)

private pure function raw_sh_xy(u_face, v_face, idxCu, idyCv, dy_dxBu, dx_dyBu, i, j, k, nx, ny, nz) result(sh_xy)

Raw shear strain sh_xy = dv/dx + du/dy at Bu corner (i,j), mirroring Phase-2’s gradient form. !$acc routine seq for the on-device cross-term loop.

Arguments

Type IntentOptional Attributes Name
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) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCv(nx,ny+1)
real(kind=wp), intent(in) :: dy_dxBu(nx+1,ny+1)
real(kind=wp), intent(in) :: dx_dyBu(nx+1,ny+1)
integer, intent(in) :: i
integer, intent(in) :: j
integer, intent(in) :: k
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

Return Value real(kind=wp)


Subroutines

public subroutine ocean_horizontal_viscosity_apply_tendencies(this, ms, dt, no_wait)

Forward-Euler accumulation of the viscous tendency onto the face velocities. Shim — hoists this%du_visc%data etc. to the host before dispatching to the flat-impl. Explicit shape dimensions are derived from ms here and passed as scalar args. no_wait (optional, default .false.): forwarded to the impl — when .true. the apply DC loops run on OpenACC queue 1 and the routine returns WITHOUT syncing, so the batched velocity-apply chain in run_stage_split !$acc wait(1)s ONCE. Default ⇒ blocking. Not pure because of the async/wait directives.

Arguments

Type IntentOptional Attributes Name
type(ocean_horizontal_viscosity_t), intent(in) :: this
type(multilayer_state_t), intent(inout) :: ms
real(kind=wp), intent(in) :: dt
logical, intent(in), optional :: no_wait

public subroutine ocean_horizontal_viscosity_compute_ke_diss(this, ms)

Fill this%ke_diss with the lateral-viscosity KE dissipation rate Σ_k ρ_k h_k (u·du_visc + v·dv_visc) at T-cell centres (kg/s³; ≤0 where the viscosity removes KE). MUST run AFTER compute_tendencies (du_visc fresh) and BEFORE the viscous apply, while u_face/v_face still hold the velocity the viscosity acted on. No-op (and bit-identical) unless compute_ke_diss is set and the density field is live. Feeds the MEKE frictional source.

Arguments

Type IntentOptional Attributes Name
type(ocean_horizontal_viscosity_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms

public subroutine ocean_horizontal_viscosity_compute_tendencies(grid, metrics, this, ms, lateral_mix, dt, u_src, v_src, h_src)

Source-selecting shim over ocean_horizontal_viscosity_compute_tendencies_on: absent u_src/v_src/h_src (all callers today) forwards the prognostic components — bit-identical; the pred_corr driver passes the u_av time-mean family (SPEC §4 S3).

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(ocean_horizontal_viscosity_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms
type(ocean_lateral_mix_t), intent(in), optional :: lateral_mix
real(kind=wp), intent(in), optional :: dt
real(kind=wp), intent(in), optional :: u_src(:,:,:)
real(kind=wp), intent(in), optional :: v_src(:,:,:)
real(kind=wp), intent(in), optional :: h_src(:,:,:)

public pure subroutine ocean_hvisc_set_aniso_direction(this, n1, n2)

Precompute the constant Smith & McWilliams (2003) direction- tensor factors from the anisotropy direction vector (n1,n2) (grid-relative i,j components). Normalises by n1²+n2² so the caller need not pass a unit vector:

Read more…

Arguments

Type IntentOptional Attributes Name
class(ocean_horizontal_viscosity_t), intent(inout) :: this
real(kind=wp), intent(in) :: n1
real(kind=wp), intent(in) :: n2

private pure subroutine hvisc_add_aniso_coef(ah_t, ah_q, kh_aniso, n1n2, nx, ny, nz)

Add the Smith & McWilliams (2003) anisotropic direction-tensor coefficients onto the co-located isotropic viscosities. The tension (T-cell) coefficient gains kh_aniso·(1−n1n2²) and the shear (Bu-corner) coefficient gains kh_aniso·n1n2². For the default grid-i direction n1n2 = 0 ⇒ T gains kh_aniso, the corner gains nothing — stronger damping of along-i tension. The corner outer ring stays untouched (the divergence stencil never reads it; hvisc_avg_A_face already zeroed it).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: ah_t(nx,ny,nz)
real(kind=wp), intent(inout) :: ah_q(nx+1,ny+1,nz)
real(kind=wp), intent(in) :: kh_aniso
real(kind=wp), intent(in) :: n1n2
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private subroutine hvisc_apply_impl(u_face, v_face, du_visc, dv_visc, dt, nx, ny, nz, lwait)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: u_face(nx+1,ny,nz)
real(kind=wp), intent(inout) :: v_face(nx,ny+1,nz)
real(kind=wp), intent(in) :: du_visc(nx+1,ny,nz)
real(kind=wp), intent(in) :: dv_visc(nx,ny+1,nz)
real(kind=wp), intent(in) :: dt
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
logical, intent(in) :: lwait

.false. ⇒ leave the apply on queue 1 without syncing (batched).

private pure subroutine hvisc_avg_A_face(ah_face_x, ah_face_y, ah_t, ah_q, nx, ny, nz)

Average the per-face harmonic viscosity (ah_face_x at u-faces, ah_face_y at v-faces) onto the T-cell centres (ah_t) and the Bu corners (ah_q). The stress form needs A co-located with the tension (T-cell) and shear (corner) strains; the lateral-mix closure produces A at faces, so this is a 4-point face→cell / face→corner reduction. On a uniform A field every average returns A, preserving the Laplacian reduction.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: ah_face_x(nx+1,ny,nz)
real(kind=wp), intent(in) :: ah_face_y(nx,ny+1,nz)
real(kind=wp), intent(out) :: ah_t(nx,ny,nz)
real(kind=wp), intent(out) :: ah_q(nx+1,ny+1,nz)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private pure subroutine hvisc_biharm_lap_closed(u_face, v_face, lap_u, lap_v, wet_q, dy_dxT, dx_dyBu, iareaCu, dx_dyT, dy_dxBu, iareaCv, nx, ny, nz, open_u, open_v)

Pass 1 of BOTH velocity biharmonics (scalar nu_4 and the flow-aware nu4_face_*) under &vcoord_nml zfixed_closed_faces: the intermediate Laplacian lap_u/lap_v with a closed face-layer treated as a FREE-SLIP wall.

Read more…

Arguments

Type IntentOptional Attributes Name
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(inout) :: lap_u(nx+1,ny,nz)
real(kind=wp), intent(inout) :: lap_v(nx,ny+1,nz)
real(kind=wp), intent(in) :: wet_q(nx+1,ny+1)

Bu-corner wet mask (free-slip corner factor, as in the ungated pass).

real(kind=wp), intent(in) :: dy_dxT(nx,ny)
real(kind=wp), intent(in) :: dx_dyBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCu(nx+1,ny)
real(kind=wp), intent(in) :: dx_dyT(nx,ny)
real(kind=wp), intent(in) :: dy_dxBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCv(nx,ny+1)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: open_u(nx+1,ny,nz)

Per-layer 0/1 u-face open mask (metrics%open_u).

real(kind=wp), intent(in) :: open_v(nx,ny+1,nz)

Per-layer 0/1 v-face open mask (metrics%open_v).

private pure subroutine hvisc_clamp_A(ah_t, ah_q, bound_coef, dt, idxT, idyT, idxCu, idyCu, idxCv, idyCv, iareaCu, iareaCv, nx, ny, nz)

Per-cell CFL viscosity limiter (MOM6 BOUND_KH). Clamps the T-cell viscosity to Kh_Max_xx and the corner viscosity to Kh_Max_xy, each derived from the actual discrete stress stencil metrics + dt so the explicit forward-Euler viscous update can never overshoot. Replaces the global ah_max cap. On a uniform square grid Kh_Max = bound_coef·0.25/(dt·(1/dx²+ 1/dy²)).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: ah_t(nx,ny,nz)
real(kind=wp), intent(inout) :: ah_q(nx+1,ny+1,nz)
real(kind=wp), intent(in) :: bound_coef
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: idxT(nx,ny)
real(kind=wp), intent(in) :: idyT(nx,ny)
real(kind=wp), intent(in) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCu(nx+1,ny)
real(kind=wp), intent(in) :: idxCv(nx,ny+1)
real(kind=wp), intent(in) :: idyCv(nx,ny+1)
real(kind=wp), intent(in) :: iareaCu(nx+1,ny)
real(kind=wp), intent(in) :: iareaCv(nx,ny+1)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private pure subroutine hvisc_compute_biharmonic_face_impl(u_face, v_face, lap_u, lap_v, du_visc, dv_visc, nu4_face_x, nu4_face_y, bound_coef, dt, idxCu, idyCu, idxCv, idyCv, wet_q, dy_dxT, dx_dyBu, iareaCu, dx_dyT, dy_dxBu, iareaCv, nx, ny, nz, open_u, open_v)

Flow-aware biharmonic friction (MOM6 SMAGORINSKY_AH analogue). Identical to hvisc_compute_biharmonic_impl except Pass 2 multiplies the second Laplacian by the per-face viscosity nu4_face_x/y instead of the scalar nu_4. The face fields are filled upstream by ocean_lateral_mix_compute_smag_ah, which sets them to C_b · L⁴ · |D| clamped to [nu4_bg, nu4_max]. Pass 2 additionally clamps each face coefficient to the per-face explicit-biharmonic CFL ceiling (hvisc_nu4_cfl_bound, scaled by bound_coef) on top of the static nu4_max floor/ceiling applied upstream — so a strain spike on a fine cell can never violate the local CFL bound.

Arguments

Type IntentOptional Attributes Name
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(inout) :: lap_u(nx+1,ny,nz)
real(kind=wp), intent(inout) :: lap_v(nx,ny+1,nz)
real(kind=wp), intent(inout) :: du_visc(nx+1,ny,nz)
real(kind=wp), intent(inout) :: dv_visc(nx,ny+1,nz)
real(kind=wp), intent(in) :: nu4_face_x(nx+1,ny,nz)
real(kind=wp), intent(in) :: nu4_face_y(nx,ny+1,nz)
real(kind=wp), intent(in) :: bound_coef
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCu(nx+1,ny)
real(kind=wp), intent(in) :: idxCv(nx,ny+1)
real(kind=wp), intent(in) :: idyCv(nx,ny+1)
real(kind=wp), intent(in) :: wet_q(nx+1,ny+1)

Bu-corner wet mask (metrics%wet_q). BOTH chained Laplacians mask their corner (shear) fluxes by it – free-slip, MOM6 sh_xy = mask2dBu·(...) for Del2u and str_xy·mask2dBu for the biharmonic stress. Unmasked, the zero stored at a land face read as a Dirichlet-0 wall and the k^4 operator rang against it at every staircase step. Always free-slip: MOM6 refuses NOSLIP with BIHARMONIC. All-wet corner => factor 1 => bit-identical.

real(kind=wp), intent(in) :: dy_dxT(nx,ny)
real(kind=wp), intent(in) :: dx_dyBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCu(nx+1,ny)
real(kind=wp), intent(in) :: dx_dyT(nx,ny)
real(kind=wp), intent(in) :: dy_dxBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCv(nx,ny+1)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in), optional :: open_u(nx+1,ny,nz)

Per-layer 0/1 u-face open mask (&vcoord_nml zfixed_closed_faces). ABSENT (the default path) => the loops below are textually the ones this routine has always run => bit-identical. PRESENT => see hvisc_biharm_lap_closed: in BOTH chained Laplacians every face-to-face difference carries open(a)*open(b), so a closed face-layer is a free-slip (Neumann/mirror) boundary of the stencil exactly as a land corner is under wet_q, and receives zero tendency.

real(kind=wp), intent(in), optional :: open_v(nx,ny+1,nz)

v-face twin. Present iff open_u is.

private pure subroutine hvisc_compute_biharmonic_impl(u_face, v_face, lap_u, lap_v, du_visc, dv_visc, nu_4, bound_coef, dt, idxCu, idyCu, idxCv, idyCv, wet_q, dy_dxT, dx_dyBu, iareaCu, dx_dyT, dy_dxBu, iareaCv, nx, ny, nz, open_u, open_v)

Constant-coefficient biharmonic friction: applies -ν₄ · ∇²(∇²u) to the face velocities via two chained 5-point Laplacians. Adds into the existing du_visc / dv_visc buffers (which Laplacian friction has already filled), so the caller can run with both nu_h and nu_4 non-zero.

Read more…

Arguments

Type IntentOptional Attributes Name
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(inout) :: lap_u(nx+1,ny,nz)
real(kind=wp), intent(inout) :: lap_v(nx,ny+1,nz)
real(kind=wp), intent(inout) :: du_visc(nx+1,ny,nz)
real(kind=wp), intent(inout) :: dv_visc(nx,ny+1,nz)
real(kind=wp), intent(in) :: nu_4
real(kind=wp), intent(in) :: bound_coef
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCu(nx+1,ny)
real(kind=wp), intent(in) :: idxCv(nx,ny+1)
real(kind=wp), intent(in) :: idyCv(nx,ny+1)
real(kind=wp), intent(in) :: wet_q(nx+1,ny+1)

Bu-corner wet mask (metrics%wet_q). BOTH chained Laplacians mask their corner (shear) fluxes by it – free-slip, MOM6 sh_xy = mask2dBu·(...) for Del2u and str_xy·mask2dBu for the biharmonic stress. Unmasked, the zero stored at a land face read as a Dirichlet-0 wall and the k^4 operator rang against it at every staircase step. Always free-slip: MOM6 refuses NOSLIP with BIHARMONIC. All-wet corner => factor 1 => bit-identical.

real(kind=wp), intent(in) :: dy_dxT(nx,ny)
real(kind=wp), intent(in) :: dx_dyBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCu(nx+1,ny)
real(kind=wp), intent(in) :: dx_dyT(nx,ny)
real(kind=wp), intent(in) :: dy_dxBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCv(nx,ny+1)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in), optional :: open_u(nx+1,ny,nz)

Per-layer 0/1 u-face open mask (&vcoord_nml zfixed_closed_faces). ABSENT (the default path) => the loops below are textually the ones this routine has always run => bit-identical. PRESENT => see hvisc_biharm_lap_closed: in BOTH chained Laplacians every face-to-face difference carries open(a)*open(b), so a closed face-layer is a free-slip (Neumann/mirror) boundary of the stencil exactly as a land corner is under wet_q, and receives zero tendency.

real(kind=wp), intent(in), optional :: open_v(nx,ny+1,nz)

v-face twin. Present iff open_u is.

private pure subroutine hvisc_compute_face_impl(u_face, v_face, ah_face_x, ah_face_y, du_visc, dv_visc, dy_dxT, dx_dyBu, iareaCu, dx_dyT, dy_dxBu, iareaCv, bound_kh, bound_coef, dt, idxCu, idyCu, idxCv, idyCv, wet_q, ns, nx, ny, nz, open_u, open_v)

Per-face metric Laplacian × spatially-varying viscosity. Explicit-shape dummies so NVHPC stdpar emits a device kernel without per-launch descriptor walks. See metric_lap_u/metric_lap_v for the curvilinear FV form. bound_kh engages the per-face harmonic CFL ceiling (hvisc_kh_cfl_bound); .false. ⇒ bit-identical.

Arguments

Type IntentOptional Attributes Name
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) :: ah_face_x(nx+1,ny,nz)
real(kind=wp), intent(in) :: ah_face_y(nx,ny+1,nz)
real(kind=wp), intent(inout) :: du_visc(nx+1,ny,nz)
real(kind=wp), intent(inout) :: dv_visc(nx,ny+1,nz)
real(kind=wp), intent(in) :: dy_dxT(nx,ny)
real(kind=wp), intent(in) :: dx_dyBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCu(nx+1,ny)
real(kind=wp), intent(in) :: dx_dyT(nx,ny)
real(kind=wp), intent(in) :: dy_dxBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCv(nx,ny+1)
logical, intent(in) :: bound_kh
real(kind=wp), intent(in) :: bound_coef
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCu(nx+1,ny)
real(kind=wp), intent(in) :: idxCv(nx,ny+1)
real(kind=wp), intent(in) :: idyCv(nx,ny+1)
real(kind=wp), intent(in) :: wet_q(nx+1,ny+1)

Bu-corner wet mask (metrics%wet_q). Each corner (shear) flux of the velocity Laplacian is scaled by the slip factor (1 - 2·ns)·wet_q + 2·ns – the same C1 factor the Smagorinsky strain, the Coriolis corner vorticity and the stress-tensor path use (MOM6 sh_xy = mask2dBu·(dvdx+dudy) free-slip, (2-mask2dBu) no-slip). Free-slip: a land corner carries NO shear flux, so the zero stored at a land face never acts as a Dirichlet-0 wall. All-wet corner => factor 1 => bit-identical.

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

1 = no-slip (&ocean_hvisc_nml no_slip), 0 = free-slip.

integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in), optional :: open_u(nx+1,ny,nz)

Per-layer 0/1 u-face open mask (&vcoord_nml zfixed_closed_faces). ABSENT (the default path) => the interior loops below are textually the ones this routine has always run => bit-identical.

Read more…
real(kind=wp), intent(in), optional :: open_v(nx,ny+1,nz)

v-face twin. Present iff open_u is.

private pure subroutine hvisc_compute_scalar_impl(u_face, v_face, du_visc, dv_visc, nu_h, dy_dxT, dx_dyBu, iareaCu, dx_dyT, dy_dxBu, iareaCv, bound_kh, bound_coef, dt, idxCu, idyCu, idxCv, idyCv, wet_q, ns, nx, ny, nz, open_u, open_v)

Per-face metric Laplacian × scalar viscosity. Used when no lateral-mix closure is active — falls back to constant nu_h. When nu_h = 0 the kernel still zeros all interior + boundary cells so the apply step sees a defined state. bound_kh engages the per-face harmonic CFL ceiling (hvisc_kh_cfl_bound); .false. ⇒ bit-identical.

Arguments

Type IntentOptional Attributes Name
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(inout) :: du_visc(nx+1,ny,nz)
real(kind=wp), intent(inout) :: dv_visc(nx,ny+1,nz)
real(kind=wp), intent(in) :: nu_h
real(kind=wp), intent(in) :: dy_dxT(nx,ny)
real(kind=wp), intent(in) :: dx_dyBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCu(nx+1,ny)
real(kind=wp), intent(in) :: dx_dyT(nx,ny)
real(kind=wp), intent(in) :: dy_dxBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCv(nx,ny+1)
logical, intent(in) :: bound_kh
real(kind=wp), intent(in) :: bound_coef
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCu(nx+1,ny)
real(kind=wp), intent(in) :: idxCv(nx,ny+1)
real(kind=wp), intent(in) :: idyCv(nx,ny+1)
real(kind=wp), intent(in) :: wet_q(nx+1,ny+1)

Bu-corner wet mask (metrics%wet_q). Each corner (shear) flux of the velocity Laplacian is scaled by the slip factor (1 - 2·ns)·wet_q + 2·ns – the same C1 factor the Smagorinsky strain, the Coriolis corner vorticity and the stress-tensor path use (MOM6 sh_xy = mask2dBu·(dvdx+dudy) free-slip, (2-mask2dBu) no-slip). Free-slip: a land corner carries NO shear flux, so the zero stored at a land face never acts as a Dirichlet-0 wall. All-wet corner => factor 1 => bit-identical.

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

1 = no-slip (&ocean_hvisc_nml no_slip), 0 = free-slip.

integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in), optional :: open_u(nx+1,ny,nz)

Per-layer 0/1 u-face open mask (&vcoord_nml zfixed_closed_faces) — the FREE-SLIP closure of a z-level partial step. See hvisc_compute_face_impl’s open_u docstring for the full argument; ABSENT (the default path) ⇒ the loops below are textually unchanged ⇒ bit-identical.

real(kind=wp), intent(in), optional :: open_v(nx,ny+1,nz)

v-face twin. Present iff open_u is.

private pure subroutine hvisc_compute_stress(u_face, v_face, h_layer, str_xx, str_xy, ah_t, ah_q, du_visc, dv_visc, ns, kh_aniso, n1n2, n1n1_m_n2n2, idxCu, idyCu, idxCv, idyCv, dx2h, dy2h, dx2q, dy2q, dy_dxT, dx_dyT, dy_dxBu, dx_dyBu, iareaCu, iareaCv, wet_u, wet_v, wet_q, nx, ny, nz)

MOM6 thickness-weighted stress-divergence operator. Three phases: (1) tension str_xx at T-cells, (2) shear str_xy at Bu corners, (3) the divergence (1/(h_u+h_neglect))·∂str. wet_u/wet_v/wet_q mask the stress so no momentum is diffused across a coastline; h_neglect = H_VANISHED floors the velocity-point thickness. See module header for the form + the all-wet uniform-h Laplacian reduction.

Read more…

Arguments

Type IntentOptional Attributes Name
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) :: h_layer(nx,ny,nz)
real(kind=wp), intent(inout) :: str_xx(nx,ny,nz)
real(kind=wp), intent(inout) :: str_xy(nx+1,ny+1,nz)
real(kind=wp), intent(in) :: ah_t(nx,ny,nz)
real(kind=wp), intent(in) :: ah_q(nx+1,ny+1,nz)
real(kind=wp), intent(inout) :: du_visc(nx+1,ny,nz)
real(kind=wp), intent(inout) :: dv_visc(nx,ny+1,nz)
real(kind=wp), intent(in) :: ns
real(kind=wp), intent(in) :: kh_aniso
real(kind=wp), intent(in) :: n1n2
real(kind=wp), intent(in) :: n1n1_m_n2n2
real(kind=wp), intent(in) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCu(nx+1,ny)
real(kind=wp), intent(in) :: idxCv(nx,ny+1)
real(kind=wp), intent(in) :: idyCv(nx,ny+1)
real(kind=wp), intent(in) :: dx2h(nx,ny)
real(kind=wp), intent(in) :: dy2h(nx,ny)
real(kind=wp), intent(in) :: dx2q(nx+1,ny+1)
real(kind=wp), intent(in) :: dy2q(nx+1,ny+1)
real(kind=wp), intent(in) :: dy_dxT(nx,ny)
real(kind=wp), intent(in) :: dx_dyT(nx,ny)
real(kind=wp), intent(in) :: dy_dxBu(nx+1,ny+1)
real(kind=wp), intent(in) :: dx_dyBu(nx+1,ny+1)
real(kind=wp), intent(in) :: iareaCu(nx+1,ny)
real(kind=wp), intent(in) :: iareaCv(nx,ny+1)
real(kind=wp), intent(in) :: wet_u(nx+1,ny)
real(kind=wp), intent(in) :: wet_v(nx,ny+1)
real(kind=wp), intent(in) :: wet_q(nx+1,ny+1)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private pure subroutine hvisc_fill_A_scalar(ah_t, ah_q, nu_h, nx, ny, nz)

Fill the T-cell and corner harmonic-viscosity fields with the scalar nu_h (no flow-aware closure active). Constant ⇒ the T/corner averaging is exact, so the all-wet uniform-h reduction to the velocity Laplacian holds bit-for-bit.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(out) :: ah_t(nx,ny,nz)
real(kind=wp), intent(out) :: ah_q(nx+1,ny+1,nz)
real(kind=wp), intent(in) :: nu_h
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private pure subroutine hvisc_ke_diss_impl(nx, ny, nz, u_face, v_face, du_visc, dv_visc, rho_layer, h_layer, ke_diss)

C-grid KE budget: each face’s u·du_visc rate is split half to each adjacent T-cell and depth-integrated with ρ_k h_k. Race-free — every (i,j) writes only its own ke_diss(i,j).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: 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) :: du_visc(nx+1,ny,nz)
real(kind=wp), intent(in) :: dv_visc(nx,ny+1,nz)
real(kind=wp), intent(in) :: rho_layer(nx,ny,nz)
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(inout) :: ke_diss(nx,ny)

private subroutine ocean_horizontal_viscosity_compute_tendencies_on(grid, metrics, this, ms, u, v, h, lateral_mix, dt)

Fill du_visc and dv_visc with nu_h * Laplacian of the face velocities, per layer. Closed-wall faces (i=1, i=nx+1 for u; j=1, j=ny+1 for v) get zero tendency. Interior y- boundary rows on u (j=1, j=ny) and interior x-boundary columns on v (i=1, i=nx) also get zero — equivalent to a free-slip wall condition on the tangential velocity.

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(ocean_horizontal_viscosity_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in) :: u(:,:,:)

Velocity/thickness source arrays — the prognostic components on the historical path, the u_av time-mean family under split_scheme = "pred_corr" (MOM6 evaluates horizontal_viscosity on u_av/h_av).

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

Velocity/thickness source arrays — the prognostic components on the historical path, the u_av time-mean family under split_scheme = "pred_corr" (MOM6 evaluates horizontal_viscosity on u_av/h_av).

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

Velocity/thickness source arrays — the prognostic components on the historical path, the u_av time-mean family under split_scheme = "pred_corr" (MOM6 evaluates horizontal_viscosity on u_av/h_av).

type(ocean_lateral_mix_t), intent(in), optional :: lateral_mix
real(kind=wp), intent(in), optional :: dt

Outer (or RK2-stage) time step. Required when this%stress_tensor is true (drives the per-cell CFL viscosity limiter) and whenever a biharmonic add-on is active (drives its own per-face CFL clamp) — the velocity- Laplacian dispatch itself does not consume it, but the biharmonic block reached from every dispatch arm does. Omitting it defaults the biharmonic clamp to dt_local = 1 (a huge, effectively-inactive bound); the caller is responsible for CFL in that case.

private subroutine ocean_hvisc_destroy(this)

Arguments

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

private subroutine ocean_hvisc_enter_data(this)

Arguments

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

private subroutine ocean_hvisc_enter_data_impl(this)

Arguments

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

private subroutine ocean_hvisc_exit_data(this)

Arguments

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

private subroutine ocean_hvisc_exit_data_impl(this)

Arguments

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

private subroutine ocean_hvisc_init(this, grid, nz_ml)

Allocate the two tendency scratch buffers. Same optional-nz_ml pattern as the other ocean kernels — default 1 keeps the barotropic-only constructor valid; pass nz_ml to size for the multilayer driver.

Arguments

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