rdb_coriolis_adv Module

Holds variant flags + PV-at-corner workspace for the combined Coriolis + horizontal-momentum-advection kernel. We follow the Sadourny (1975) energy/enstrophy-conserving form, optionally upgraded with the Hollingsworth-Källén-Arakawa correction that removes the spurious “Hollingsworth instability” on C-grids at eddy-resolving resolutions.

The energy-conserving form adds the relative-vorticity flux + KE-gradient advection terms on top of the Coriolis force. For uniform velocity fields zeta=0 and grad(KE)=0, so the kernel reduces to plain Coriolis.


Uses

  • module~~rdb_coriolis_adv~~UsesGraph module~rdb_coriolis_adv rdb_coriolis_adv iso_fortran_env iso_fortran_env module~rdb_coriolis_adv->iso_fortran_env module~rdb_barotropic_state rdb_barotropic_state module~rdb_coriolis_adv->module~rdb_barotropic_state module~rdb_constants rdb_constants module~rdb_coriolis_adv->module~rdb_constants module~rdb_grid rdb_grid module~rdb_coriolis_adv->module~rdb_grid module~rdb_mem_report rdb_mem_report module~rdb_coriolis_adv->module~rdb_mem_report module~rdb_multilayer_state rdb_multilayer_state module~rdb_coriolis_adv->module~rdb_multilayer_state module~rdb_ocean_metrics rdb_ocean_metrics module~rdb_coriolis_adv->module~rdb_ocean_metrics module~rdb_ocean_porous rdb_ocean_porous module~rdb_coriolis_adv->module~rdb_ocean_porous module~rdb_scratch_3d rdb_scratch_3d module~rdb_coriolis_adv->module~rdb_scratch_3d module~rdb_barotropic_state->iso_fortran_env module~rdb_barotropic_state->module~rdb_constants module~rdb_barotropic_state->module~rdb_grid module~rdb_barotropic_state->module~rdb_mem_report 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_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_porous->module~rdb_constants ieee_arithmetic ieee_arithmetic module~rdb_ocean_porous->ieee_arithmetic 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 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_coriolis_adv~~UsedByGraph module~rdb_coriolis_adv rdb_coriolis_adv module~rdb_barotropic_coupling rdb_barotropic_coupling module~rdb_barotropic_coupling->module~rdb_coriolis_adv module~rdb_ocean_bt_budget_probe rdb_ocean_bt_budget_probe module~rdb_ocean_bt_budget_probe->module~rdb_coriolis_adv module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_coriolis_adv module~rdb_ocean_dyn->module~rdb_barotropic_coupling module~rdb_ocean_dyn->module~rdb_ocean_bt_budget_probe module~rdb_ocean_ke_probe rdb_ocean_ke_probe module~rdb_ocean_dyn->module~rdb_ocean_ke_probe module~rdb_ocean_halo_width rdb_ocean_halo_width module~rdb_ocean_halo_width->module~rdb_coriolis_adv module~rdb_ocean_ke_probe->module~rdb_coriolis_adv module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_setup->module~rdb_coriolis_adv 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_coriolis_adv module~rdb_ocean_state->module~rdb_ocean_dyn proc~validate_config validate_config proc~validate_config->module~rdb_coriolis_adv 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_ocean_halo_width 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

Variables

Type Visibility Attributes Name Initial
integer, public, parameter :: CORNER_H_CELL_MEAN = 0

corner_h="cell_mean" (default): wet-area 4-cell mean, H_MIN_PV-capped.

integer, public, parameter :: CORNER_H_MOM6_AREA = 1

corner_h="mom6_area": MOM6 q = abs_vort·Area_q/(hArea_q + vol_neglect).

integer, public, parameter :: PV_ADV_CENTERED = 0

2-point corner average (default; bit-identical to pre-F1).

integer, public, parameter :: PV_ADV_INVALID = -1

Sentinel for an unrecognised string (fail-loud, mirrors PV_VARIANT_INVALID).

integer, public, parameter :: PV_ADV_WENO3 = 1

3rd-order WENO-Z PV reconstruction (MOM6 WENOVI3RD, radius 2).

integer, public, parameter :: PV_ADV_WENO5 = 2

5th-order WENO-Z PV reconstruction (MOM6 WENOVI5TH, radius 3 => nghost>=4, radius + 1).

integer, public, parameter :: PV_ADV_WENO7 = 3

7th-order WENO-Z PV reconstruction (MOM6 WENOVI7TH, radius 4 => nghost>=5, radius + 1).

integer, public, parameter :: PV_VARIANT_AL81 = 2

Arakawa-Lamb 1981 PV-conserving (future; not yet wired).

integer, public, parameter :: PV_VARIANT_INVALID = -1

Sentinel returned by parse_pv_variant for an unrecognised string (PR-6 fail-loud). Distinct from PV_VARIANT_AL81 (reserved-but-unwired): both are rejected by pv_variant_is_implemented, but a caller/validate_config can give a “typo” a different message than a “not-yet-implemented”.

integer, public, parameter :: PV_VARIANT_SADOURNY = 1

Sadourny enstrophy-conserving — the default.

integer, public, parameter :: PV_VARIANT_SADOURNY_ENERGY = 4

Sadourny 1975 ENERGY-conserving (MOM6’s SADOURNY75_ENERGY). Same stencil as the enstrophy form but the PV is grouped per-corner with its own v-fluxes rather than averaged:

ENSTRO (ours, default): CAu = 0.5·avg(q_N, q_S) · avg(v_NW, v_NE, v_SW, v_SE) ENERGY (MOM6): CAu = 0.5·[q_N · 0.5·(v_NW + v_NE) + q_S · 0.5·(v_SW + v_SE)]

Conserves total kinetic energy; enstrophy form conserves squared vorticity instead. ENSTRO dissipates v specifically at asymmetric WBC fronts; ENERGY preserves it (matches MOM6 double_gyre at NK=1 within geostrophy on v). Engaged via ocean_coriolis_form = "sadourny_energy".

integer, public, parameter :: PV_VARIANT_SADOURNY_HK = 3

Sadourny + Arakawa-Hsu (1990) “HK correction” — wider 3-corner stencil; suppresses the Hollingsworth-Källén instability that biases vanilla Sadourny at eddy-resolving resolutions. Implemented in coriolis_adv_compute_tendencies_hk.

real(kind=wp), private, parameter :: CORIOLIS_H_MIN_PV = 1.0e-12_wp

Floor for the corner-h divide in the PV construction (shared by the energy/hk impls and the BOUND_CORIOLIS abs_vort recovery so the recomputed h_corner matches the pass-2 value bit-for-bit).

real(kind=wp), private, parameter :: PV_VOL_NEGLECT = 1.0e-20_wp

Roundabout analogue of MOM6’s vol_neglect (H_subroundoff·(1e-4 m)²): a corner VOLUME so small it is pure 1/0 armor, NOT a physical floor. Used only under corner_h="mom6_area" in q = abs_vort·Area_q/(hArea_q + PV_VOL_NEGLECT). Unlike the cell_mean path’s CORIOLIS_H_MIN_PV thickness cap, this does NOT bound q at a vanishing corner — matching MOM6.

real(kind=wp), private, parameter :: PV_WENO_EPS_REL = 1.0e-20_wp

MOM6 fac_fn guard threshold: a smoothness indicator b with |b| <= PV_WENO_EPS_REL*tau is treated as degenerate (that stencil dominates, factor -> PV_WENO_FAC_DEGEN). Matches MOM6’s implicit (no additive-epsilon) WENO-Z regulariser exactly.

real(kind=wp), private, parameter :: PV_WENO_FAC_DEGEN = 1.0e40_wp

Degenerate-stencil nonlinear factor (MOM6’s literal 1.0e40).


Derived Types

type, public ::  coriolis_adv_t

Components

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

Meridional gradient df/dy (1/(s·m)). Diagnostic / convenience storage — the field-of-record is f_corner.

logical, public :: bound_coriolis = .false.

MOM6 BOUND_CORIOLIS. When .true. (energy scheme only — &ocean_coriolis_nml bound_coriolis, config fail-loud on any other form) the energy-scheme PV flux is clamped into the range of the four neighbouring (f+ζ)·v velocity-form estimates BEFORE the KE-gradient subtraction, capping the thin-layer q·vh blow-up. Read host-side into a local flag in the kernel; default .false. ⇒ untaken branch ⇒ bit-identical.

integer, public :: corner_h_variant = CORNER_H_CELL_MEAN

PV corner-thickness construction (energy scheme). CORNER_H_CELL_MEAN (default, bit-identical) — wet-area 4-cell mean, CORIOLIS_H_MIN_PV-capped. CORNER_H_MOM6_AREA — MOM6’s q = abs_vort·Area_q/(hArea_q + PV_VOL_NEGLECT). The two are algebraically identical above the floor; they differ only in the vanishing-thickness guard. Set from &ocean_coriolis_nml corner_h at setup (config fail-loud on non-energy forms); read host-side into a local flag in the kernel.

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

f-plane baseline. init fills f_corner = f_0; later re-population (set_beta_plane) overrides on a per- corner basis.

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

Coriolis parameter (1/s) at C-grid corners, shape (nx+1, ny+1).

logical, public :: hk_pair_floor = .false.

Run the sadourny_hk PAIR-FLOORED passes (hk_pair_coef) even with &vcoord_nml zfixed_closed_faces off. Latched at setup for the coordinates that lay STATIC bed fillers (z_fixed, zstar, zstar_full): over a stepped bed an OPEN face then pairs a live cell with a zstar_h_min filler, so an HK “cross” pair meets a corner of fillers (h_corner ~ 1e-4 m) against a transport through a live face — the same unbounded h_face/h_corner the floor exists for, four decades larger than on the closed-face partial cells it was written against. The floor is inactive wherever no cell outweighs the other three of its corner, so a flat bed stays bit-identical. Off (default) for every other coordinate ⇒ bit-identical there.

logical, public :: is_init = .false.

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

type(scratch_3d_buffer_t), public :: ke_centre

Kinetic energy at cell centres for the gradient(KE) form.

type(scratch_3d_buffer_t), public :: mass_flux_u

HK path: u_face_x * h_at_u_face (mass flux per face) at east faces. Same shape as pv_flux_x — (nx+1, ny, nz). Unused by the Sadourny path; allocated regardless so the lifecycle is uniform.

type(scratch_3d_buffer_t), public :: mass_flux_v

HK path: v_face_y * h_at_v_face at north faces. Shape (nx, ny+1, nz). Same usage caveat as mass_flux_u.

logical, public :: no_slip = .false.

Lateral boundary condition at coasts (spec §14 C1). .false. (default) = FREE-SLIP: the corner relative vorticity is multiplied by wet_q so a land corner contributes zero rel-vort (tangential velocity free at the wall, MOM6’s free-slip closure). .true. = NO-SLIP: the factor becomes 2 - wet_q (image vorticity, MOM6’s no-slip). Set from &ocean_hvisc_nml no_slip (shared with the lateral strain). All-wet ⇒ wet_q≡1 ⇒ factor ≡1 ⇒ bit-identical.

integer, public :: pv_adv_scheme = PV_ADV_CENTERED

PV face-interpolation scheme (orthogonal to pv_variant): PV_ADV_CENTERED (default, 2-pt corner average -> bit-identical) or PV_ADV_WENO{3,5,7} (upwind-biased WENO-Z reconstruction of the corner absolute vorticity onto the faces in the Sadourny path). Driven from &ocean_coriolis_nml pv_adv_scheme via parse_pv_adv_scheme; weno5/weno7 require nghost>=4/5 (pv_adv_required_nghost).

type(scratch_3d_buffer_t), public :: pv_flux_x

du/dt at east faces.

type(scratch_3d_buffer_t), public :: pv_flux_y

dv/dt at north faces.

integer, public :: pv_variant = PV_VARIANT_SADOURNY

Active PV/Coriolis scheme variant. Defaults to Sadourny; set to PV_VARIANT_SADOURNY_HK to engage the Arakawa-Hsu correction. Driven from &ocean_setup_nml ocean_coriolis_form via parse_pv_variant.

type(scratch_3d_buffer_t), public :: q_corner

Sadourny path: relative vorticity ζ at corners. HK path: per-mass PV q = (f + ζ) / h_at_corner at corners. Both kernels write-then-read this in a single call so the repurposing is safe — they never coexist within one stage.

logical, public :: state_fluxes = .false.

&ocean_coriolis_nml use_state_fluxes — mass-consistent CorAdCalc. Config guarantees form="sadourny_energy" + split_scheme="pred_corr"; the dyn driver forwards it to coriolis_adv_compute_tendencies(use_state_fluxes=) in the CORRECTOR stage only (the predictor keeps the recompute — its continuity has not run yet this step).

logical, public :: use_hk_correction = .false.

Convenience flag mirroring pv_variant == SADOURNY_HK. Diagnostic only — the dispatch in ocean_dyn reads pv_variant directly.

logical, public :: weno_velocity_smooth = .false.

&ocean_coriolis_nml weno_velocity_smooth (MOM6 WENO_VELOCITY_SMOOTH, default off): when on, the WENO smoothness indicators are computed from the tangential velocity rather than the vorticity. Off = vorticity-based (MOM6 default).

Type-Bound Procedures

procedure, public, non_overridable :: bytes => coriolis_adv_bytes
procedure, public, non_overridable :: destroy => coriolis_adv_destroy
procedure, public, non_overridable :: enter_data => coriolis_adv_enter_data
procedure, public, non_overridable :: exit_data => coriolis_adv_exit_data
procedure, public, non_overridable :: init => coriolis_adv_init
procedure, public, non_overridable :: set_beta_plane => coriolis_adv_set_beta_plane

Functions

public pure function parse_pv_adv_scheme(name) result(code)

Translate a namelist string into a PV_ADV_* code. An unrecognised string returns PV_ADV_INVALID (fail-loud — a typo must not silently degrade the PV interpolation). weno5/weno7 are implemented; they additionally require nghost >= 4/5 (pv_adv_required_nghost), checked at configure.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: name

Return Value integer

public pure function parse_pv_variant(name) result(code)

Translate a namelist string into a PV_VARIANT_* code. An unrecognised string returns PV_VARIANT_INVALID (PR-6: fail-loud — a typo must NOT silently degrade to SADOURNY, which is a materially different conservation law). "al81" still maps to PV_VARIANT_AL81 (the reservation), but that code is rejected by pv_variant_is_implemented at configure.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: name

Return Value integer

public pure function pv_adv_required_nghost(code) result(ng)

Minimum nghost for a PV face-interp scheme: the stencil RADIUS + 1. weno5 (radius 3) -> 4, weno7 (radius 4) -> 5; centered and weno3 (radius 2) keep the nghost>=2 baseline – one rank has no seam, and every decomposed run is already floored at nghost>=3 (ocean_halo_init), which is weno3’s radius + 1 (measured bitwise, 2x2 / 4x1).

Read more…

Arguments

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

Return Value integer

public pure function pv_adv_scheme_is_implemented(code) result(ok)

.true. for every wired PV face-interpolation scheme: PV_ADV_CENTERED + PV_ADV_WENO3/WENO5/WENO7. weno5/weno7 additionally require a wider halo (pv_adv_required_nghost), checked separately at configure. PV_ADV_INVALID returns .false. (fail-loud on a typo).

Arguments

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

Return Value logical

public pure function pv_variant_is_implemented(code) result(ok)

.true. only for a Coriolis-advection variant that has a real kernel wired into coriolis_adv_compute_tendencies (SADOURNY / SADOURNY_HK / SADOURNY_ENERGY). PV_VARIANT_AL81 returns .false. — the constant is reserved but the Arakawa-Lamb kernel is not yet written, and the AL81 promise (simultaneous energy + enstrophy conservation) must not be silently substituted by the enstrophy-only Sadourny kernel. PV_VARIANT_INVALID also returns .false.. The predicate is the single gate validate_config consumes (PR-6 fail-loud).

Arguments

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

Return Value logical

public pure function weno3_recon(qm1, q0, qp1, qp2, adv_vel) result(qf)

3rd-order WENO-Z reconstruction of a corner quantity onto the face between q0 and qp1, upwind-biased on the sign of the advecting velocity adv_vel (MOM6 weno_three_h_weight_reconstruction). Blends a central candidate c0 (ideal weight 2/3) with an upwind-side linear extrapolation c1 (1/3); the WENO-Z nonlinear factor (1+tau/b)^2 collapses the weight of whichever candidate straddles a PV front. In smooth flow -> the fixed upwind-biased 3rd-order stencil; across a jump -> the ENO (non-oscillatory) branch. f-baked absolute vorticity is passed in (rdb’s vector-invariant form multiplies the result by the thickness- weighted face velocity, so there is no separate h-divide – the mass-weighting lives in that velocity, not in a PV*vh product).

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: qm1
real(kind=wp), intent(in) :: q0
real(kind=wp), intent(in) :: qp1
real(kind=wp), intent(in) :: qp2
real(kind=wp), intent(in) :: adv_vel

Return Value real(kind=wp)

public pure function weno5_recon(q1, q2, q3, q4, q5, q6, adv_vel) result(qf)

5th-order WENO-Z reconstruction (MOM6 weno_five_h_weight_reconstruction) of a 6-point corner stencil onto the face between the two central points q3,q4, upwind-biased on adv_vel. Three 3-point candidates blended by WENO-Z (ideal weights 3/10, 3/5, 1/10; tau = |b0-b2|). Applied to the absolute vorticity directly (see weno3_recon). Radius 3 (nghost>=3).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: q1
real(kind=wp), intent(in) :: q2
real(kind=wp), intent(in) :: q3
real(kind=wp), intent(in) :: q4
real(kind=wp), intent(in) :: q5
real(kind=wp), intent(in) :: q6
real(kind=wp), intent(in) :: adv_vel

Return Value real(kind=wp)

public pure function weno7_recon(q1, q2, q3, q4, q5, q6, q7, q8, adv_vel) result(qf)

7th-order WENO-Z reconstruction (MOM6 weno_seven_h_weight_reconstruction) of an 8-point corner stencil onto the face between the two central points q4,q5, upwind-biased on adv_vel. Four 4-point candidates blended by WENO-Z (ideal weights 4/35, 18/35, 12/35, 1/35; Balsara-Shu smoothness; tau = |(b0-b3) + 3(b1-b2)|). Applied to the absolute vorticity directly. Radius 4 (nghost>=4).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: q1
real(kind=wp), intent(in) :: q2
real(kind=wp), intent(in) :: q3
real(kind=wp), intent(in) :: q4
real(kind=wp), intent(in) :: q5
real(kind=wp), intent(in) :: q6
real(kind=wp), intent(in) :: q7
real(kind=wp), intent(in) :: q8
real(kind=wp), intent(in) :: adv_vel

Return Value real(kind=wp)

private pure function beta5_0(a, b, c) result(w)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: a
real(kind=wp), intent(in) :: b
real(kind=wp), intent(in) :: c

Return Value real(kind=wp)

private pure function beta5_1(a, b, c) result(w)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: a
real(kind=wp), intent(in) :: b
real(kind=wp), intent(in) :: c

Return Value real(kind=wp)

private pure function beta5_2(a, b, c) result(w)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: a
real(kind=wp), intent(in) :: b
real(kind=wp), intent(in) :: c

Return Value real(kind=wp)

private pure function beta7_0(a, b, c, d) result(w)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: a
real(kind=wp), intent(in) :: b
real(kind=wp), intent(in) :: c
real(kind=wp), intent(in) :: d

Return Value real(kind=wp)

private pure function beta7_1(a, b, c, d) result(w)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: a
real(kind=wp), intent(in) :: b
real(kind=wp), intent(in) :: c
real(kind=wp), intent(in) :: d

Return Value real(kind=wp)

private pure function beta7_2(a, b, c, d) result(w)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: a
real(kind=wp), intent(in) :: b
real(kind=wp), intent(in) :: c
real(kind=wp), intent(in) :: d

Return Value real(kind=wp)

private pure function beta7_3(a, b, c, d) result(w)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: a
real(kind=wp), intent(in) :: b
real(kind=wp), intent(in) :: c
real(kind=wp), intent(in) :: d

Return Value real(kind=wp)

private pure function coriolis_adv_bytes(this) result(nbytes)

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

Return Value integer(kind=int64)

private pure function corner_abs_vort(ic, jc, k, nx, ny, nz, q_val, h, wet_T, areaT, use_mom6_ch) result(av)

BOUND_CORIOLIS abs_vort recovery: return (f+ζ) at corner (ic,jc) by multiplying the PV q_val back by the corner thickness Pass 2 divided abs_vort by — recovering (f+ζ) to round-off. Recomputes the SAME wet-area-weighted 4-cell hm_num/hm_den, then reconstructs the effective corner thickness with the SAME formula (and floors) the active corner_h variant used in Pass 2: cell_mean (default): h_corner = max(hm_num/max(hm_den,H_DIV_EPS), CORIOLIS_H_MIN_PV); av = q·h_corner. mom6_area (use_mom6_ch): Pass 2 formed q = abs_vort·hm_den/ (hm_num + PV_VOL_NEGLECT), so the consistent inverse is av = q·(hm_num + PV_VOL_NEGLECT)/hm_den (hm_den guarded by H_DIV_EPS for the fully-land corner, where q≡0 ⇒ av≡0 anyway). Without matching the variant the recovered abs_vort would be slightly inconsistent with how q was made whenever BOTH bound_coriolis and corner_h=”mom6_area” are on — visible only in the truly-vanishing- thickness limit. Chosen over a persistent abs_vort buffer so the default-off knob costs ZERO memory.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: ic
integer, intent(in) :: jc
integer, intent(in) :: k
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: q_val
real(kind=wp), intent(in) :: h(nx,ny,nz)
real(kind=wp), intent(in) :: wet_T(nx,ny)
real(kind=wp), intent(in) :: areaT(nx,ny)
logical, intent(in) :: use_mom6_ch

Return Value real(kind=wp)

private pure function fac_weno(tau, b) result(fac)

MOM6 fac_fn: the WENO-Z nonlinear factor (1+tau/b)^2, EXACT-clamped (not an additive epsilon) to a dominant value when the smoothness indicator b is degenerate (|b| <= eps*tau), so a locally constant stencil takes over without a 0/0. Shared by weno3/5/7.

Arguments

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

Return Value real(kind=wp)

private pure function hk_corner_h(ic, jc, k, nx, ny, nz, h, wet_T, areaT) result(hc)

Corner thickness of the HK PV, recomputed with EXACTLY the Pass 2 formula of coriolis_adv_compute_tendencies_hk (wet-area-weighted 4-cell mean, array-edge clamps, CORIOLIS_H_MIN_PV floor), so the closed-face branch knows the h_corner each stored q was divided by without a persistent buffer.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: ic
integer, intent(in) :: jc
integer, intent(in) :: k
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: h(nx,ny,nz)
real(kind=wp), intent(in) :: wet_T(nx,ny)
real(kind=wp), intent(in) :: areaT(nx,ny)

Return Value real(kind=wp)

private pure function hk_pair_coef(q1, h1, q2, h2, q3, h3, h_ref) result(coef)

One Arakawa-Hsu pair coefficient (q1 + q2 + q3)/12 with each PV re-evaluated at a corner thickness of at least h_ref/2: q → q·h_X/(h_ref/2) where h_X < h_ref/2, q unchanged otherwise (so an inactive floor returns the unfloored sum bit for bit). h_ref is the larger thickness of the pair’s u- and v-face.

Read more…

Arguments

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

The three corner PVs and the corner thicknesses they carry.

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

The three corner PVs and the corner thicknesses they carry.

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

The three corner PVs and the corner thicknesses they carry.

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

The three corner PVs and the corner thicknesses they carry.

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

The three corner PVs and the corner thicknesses they carry.

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

The three corner PVs and the corner thicknesses they carry.

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

max(h_U, h_V) of the pair’s two faces (m).

Return Value real(kind=wp)


Subroutines

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

Per-layer forward-Euler velocity update. no_wait (optional, default .false.): when .true. the apply DC loops are issued on OpenACC queue 1 and the routine returns WITHOUT syncing, so a batched caller (run_stage_split velocity-apply chain) can pipeline the whole additive apply sequence and !$acc wait(1) ONCE. Default ⇒ self-contained blocking apply (historical, safe for non-batched callers — e.g. the unsplit run_stage). Not pure because of the async/wait directives; still functionally pure.

Arguments

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

public pure subroutine coriolis_adv_apply_tendencies_barotropic(this, bs, dt)

Forward-Euler velocity update from the tendencies the compute step wrote into pv_flux_x / pv_flux_y. u_face_x(i, j) <- u_face_x(i, j) + dt * pv_flux_x(i, j) v_face_y(i, j) <- v_face_y(i, j) + dt * pv_flux_y(i, j) Split-explicit RK2 (Phase 4) wraps a pair of these around an RK2 averaging pass.

Arguments

Type IntentOptional Attributes Name
type(coriolis_adv_t), intent(in) :: this
type(barotropic_state_t), intent(inout) :: bs
real(kind=wp), intent(in) :: dt

public subroutine coriolis_adv_compute_tendencies(grid, metrics, this, ms, u_src, v_src, h_src, use_state_fluxes)

Per-layer Coriolis + advection dispatcher. Reads this%pv_variant and routes to the matching kernel body:

Read more…

Arguments

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

Optional velocity/thickness source override (SPEC §4 S3, split_scheme = "pred_corr"): the driver passes the step time-means ms%u_av_layer / v_av_layer / h_av_layer so the Coriolis-advection tendency is evaluated on the MOM6 u_av family, never the prognostic (MOM6’s CorAdCalc(u_av, v_av, h_av, ...)). All three must be passed together. Absent ⇒ prognostic arrays, bit-identical.

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

Optional velocity/thickness source override (SPEC §4 S3, split_scheme = "pred_corr"): the driver passes the step time-means ms%u_av_layer / v_av_layer / h_av_layer so the Coriolis-advection tendency is evaluated on the MOM6 u_av family, never the prognostic (MOM6’s CorAdCalc(u_av, v_av, h_av, ...)). All three must be passed together. Absent ⇒ prognostic arrays, bit-identical.

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

Optional velocity/thickness source override (SPEC §4 S3, split_scheme = "pred_corr"): the driver passes the step time-means ms%u_av_layer / v_av_layer / h_av_layer so the Coriolis-advection tendency is evaluated on the MOM6 u_av family, never the prognostic (MOM6’s CorAdCalc(u_av, v_av, h_av, ...)). All three must be passed together. Absent ⇒ prognostic arrays, bit-identical.

logical, intent(in), optional :: use_state_fluxes

public pure subroutine coriolis_adv_compute_tendencies_barotropic(grid, metrics, this, bs)

Sadourny (1975) energy-conserving Coriolis + horizontal- momentum-advection form on the barotropic C-grid state:

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(coriolis_adv_t), intent(inout) :: this
type(barotropic_state_t), intent(in) :: bs

public pure subroutine coriolis_adv_compute_tendencies_hk(grid, metrics, this, ms, u, v, h)

Public only for the unit-test suite (no production module imports it); ignore when developing production code in other modules. Per-layer PV-conserving Coriolis + horizontal-advection tendency in the Arakawa-Hsu (1990) form (“HK correction”). The wider 3-corner PV stencil at each face suppresses the spurious Hollingsworth-Källén instability that biases the simpler Sadourny 2-corner form at eddy-resolving resolutions.

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(coriolis_adv_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in) :: u(grid%nx_total+1,grid%ny_total,ms%nz_ml)

Face-velocity / thickness source arrays (outer-shim; the dispatcher forwards either the prognostic components or the u_av time-mean family under split_scheme = "pred_corr").

real(kind=wp), intent(in) :: v(grid%nx_total,grid%ny_total+1,ms%nz_ml)
real(kind=wp), intent(in) :: h(grid%nx_total,grid%ny_total,ms%nz_ml)

public pure subroutine coriolis_adv_compute_tendencies_sadourny_energy(grid, metrics, this, ms, u, v, h, use_state_fluxes)

Faithful MOM6 SADOURNY75_ENERGY (Sadourny 1975 energy-conserving) per-layer Coriolis + horizontal-advection tendency. This is the TRANSPORT form: the absolute-vorticity flux is the potential vorticity q = (f + ζ)/h_at_corner times the layer MASS TRANSPORT (vh/uh), so the discrete Coriolis term produces zero net domain kinetic energy (energy-conserving). The default enstrophy form (_sadourny, (f+ζ)·v) only matches this under uniform thickness.

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(coriolis_adv_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in) :: u(grid%nx_total+1,grid%ny_total,ms%nz_ml)

Face-velocity / thickness source arrays (outer-shim; the dispatcher forwards either the prognostic components or the u_av time-mean family under split_scheme = "pred_corr").

real(kind=wp), intent(in) :: v(grid%nx_total,grid%ny_total+1,ms%nz_ml)
real(kind=wp), intent(in) :: h(grid%nx_total,grid%ny_total,ms%nz_ml)
logical, intent(in), optional :: use_state_fluxes

Mass-consistent CorAdCalc (MOM6 parity): fill the transport buffers from ms%mass_flux_*_layer — the continuity solve’s renormalised uh/vh (same u·h_face·dy_cu m³/s convention, same shape, physical walls already zeroed) — instead of recomputing from the u/h source arrays. In the pred_corr CORRECTOR those are the PREDICTOR chain’s fluxes, i.e. exactly the transport field that produced the u_av evaluation state, so the q·vh product is energy-consistent on rim columns where the renorm/wall-zero and the naive u·h_face recompute disagree. Absent / .false. ⇒ bit-identical recompute path.

private pure subroutine coriolis_adv_compute_tendencies_sadourny(grid, metrics, this, ms, u, v, h)

Per-layer Sadourny Coriolis + advection tendency. Same algorithm as the barotropic counterpart, lifted with a k-axis on every loop. Each k-slice is independent (ζ stencil only reads same-k velocities; KE at centre only reads same-k face values), so the do-concurrent kernels parallelise over (k, j, i) for full GPU occupancy.

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(coriolis_adv_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in) :: u(grid%nx_total+1,grid%ny_total,ms%nz_ml)

Face-velocity / thickness source arrays (outer-shim; the dispatcher forwards either the prognostic components or the u_av time-mean family under split_scheme = "pred_corr").

real(kind=wp), intent(in) :: v(grid%nx_total,grid%ny_total+1,ms%nz_ml)
real(kind=wp), intent(in) :: h(grid%nx_total,grid%ny_total,ms%nz_ml)

private subroutine coriolis_adv_destroy(this)

Arguments

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

private subroutine coriolis_adv_enter_data(this)

Arguments

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

private subroutine coriolis_adv_enter_data_impl(this)

Arguments

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

private subroutine coriolis_adv_exit_data(this)

Arguments

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

private subroutine coriolis_adv_exit_data_impl(this)

Arguments

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

private subroutine coriolis_adv_init(this, grid, nz_ml)

Allocate the 4 scratch buffers sized at (nx_face / ny_face / corner, nz). Default nz=1 covers the barotropic kernel; passing nz_ml sizes them for the multilayer kernel. Same backward-compatible pattern as continuity_init.

Arguments

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

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

Populate f_corner with a beta-plane profile f(y) = f_0 + beta * (y - y_ref)

Read more…

Arguments

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