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).
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| type(scratch_3d_buffer_t), | public | :: | ah_q |
Harmonic viscosity averaged onto Bu corners (m²/s),
shape |
|||
| type(scratch_3d_buffer_t), | public | :: | ah_t |
Harmonic viscosity averaged onto T-cell centres (m²/s),
shape |
|||
| real(kind=wp), | public | :: | aniso_n1n1_m_n2n2 | = | 1.0_wp |
Precomputed direction-tensor factor |
|
| real(kind=wp), | public | :: | aniso_n1n2 | = | 0.0_wp |
Precomputed direction-tensor factor |
|
| real(kind=wp), | public | :: | bound_coef | = | 0.8_wp |
CFL safety coefficient for the per-cell viscosity limiter
(MOM6 |
|
| logical, | public | :: | bound_kh | = | .false. |
MOM6 |
|
| logical, | public | :: | compute_ke_diss | = | .false. |
When set (by configure when MEKE’s frictional source is on),
the apply step fills |
|
| 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 |
|
| real(kind=wp), | public, | allocatable | :: | ke_diss(:,:) |
Depth-integrated KE dissipation rate by the lateral viscosity,
|
||
| 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 |
|
| type(scratch_3d_buffer_t), | public | :: | lap_u |
First-pass Laplacian buffer used by the biharmonic path —
holds |
|||
| type(scratch_3d_buffer_t), | public | :: | lap_v |
v-face counterpart of |
|||
| logical, | public | :: | no_slip | = | .false. |
Coastal lateral BC selector (shared with the lateral-mix /
Coriolis kernels). |
|
| 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: |
|
| 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:
|
|
| type(scratch_3d_buffer_t), | public | :: | str_xx |
Thickness-weighted tension stress |
|||
| type(scratch_3d_buffer_t), | public | :: | str_xy |
Thickness-weighted shear stress
|
|||
| logical, | public | :: | stress_tensor | = | .false. |
When true, the compute step uses the MOM6-faithful
thickness-weighted stress-divergence operator
|
| 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 |
.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.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | mode |
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.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | idx |
Metric inverses |
||
| real(kind=wp), | intent(in) | :: | idy |
Metric inverses |
||
| real(kind=wp), | intent(in) | :: | bound_coef |
CFL safety coefficient ( |
||
| real(kind=wp), | intent(in) | :: | idt |
Reciprocal time step |
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.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | idx |
Metric inverses |
||
| real(kind=wp), | intent(in) | :: | idy |
Metric inverses |
||
| real(kind=wp), | intent(in) | :: | bound_coef |
CFL safety coefficient ( |
||
| real(kind=wp), | intent(in) | :: | idt |
Reciprocal time step |
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.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_horizontal_viscosity_t), | intent(in) | :: | this |
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.
| Type | Intent | Optional | 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 |
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.
| Type | Intent | Optional | 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 |
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.
| Type | Intent | Optional | 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 |
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.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(ocean_horizontal_viscosity_t), | intent(inout) | :: | this | |||
| type(multilayer_state_t), | intent(in) | :: | ms |
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).
| Type | Intent | Optional | 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(:,:,:) |
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:
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_horizontal_viscosity_t), | intent(inout) | :: | this | |||
| real(kind=wp), | intent(in) | :: | n1 | |||
| real(kind=wp), | intent(in) | :: | n2 |
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).
| Type | Intent | Optional | 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 |
| Type | Intent | Optional | 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). |
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.
| Type | Intent | Optional | 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 |
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.
| Type | Intent | Optional | 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 ( |
||
| real(kind=wp), | intent(in) | :: | open_v(nx,ny+1,nz) |
Per-layer 0/1 v-face open mask ( |
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²)).
| Type | Intent | Optional | 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 |
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.
| Type | Intent | Optional | 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 ( |
||
| 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 ( |
|
| real(kind=wp), | intent(in), | optional | :: | open_v(nx,ny+1,nz) |
v-face twin. Present iff |
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.
| Type | Intent | Optional | 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 ( |
||
| 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 ( |
|
| real(kind=wp), | intent(in), | optional | :: | open_v(nx,ny+1,nz) |
v-face twin. Present iff |
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.
| Type | Intent | Optional | 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 ( |
||
| real(kind=wp), | intent(in) | :: | ns |
1 = no-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
( |
|
| real(kind=wp), | intent(in), | optional | :: | open_v(nx,ny+1,nz) |
v-face twin. Present iff |
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.
| Type | Intent | Optional | 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 ( |
||
| real(kind=wp), | intent(in) | :: | ns |
1 = no-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
( |
|
| real(kind=wp), | intent(in), | optional | :: | open_v(nx,ny+1,nz) |
v-face twin. Present iff |
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.
| Type | Intent | Optional | 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 |
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.
| Type | Intent | Optional | 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 |
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).
| Type | Intent | Optional | 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) |
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.
| Type | Intent | Optional | 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 |
||
| real(kind=wp), | intent(in) | :: | v(:,:,:) |
Velocity/thickness source arrays — the prognostic components on
the historical path, the |
||
| real(kind=wp), | intent(in) | :: | h(:,:,:) |
Velocity/thickness source arrays — the prognostic components on
the historical path, the |
||
| 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
|
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_horizontal_viscosity_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_horizontal_viscosity_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(ocean_horizontal_viscosity_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_horizontal_viscosity_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(ocean_horizontal_viscosity_t), | intent(inout) | :: | this |
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.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_horizontal_viscosity_t), | intent(inout) | :: | this | |||
| type(hgrid_t), | intent(in) | :: | grid | |||
| integer, | intent(in), | optional | :: | nz_ml |