rdb_ocean_redi Module

Redi neutral (along-isopycnal) tracer diffusion. Continuous (non-iterative) neutral-surface geometry of the Griffies rotated-diffusion tensor + the two-phase GPU flux kernel. Clean-room from Redi (1982), Griffies et al. (1998), Griffies (2004).

INDEXING CONVENTION (load-bearing). The neutral-surface sweep routines are transcribed TOP-DOWN — k=1 surface interface, k=nk+1 bed. rdb is bottom-up (k=1 bed, k=nk surface); the k-flip is confined to the Phase-B flux scatter (native k = nz+1-Ko). Do NOT call the sweep routines with bottom-up arrays without flipping first.

Partial-step z-level faces (&vcoord_nml zfixed_closed_faces)

Under z_fixed a face column is not the whole water column: a layer that is an inert FILLER on either side (inside the bed, or inside the ice draft) is a WALL for that layer at that face (metrics%open_u/open_v == 0). Neutral diffusion pairs the two columns of a face by sweeping neutral surfaces through BOTH columns, so without a seam it pairs a layer that is closed at the face (live on the deep side, below the shallow side’s bed) with live water on the other side — a tracer flux THROUGH the wall, as large as any open-face flux once the isopycnals tilt by more than a bed step — plus an O(h_min) one into the fillers themselves (test_ocean_redi_zfixed).

With the knob on (metrics%use_closed_faces) each face is reduced to its OPEN WINDOW before anything else is done with it:

ok(k)  = open(k) .and. live(h_W(k)) .and. live(h_E(k))
kt     = the topmost ok layer
kb..kt = the contiguous run of ok layers counted down from kt

(rdb_vl_is_live, the one vanished-layer predicate; ok is the set GM’s open-column streamfunction uses). Phase A builds both columns’ interface T/S/P and runs the continuous sweep on the nk = kt-kb+1 window layers ONLY, so a neutral surface can neither start nor end in a filler or a closed face-layer, and the PPM edge reconstruction never reads a filler (PCM ends at the window edges — the sweep’s own b_method = 1). Phase B rebuilds the tracer columns on the same window and scatters only into native layers inside it, so the flux on every closed face-layer and every filler is EXACTLY zero.

  • Pairing rules. Only window layers are paired, and the window is CONTIGUOUS, so no pairing crosses a closed layer either. The mask is the intersection of two contiguous live ranges, so ok is contiguous by construction; a gap (a layer that thinned to the marker in mid-column) would cut the window there — the layers below it take no Redi flux at that face — rather than be paired across.
  • Pressure frame. P is the surface-relative hydrostatic pressure of the FULL column sliced to the window (fillers above it add their h_min), so an all-open face is the original arithmetic operation for operation.
  • Storage. Ko stays in the full TOP-DOWN frame (Ko_win + nz - kt), so the Phase-B scatter native k = nz+1-Ko is unchanged; the trailing 2*(nz-nk) surfaces of a short window are inert padding (hEff = 0, never read). The window is stored per face (uKb/uKt, vKb/vKt) because Phase B runs at later time levels than Phase A; a window layer that is no longer live on both sides when Phase B runs skips the whole face — both cells of the face read the same h, so the skip is symmetric and content is still conserved.

Content is conserved exactly as before (each sublayer flux is formed identically by both cells of the face and enters one with +, the other with -), and the down-gradient sign guard is untouched, so the flux is still down the along-neutral gradient. Every divisor in the window is a live thickness (> H_VANISHED), so the port opens no new non-finite path. Knob OFF ⇒ kb = 1, kt = nz on every face, the window IS the column and the arithmetic is the full-column form ⇒ byte-identical.


Uses

  • module~~rdb_ocean_redi~~UsesGraph module~rdb_ocean_redi rdb_ocean_redi iso_fortran_env iso_fortran_env module~rdb_ocean_redi->iso_fortran_env module~rdb_constants rdb_constants module~rdb_ocean_redi->module~rdb_constants module~rdb_eos rdb_eos module~rdb_ocean_redi->module~rdb_eos module~rdb_grid rdb_grid module~rdb_ocean_redi->module~rdb_grid module~rdb_mem_report rdb_mem_report module~rdb_ocean_redi->module~rdb_mem_report module~rdb_multilayer_state rdb_multilayer_state module~rdb_ocean_redi->module~rdb_multilayer_state module~rdb_ocean_boundary_types rdb_ocean_boundary_types module~rdb_ocean_redi->module~rdb_ocean_boundary_types module~rdb_ocean_metrics rdb_ocean_metrics module~rdb_ocean_redi->module~rdb_ocean_metrics module~rdb_tracer rdb_tracer module~rdb_ocean_redi->module~rdb_tracer pic_types pic_types module~rdb_constants->pic_types module~rdb_eos->module~rdb_constants module~rdb_eos->module~rdb_grid 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_multilayer_state->module~rdb_tracer 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_multilayer_state->pic_logger module~rdb_ocean_boundary_types->iso_fortran_env module~rdb_ocean_boundary_types->module~rdb_constants module~rdb_ocean_boundary_types->module~rdb_grid module~rdb_ocean_boundary_types->module~rdb_mem_report module~rdb_ocean_status rdb_ocean_status module~rdb_ocean_boundary_types->module~rdb_ocean_status module~rdb_ocean_tide_astro rdb_ocean_tide_astro module~rdb_ocean_boundary_types->module~rdb_ocean_tide_astro pic_ascii pic_ascii module~rdb_ocean_boundary_types->pic_ascii module~rdb_ocean_boundary_types->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_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_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_efp->iso_fortran_env ieee_arithmetic ieee_arithmetic module~rdb_efp->ieee_arithmetic module~rdb_error_ring->pic_logger module~rdb_io_netcdf->iso_fortran_env module~rdb_io_netcdf->module~rdb_constants module~rdb_io_netcdf->module~rdb_error_ring module~rdb_io_netcdf->netcdf module~rdb_io_netcdf->pic_logger module~rdb_io_netcdf->pic_strings iso_c_binding iso_c_binding module~rdb_io_netcdf->iso_c_binding module~rdb_ocean_bipolar->module~rdb_constants module~rdb_ocean_fold->module~rdb_constants module~rdb_ocean_tide_astro->iso_fortran_env module~rdb_ocean_tide_astro->module~rdb_constants

Used by

  • module~~rdb_ocean_redi~~UsedByGraph module~rdb_ocean_redi rdb_ocean_redi module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_ocean_redi module~rdb_ocean_state rdb_ocean_state module~rdb_ocean_state->module~rdb_ocean_redi 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

Derived Types

type, public ::  ocean_redi_t

Redi continuous neutral-diffusion state. Defaults inert (enable=.false.) ⇒ bit-identical. Reads the prognostic T/S + EOS directly (recomputes its own interface dR/dT, dR/dS — does NOT consume GM slopes). Augments rdb_ocean_hdiff_tracer.

Components

Type Visibility Attributes Name Initial
logical, public :: continuous = .true.

Continuous variant (the only one shipped). Discontinuous deferred (R4); a .false. value is rejected at configure.

logical, public :: enable = .false.

Master switch. Off => redi_calc_coeffs/redi_apply_flux no-op => bit-identity preserved.

logical, public :: is_init = .false.

True between init/destroy; gate on this, not on allocated.

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

Scalar Redi neutral diffusivity (m^2/s); the FALLBACK used when VarMix is off (0 => no flux). When VarMix is enabled its spatially-varying khtr_u/khtr_v override this per face.

real(kind=wp), public, allocatable :: khtr_u(:,:)
real(kind=wp), public, allocatable :: khtr_v(:,:)
integer, public :: nsurf = 0

2*nz_ml + 2 (the neutral-surface count of the continuous sweep).

integer, public :: nx_total = 0
integer, public :: ny_total = 0
integer, public :: nz_ml = 0
real(kind=wp), public, allocatable :: tr_snap(:,:,:)
integer, public, allocatable :: uKb(:,:)
integer, public, allocatable :: uKoL(:,:,:)
integer, public, allocatable :: uKoR(:,:,:)
integer, public, allocatable :: uKt(:,:)
real(kind=wp), public, allocatable :: uPoL(:,:,:)
real(kind=wp), public, allocatable :: uPoR(:,:,:)
real(kind=wp), public, allocatable :: uhEff(:,:,:)
integer, public, allocatable :: vKb(:,:)
integer, public, allocatable :: vKoL(:,:,:)
integer, public, allocatable :: vKoR(:,:,:)
integer, public, allocatable :: vKt(:,:)
real(kind=wp), public, allocatable :: vPoL(:,:,:)
real(kind=wp), public, allocatable :: vPoR(:,:,:)
real(kind=wp), public, allocatable :: vhEff(:,:,:)

Type-Bound Procedures

procedure, public, non_overridable :: bytes => ocean_redi_bytes
procedure, public, non_overridable :: destroy => ocean_redi_destroy
procedure, public, non_overridable :: enter_data => ocean_redi_enter_data
procedure, public, non_overridable :: exit_data => ocean_redi_exit_data
procedure, public, non_overridable :: init => ocean_redi_init

Functions

public pure function redi_interpolate_position(dRhoNeg, Pneg, dRhoPos, Ppos) result(pos)

Non-dimensional position in [0,1] where the interpolated density difference is zero. Guards the vanished/inverted (Ppos==Pneg) and degenerate (dRhoPos==dRhoNeg) cases device-safely (clamped values, no host I/O).

Arguments

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

negative density difference

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

position of the negative difference

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

positive density difference

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

position of the positive difference

Return Value real(kind=wp)

private pure function ocean_redi_bytes(this) result(nbytes)

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

Return Value integer(kind=int64)

private pure function redi_absolute_position(nk, Pint, Karr, frac) result(p)

Absolute (pressure) position of neutral surface ks in a column (MOM6 absolute_position): Pint(K) + frac*(Pint(K+1)-Pint(K)).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nk
real(kind=wp), intent(in) :: Pint(nk+1)

interface pressures

integer, intent(in) :: Karr

layer index for this surface

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

fractional position within the layer

Return Value real(kind=wp)

private pure function redi_fv_diff(hkm1, hk, hkp1, skm1, sk, skp1) result(d)

Second-order centred finite-volume slope of a layer scalar (MOM6 fv_diff; Colella & Woodward 1984). Returns the cell-centred difference across layer k given the three layer thicknesses and values.

Arguments

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

layer thicknesses (above/centre/below)

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

layer thicknesses (above/centre/below)

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

layer thicknesses (above/centre/below)

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

layer scalar values

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

layer scalar values

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

layer scalar values

Return Value real(kind=wp)

private pure function redi_ppm_ave(xL, xR, aL, aR, aMean) result(av)

Mean of a PPM parabola between fractional positions xL,xR in [0,1] (MOM6 ppm_ave). Device-safe: dx<0 / dx>1 FATALs collapse to the dx==0 branch value (no host I/O on device).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: xL
real(kind=wp), intent(in) :: xR
real(kind=wp), intent(in) :: aL
real(kind=wp), intent(in) :: aR
real(kind=wp), intent(in) :: aMean

Return Value real(kind=wp)

private pure function redi_ppm_edge(hkm1, hk, hkp1, hkp2, ak, akp1, pk, pkp1, h_neglect) result(e)

PPM quasi-fourth-order edge value at interface k+1/2 (MOM6 ppm_edge; Colella & Woodward 1984 eq. 1.6).

Arguments

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

widths of cells k-1..k+2

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

widths of cells k-1..k+2

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

widths of cells k-1..k+2

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

widths of cells k-1..k+2

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

cell averages k, k+1

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

cell averages k, k+1

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

PLM slopes k, k+1

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

PLM slopes k, k+1

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

negligible thickness floor

Return Value real(kind=wp)

private pure function redi_signum(a, x) result(s)

A true signum: -|a| if x<0, +|a| if x>0, 0 if x==0 (MOM6 signum).

Arguments

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

Return Value real(kind=wp)

private pure function redi_signum1(x) result(s)

signum(1.,x): -1 if x<0, +1 if x>0, 0 if x==0 (MOM6 sign guard).

Arguments

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

Return Value real(kind=wp)

private pure function redi_sublayer_dT(nz, klt, klb, krt, krb, PoLt, PoLb, PoRt, PoRb, TlL, TiL, aLL, aRL, TlR, TiR, aLR, aRR) result(dT)

Along-neutral tracer difference for one sublayer (MOM6 neutral_surface_flux continuous branch). TOP-DOWN layer indices (klt/klb = KoL at the surface/bed bound of the sublayer; krt/krb mirror) and fractional positions. Returns dT_layer when the top/bottom/ave/layer triad is sign-consistent, else 0 (the down-gradient guard that prevents up-gradient transport).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
integer, intent(in) :: klt
integer, intent(in) :: klb
integer, intent(in) :: krt
integer, intent(in) :: krb
real(kind=wp), intent(in) :: PoLt
real(kind=wp), intent(in) :: PoLb
real(kind=wp), intent(in) :: PoRt
real(kind=wp), intent(in) :: PoRb
real(kind=wp), intent(in) :: TlL(NZ_STACK_MAX)
real(kind=wp), intent(in) :: TiL(NZ_STACK_MAX+1)
real(kind=wp), intent(in) :: aLL(NZ_STACK_MAX)
real(kind=wp), intent(in) :: aRL(NZ_STACK_MAX)
real(kind=wp), intent(in) :: TlR(NZ_STACK_MAX)
real(kind=wp), intent(in) :: TiR(NZ_STACK_MAX+1)
real(kind=wp), intent(in) :: aLR(NZ_STACK_MAX)
real(kind=wp), intent(in) :: aRR(NZ_STACK_MAX)

Return Value real(kind=wp)


Subroutines

public subroutine redi_apply_flux(grid, metrics, this, ms, dt, khtr_u_ext, khtr_v_ext, bc)

Public Phase-B entry: apply the neutral-diffusion tracer update for every registered tracer. No-op if absent / uninit / disabled / zero diffusivity. Run at thermo cadence after redi_calc_coeffs and the along-coordinate tracer_hdiff (Redi augments it). Per-face KhTr comes from khtr_u_ext/khtr_v_ext (VarMix) when supplied, else the scalar this%khtr broadcast onto every face.

Arguments

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

VarMix per-face KhTr at u-faces (nx+1, ny) (m^2/s).

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

VarMix per-face KhTr at v-faces (nx, ny+1) (m^2/s).

type(ocean_bc_state_t), intent(in), optional :: bc

Per-edge OBC tags. No along-isopycnal flux crosses a no-normal-flow (OBC_WALL) physical-domain boundary face — else Redi bleeds tracer into the ghost halo. An OPEN (tracer-open) edge KEEPS its face flux, read against the OBC-filled ghost column — MOM6 neutral_diffusion gates its faces on G%mask2dCu, which open_boundary_impose_land_mask leaves at 1 on an open segment’s normal face (it zeroes only OBCmaskCu there), so neutral diffusion exchanges tracer with the exterior; that exchange is booked in *_budget_hdiff below so the closed budget stays closed. An MPI seam (has_* = .false.) is never a wall. Absent ⇒ all edges WALL.

public subroutine redi_calc_coeffs(grid, metrics, eos, this, ms)

Public entry: fill the Phase-A coefficient arrays. No-op if absent / uninitialised / disabled. Run once per outer step at THERMO cadence (a slow, tracer-independent geometry). Outer-shim: dereference the tracer-registry hTr arrays on the host, pass the flat top-level allocatables to the flat-impl kernels.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(eos_t), intent(in) :: eos
type(ocean_redi_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms

public pure subroutine redi_interface_scalar(nk, h, tr, edge, h_neglect)

PPM continuous edge reconstruction of a layer scalar to interfaces (MOM6 interface_scalar with i_method=2). edge(1)=surface, edge(nk+1)=bed in the MOM6 top-down sense (see module header on the deferred k-flip).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nk
real(kind=wp), intent(in) :: h(nk)

layer thicknesses

real(kind=wp), intent(in) :: tr(nk)

layer scalar (e.g. T)

real(kind=wp), intent(out) :: edge(nk+1)

interface scalar

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

negligible thickness (default 1e-30)

public pure subroutine redi_neutral_positions_continuous(nk, Pl, Tl, Sl, dRdTl, dRdSl, Pr, Tr, Sr, dRdTr, dRdSr, PoL, PoR, KoL, KoR, hEff)

The continuous neutral-surface sweep over a column pair. A single deterministic top→bottom sweep of 2*nk+2 surfaces walking two interface pointers; closed-form linear crossing per step (no inner iteration). Inputs are interface T/S/P + interface dR/dT, dR/dS (nk+1 each). Outputs PoL/PoR (fractional position within layer KoL/KoR) and hEff (harmonic-mean effective thickness between consecutive neutral surfaces; outcrops get hEff=0, not skipped). TOP-DOWN indexing (see module header).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nk
real(kind=wp), intent(in) :: Pl(nk+1)

left interface P, T, S

real(kind=wp), intent(in) :: Tl(nk+1)

left interface P, T, S

real(kind=wp), intent(in) :: Sl(nk+1)

left interface P, T, S

real(kind=wp), intent(in) :: dRdTl(nk+1)

left interface dR/dT, dR/dS

real(kind=wp), intent(in) :: dRdSl(nk+1)

left interface dR/dT, dR/dS

real(kind=wp), intent(in) :: Pr(nk+1)

right interface P, T, S

real(kind=wp), intent(in) :: Tr(nk+1)

right interface P, T, S

real(kind=wp), intent(in) :: Sr(nk+1)

right interface P, T, S

real(kind=wp), intent(in) :: dRdTr(nk+1)

right interface dR/dT, dR/dS

real(kind=wp), intent(in) :: dRdSr(nk+1)

right interface dR/dT, dR/dS

real(kind=wp), intent(out) :: PoL(2*nk+2)

fractional positions

real(kind=wp), intent(out) :: PoR(2*nk+2)

fractional positions

integer, intent(out) :: KoL(2*nk+2)

layer indices

integer, intent(out) :: KoR(2*nk+2)

layer indices

real(kind=wp), intent(out) :: hEff(2*nk+1)

effective thicknesses

private subroutine ocean_redi_destroy(this)

Arguments

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

private subroutine ocean_redi_enter_data(this)

Arguments

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

private subroutine ocean_redi_enter_data_impl(this)

Arguments

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

private subroutine ocean_redi_exit_data(this)

Arguments

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

private subroutine ocean_redi_exit_data_impl(this)

Arguments

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

private subroutine ocean_redi_init(this, grid, nz_ml)

Allocate the Phase-A coefficient arrays. Always allocates (configure runs after init); off-state footprint is the six face-shaped (nsurf) coefficient arrays. Plain host allocation (no do concurrent before enter_data).

Arguments

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

private subroutine redi_apply_flux_impl(nx, ny, nz, ns, dt, nghost, nxp, nyp, wall_w, wall_e, wall_s, wall_n, khtr_u, khtr_v, dy_cu, dx_cv, idxCu, idyCv, areaT, h_layer, hTr_in, hTr, uPoL, uPoR, uKoL, uKoR, uhEff, vPoL, vPoR, vKoL, vKoR, vhEff, uKb, uKt, vKb, vKt, use_open)

Flat-impl Phase-B kernel for ONE tracer. Cell-centric double-visit (no-scatter rule on the C-grid): cell (i,j) recomputes the along-neutral flux on each of its four bounding faces and accumulates ONLY into its own dTr; interior-face fluxes are computed twice but no thread writes a neighbour ⇒ race-free. Face sign: the LEFT (west/south) cell of a face gets +Flx into native layer nz+1-KoL; the RIGHT (east/north) cell gets -Flx into nz+1-KoR. The top-down→native flip k=nz+1-Ko is the only k-flip. Flux = dT_layer * hEff * Coef, Coef_u = dtkhtr_udy_cu*idxCu; divergence hTr(k) += dTr(k)/areaT (conservative).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
integer, intent(in) :: ns
real(kind=wp), intent(in) :: dt
integer, intent(in) :: nghost
integer, intent(in) :: nxp
integer, intent(in) :: nyp
logical, intent(in) :: wall_w
logical, intent(in) :: wall_e
logical, intent(in) :: wall_s
logical, intent(in) :: wall_n
real(kind=wp), intent(in) :: khtr_u(nx+1,ny)
real(kind=wp), intent(in) :: khtr_v(nx,ny+1)
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) :: areaT(nx,ny)
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hTr_in(nx,ny,nz)
real(kind=wp), intent(inout) :: hTr(nx,ny,nz)
real(kind=wp), intent(in) :: uPoL(nx+1,ny,ns)
real(kind=wp), intent(in) :: uPoR(nx+1,ny,ns)
integer, intent(in) :: uKoL(nx+1,ny,ns)
integer, intent(in) :: uKoR(nx+1,ny,ns)
real(kind=wp), intent(in) :: uhEff(nx+1,ny,ns-1)
real(kind=wp), intent(in) :: vPoL(nx,ny+1,ns)
real(kind=wp), intent(in) :: vPoR(nx,ny+1,ns)
integer, intent(in) :: vKoL(nx,ny+1,ns)
integer, intent(in) :: vKoR(nx,ny+1,ns)
real(kind=wp), intent(in) :: vhEff(nx,ny+1,ns-1)
integer, intent(in) :: uKb(nx+1,ny)

Phase-A u-face open windows (1..nz off the closed-face path).

integer, intent(in) :: uKt(nx+1,ny)

Phase-A u-face open windows (1..nz off the closed-face path).

integer, intent(in) :: vKb(nx,ny+1)

Phase-A v-face open windows.

integer, intent(in) :: vKt(nx,ny+1)

Phase-A v-face open windows.

logical, intent(in) :: use_open

z-level closed faces active: re-check window liveness.

private pure subroutine redi_budget_accumulate(nx, ny, nz, hTr, snap, budget)

budget += hTr - snap (device-side): book the realised Redi increment of one budgeted tracer into its lateral-diffusion budget accumulator (ms%salt_budget_hdiff / heat_budget_hdiff).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: hTr(nx,ny,nz)
real(kind=wp), intent(in) :: snap(nx,ny,nz)
real(kind=wp), intent(inout) :: budget(nx,ny,nz)

private pure subroutine redi_build_column(nz, kb, kt, h_col, thtr_col, shtr_col, eos, Pint, Tint, Sint, dRdT, dRdS)

Build one column’s TOP-DOWN interface P/T/S + density derivs from the BOTTOM-UP native column, restricted to the face’s open window kb..kt (nk = kt-kb+1 layers; the whole column 1..nz off the z-level closed-face path). Layer T/S = the I1′ column read (rdb_vl_column_conc: hTr/h on a live layer, the donor’s concentration on a vanished one); interface T/S = PPM edge reconstruction on the flipped window; interface P = surface-relative hydrostatic, seeded with the column ABOVE the window (fillers under an ice draft; nothing — exactly 0 — when kt = nz); dR/dT, dR/dS = -rho² dSV/dX at each interface. Only the first nk+1 entries of the outputs are written.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
integer, intent(in) :: kb

Native (bottom-up) open window, 1 <= kb <= kt <= nz.

integer, intent(in) :: kt

Native (bottom-up) open window, 1 <= kb <= kt <= nz.

real(kind=wp), intent(in) :: h_col(nz)
real(kind=wp), intent(in) :: thtr_col(nz)
real(kind=wp), intent(in) :: shtr_col(nz)
type(eos_t), intent(in) :: eos
real(kind=wp), intent(out) :: Pint(nz+1)
real(kind=wp), intent(out) :: Tint(nz+1)
real(kind=wp), intent(out) :: Sint(nz+1)
real(kind=wp), intent(out) :: dRdT(nz+1)
real(kind=wp), intent(out) :: dRdS(nz+1)

private subroutine redi_calc_coeffs_x(nx, ny, nz, ns, eos, h_layer, t_htr, s_htr, wet_u, uKb, uKt, uPoL, uPoR, uKoL, uKoR, uhEff)

Flat-impl Phase-A u-face kernel. Parallel over (i,j) interior u-faces (i=2..nx); the 2*nz+2 sweep runs serially inside each thread over a column pair (iw=i-1 west, i east). Wall faces and land faces leave the inert (zero/identity) coefficients. The sweep runs on each face’s open window uKb..uKt (1..nz off the z-level closed-face path; module header); a face with no open layer (uKt < uKb) is inert.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
integer, intent(in) :: ns
type(eos_t), intent(in) :: eos
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: t_htr(nx,ny,nz)
real(kind=wp), intent(in) :: s_htr(nx,ny,nz)
real(kind=wp), intent(in) :: wet_u(nx+1,ny)
integer, intent(in) :: uKb(nx+1,ny)

Per-face open window (native layers).

integer, intent(in) :: uKt(nx+1,ny)

Per-face open window (native layers).

real(kind=wp), intent(out) :: uPoL(nx+1,ny,ns)
real(kind=wp), intent(out) :: uPoR(nx+1,ny,ns)
integer, intent(out) :: uKoL(nx+1,ny,ns)
integer, intent(out) :: uKoR(nx+1,ny,ns)
real(kind=wp), intent(out) :: uhEff(nx+1,ny,ns-1)

private subroutine redi_calc_coeffs_y(nx, ny, nz, ns, eos, h_layer, t_htr, s_htr, wet_v, vKb, vKt, vPoL, vPoR, vKoL, vKoR, vhEff)

Flat-impl Phase-A v-face kernel — mirror of _x with v-stagger (js=j-1 south, j north), loop j=2..ny, open window vKb..vKt.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
integer, intent(in) :: ns
type(eos_t), intent(in) :: eos
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: t_htr(nx,ny,nz)
real(kind=wp), intent(in) :: s_htr(nx,ny,nz)
real(kind=wp), intent(in) :: wet_v(nx,ny+1)
integer, intent(in) :: vKb(nx,ny+1)

Per-face open window (native layers).

integer, intent(in) :: vKt(nx,ny+1)

Per-face open window (native layers).

real(kind=wp), intent(out) :: vPoL(nx,ny+1,ns)
real(kind=wp), intent(out) :: vPoR(nx,ny+1,ns)
integer, intent(out) :: vKoL(nx,ny+1,ns)
integer, intent(out) :: vKoR(nx,ny+1,ns)
real(kind=wp), intent(out) :: vhEff(nx,ny+1,ns-1)

private pure subroutine redi_face_coeffs(nz, ns, kb, kt, hL, tL, sL, hR, tR, sR, eos, PoLo, PoRo, KoLo, KoRo, hEffo)

The per-face Phase-A core: build both columns TOP-DOWN on the open window kb..kt (the whole column off the closed-face path), run the sweep on its nk = kt-kb+1 layers, and store PoL/PoR/KoL/KoR/hEff in the FULL top-down frame (Ko + nz - kt). The k-flip is confined to the Phase-B scatter; keeping Po/Ko top-down here lets the flux re-use the sweep’s interface-edge convention with no position arithmetic on the flipped frame. A short window (nk < nz) leaves 2*(nz-nk) trailing surfaces, filled as inert padding: the last surface repeated and hEff = 0, which Phase B skips.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
integer, intent(in) :: ns
integer, intent(in) :: kb

Native (bottom-up) open window, 1 <= kb <= kt <= nz.

integer, intent(in) :: kt

Native (bottom-up) open window, 1 <= kb <= kt <= nz.

real(kind=wp), intent(in) :: hL(nz)
real(kind=wp), intent(in) :: tL(nz)
real(kind=wp), intent(in) :: sL(nz)
real(kind=wp), intent(in) :: hR(nz)
real(kind=wp), intent(in) :: tR(nz)
real(kind=wp), intent(in) :: sR(nz)
type(eos_t), intent(in) :: eos
real(kind=wp), intent(out) :: PoLo(ns)
real(kind=wp), intent(out) :: PoRo(ns)
integer, intent(out) :: KoLo(ns)
integer, intent(out) :: KoRo(ns)
real(kind=wp), intent(out) :: hEffo(ns-1)

private pure subroutine redi_face_const(n1, n2, val, dst)

Broadcast the scalar KhTr val onto every face of dst (n1, n2).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: n1
integer, intent(in) :: n2
real(kind=wp), intent(in) :: val
real(kind=wp), intent(inout) :: dst(n1,n2)

private pure subroutine redi_face_copy(n1, n2, src, dst)

Copy a face KhTr field src -> dst (both (n1, n2)), device-side.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: n1
integer, intent(in) :: n2
real(kind=wp), intent(in) :: src(n1,n2)
real(kind=wp), intent(inout) :: dst(n1,n2)

private pure subroutine redi_face_flux(nz, ns, nxc, nyc, nfa, nfb, h_layer, hTr_in, iL, jL, iR, jR, fa, fb, PoL, PoR, KoL, KoR, hEff, kb, kt, use_open, coef, is_left, dTr)

Accumulate ONE C-grid face’s neutral-surface tracer flux into the owning cell’s dTr. Builds the left/right tracer columns from the READ-ONLY snapshot hTr_in (the live hTr is also written by the loop — reading it would be a do-concurrent read-write race) and loops the ns-1 neutral sublayers. The per-face column locals live in this frame to keep the caller’s loop-body footprint small. is_left: this cell is the LEFT (west/south) column ⇒ +flx into native layer nz+1-KoL; else the RIGHT column ⇒ -flx into nz+1-KoR. (iL,jL)/(iR,jR) index the columns; (fa,fb) the faces. kb..kt is the face’s Phase-A open window (1..nz off the z-level closed-face path): the tracer columns are reconstructed on it alone and the full-frame Ko are read in the window frame (Ko - nz + kt). An empty window, or (use_open) one whose layers are no longer all live on both sides, contributes nothing — both cells of the face take the same decision from the same h.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
integer, intent(in) :: ns
integer, intent(in) :: nxc
integer, intent(in) :: nyc
integer, intent(in) :: nfa
integer, intent(in) :: nfb
real(kind=wp), intent(in) :: h_layer(nxc,nyc,nz)
real(kind=wp), intent(in) :: hTr_in(nxc,nyc,nz)
integer, intent(in) :: iL
integer, intent(in) :: jL
integer, intent(in) :: iR
integer, intent(in) :: jR
integer, intent(in) :: fa
integer, intent(in) :: fb
real(kind=wp), intent(in) :: PoL(nfa,nfb,ns)
real(kind=wp), intent(in) :: PoR(nfa,nfb,ns)
integer, intent(in) :: KoL(nfa,nfb,ns)
integer, intent(in) :: KoR(nfa,nfb,ns)
real(kind=wp), intent(in) :: hEff(nfa,nfb,ns-1)
integer, intent(in) :: kb
integer, intent(in) :: kt
logical, intent(in) :: use_open
real(kind=wp), intent(in) :: coef
logical, intent(in) :: is_left
real(kind=wp), intent(inout) :: dTr(nz)

private pure subroutine redi_open_window(nz, ok, kb, kt)

A face’s OPEN WINDOW from its per-layer ok flags (open .and. live on both sides; module header): kt the topmost ok layer, kb..kt the contiguous ok run counted down from it. No ok layer ⇒ kb = 1, kt = 0 (an empty window, kt < kb).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
logical, intent(in) :: ok(NZ_STACK_MAX)
integer, intent(out) :: kb
integer, intent(out) :: kt

private pure subroutine redi_open_windows_x(nx, ny, nz, h_layer, open_u, uKb, uKt)

Fill every interior u-face’s OPEN WINDOW (&vcoord_nml zfixed_closed_faces; module header): ok(k) = open_u .and. live on both sides (rdb_vl_is_live), then redi_open_window.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: open_u(nx+1,ny,nz)

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

integer, intent(inout) :: uKb(nx+1,ny)
integer, intent(inout) :: uKt(nx+1,ny)

private pure subroutine redi_open_windows_y(nx, ny, nz, h_layer, open_v, vKb, vKt)

v-face twin of redi_open_windows_x (south j-1, north j).

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: open_v(nx,ny+1,nz)

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

integer, intent(inout) :: vKb(nx,ny+1)
integer, intent(inout) :: vKt(nx,ny+1)

private pure subroutine redi_plm_diff(nk, h, s, diff)

PLM van-Leer-limited layer-difference array (MOM6 PLM_diff with c_method=2 finite-volume slope, b_method=1 PCM ends) — the slope input to the PPM edge interpolation.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nk
real(kind=wp), intent(in) :: h(nk)

layer thicknesses

real(kind=wp), intent(in) :: s(nk)

layer scalar values

real(kind=wp), intent(out) :: diff(nk)

limited layer difference (PCM ends)

private pure subroutine redi_snapshot(nx, ny, nz, src, dst)

Device-side copy dst = src of a (nx,ny,nz) tracer field.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: src(nx,ny,nz)
real(kind=wp), intent(inout) :: dst(nx,ny,nz)

private pure subroutine redi_tracer_column(kb, kt, h_col, htr_col, Tlay, Tint, aLe, aRe)

Build one column’s TOP-DOWN layer-average tracer Tlay, PPM interface edges Tint, and per-layer limited PPM left/right edges aLe/aRe from the bottom-up native (h, hTr) column (fixed-size NZ_STACK_MAX copies), restricted to the face’s open window kb..kt (1..nz off the z-level closed-face path) — indexed in the WINDOW top-down frame, 1..nk, nk = kt-kb+1. Tlay = the I1′ column read (rdb_vl_column_conc, see redi_build_column). Mirrors MOM6 interface_scalar + ppm_left_right_edge_values.

Arguments

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

Native (bottom-up) open window, 1 <= kb <= kt.

integer, intent(in) :: kt

Native (bottom-up) open window, 1 <= kb <= kt.

real(kind=wp), intent(in) :: h_col(NZ_STACK_MAX)
real(kind=wp), intent(in) :: htr_col(NZ_STACK_MAX)
real(kind=wp), intent(out) :: Tlay(NZ_STACK_MAX)
real(kind=wp), intent(out) :: Tint(NZ_STACK_MAX+1)
real(kind=wp), intent(out) :: aLe(NZ_STACK_MAX)
real(kind=wp), intent(out) :: aRe(NZ_STACK_MAX)