rdb_ocean_meke Module

Mesoscale eddy kinetic energy parameterization. Carries a single 2D vertically-averaged eddy-energy field meke(nx,ny) [m^2/s^2], sourced by the GM potential-energy release, damped by an implicit (backward-Euler) bottom drag, transported laterally (harmonic-mass Laplacian + optional biharmonic + advection), and fed back as a thickness/tracer diffusivity kh = khcoeff·sqrt(2·gamma_t²·E)·Lmix added (geom mean) into VarMix’s per-face KhTh before GM’s CFL clamp — closing the GM↔eddy-energy loop. Updated via a Strang split each thermo step.

Clean-room (no source ported). meke is PROGNOSTIC (restart-persistent). Default off (&ocean_meke_nml enable = .false.) ⇒ meke_step never called ⇒ bit-identical. Bottom-up convention (k=1 bed, k=nz surface); only the column mass sum touches k and it is orientation-independent.

References: Jansen, Adcroft, Hallberg & Held (2015); Eden & Greatbatch (2008); Marshall, Maddison & Berloff (2012).


Uses

  • module~~rdb_ocean_meke~~UsesGraph module~rdb_ocean_meke rdb_ocean_meke iso_fortran_env iso_fortran_env module~rdb_ocean_meke->iso_fortran_env module~rdb_constants rdb_constants module~rdb_ocean_meke->module~rdb_constants module~rdb_grid rdb_grid module~rdb_ocean_meke->module~rdb_grid module~rdb_mem_report rdb_mem_report module~rdb_ocean_meke->module~rdb_mem_report module~rdb_multilayer_state rdb_multilayer_state module~rdb_ocean_meke->module~rdb_multilayer_state module~rdb_ocean_gm rdb_ocean_gm module~rdb_ocean_meke->module~rdb_ocean_gm module~rdb_ocean_metrics rdb_ocean_metrics module~rdb_ocean_meke->module~rdb_ocean_metrics module~rdb_ocean_varmix rdb_ocean_varmix module~rdb_ocean_meke->module~rdb_ocean_varmix module~rdb_ocean_wave_speed rdb_ocean_wave_speed module~rdb_ocean_meke->module~rdb_ocean_wave_speed 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_gm->iso_fortran_env module~rdb_ocean_gm->module~rdb_constants module~rdb_ocean_gm->module~rdb_grid module~rdb_ocean_gm->module~rdb_mem_report module~rdb_ocean_gm->module~rdb_multilayer_state module~rdb_ocean_gm->module~rdb_ocean_metrics ieee_arithmetic ieee_arithmetic module~rdb_ocean_gm->ieee_arithmetic module~rdb_ocean_isopycnal_slopes rdb_ocean_isopycnal_slopes module~rdb_ocean_gm->module~rdb_ocean_isopycnal_slopes 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_ocean_varmix->iso_fortran_env module~rdb_ocean_varmix->module~rdb_constants module~rdb_ocean_varmix->module~rdb_grid module~rdb_ocean_varmix->module~rdb_mem_report module~rdb_ocean_varmix->module~rdb_multilayer_state module~rdb_ocean_varmix->module~rdb_ocean_metrics module~rdb_ocean_varmix->module~rdb_ocean_wave_speed module~rdb_ocean_varmix->module~rdb_ocean_isopycnal_slopes module~rdb_ocean_wave_speed->iso_fortran_env module~rdb_ocean_wave_speed->module~rdb_constants module~rdb_ocean_wave_speed->module~rdb_grid module~rdb_ocean_wave_speed->module~rdb_mem_report module~rdb_ocean_wave_speed->module~rdb_multilayer_state module~rdb_ocean_wave_speed->module~rdb_ocean_metrics module~rdb_efp->iso_fortran_env 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_ocean_isopycnal_slopes->iso_fortran_env module~rdb_ocean_isopycnal_slopes->module~rdb_constants module~rdb_ocean_isopycnal_slopes->module~rdb_grid module~rdb_ocean_isopycnal_slopes->module~rdb_mem_report module~rdb_ocean_isopycnal_slopes->module~rdb_multilayer_state module~rdb_ocean_isopycnal_slopes->module~rdb_ocean_metrics module~rdb_eos rdb_eos module~rdb_ocean_isopycnal_slopes->module~rdb_eos 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 module~rdb_eos->module~rdb_constants module~rdb_eos->module~rdb_grid

Used by

  • module~~rdb_ocean_meke~~UsedByGraph module~rdb_ocean_meke rdb_ocean_meke module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_ocean_meke module~rdb_ocean_state rdb_ocean_state module~rdb_ocean_state->module~rdb_ocean_meke 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 :: BACKSCATTER_CFL = 0.8_wp

Forward-Euler viscous-CFL safety coefficient for the backscatter lower bound (MOM6 BACKSCATTER_UNDERBOUND analogue). The NET (resolved − Ku) harmonic viscosity is floored at −BACKSCATTER_CFL·0.5/(dt·(idx²+idy²)) so the negative mode’s growth RATE is bounded — NOT so the operator is stable on its own (a negative Laplacian always amplifies; a positive biharmonic backstop, mandatory at configure, dissipates the fed grid-scale mode). Matches the bound_coef = 0.8 convention the resolved hvisc CFL limiter uses.

real(kind=wp), private, parameter :: MASS_NEGLECT = 1.0e-30_wp

Floor in the harmonic-mass denominator (MOM6 mass_neglect).


Derived Types

type, public ::  ocean_meke_t

Mesoscale eddy kinetic energy state. All fields default to the inert (enable=.false.) configuration so an ocean run that never sets &ocean_meke_nml is bit-identical.

Components

Type Visibility Attributes Name Initial
real(kind=wp), public :: advection_factor = 0.0_wp

Scaling on the barotropic-transport advection of MEKE (nondim); 0 (default) ⇒ the advection stage is skipped ⇒ bit-identical.

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

Weight on the deformation length scale Ldeform (nondim).

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

Weight on the Eady length scale Leady (needs VarMix SN) (nondim).

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

Weight on the frictional-arrest length scale Lfrict (nondim).

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

Weight on the grid length scale Lgrid (nondim).

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

Weight on the Rhines length scale Lrhines = sqrt(Ueddy/beta) (nondim). Default 0 ⇒ beta term inert; > 0 activates the Rhines scale (needs f_centre filled, done at setup).

logical, public :: backscatter = .false.

Master switch for the MEKE → momentum negative-viscosity energy return (capability Gap 2). Default .false. ⇒ the ku field stays 0 and meke_backscatter_apply adds nothing ⇒ bit-identical. When .true., meke_step fills ku and the driver subtracts it from the per-face harmonic viscosity, returning eddy energy to the resolved flow. HARMONIC only in v1 (the biharmonic Au return + EBT/SQG vertical structure BS_struct are deferred). Needs enable=.true. (so meke_step runs and refreshes ku) — inert otherwise.

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

Depth-integrated u-face mass transport (kg/s), (nx+1,ny); the barotropic transport that advects E. Filled only when advection_factor > 0.

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

Depth-integrated v-face mass transport (kg/s), (nx,ny+1).

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

gamma_t^2 (nondim), (nx,ny).

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

Background energy source (m^2/s^3).

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

gamma_b^2 (nondim), (nx,ny).

real(kind=wp), public :: cb = 25.0_wp

Coefficient in the gamma_bot (bottomFac2) expression (nondim).

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

Ratio of bottom eddy velocity to column-mean eddy velocity (nondim); enters bottomFac2.

real(kind=wp), public :: cdrag = 2.5e-3_wp

Bottom drag coefficient for MEKE (nondim). Copied from &ocean_bdrag_nml cdrag_side at configure if that is > 0, else this default; enters drag_rate + Lfrict.

real(kind=wp), public :: ct = 50.0_wp

Coefficient in the gamma_bt (barotrFac2) expression (nondim).

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

Local depth-independent linear MEKE dissipation rate (1/s).

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

Laplacian of MEKE workspace (biharmonic), (nx,ny).

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

Total column thickness (m), (nx,ny); used in Lfrict.

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

Scale factor accelerating MEKE time-stepping (nondim).

logical, public :: enable = .false.

Master switch. Off ⇒ meke_step is never called. Requires &ocean_gm_nml enable (needs gm%gm_src); loud configure check.

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

|f| at cell centres (1/s), (nx,ny); filled by set_f_centre from the same beta-plane the Coriolis slot uses (mirrors EPBL / kappa-shear). Drives beta = |grad f| for the Rhines length. Zero until filled ⇒ Rhines weight inert (alpha_rhines default 0).

real(kind=wp), public :: frcoeff = -1.0_wp

Efficiency of mean->eddy frictional conversion (nondim); < 0 (default) ⇒ off ⇒ bit-identical. When >= 0, adds the frictional source -frcoeff*i_mass*ke_diss from the lateral-viscosity KE dissipation (hvisc%ke_diss).

real(kind=wp), public :: gmcoeff = -1.0_wp

Efficiency of PE->MEKE conversion (nondim). < 0 ⇒ GM source off.

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

1 / column mass (m^2/kg), (nx,ny).

logical, public :: is_init = .false.

True between init and destroy; gate on this (never on allocated, which misses the GPU mapping).

real(kind=wp), public :: k4 = -1.0_wp

Background biharmonic diffusion of MEKE (m^4/s). < 0 ⇒ off.

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

Staged hvisc KE-dissipation rate (kg/s³, ≤0), (nx,ny); copied from hv%ke_diss when the frictional source is wired, else 0. Feeds meke_source as -frcoeff·i_mass·ke_diss.

real(kind=wp), public :: kh = -1.0_wp

Background lateral diffusion of MEKE (m^2/s). < 0 ⇒ diffusion stage off.

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

Derived MEKE diffusivity kh (m^2/s), (nx,ny); fed (geom-mean) into VarMix’s face KhTh/KhTr.

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

Scaling converting MEKE into Kh (nondim). <= 0 ⇒ closure off.

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

Factor relating meke%kh (the derived diffusivity) to the diffusivity used for MEKE’s own lateral spreading (nondim).

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

Factor on the geometric-mean kh added into VarMix’s KhTh face field (nondim). 0 (default) ⇒ feedback inert ⇒ bit-identical.

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

Factor on the geometric-mean kh added into VarMix’s KhTr face field (nondim). 0 (default) ⇒ inert.

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

Derived harmonic backscatter viscosity Ku (m²/s), (nx,ny); ku = visc_coeff_ku·sqrt(2·gamma_t²·E)·Lmix. Zero unless backscatter. Subtracted (face-averaged, stability-floored) from the resolved harmonic viscosity by meke_backscatter_apply.

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

Mixing length scale Lmix (m), (nx,ny) (diagnostic).

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

Column mass (kg/m^2), (nx,ny); the harmonic-mass input for the lateral flux (= 1/i_mass where i_mass>0).

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

Eddy kinetic energy E (m^2/s^2), (nx,ny). PROGNOSTIC — restart-persistent.

real(kind=wp), public :: min_gamma2 = 1.0e-4_wp

Floor on gamma_b^2 / gamma_t^2 (nondim).

integer, public :: nx_total = 0
integer, public :: ny_total = 0
integer, public :: nz_ml = 0
real(kind=wp), public, allocatable :: rd_ws(:,:)

Staged copy of wavespeed%rd_over_dx (nondim), (nx,ny); zero when wavespeed absent ⇒ Ldeform→0.

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

Staged copy of varmix%sn_u (1/s), (nx+1,ny); zero when VarMix absent ⇒ Eady scale inert.

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

Staged copy of varmix%sn_v (1/s), (nx,ny+1).

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

Aggregate source (m^2/s^3), (nx,ny).

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

Resolved bed-layer speed² (m²/s²) at cell centres, (nx,ny); filled from ms when use_bbl_drag, else 0. Feeds meke_drag.

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

u-face MEKE flux workspace, (nx+1,ny).

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

Background (e.g. tidal) eddy velocity scale for bottom drag (m/s).

logical, public :: use_bbl_drag = .false.

Add the resolved bed-layer eddy velocity |u_bed|² to the MEKE bottom-drag rate drag_rate = rho0·i_mass·sqrt(cdrag²·(2·bf2·E + |u_bed|² + uscale²)) (MOM6 drag_rate_visc). Default off ⇒ the u_bbl² workspace stays 0 ⇒ bit-identical to the prior drag.

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

v-face MEKE flux workspace, (nx,ny+1).

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

MOM6 MEKE_VISCOSITY_COEFF_KU — the nondimensional efficiency of the harmonic backscatter viscosity Ku = visc_coeff_ku·sqrt(2·gamma_t²·E)·Lmix (m²/s). May be negative in MOM6 (negative viscosity); here it is the magnitude coefficient and the SIGN of the momentum effect is set by the SUBTRACTION in meke_backscatter_apply (A_net = A − Ku), so a positive visc_coeff_ku returns energy. 0 (default) ⇒ inert.

Type-Bound Procedures

procedure, public, non_overridable :: bytes => ocean_meke_bytes
procedure, public, non_overridable :: destroy => ocean_meke_destroy
procedure, public, non_overridable :: enter_data => ocean_meke_enter_data
procedure, public, non_overridable :: exit_data => ocean_meke_exit_data
procedure, public, non_overridable :: init => ocean_meke_init
procedure, public, non_overridable :: set_f_centre => ocean_meke_set_f_centre

Functions

private pure function meke_inv_lmix(ueddy, sn, beta, area, rd_over_dx, depth, cdrag, a_deform, a_rhines, a_eady, a_frict, a_grid) result(inv_l)

Harmonic inverse mixing length 1/Lmix = Sum aX/LX over the five length scales (deformation, frictional, Rhines, Eady, grid). Each scale is gated aX*LX > 0 so a zero weight or a degenerate scale contributes nothing. Returns 1/Lmix (0 ⇒ Lmix degenerate).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: ueddy
real(kind=wp), intent(in) :: sn
real(kind=wp), intent(in) :: beta
real(kind=wp), intent(in) :: area
real(kind=wp), intent(in) :: rd_over_dx
real(kind=wp), intent(in) :: depth
real(kind=wp), intent(in) :: cdrag
real(kind=wp), intent(in) :: a_deform
real(kind=wp), intent(in) :: a_rhines
real(kind=wp), intent(in) :: a_eady
real(kind=wp), intent(in) :: a_frict
real(kind=wp), intent(in) :: a_grid

Return Value real(kind=wp)

private pure function ocean_meke_bytes(this) result(nbytes)

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

Return Value integer(kind=int64)


Subroutines

public pure subroutine meke_backscatter_apply(grid, metrics, this, dt, ah_face_x, ah_face_y)

Inject the MEKE harmonic backscatter into the per-face resolved harmonic viscosity (capability Gap 2, v1). Subtracts a face-average of the cell-centred ku field from ah_face_x/ah_face_y so the NET coefficient A_net = A_resolved − Ku can go NEGATIVE — that negative viscosity is the energy return into the momentum tendency (the hvisc Laplacian kernel reads these same face fields).

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(ocean_meke_t), intent(in) :: this
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(inout) :: ah_face_x(:,:,:)
real(kind=wp), intent(inout) :: ah_face_y(:,:,:)

public subroutine meke_step(grid, metrics, this, gm, varmix, wavespeed, ms, dt, ke_diss_ext)

Advance the MEKE field one thermo step (Strang split), update the derived diffusivity kh_diff, and feed the geometric-mean kh into VarMix’s per-face KhTh/KhTr (the GM↔MEKE feedback). Run once per outer step at thermo cadence, after varmix_compute and before gm_compute_transports (MEKE reads gm%gm_src from the previous thermo step — a one-step lag). No-op when enable=.false., uninitialised, or the GM slot is absent.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(ocean_meke_t), intent(inout) :: this
type(ocean_gm_t), intent(in) :: gm
type(ocean_varmix_t), intent(inout), optional :: varmix
type(ocean_wave_speed_t), intent(in), optional :: wavespeed
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in), optional :: ke_diss_ext(:,:)

hvisc KE-dissipation rate (nx,ny) for the frictional source; absent ⇒ the source is inert (staged to 0).

private pure subroutine meke_advect(nx, ny, sdt, adv_fac, iareaT, i_mass, baro_hu, baro_hv, uflux, vflux, meke)

Upwind flux-form advection of E by the barotropic mass transport. advFac = adv_fac/sdt uflux(I) = baroHu(I)advFacE_upwind (E_{I-1} if baroHu>0 else E_I) E += sdtIareaTI_mass((uflux_{i-1}-uflux_i)+(vflux_{j-1}-vflux_j)) Conservative on a closed domain (interior faces only; the divergence telescopes ⇒ Sum Earea*mass conserved). adv_fac=0 never reaches here (gated by the caller) ⇒ default bit-identity.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: sdt
real(kind=wp), intent(in) :: adv_fac
real(kind=wp), intent(in) :: iareaT(nx,ny)
real(kind=wp), intent(in) :: i_mass(nx,ny)
real(kind=wp), intent(in) :: baro_hu(nx+1,ny)
real(kind=wp), intent(in) :: baro_hv(nx,ny+1)
real(kind=wp), intent(inout) :: uflux(nx+1,ny)
real(kind=wp), intent(inout) :: vflux(nx,ny+1)
real(kind=wp), intent(inout) :: meke(nx,ny)

private pure subroutine meke_backscatter_apply_impl(nx, ny, nz, dt, cfl_safety, ku, idxCu, idyCu, idxCv, idyCv, ah_face_x, ah_face_y)

harmonic viscosity and floor the net at the CFL-stable minimum. Explicit-shape dummies for NVHPC stdpar (no descriptor walk). The backscatter coefficient is z-independent in v1 (BS_struct=1), so the cell-centred ku(i,j) is broadcast to every layer.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: cfl_safety
real(kind=wp), intent(in) :: ku(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(inout) :: ah_face_x(nx+1,ny,nz)
real(kind=wp), intent(inout) :: ah_face_y(nx,ny+1,nz)

private pure subroutine meke_baro_transport(nx, ny, nz, rho0, have_rho, mflux_x, mflux_y, rho_layer, baro_hu, baro_hv)

Depth-integrated, mass-weighted barotropic transport through each C-grid face: baroHu(I,j) = Sum_k rho_face * mass_flux_x_layer, where mass_flux_*_layer is the per-layer VOLUME transport (m^3/s, = uh_facedy) and rho_face the two-cell average density. The result is a MASS transport (kg/s) so the advective divergence pairs exactly with IareaT*I_mass (1/(areamass)) ⇒ Sum Earea*mass is conserved. Array-edge faces carry zero transport (closed domain).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: rho0
logical, intent(in) :: have_rho
real(kind=wp), intent(in) :: mflux_x(nx+1,ny,nz)
real(kind=wp), intent(in) :: mflux_y(nx,ny+1,nz)
real(kind=wp), intent(in) :: rho_layer(nx,ny,nz)
real(kind=wp), intent(out) :: baro_hu(nx+1,ny)
real(kind=wp), intent(out) :: baro_hv(nx,ny+1)

private pure subroutine meke_bbl_speed2(nx, ny, nz, u_face, v_face, k_bot_u, k_bot_v, u_bbl2)

Resolved bed-layer speed² at cell centres: u_bbl2 = u_c² + v_c² with u_c = ½(u_face(i)+u_face(i+1)), v_c = ½(v_face(j)+v_face(j+1)), each face read on ITS OWN bed layer k_bot_u/v (the first layer live on both sides counting up; 1 off z_fixed ⇒ the historical k=1 read). Under z_fixed the layers below are inert fillers whose velocity the closed-face mask has zeroed, so a k = 1 read reported a motionless bed. The bottom eddy velocity the MEKE drag law needs (MOM6 drag_rate_visc).

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)
integer, intent(in) :: k_bot_u(nx+1,ny)
integer, intent(in) :: k_bot_v(nx,ny+1)
real(kind=wp), intent(inout) :: u_bbl2(nx,ny)

private pure subroutine meke_drag(nx, ny, sdt_damp, damping, cdrag, uscale, rho0, i_mass, bottom_fac2, u_bbl2, meke)

Implicit (backward-Euler) bottom-drag half-step. drag_rate = rho0i_masssqrt(cdrag^2(max(0,2bf2E)+u_bbl2+uscale^2)) [1/s] damp_rate = damping + drag_ratebf2 ; =0 where E<0 E <- E/(1 + sdt_dampdamp_rate) rho0*i_mass = rho0/(Sum_k rho_kh_k) ~= 1/depth_tot [1/m], so drag_rate ~= cdrag|U_d|/H – the MOM6 GV%H_to_RZ * I_mass factor. Without it drag_rate is m^3/(kgs), not a rate. i_mass=0 on dry columns still gives drag_rate=0. u_bbl2 is the resolved bed-layer speed² (MOM6 drag_rate_visc); it is 0 unless use_bbl_drag is set, so the default is bit-identical.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: sdt_damp
real(kind=wp), intent(in) :: damping
real(kind=wp), intent(in) :: cdrag
real(kind=wp), intent(in) :: uscale
real(kind=wp), intent(in) :: rho0
real(kind=wp), intent(in) :: i_mass(nx,ny)
real(kind=wp), intent(in) :: bottom_fac2(nx,ny)
real(kind=wp), intent(in) :: u_bbl2(nx,ny)
real(kind=wp), intent(inout) :: meke(nx,ny)

private pure subroutine meke_feed_khth(nx, ny, khth_fac, khtr_fac, kh_diff, khth_u, khth_v, khtr_u, khtr_v)

Add the geometric mean of neighbour kh_diff into VarMix’s per-face KhTh (and KhTr) base BEFORE GM’s CFL clamp: khth_u(i,j) += khth_facsqrt(kh(i-1,j)kh(i,j)) khth_fac=0 ⇒ nothing added ⇒ bit-identical seam.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: khth_fac
real(kind=wp), intent(in) :: khtr_fac
real(kind=wp), intent(in) :: kh_diff(nx,ny)
real(kind=wp), intent(inout) :: khth_u(nx+1,ny)
real(kind=wp), intent(inout) :: khth_v(nx,ny+1)
real(kind=wp), intent(inout) :: khtr_u(nx+1,ny)
real(kind=wp), intent(inout) :: khtr_v(nx,ny+1)

private pure subroutine meke_kh_closure(nx, ny, khcoeff, barotr_fac2, meke, le, kh_diff)

Derived diffusivity kh = khcoeff*sqrt(2*max(0,gamma_t2*E))*Lmix. khcoeff<=0 ⇒ kh left at 0.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: khcoeff
real(kind=wp), intent(in) :: barotr_fac2(nx,ny)
real(kind=wp), intent(in) :: meke(nx,ny)
real(kind=wp), intent(in) :: le(nx,ny)
real(kind=wp), intent(out) :: kh_diff(nx,ny)

private pure subroutine meke_ku_closure(nx, ny, backscatter, visc_coeff_ku, meke, le, ku)

Derived harmonic backscatter viscosity ku = visc_coeff_ku*sqrt(2*max(0,E))*Lmix (m²/s), matching MOM6 MEKE%Ku = MEKE_VISCOSITY_COEFF_KU*sqrt(2*MEKE)*Lmix. Unlike the kh closure (which carries the barotropic-mode factor gamma_t2 inside the eddy velocity), MOM6’s Ku uses the PLAIN sqrt(2*MEKE) — no vertical-structure factor — so gamma_t2 is deliberately absent here (vertical structure BS_struct = 1; EBT/SQG deferred). Off ⇒ ku left at 0 (bit-identical seam). Always ≥ 0; the SIGN of the momentum effect is set by the subtraction downstream.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
logical, intent(in) :: backscatter
real(kind=wp), intent(in) :: visc_coeff_ku
real(kind=wp), intent(in) :: meke(nx,ny)
real(kind=wp), intent(in) :: le(nx,ny)
real(kind=wp), intent(out) :: ku(nx,ny)

private pure subroutine meke_lateral(nx, ny, sdt, kh_bg, k4, khmeke_fac, kh_flux_enabled, dy_cu, dx_cv, idxCu, idyCv, iareaT, i_mass, mass, kh_diff, uflux, vflux, del2, meke)

Harmonic-mass Laplacian diffusion of MEKE (+ optional biharmonic). Flux-form, conservative on a closed domain (interior faces only; array-edge faces carry zero flux). Kh_u = max(0,kh_bg) + khmeke_fac0.5(kh_i+kh_{i+1}), CFL-capped 0.25 uflux = Kh_u(dy_cuidxCu)[2 m_i m_{i+1}/(m_i+m_{i+1}+eps)](E_i-E_{i+1}) E += sdtiareaTi_mass((uflux_{i-1}-uflux_i)+(vflux_{j-1}-vflux_j)) Biharmonic: del2 = iareaT(d uflux’ + d vflux’) with the bare-gradient flux uflux’ = (dy_cuidxCu)(E_{i+1}-E_i); then a harmonic-mass flux of del2 with CFL cap 0.3 and E += that divergence (additive).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: sdt
real(kind=wp), intent(in) :: kh_bg
real(kind=wp), intent(in) :: k4
real(kind=wp), intent(in) :: khmeke_fac
logical, intent(in) :: kh_flux_enabled
real(kind=wp), intent(in) :: dy_cu(nx+1,ny)
real(kind=wp), intent(in) :: dx_cv(nx,ny+1)
real(kind=wp), intent(in) :: idxCu(nx+1,ny)
real(kind=wp), intent(in) :: idyCv(nx,ny+1)
real(kind=wp), intent(in) :: iareaT(nx,ny)
real(kind=wp), intent(in) :: i_mass(nx,ny)
real(kind=wp), intent(in) :: mass(nx,ny)
real(kind=wp), intent(in) :: kh_diff(nx,ny)
real(kind=wp), intent(inout) :: uflux(nx+1,ny)
real(kind=wp), intent(inout) :: vflux(nx,ny+1)
real(kind=wp), intent(inout) :: del2(nx,ny)
real(kind=wp), intent(inout) :: meke(nx,ny)

private pure subroutine meke_length_scales(nx, ny, cd_scale, cb, ct, min_gamma2, cdrag, a_deform, a_rhines, a_eady, a_frict, a_grid, areaT, idxT, idyT, f_centre, depth_tot, meke, rd_over_dx, sn_u, sn_v, bottom_fac2, barotr_fac2, le)

Fill the structure factors gamma_b^2 (bottom_fac2) and gamma_t^2 (barotr_fac2) plus the mixing length le (Lmix) at each cell centre. Ldeform/Lfrict drives both gammas; Lmix is the harmonic sum of the alpha-weighted scales. beta = |grad f| from centred f_centre differences scaled by idxT/idyT (zero when f_centre is unfilled ⇒ Rhines inert). SN = 0.25*(sn_u(i)+sn_u(i-1)+ sn_v(j)+sn_v(j-1)) only when aEady>0.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: cd_scale
real(kind=wp), intent(in) :: cb
real(kind=wp), intent(in) :: ct
real(kind=wp), intent(in) :: min_gamma2
real(kind=wp), intent(in) :: cdrag
real(kind=wp), intent(in) :: a_deform
real(kind=wp), intent(in) :: a_rhines
real(kind=wp), intent(in) :: a_eady
real(kind=wp), intent(in) :: a_frict
real(kind=wp), intent(in) :: a_grid
real(kind=wp), intent(in) :: areaT(nx,ny)
real(kind=wp), intent(in) :: idxT(nx,ny)
real(kind=wp), intent(in) :: idyT(nx,ny)
real(kind=wp), intent(in) :: f_centre(nx,ny)
real(kind=wp), intent(in) :: depth_tot(nx,ny)
real(kind=wp), intent(in) :: meke(nx,ny)
real(kind=wp), intent(in) :: rd_over_dx(nx,ny)
real(kind=wp), intent(in) :: sn_u(nx+1,ny)
real(kind=wp), intent(in) :: sn_v(nx,ny+1)
real(kind=wp), intent(out) :: bottom_fac2(nx,ny)
real(kind=wp), intent(out) :: barotr_fac2(nx,ny)
real(kind=wp), intent(out) :: le(nx,ny)

private pure subroutine meke_mass(nx, ny, nz, rho0, have_rho, h_layer, rho_layer, i_mass, depth_tot, mass_ws)

Column mass mass = Sum_k rho*max(h,H_VANISHED) (kg/m^2), its inverse i_mass (0 where mass<=0), depth_tot = Sum_k h (m), and mass_ws = mass (the harmonic-mass input for the lateral flux).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: rho0
logical, intent(in) :: have_rho
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: rho_layer(nx,ny,nz)
real(kind=wp), intent(out) :: i_mass(nx,ny)
real(kind=wp), intent(out) :: depth_tot(nx,ny)
real(kind=wp), intent(out) :: mass_ws(nx,ny)

private pure subroutine meke_source(nx, ny, bgsrc, gmcoeff, frcoeff, sdt, i_mass, gm_src, ke_diss, src, meke)

Aggregate source src = bgsrc + gmcoeff*I_mass*gm_src - frcoeff*I_mass*ke_diss and the explicit bump E += sdt*src. gmcoeff<0 ⇒ GM source off; frcoeff<0 ⇒ frictional source off. ke_diss is the lateral-viscosity KE dissipation rate (≤0), so -frcoeff*I_mass*ke_diss ≥ 0 is a mean→eddy source (0 ⇒ inert).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: bgsrc
real(kind=wp), intent(in) :: gmcoeff
real(kind=wp), intent(in) :: frcoeff
real(kind=wp), intent(in) :: sdt
real(kind=wp), intent(in) :: i_mass(nx,ny)
real(kind=wp), intent(in) :: gm_src(nx,ny)
real(kind=wp), intent(in) :: ke_diss(nx,ny)
real(kind=wp), intent(inout) :: src(nx,ny)
real(kind=wp), intent(inout) :: meke(nx,ny)

private pure subroutine meke_stage_rd(nx, ny, rd_in, rd_ws)

Copy rd_over_dx onto the device workspace. Thermo-cadence; explicit-shape; on-device (source slot is device-resident).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: rd_in(nx,ny)
real(kind=wp), intent(out) :: rd_ws(nx,ny)

private pure subroutine meke_stage_sn(nx, ny, sn_u_in, sn_v_in, sn_u_ws, sn_v_ws)

Copy the SN faces onto the device workspaces. Thermo-cadence; explicit-shape; on-device (VarMix slot is device-resident).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
real(kind=wp), intent(in) :: sn_u_in(nx+1,ny)
real(kind=wp), intent(in) :: sn_v_in(nx,ny+1)
real(kind=wp), intent(out) :: sn_u_ws(nx+1,ny)
real(kind=wp), intent(out) :: sn_v_ws(nx,ny+1)

private pure subroutine meke_zero_2d(n1, n2, a)

Zero a device-resident 2D workspace (absent-source fallback).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: n1
integer, intent(in) :: n2
real(kind=wp), intent(out) :: a(n1,n2)

private subroutine ocean_meke_destroy(this)

Arguments

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

private subroutine ocean_meke_enter_data(this)

Arguments

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

private subroutine ocean_meke_enter_data_impl(this)

Arguments

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

private subroutine ocean_meke_exit_data(this)

Arguments

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

private subroutine ocean_meke_exit_data_impl(this)

Arguments

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

private subroutine ocean_meke_init(this, grid, nz_ml)

Allocate the prognostic field, the derived diffusivity, and the Strang-stage workspaces. Always allocates (configure runs after init); setup uses plain host allocation (no do concurrent before enter_data).

Arguments

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

private subroutine ocean_meke_set_f_centre(this, grid, f_centre)

Copy a pre-filled cell-centre Coriolis magnitude |f| (1/s) onto the MEKE slot, so beta = |grad f| for the Rhines length is live. The caller (setup) builds f_centre with the same metrics_fill_coriolis path the Coriolis / VarMix / EPBL slots use (handles beta-plane AND spherical). Host loop — call after init, before enter_data.

Arguments

Type IntentOptional Attributes Name
class(ocean_meke_t), intent(inout) :: this
type(hgrid_t), intent(in) :: grid
real(kind=wp), intent(in) :: f_centre(grid%nx_total,grid%ny_total)