rdb_ocean_ideal_age Module

Passive ideal-age tracer: interior tendency dA/dt = 1 (s/s) at thermo cadence, surface-layer value held at a Dirichlet condition A = A_young(t) (0 by default). With the standard advection + diffusion this gives “time since this parcel last touched the surface” — a diagnostic of circulation pathways and spurious diapycnal mixing.

Storage: hTr_age is h_layer · age (m · s), like S/T; the diagnostic reports age = hTr_age / h_layer. k = 1 bed, k = nz surface.

Two-entry-point call contract (see src/core/ocean/dynamics/ split_rk2/rdb_ocean_dyn.F90, run_stage[_split] / ocean_dyn_step[_split]):

  • ocean_ideal_age_apply — the interior-aging SOURCE term. Called once per RK2 STAGE, thermo-cadence gated (dt = therm_dt, active = dyn%is_thermo_step()). The RK2 average turns the per-stage +therm_dt into exactly one +therm_dt per outer step — by design, not by accident.
  • ocean_ideal_age_reset_surface — the surface Dirichlet BC. Called exactly ONCE per outer step, AFTER rk2_average, AFTER continuity_tracer_drain, and AFTER the ALE remap — i.e. it is the last operator to touch hTr_age(:, :, nz) each step. A reset applied inside an RK2 stage is halved by the subsequent average (0.5*(A0 + 0) = 0.5*A0, not 0) — that was the historical bug this split fixes. A reset applied before the ALE remap is overwritten by the remap’s vertical redistribution of subsurface age into the new top cell.

Both entry points self-gate on ms%idx_age <= 0 (no-op when the ideal-age tracer isn’t registered) and keep the outer-shim + flat-impl split: the ms%tracers(idx)%hTr registry dereference happens on the host, only flat arrays cross into do concurrent (two-level derived-type indirection segfaults on the GPU).

Two deliberate divergences from the MOM6 reference (ideal_age_example.F90): - Units are SECONDS, not years (hTr_age units s, rdb_multilayer_state.F90); MOM6 works in years (Isecs_per_year). Do not convert — the diagnostic registry and the console status line both assume seconds. - Reset-after-remap. MOM6’s ideal_age_tracer_column_physics runs in the diabatic driver, itself followed by ALE_main. Roundabout places the reset explicitly after its own ALE remap call, which is strictly stronger (idempotent, exact) and matches the tracer’s definition as a hard Dirichlet condition rather than a relaxation.


Uses

  • module~~rdb_ocean_ideal_age~~UsesGraph module~rdb_ocean_ideal_age rdb_ocean_ideal_age module~rdb_constants rdb_constants module~rdb_ocean_ideal_age->module~rdb_constants module~rdb_grid rdb_grid module~rdb_ocean_ideal_age->module~rdb_grid module~rdb_multilayer_state rdb_multilayer_state module~rdb_ocean_ideal_age->module~rdb_multilayer_state pic_types pic_types module~rdb_constants->pic_types module~rdb_grid->module~rdb_constants module~rdb_multilayer_state->module~rdb_constants module~rdb_multilayer_state->module~rdb_grid iso_fortran_env iso_fortran_env module~rdb_multilayer_state->iso_fortran_env 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_mem_report rdb_mem_report module~rdb_multilayer_state->module~rdb_mem_report module~rdb_tracer rdb_tracer module~rdb_multilayer_state->module~rdb_tracer pic_logger pic_logger module~rdb_multilayer_state->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_mem_report->module~rdb_constants module~rdb_mem_report->iso_fortran_env module~rdb_mem_report->pic_logger pic_strings pic_strings module~rdb_mem_report->pic_strings module~rdb_tracer->module~rdb_constants module~rdb_tracer->module~rdb_grid module~rdb_tracer->iso_fortran_env module~rdb_tracer->module~rdb_mem_report

Used by

  • module~~rdb_ocean_ideal_age~~UsedByGraph module~rdb_ocean_ideal_age rdb_ocean_ideal_age module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_ocean_ideal_age module~rdb_driver rdb_driver module~rdb_driver->module~rdb_ocean_dyn module~rdb_ocean_engine rdb_ocean_engine module~rdb_driver->module~rdb_ocean_engine module~rdb_ocean_state rdb_ocean_state module~rdb_driver->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_handle rdb_handle 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_engine->module~rdb_ocean_dyn module~rdb_ocean_setup rdb_ocean_setup 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 module~rdb_ocean_setup->module~rdb_ocean_dyn module~rdb_ocean_setup->module~rdb_ocean_state module~rdb_ocean_state->module~rdb_ocean_dyn module~rdb_handle->module~rdb_ocean_engine module~rdb_handle->module~rdb_ocean_state 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

Functions

public pure function ocean_ideal_age_young_val(young_val, sfc_growth_rate, t) result(young)

Host-scalar evaluation of the surface-band age value A_young(t) (s), MOM6 ideal_age_example.F90’s young_val computation (:380-385), taken exactly in spirit including the growth_rate == 0 short-circuit (no exp call, no t dependence) — this is what keeps the default bit-identical: young = young_val if sfc_growth_rate == 0 young = young_val * exp(sfc_growth_rate*t) otherwise t is model time (s) since run start. young_val (s) and sfc_growth_rate (1/s) are host scalars from ocean_dyn_t; call this ONCE per outer step on the host, never inside a do concurrent body.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: young_val
real(kind=wp), intent(in) :: sfc_growth_rate
real(kind=wp), intent(in) :: t

Return Value real(kind=wp)


Subroutines

public pure subroutine ocean_ideal_age_age_step(hTr_age, h_layer, dt, nx, ny, nz)

Interior aging, k = 1 .. nz-1 (subsurface layers only — k = nz is the surface and is owned exclusively by ocean_ideal_age_reset_step): hTr_age(i,j,k) += dt * h_layer(i,j,k) dt here is the caller’s therm_dt, so this is a per-RK2- stage source; two stages + rk2_average_field_3d net exactly one +dt per outer step.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: hTr_age(nx,ny,nz)
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: dt
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

public subroutine ocean_ideal_age_apply(grid, ms, dt, active)

Driver-facing entry point for the interior-aging SOURCE term. Self-gates on ms%idx_age <= 0 (no-op when the ideal-age tracer isn’t registered), else delegates to the flat-impl ocean_ideal_age_age_step kernel. Call once per RK2 stage with dt = therm_dt and active = dyn%is_thermo_step() — see the module header for the full contract.

Arguments

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

Optional gate (thermo cadence). Absent => kernel runs; present-and-false => early return.

public pure subroutine ocean_ideal_age_reset_step(hTr_age, h_layer, young_eff, nx, ny, nz)

Surface Dirichlet BC, k = nz only: hTr_age(i,j,nz) = young_eff * h_layer(i,j,nz) young_eff is A_young(t) (s), a host scalar from ocean_ideal_age_young_val — this writes a CONCENTRATION times thickness, not a raw concentration (today’s hard-coded young_eff = 0 reset never exercised the h_layer factor). Writes the full 1:nx, 1:ny extent including ghosts/land, same as the historical kernel; land h_layer -> H_VANISHED makes the product negligible and no kernel reads land age.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: hTr_age(nx,ny,nz)
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: young_eff
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

public subroutine ocean_ideal_age_reset_surface(grid, ms, young_eff, active)

Driver-facing entry point for the surface Dirichlet BC. Self-gates on ms%idx_age <= 0, else delegates to the flat-impl ocean_ideal_age_reset_step kernel. Call exactly ONCE per outer step, after rk2_average + the ALE remap — see the module header for the full ordering contract.

Arguments

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

Host-computed surface value (s) — see ocean_ideal_age_young_val. Computed ONCE on the host per outer step and passed by value; never call exp() inside a do concurrent body.

logical, intent(in), optional :: active

Optional gate (thermo cadence). Absent => kernel runs; present-and-false => early return.