| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | public, | parameter | :: | DERIVED_REQ_CAVITY_DYN | = | 1 |
Needs |
| integer, | public, | parameter | :: | DERIVED_REQ_CAVITY_MELT | = | 2 |
Needs |
| integer, | public, | parameter | :: | DERIVED_REQ_NONE | = | 0 |
No prerequisite — the entry reads state that always exists. |
| type(derived_entry_t), | private | :: | CATALOG(N_CATALOG) | ||||
| real(kind=wp), | private, | parameter | :: | MLD_DENSITY_THRESHOLD | = | 0.03_wp |
De Boyer Montégut (2004) MLD criterion: Δσ_0 = 0.03 kg/m³ vs surface. |
| integer, | private, | parameter | :: | N_CATALOG | = | 23 | |
| real(kind=wp), | private, | parameter | :: | RHO_FRESHWATER_ISOMIP | = | 1000.0_wp |
Freshwater density (kg/m³) the ISOMIP+ melt-rate CONVENTION
divides by — Asay-Davis et al. (2016), GMD 9, 2471–2497,
§3.3: melt rates are reported in m/yr of ICE-EQUIVALENT
freshwater. It is a reporting convention, deliberately NOT the
model’s Boussinesq |
| real(kind=wp), | private, | parameter | :: | SECONDS_PER_YEAR | = | 365.0_wp*86400.0_wp |
365-day year, the ISOMIP+ protocol’s own calendar. A Julian year (365.25 d) would move every melt rate by 0.07 %, which is below the noise but not below the reader’s attention. |
| logical, | private | :: | catalog_initialised | = | .false. |
One entry in the static catalog. Buffer layout is implicit
in is_layered: layered → (nx, ny, nz_ml), 2D → (nx, ny, 1).
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| procedure(diag_fill_proc), | public, | pointer, nopass | :: | fill | => | null() | |
| logical, | public | :: | is_layered | = | .true. | ||
| character(len=128), | public | :: | long_name | = | "" | ||
| logical, | public | :: | masks_land | = | .false. |
The fill writes |
|
| character(len=64), | public | :: | name | = | "" | ||
| integer, | public | :: | requires | = | DERIVED_REQ_NONE |
Prerequisite knob, if any. Registering an entry whose
prerequisite is off FAILS LOUD at configure — the same
stance |
|
| character(len=64), | public | :: | standard_name | = | "" | ||
| character(len=32), | public | :: | units | = | "" |
Public only for the unit-test suite.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | i |
The DERIVED_REQ_* code a catalog entry is gated on, -1 for
an unknown name. The DECISION register_derived fails loud on,
exposed as a lookup so the suite can assert it without
provoking error stop — the repo’s standing pattern for
testing a fail-loud rule.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | name |
Public only for the unit-test suite.
The ISOMIP+ melt-rate conversion, kg m-2 s-1 → m yr-1 of
ice-equivalent freshwater: f = SECONDS_PER_YEAR / rho_fw.
Comma-separated list of catalog diagnostic names (for error text).
Render canonical_diag_gate_hint(name) into a human-readable
sentence for the “requested but not registered” warning. Phrased
as a QUESTION, never a diagnosis: the hint is a hand-maintained
mirror of register_default_diags’s gates (§11.2 of the PR-64
plan) and can drift from the true cause. An empty hint means the
diagnostic is one of the four unconditional ones (SSH/u/v/KE) and
SHOULD have registered — that is a bug, not a closed gate.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | name |
Configure the ocean diagnostic set from the unified
&ocean_diag_nml diags selection string. Single production entry
point: parses spec once, registers the canonical defaults
(consulting the spec for per-diagnostic :off skips and
:cadence / :op / :coord overrides), then registers any spec
entries that name a derived-catalog diagnostic (with their overrides).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(ocean_state_t), | intent(inout), | target | :: | state | ||
| character(len=*), | intent(in) | :: | spec | |||
| real(kind=wp), | intent(in), | optional | :: | dt_out | ||
| integer, | intent(in), | optional | :: | default_coord |
Copy a 2-D cavity field into the diag buffer, writing IEEE NaN wherever the column was not solved. Every cavity fill that is a plain read of a slot array routes through here, so the missing-value convention cannot drift between them.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | src(:,:) | |||
| real(kind=wp), | intent(in) | :: | active(:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Per-column CAVITY_MELT_* status code, as a real. 0 is OK;
anything else is a column that took the kernel’s documented
zero-melt safe state, and the console’s warning line says how
many there were. Masked like the rest, so the plane cannot be
read as “everything is fine” over open ocean.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Haline exchange velocity gamma_S (m/s) — see fill_exch_vel_t.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Thermal exchange velocity gamma_T (m/s) the solve CONVERGED
on — not Gamma_T·u* re-derived here. Under hj99/yung25
the two differ by the stratification suppression.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
S* = S_far − S_b (g/kg) — the salinity contrast the haline
exchange acts on. Positive under melting (meltwater freshens
the interface).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Sea-ice drift speed |u_ice| at T-centres (m/s) — C-face pairs
averaged to centre, 2-D twin of the ocean fill_ke stencil.
Ice off / not init ⇒ zeros (never registered by default; opt-in
via &ocean_diag_nml diags).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Eastward sea-ice velocity at T-centres (m/s) — u-face pair
averaged to centre, 2-D twin of fill_u_centre. Ice off / not
init ⇒ zeros.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Northward sea-ice velocity at T-centres (m/s) — v-face pair
averaged to centre, 2-D twin of fill_v_centre. Ice off / not
init ⇒ zeros.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Depth-integrated kinetic energy at each cell: KE_total(i, j) = Σ_k 0.5 · h_layer(k) · (u_c² + v_c²)
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Basal melt mass flux (kg m-2 s-1), positive = melting.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Basal melt rate in the ISOMIP+ reporting unit (m yr-1 of
ice-equivalent freshwater) — see melt_m_per_yr_factor.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Mixed-layer depth via the de Boyer Montégut threshold.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
In-situ density per layer — direct read of the EOS slot (driver
must have run ocean_eos_compute this step; not re-invoked here),
with the NaN missing-data sentinel on every vanished layer.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Interface salinity S_b (g/kg).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Interface temperature T_b (degC) — on the liquidus at S_b.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
T_f(S_far, p_top) (degC) — the in-situ freezing point of the
FAR FIELD at the ice base. Distinct from tbdry, which is the
freezing point of the INTERFACE salinity S_b; their
difference is the whole three-equation correction, so shipping
both makes it visible.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
T* = T_far − T_f(S_far, p_top) (degC) — the far-field
temperature above the IN-SITU freezing point at the interface
pressure. Positive drives melting. This is the single number
the melt rate is roughly linear in, so it is the first thing to
look at when a melt rate looks wrong.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Depth-integrated zonal transport (m²/s): T_x(i, j) = Σ_k h_c · u_c (cell-centre form)
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Depth-integrated meridional transport (m²/s) — mirror of x.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Melt friction velocity u* (m/s), max(sqrt(C_d(u²+v²+u_tide²)),
u*_min). This is the MELT law’s u* and nothing else: it
drives no momentum drag, and KPP/EPBL do not read it.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
The model’s OWN relative vorticity ζ = ∂v/∂x − ∂u/∂y, at the same
C-grid corners coriolis_adv computes it at (circulation/area
form, C1 slip factor), averaged corner -> cell centre. See
fill_vorticity_z_impl for the formula and why it replaced a
centred T-point stencil that differenced ocean velocity straight
against the zero stored at land faces (a spurious no-slip-like
vorticity sheet at every coast, even under the free-slip default).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Cell-centred relative vorticity that MIRRORS the dynamics’ own
corner ζ bit-for-bit — the shared rdb_rvc_zeta_corner body
(src/shared_module_utilities/rdb_rel_vort_corner.inc), the same
circulation-form formula coriolis_adv_compute_tendencies Pass 1
evaluates inline for its q_corner scratch
(src/core/ocean/kernels/coriolis_adv/rdb_coriolis_adv.F90), incl.
the C1 slip factor: no_slip=.false. (default, free-slip) masks a
land corner (wet_q=0) to zero rel-vort; .true. (no-slip) gives
it the 2-wet_q image-vorticity value instead.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | u_face(:,:,:) | |||
| real(kind=wp), | intent(in) | :: | v_face(:,:,:) | |||
| real(kind=wp), | intent(in) | :: | dyCv(:,:) | |||
| real(kind=wp), | intent(in) | :: | dxCu(:,:) | |||
| real(kind=wp), | intent(in) | :: | wet_q(:,:) | |||
| real(kind=wp), | intent(in) | :: | wet_T(:,:) | |||
| real(kind=wp), | intent(in) | :: | iareaBu(:,:) | |||
| logical, | intent(in) | :: | no_slip | |||
| integer, | intent(in) | :: | nz_ml | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Water-column thickness under the shelf, D = bt_H_ref + bt_eta
(m) — the ONE expression every consumer of the column depth
uses, which is exactly what the cavity datum
(bt_H_ref = b − z_draft) buys. Masked by cover_frac: the
quantity is perfectly well defined in open water, but as a
CAVITY diagnostic a domain mean should be the mean cavity
thickness, not that diluted by the open basin.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Ice-shelf draft (m, POSITIVE DOWN from the geoid) — geometry,
so it needs only &ocean_cavity_dyn_nml and is masked by
cover_frac rather than by the melt slot’s active. Beyond
the calving front there is no draft, which is missing data, not
a draft of zero.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(*), | intent(in) | :: | state_handle | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Register ONE derived diagnostic by catalog name (used by
apply_diag_selection): look it up and forward to
state%diag%register(...) with the catalog metadata + correct buffer
shape. Error-stops on an unknown name. coord (a DIAG_VGRID_*)
sets the output vgrid for a LAYERED diag + attaches the conservative
remap (2D entries ignore it); default LAYER. Remaps INTENSIVE.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(ocean_state_t), | intent(inout), | target | :: | state | ||
| character(len=*), | intent(in) | :: | name | |||
| integer, | intent(in), | optional | :: | time_op | ||
| real(kind=wp), | intent(in), | optional | :: | dt_out | ||
| integer, | intent(in), | optional | :: | coord |
a - b with the cavity missing-value convention.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | a(:,:) | |||
| real(kind=wp), | intent(in) | :: | b(:,:) | |||
| real(kind=wp), | intent(in) | :: | active(:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
cavity_mask_impl with a constant multiplier folded in.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | src(:,:) | |||
| real(kind=wp), | intent(in) | :: | active(:,:) | |||
| real(kind=wp), | intent(in) | :: | scale | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | status(:,:) | |||
| real(kind=wp), | intent(in) | :: | active(:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Shared device copy buf = src with shape clipping — every
direct-copy derived field routes through here.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | src(:,:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Fail loud on a derived diagnostic whose prerequisite knob is
off. Same three-step shape register_derived uses for an
unknown name — error ring, logger, error stop — so the C ABI
and the console both see it.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | name |
Catalog name the user asked for. |
||
| character(len=*), | intent(in) | :: | knob |
The namelist key that has to be on. |
||
| character(len=*), | intent(in) | :: | what |
One phrase naming what that knob builds. |
Populate the catalog at runtime (procedure pointers can’t be a parameter constructor). Idempotent.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | u_ice(:,:) | |||
| real(kind=wp), | intent(in) | :: | v_ice(:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | u_ice(:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | v_ice(:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | h_layer(:,:,:) | |||
| real(kind=wp), | intent(in) | :: | u_face(:,:,:) | |||
| real(kind=wp), | intent(in) | :: | v_face(:,:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Per-column scan from the first LIVE layer (k_top, which is
nz_ml unless a rigid top has vanished the layers above it)
toward the bed; first layer whose ρ exceeds (surface ρ +
MLD_DENSITY_THRESHOLD) marks the MLD as the cumulative h-sum
above it. No crossing → MLD = full column depth. Threshold met
at the surface itself → MLD = 0.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | h_layer(:,:,:) | |||
| real(kind=wp), | intent(in) | :: | rho_layer(:,:,:) | |||
| integer, | intent(in) | :: | k_top(:,:) | |||
| integer, | intent(in) | :: | nz_ml | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
buf = rho_layer on live layers, IEEE NaN on vanished ones.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | h_layer(:,:,:) | |||
| real(kind=wp), | intent(in) | :: | rho_layer(:,:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | h_layer(:,:,:) | |||
| real(kind=wp), | intent(in) | :: | u_face(:,:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | h_layer(:,:,:) | |||
| real(kind=wp), | intent(in) | :: | v_face(:,:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | s_far(:,:) | |||
| real(kind=wp), | intent(in) | :: | p_top(:,:) | |||
| real(kind=wp), | intent(in) | :: | active(:,:) | |||
| type(eos_t), | intent(in) | :: | eos | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
T_far − eos_freezing_point(eos, S_far, p_top). The liquidus
comes off the SAME eos_t handle the solve used
(&ocean_eos_nml tfreeze_set), never a local copy of the
coefficients — the two sets differ by ~0.03 degC, which is
enough to flip the sign of this field.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | t_far(:,:) | |||
| real(kind=wp), | intent(in) | :: | s_far(:,:) | |||
| real(kind=wp), | intent(in) | :: | p_top(:,:) | |||
| real(kind=wp), | intent(in) | :: | active(:,:) | |||
| type(eos_t), | intent(in) | :: | eos | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | bt_H_ref(:,:) | |||
| real(kind=wp), | intent(in) | :: | bt_eta(:,:) | |||
| real(kind=wp), | intent(in) | :: | cover(:,:) | |||
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |
Device-side zero of buf (seeds column sums / clean bail-out).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(inout) | :: | buf(:,:,:) |