rdb_ocean_sponge Module

Two sponge implementations share this module.

LEGACY BAND PATH (ocean_sponge_apply / ocean_sponge_apply_tracers, &ocean_bc_nml): dissipates outgoing waves in a band of cells next to a sponge-tagged edge. Reads bc%<edge>%bc_type == OBC_SPONGE + per-edge sponge_width (cells) and sponge_strength (1/s) and applies du/dt += -τ·u, with τ a cosine ramp from 0 at the interior to sponge_strength at the outer edge, toward zero momentum and a scalar per-edge tracer target. State evolution (hTr, momentum) kept byte-identical — this is what &ocean_sponge_nml enable=.false. (the default) still runs. PR-23 additionally mirrors the tracer relaxation’s salinity/temperature increment into ms%salt_budget_sponge / heat_budget_sponge (a pure side-channel add, new arrays that were always zero before — no change to hTr itself) so the console salt/heat budget closes with the legacy sponge_relax_tracers = .true. knob too, not just the map-driven path.

MAP-DRIVEN PATH (ocean_sponge_t + ocean_sponge_apply_maps, &ocean_sponge_nml enable=.true.): a real sponge (MOM6’s ALE sponge is the model in spirit, not in code — see below). A per-cell inverse-damping-time map (idamp_h/idamp_u/idamp_v, 1/s; Idamp = 0 IS the sponge mask, no separate width/extent bookkeeping) relaxes momentum toward a 3-D u_ref/v_ref (not zero) and every registered tracer toward a 3-D ref_tracer(i,j,k,it) concentration field (not a scalar). damp_source="band" (the only implemented source) fills the maps from a ramp off every OBC_SPONGE-tagged edge, at the exact cell/u-face/v-face offsets the legacy kernel touches, so enable=.true., damp_source="band", ramp="cosine" is physically the same band as today (see sponge_source_is_implemented / rdb_ocean_setup::configure_ocean_sponge). ramp="linear" swaps the cosine for ISOMIP+ Eq. (20)’s linear rise — see sponge_band_alpha.

Two reference states ship:

  • target_source="ic" (default) snapshots the reference from the seeded initial condition via ocean_sponge_snapshot_reference — reachable as a “nudge toward a parent climatology” via &ocean_zinit_nml with no new file reader.
  • target_source="linear_z" builds an ANALYTIC affine geopotential T(z)/S(z) profile, re-evaluated on the LIVE layer geometry once per outer step by ocean_sponge_refresh_target. The target is then INDEPENDENT of the initial condition, which is what makes an ISOMIP+ Ocean1/Ocean2 (restore to a different water mass than you start from) expressible at all. Non-S/T tracers and u_ref/v_ref still come from the IC snapshot.

Divergences from MOM6’s ALE-sponge / sponge approach (deliberate, documented per CLAUDE.md “cite the paper not other codebases” — this list records WHERE we differ, not MOM6’s implementation): 1. Exponential relaxation decay = exp(-Idamp*dt); phi <- decay*phi + (1-decay)*phi_ref (exact for constant phi_ref over the step, unconditionally stable, monotone) instead of MOM6’s backward-Euler I1pdamp = 1/(1+Idamp*dt) (physics-equivalent to O(dt), less accurate). 2. Dense (nx,ny)/(nx,ny,nz) maps, not MOM6’s compressed-column Iresttime_col/Ref_val%p(k,c) sparse structure — a gather/scatter indirection is worse than idamp=0 (one multiply, race-free do concurrent) on GPU. 3. Under target_source="ic" the reference is snapshotted ONCE on model layers at seed time, not held on a source z-grid and remapped to the live column every apply (MOM6’s Ref_dz + remapping_core_h). Valid because Roundabout’s ALE remap pins layer depths to the coordinate every thermo step; the residual motion is far below the target’s own vertical resolution. It also needs the SEED to be on the coordinate already: the snapshot runs before the first regrid. z_fixed + &ocean_zinit_nml seeds on its target for exactly this reason (ocean_state_seed_from_cfg). Coordinates whose seed is not their target still snapshot on the seed layers. z_fixed with a per-layer-index &tracer_nml IC is one; zstar_full, rho and hycom have not been checked. target_source="linear_z" does NOT take that shortcut — it rebuilds the profile on the live geometry every outer step, which is cheaper here than a remap because the source is a closed-form function of depth rather than a table. 4. Applied at dt (every RK2 stage), not MOM6’s diabatic/thermo cadence.

Restart contract (mirrors rdb_ocean_surface_flux’s docstring): the idamp_* maps and the ref_* fields are NOT registered in the restart registry. The maps are a pure function of config + geometry (restart-invariant, rebuilt every configure). The reference is re-derived from the re-seeded initial condition on every resume via ocean_sponge_snapshot_reference, called BEFORE the restart read (see rdb_driver.F90) — this is deliberate: target_source="ic" must mean the IC, not whatever state a resumed run happens to be in (see docs/plans/PLAN_PR23_real_sponge.md §13.1 item 4 for the reasoning; test_ocean_sponge::sponge_reference_is_the_ic_not_the_restart pins it).


Uses

  • module~~rdb_ocean_sponge~~UsesGraph module~rdb_ocean_sponge rdb_ocean_sponge iso_fortran_env iso_fortran_env module~rdb_ocean_sponge->iso_fortran_env module~rdb_constants rdb_constants module~rdb_ocean_sponge->module~rdb_constants module~rdb_grid rdb_grid module~rdb_ocean_sponge->module~rdb_grid module~rdb_mem_report rdb_mem_report module~rdb_ocean_sponge->module~rdb_mem_report module~rdb_multilayer_state rdb_multilayer_state module~rdb_ocean_sponge->module~rdb_multilayer_state module~rdb_ocean_boundary_types rdb_ocean_boundary_types module~rdb_ocean_sponge->module~rdb_ocean_boundary_types 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_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_efp->iso_fortran_env ieee_arithmetic ieee_arithmetic module~rdb_efp->ieee_arithmetic module~rdb_error_ring->pic_logger module~rdb_ocean_tide_astro->iso_fortran_env module~rdb_ocean_tide_astro->module~rdb_constants module~rdb_tracer->iso_fortran_env module~rdb_tracer->module~rdb_constants module~rdb_tracer->module~rdb_grid module~rdb_tracer->module~rdb_mem_report

Used by

  • module~~rdb_ocean_sponge~~UsedByGraph module~rdb_ocean_sponge rdb_ocean_sponge module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_ocean_sponge module~rdb_ocean_engine rdb_ocean_engine module~rdb_ocean_engine->module~rdb_ocean_sponge module~rdb_ocean_engine->module~rdb_ocean_dyn module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_setup module~rdb_ocean_state rdb_ocean_state module~rdb_ocean_engine->module~rdb_ocean_state module~rdb_ocean_diag_derived rdb_ocean_diag_derived module~rdb_ocean_engine->module~rdb_ocean_diag_derived module~rdb_ocean_diag_fills rdb_ocean_diag_fills module~rdb_ocean_engine->module~rdb_ocean_diag_fills module~rdb_ocean_setup->module~rdb_ocean_sponge module~rdb_ocean_setup->module~rdb_ocean_dyn module~rdb_ocean_setup->module~rdb_ocean_state module~rdb_ocean_state->module~rdb_ocean_sponge 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_engine module~rdb_driver->module~rdb_ocean_state module~rdb_handle rdb_handle module~rdb_handle->module~rdb_ocean_engine module~rdb_handle->module~rdb_ocean_state module~rdb_ocean_api rdb_ocean_api module~rdb_ocean_api->module~rdb_ocean_dyn module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_ocean_api->module~rdb_handle module~rdb_ocean_api->module~rdb_ocean_diag_derived module~rdb_ocean_api->module~rdb_ocean_diag_fills 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

Variables

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

&ocean_sponge_nml ramp = "cosine" (default): the legacy band kernel’s 0.5*(1 + cos(pi*d/band)).

integer, public, parameter :: SPONGE_RAMP_LINEAR = 1

&ocean_sponge_nml ramp = "linear": (band - d - 0.5)/band.


Derived Types

type, public ::  ocean_sponge_t

Map-driven sponge state: per-cell Idamp [1/s] + a 3-D reference state. See the module docstring for the physics + the MOM6 divergences. Value-semantics slot (no CS pointer, no associated(CS) guards) per src/core/ocean/README.md.

Components

Type Visibility Attributes Name Initial
character(len=16), public :: damp_source = "band"

How idamp_h/idamp_u/idamp_v are filled. "band" (only value implemented in v1): cosine ramp from every bc%<edge>%bc_type == OBC_SPONGE edge tag, summed at overlaps (§3.2 of the plan). "file" recognised but aborts at validate_config (PR-23b, needs the PR-14 reader).

logical, public :: enable = .false.

Master switch. Default .false. ⇒ the legacy band kernels run unchanged ⇒ existing nmls + tests are bit-identical.

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

Inverse damping time for tracers + (deferred) thickness, 1/s, shape (nx_total, ny_total). Zero outside the sponge and in every ghost cell — Idamp = 0 IS the sponge mask (no separate width/extent bookkeeping).

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

Inverse damping time for u_face_x_layer, 1/s, shape (nx_total+1, ny_total) (matches the u-face stagger).

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

Inverse damping time for v_face_y_layer, 1/s, shape (nx_total, ny_total+1) (matches the v-face stagger).

integer, public :: idx_s = 0

Tracer-registry index of salinity; see idx_t.

integer, public :: idx_t = 0

Tracer-registry index of temperature, latched at configure so the per-step refresh needs no registry walk (and no array-of-derived-types device indirection). 0 = not registered ⇒ the temperature refresh is skipped.

logical, public :: is_init = .false.

True once init has run (only called when enable, mirroring the gated-closure convention — epbl/kshear/… — so a disabled sponge never allocates the (potentially large) ref_tracer).

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

target_source="linear_z": dS/dz (PSU/m), z positive UP.

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

target_source="linear_z": dT/dz (degC/m), z positive UP.

real(kind=wp), public :: lin_s_ref = 35.0_wp

target_source="linear_z": S (PSU) at the z = 0 datum.

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

target_source="linear_z": T (degC) at the z = 0 datum.

integer, public :: n_tracers = 0

Registered tracer count — sizes ref_tracer’s 4th dimension.

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

Reference tracer CONCENTRATION (not hTr) — PSU, degC, … per the registered tracer’s own units — shape (nx_total, ny_total, nz, n_tracers), indexed by the multilayer tracer-registry index (matches ms%idx_salinity / ms%idx_temperature).

logical, public :: relax_h = .false.

Interior-interface thickness damping. NOT IMPLEMENTED in PR-23 v1 (deferred to PR-23b alongside the file-backed targets — docs/plans/PLAN_PR23_real_sponge.md §14 Q1); enable=.true., relax_h=.true. aborts fail-loud at validate_config.

logical, public :: relax_tracers = .true.

Relax every registered tracer’s concentration toward ref_tracer.

logical, public :: relax_uv = .true.

Relax u_face_x_layer/v_face_y_layer toward u_ref/v_ref. Default .true. matches the legacy path (momentum is the one thing today’s sponge always damps); MOM6’s SPONGE_UV defaults .false. — a deliberate divergence to preserve legacy parity.

character(len=16), public :: target_source = "ic"

Reference-state source. "ic": ocean_sponge_snapshot_reference copies the seeded initial condition. "linear_z": the analytic affine geopotential profile below, re-evaluated on the LIVE layer geometry by ocean_sponge_refresh_target once per outer step — see that routine’s docstring for why re-evaluated rather than frozen. "file" recognised but aborts at validate_config (PR-23b).

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

Reference x-velocity, shape (nx_total+1, ny_total, nz) — matches ms%u_face_x_layer.

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

Reference y-velocity, shape (nx_total, ny_total+1, nz) — matches ms%v_face_y_layer.

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

Geopotential DEPTH of the top of the water column (m, positive down), shape (nx_total, ny_total). 0 in the open ocean (the free-surface datum), metrics%z_draft(i,j) under an ice shelf. Latched ONCE at configure — the draft is static by design — so the per-step linear_z refresh is self-contained and the sponge slot never has to reach into ocean_metrics_t. Same role z_top plays in rdb_ocean_z_init::build_z_ctr; without it a geopotential target would land z_draft metres too shallow on every shelf column and tilt with the ice base.

Type-Bound Procedures

procedure, public, non_overridable :: bytes => ocean_sponge_bytes
procedure, public, non_overridable :: destroy => ocean_sponge_destroy
procedure, public, non_overridable :: enter_data => ocean_sponge_enter_data
procedure, public, non_overridable :: exit_data => ocean_sponge_exit_data
procedure, public, non_overridable :: init => ocean_sponge_init

Functions

public pure function sponge_band_alpha(d, band, ramp) result(alpha)

Shape factor of the damp_source="band" ramp at cell offset d (0 = hard against the sponge-tagged wall) for a band of band cells. The per-cell rate is sponge_strength * alpha.

Read more…

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: d
integer, intent(in) :: band
integer, intent(in) :: ramp

Return Value real(kind=wp)

private pure function ocean_sponge_bytes(this) result(nbytes)

Counted allocatable footprint (0 when enable=.false. — every array is unallocated then). tools/check_bytes_accounting.py reconciles this against the measured device mapping.

Arguments

Type IntentOptional Attributes Name
class(ocean_sponge_t), intent(in) :: this

Return Value integer(kind=int64)

private pure function relax_one(hTr_cur, tgt, strength, d, band, dt) result(res)

Cosine-ramp implicit relaxation of one cell toward tgt. decay = exp(-strength*alpha*dt), alpha the cosine ramp at band position d; result = hTr_curdecay + tgt(1-decay). Funnels through relax_toward for the shared core algebra (§3.1 / step 10 “one home” — the map-driven kernels derive decay from a per-cell Idamp instead of this band/strength ramp, but both land on the same cur*decay + tgt*(1-decay) update).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: hTr_cur
real(kind=wp), intent(in) :: tgt
real(kind=wp), intent(in) :: strength
integer, intent(in) :: d
integer, intent(in) :: band
real(kind=wp), intent(in) :: dt

Return Value real(kind=wp)

private pure function relax_toward(cur, tgt, decay) result(res)

Shared core algebra for BOTH sponge paths: cur*decay + tgt*(1-decay) — the exact solution of dphi/dt = -Idamp*(phi - phi_ref) over one step at constant phi_ref, for whatever decay = exp(-rate*dt) the caller derived (relax_one derives it from the legacy edge/band cosine ramp; the map kernels above derive it directly from a per-cell Idamp). “One home” for the algebra per the plan (§3.1 / step 10).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: cur
real(kind=wp), intent(in) :: tgt
real(kind=wp), intent(in) :: decay

Return Value real(kind=wp)


Subroutines

public subroutine ocean_sponge_apply(grid, bc, ms, dt)

Apply momentum relaxation in any sponge-tagged edge band. No-op when no edge is OBC_SPONGE.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_bc_state_t), intent(in) :: bc
type(multilayer_state_t), intent(inout) :: ms
real(kind=wp), intent(in) :: dt

public subroutine ocean_sponge_apply_maps(grid, sp, ms, dt)

Map-driven sponge dispatch (&ocean_sponge_nml enable=.true.). Relaxes momentum toward u_ref/v_ref (when relax_uv) and every registered tracer toward ref_tracer (when relax_tracers), mirroring the S/T tracer relaxation into ms%salt_budget_sponge / ms%heat_budget_sponge so the console salt/heat budget can close with the sponge on (rdb_ocean_console_stats::ocean_heat_src_sum / ocean_salt_src_sum). No-op when .not. sp%is_init .or. .not. sp%enable. Run from the same slot as the legacy path — see rdb_ocean_dyn::run_stage_split’s dispatch (exactly one of the legacy band / map-driven path runs).

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_sponge_t), intent(in) :: sp
type(multilayer_state_t), intent(inout) :: ms
real(kind=wp), intent(in) :: dt

public subroutine ocean_sponge_apply_tracers(grid, bc, ms, dt)

Relax tracer concentrations (hTr/h) toward per-edge targets in any sponge band with sponge_relax_tracers = .true.. Same cosine ramp and sponge_strength as the momentum kernel. No-op otherwise. Per-tracer loop outside the inner loops (outer-shim for the array-of-derived-types registry).

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_bc_state_t), intent(in) :: bc
type(multilayer_state_t), intent(inout) :: ms
real(kind=wp), intent(in) :: dt

public pure subroutine ocean_sponge_refresh_target(grid, sp, ms)

Re-evaluate the ANALYTIC target_source="linear_z" reference on the LIVE layer geometry: for every sponge cell (idamp_h > 0) and every layer,

Read more…

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_sponge_t), intent(inout) :: sp
type(multilayer_state_t), intent(in) :: ms

public subroutine ocean_sponge_snapshot_reference(sp, grid, ms)

Snapshot the seeded initial condition into sp%ref_tracer / sp%u_ref / sp%v_ref (target_source = "ic", the only implemented source in v1). HOST-side, plain do loops (mirrors seed_ts_from_zfile — CLAUDE.md gotcha: this runs before ocean_state_enter_data).

Read more…

Arguments

Type IntentOptional Attributes Name
type(ocean_sponge_t), intent(inout) :: sp
type(hgrid_t), intent(in) :: grid
type(multilayer_state_t), intent(in) :: ms

private subroutine ocean_sponge_destroy(this)

Arguments

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

private subroutine ocean_sponge_enter_data(this)

Type-bound wrapper — delegates to the non-polymorphic impl so the device-attach map base is the heap object, not a polymorphic stack box (AMD libomptarget cross-slot-overlap fix; copied verbatim from rdb_ocean_surface_flux.F90).

Arguments

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

private subroutine ocean_sponge_enter_data_impl(this)

Arguments

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

private subroutine ocean_sponge_exit_data(this)

Arguments

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

private subroutine ocean_sponge_exit_data_impl(this)

Arguments

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

private subroutine ocean_sponge_init(this, grid, nz_ml, n_tracers)

Allocate the maps + reference-state arrays. Call ONLY when this%enable (the gated-closure convention, mirroring epbl/kshear/…) — the caller sets enable before calling, and ocean_state_init only reaches this when this%enable is .true. (see rdb_ocean_state.F90). ref_tracer is sized at max(n_tracers, 1) so an as-yet-empty tracer registry never trips a zero-extent allocate.

Arguments

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

private pure subroutine refresh_linear_z_impl(ref_tracer, h_layer, z_top, idamp_h, it, n_tr, v_ref, dv_dz, nx, ny, nz)

One tracer plane of the linear_z target. Explicit-shape dummies, dims declared first (decl-order hook). ref_tracer is passed WHOLE and indexed by the scalar it inside the kernel — never sliced by the caller (the descriptor-walk trap the dc-assumed-shape hook exists to catch), matching relax_map_tracer_impl.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: ref_tracer(nx,ny,nz,n_tr)
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: z_top(nx,ny)
real(kind=wp), intent(in) :: idamp_h(nx,ny)
integer, intent(in) :: it
integer, intent(in) :: n_tr
real(kind=wp), intent(in) :: v_ref
real(kind=wp), intent(in) :: dv_dz
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private pure subroutine relax_band_x_impl(hTr, h_layer, nx_total, ny_total, nz, j0, j1, wall_face, band, side, strength, C_bc, dt, budget)

Cosine-ramp tracer relaxation in a west/east sponge band. Explicit-shape dummies (flat-impl + outer-shim — the per-tracer slice is passed by the caller, never dereferenced inside the DC). side = +1 for the west edge (cells wall_face .. wall_face+band-1), side = -1 for the east edge (cells wall_face-1 .. wall_face-band).

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: hTr(nx_total,ny_total,nz)
real(kind=wp), intent(in) :: h_layer(nx_total,ny_total,nz)
integer, intent(in) :: nx_total
integer, intent(in) :: ny_total
integer, intent(in) :: nz
integer, intent(in) :: j0
integer, intent(in) :: j1
integer, intent(in) :: wall_face
integer, intent(in) :: band
integer, intent(in) :: side
real(kind=wp), intent(in) :: strength
real(kind=wp), intent(in) :: C_bc
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(inout), optional :: budget(nx_total,ny_total,nz)

private pure subroutine relax_band_y_impl(hTr, h_layer, nx_total, ny_total, nz, i0, i1, wall_face, band, side, strength, C_bc, dt, budget)

Cosine-ramp tracer relaxation in a south/north sponge band. Mirror of relax_band_x_impl for the y-direction; see its docstring for the optional budget mirror (PR-23).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: hTr(nx_total,ny_total,nz)
real(kind=wp), intent(in) :: h_layer(nx_total,ny_total,nz)
integer, intent(in) :: nx_total
integer, intent(in) :: ny_total
integer, intent(in) :: nz
integer, intent(in) :: i0
integer, intent(in) :: i1
integer, intent(in) :: wall_face
integer, intent(in) :: band
integer, intent(in) :: side
real(kind=wp), intent(in) :: strength
real(kind=wp), intent(in) :: C_bc
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(inout), optional :: budget(nx_total,ny_total,nz)

private pure subroutine relax_map_tracer_budget_impl(hTr, h_layer, ref_tracer, it, n_tr, idamp_h, budget, nx, ny, nz, dt)

relax_map_tracer_impl + mirror the per-cell increment into budget (salt or heat), the S/T budget-instrumented variant — mirrors apply_geothermal_src_impl’s host-shim + flat-impl + budget-mirror shape. delta is the ALGEBRAIC increment hTr_new - hTr_old = (1-decay)*(tgt-hTr_old), written once and used for BOTH the state update and the budget mirror so the two stay exactly consistent (no independent recomputation to drift).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: hTr(nx,ny,nz)
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: ref_tracer(nx,ny,nz,n_tr)
integer, intent(in) :: it
integer, intent(in) :: n_tr
real(kind=wp), intent(in) :: idamp_h(nx,ny)
real(kind=wp), intent(inout) :: budget(nx,ny,nz)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt

private pure subroutine relax_map_tracer_impl(hTr, h_layer, ref_tracer, it, n_tr, idamp_h, nx, ny, nz, dt)

Relax one tracer’s hTr toward ref_tracer(:,:,:,it)*h_layer at rate idamp_h, no budget mirror (every tracer except S/T — see relax_map_tracer_budget_impl). ref_tracer is passed WHOLE + indexed by the scalar it inside the kernel — never sliced by the caller (§6.4: a device array section of a mapped array is the descriptor-walk trap the dc-assumed-shape hook exists to catch).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: hTr(nx,ny,nz)
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: ref_tracer(nx,ny,nz,n_tr)
integer, intent(in) :: it
integer, intent(in) :: n_tr
real(kind=wp), intent(in) :: idamp_h(nx,ny)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt

private pure subroutine relax_map_u_impl(u, u_ref, idamp_u, nxu, ny, nz, dt)

Relax u_face_x_layer toward u_ref at rate idamp_u. Explicit- shape dummies, dims declared first (decl-order hook). idamp_u(i,j) <= 0 is a bit-exact no-op (Idamp = 0 IS the sponge mask; also guarantees sponge_idamp_zero_is_exact_identity, since decay = exp(-0*dt) = 1.0 exactly would already be a no-op algebraically — the early if skips the arithmetic entirely instead of relying on that).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: u(nxu,ny,nz)
real(kind=wp), intent(in) :: u_ref(nxu,ny,nz)
real(kind=wp), intent(in) :: idamp_u(nxu,ny)
integer, intent(in) :: nxu
integer, intent(in) :: ny
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt

private pure subroutine relax_map_v_impl(v, v_ref, idamp_v, nx, nyv, nz, dt)

Relax v_face_y_layer toward v_ref at rate idamp_v. Mirror of relax_map_u_impl for the y-direction.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: v(nx,nyv,nz)
real(kind=wp), intent(in) :: v_ref(nx,nyv,nz)
real(kind=wp), intent(in) :: idamp_v(nx,nyv)
integer, intent(in) :: nx
integer, intent(in) :: nyv
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt

private pure subroutine snapshot_column_concentration(conc, hTr_col, h_col, nz)

Fill one water column’s reference concentration from hTr/h, falling back to the nearest massive layer (h > H_VANISHED) for any vanished layer — bed-up pass first, then a surface-down pass to backfill any vanished layers below the first massive one. Plain host loop (called from a host do i,j loop, never do concurrent — this is configure-time setup, not a per-step kernel).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(out) :: conc(nz)
real(kind=wp), intent(in) :: hTr_col(nz)
real(kind=wp), intent(in) :: h_col(nz)
integer, intent(in) :: nz

private subroutine sponge_relax_band_x_tracer(ms, it, nx_total, ny_total, nz, j0, j1, wall_face, band, side, strength, C_bc, dt)

Host dispatch for one tracer’s relax_band_x_impl call: routes the salinity/temperature budget mirror (PR-23) so the legacy band sponge’s tracer sink is instrumented exactly like the map-driven path, without threading an if-branch into every one of the four edge blocks in ocean_sponge_apply_tracers.

Arguments

Type IntentOptional Attributes Name
type(multilayer_state_t), intent(inout) :: ms
integer, intent(in) :: it
integer, intent(in) :: nx_total
integer, intent(in) :: ny_total
integer, intent(in) :: nz
integer, intent(in) :: j0
integer, intent(in) :: j1
integer, intent(in) :: wall_face
integer, intent(in) :: band
integer, intent(in) :: side
real(kind=wp), intent(in) :: strength
real(kind=wp), intent(in) :: C_bc
real(kind=wp), intent(in) :: dt

private subroutine sponge_relax_band_y_tracer(ms, it, nx_total, ny_total, nz, i0, i1, wall_face, band, side, strength, C_bc, dt)

Mirror of sponge_relax_band_x_tracer for the y-direction.

Arguments

Type IntentOptional Attributes Name
type(multilayer_state_t), intent(inout) :: ms
integer, intent(in) :: it
integer, intent(in) :: nx_total
integer, intent(in) :: ny_total
integer, intent(in) :: nz
integer, intent(in) :: i0
integer, intent(in) :: i1
integer, intent(in) :: wall_face
integer, intent(in) :: band
integer, intent(in) :: side
real(kind=wp), intent(in) :: strength
real(kind=wp), intent(in) :: C_bc
real(kind=wp), intent(in) :: dt