rdb_ocean_diag Module

Owns the diagnostics pipeline for the ocean dynamical core. Built around the following design points (Phase 6 work):

  1. Pull model, not push. Kernels write to state and stay diag-agnostic; this manager iterates a registry each output step, fetches the source array (by pointer / by enum tag), remaps onto the configured output vertical grid, accumulates time means, and hands the ready buffer to the I/O server. Kernels never call diag%send(...).

  2. Compute-side remap. Layer→z, layer→isopycnal, and time-mean accumulation all happen on the compute rank before hand-off. The I/O server then only has to do compression + netcdf write. Keeps the I/O server lean (no physics-aware code) at the cost of carrying the remap target buffers on every compute rank — which is cheap since they’re shape (nx, ny, nz_out) not (nx_global, ny_global, nz_out).

  3. I/O server interaction. When a diag_var_t’s cadence fires, this manager calls io_server_send(buf, meta) on the per-rank send queue. Backpressure (server slower than compute) shows up as a hung send and is logged. Hand-off contract: the manager owns the buffer lifetime; the I/O server gets a non-owning view.

Phase 0e status: empty scaffold. Components are declared so Phase 6 can fill in register / step / send_ready without restructuring the god state. The bound procedures init/destroy are no-ops on the empty registry.

Collaborator hand-off: this slot is independent of the dynamical core — once ocean_diag_register lands, every other kernel can be diagnosed without modification. Good first task for a new contributor with FMS / diag_manager experience.

Exposed for test_ocean_diag_reduce: the emit-path statistics reduction is the only device kernel in this module, so it is unit tested directly (numerics + mem:separate residency) rather than only through step.


Uses

  • module~~rdb_ocean_diag~~UsesGraph module~rdb_ocean_diag rdb_ocean_diag ieee_arithmetic ieee_arithmetic module~rdb_ocean_diag->ieee_arithmetic iso_fortran_env iso_fortran_env module~rdb_ocean_diag->iso_fortran_env module~rdb_constants rdb_constants module~rdb_ocean_diag->module~rdb_constants module~rdb_error_ring rdb_error_ring module~rdb_ocean_diag->module~rdb_error_ring module~rdb_grid rdb_grid module~rdb_ocean_diag->module~rdb_grid module~rdb_mem_report rdb_mem_report module~rdb_ocean_diag->module~rdb_mem_report module~rdb_ocean_diag_mask rdb_ocean_diag_mask module~rdb_ocean_diag->module~rdb_ocean_diag_mask module~rdb_ocean_status rdb_ocean_status module~rdb_ocean_diag->module~rdb_ocean_status pic_logger pic_logger module~rdb_ocean_diag->pic_logger pic_types pic_types module~rdb_constants->pic_types module~rdb_error_ring->pic_logger module~rdb_grid->module~rdb_constants module~rdb_mem_report->iso_fortran_env module~rdb_mem_report->module~rdb_constants module~rdb_mem_report->pic_logger pic_strings pic_strings module~rdb_mem_report->pic_strings module~rdb_ocean_diag_mask->iso_fortran_env module~rdb_ocean_diag_mask->module~rdb_constants module~rdb_ocean_diag_mask->module~rdb_grid module~rdb_ocean_diag_mask->module~rdb_mem_report

Used by

  • module~~rdb_ocean_diag~~UsedByGraph module~rdb_ocean_diag rdb_ocean_diag module~rdb_ocean_diag_derived rdb_ocean_diag_derived module~rdb_ocean_diag_derived->module~rdb_ocean_diag module~rdb_ocean_diag_fills rdb_ocean_diag_fills module~rdb_ocean_diag_derived->module~rdb_ocean_diag_fills module~rdb_ocean_state rdb_ocean_state module~rdb_ocean_diag_derived->module~rdb_ocean_state module~rdb_ocean_diag_fills->module~rdb_ocean_diag module~rdb_ocean_diag_fills->module~rdb_ocean_state module~rdb_ocean_diag_netcdf rdb_ocean_diag_netcdf module~rdb_ocean_diag_netcdf->module~rdb_ocean_diag module~rdb_ocean_engine rdb_ocean_engine module~rdb_ocean_engine->module~rdb_ocean_diag module~rdb_ocean_engine->module~rdb_ocean_diag_derived module~rdb_ocean_engine->module~rdb_ocean_diag_fills module~rdb_ocean_engine->module~rdb_ocean_diag_netcdf module~rdb_ocean_engine->module~rdb_ocean_state module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_setup module~rdb_ocean_state->module~rdb_ocean_diag module~rdb_driver rdb_driver 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_diag_derived module~rdb_ocean_api->module~rdb_ocean_diag_fills module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_ocean_api->module~rdb_handle module~rdb_ocean_setup->module~rdb_ocean_state

Variables

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

diag_spec_t%coord sentinel: no :coord attribute given => keep the diagnostic’s default output vgrid (the global &ocean_diag_nml vgrid). Set values are DIAG_VGRID_* (layer / z / zstar / sigma).

integer, public, parameter :: DIAG_LOC_CENTER = 1

cell centre

integer, public, parameter :: DIAG_LOC_CORNER = 4

south-west corner

integer, public, parameter :: DIAG_LOC_FACE_X = 2

east face

integer, public, parameter :: DIAG_LOC_FACE_Y = 3

north face

real(kind=wp), public, parameter :: DIAG_MISSING_VALUE = 1.0e20_wp

Sentinel written into remapped output cells that overlap no water (below-bottom / pinched-out) when vanished-target masking is on, and advertised as the NetCDF _FillValue / missing_value.

integer, public, parameter :: DIAG_OP_INSTANT = 1

snapshot at cadence

integer, public, parameter :: DIAG_OP_INTEGRAL = 5

Cumulative time integral over the cadence window — emits Σ(sample · dt) without dividing. Used for budget closure: time-integrated fluxes through a surface or through an OBC face are the conserved quantity, not their per-step rate.

integer, public, parameter :: DIAG_OP_MAX = 3
integer, public, parameter :: DIAG_OP_MEAN = 2

dt-weighted time mean over cadence

integer, public, parameter :: DIAG_OP_MIN = 4
integer, public, parameter :: DIAG_OP_UNSET = -1

diag_spec_t%time_op sentinel: attribute not given => keep the diagnostic’s canonical default time-operator.

integer, public, parameter :: DIAG_VGRID_BOTTOM = 5

2D bed slice.

integer, public, parameter :: DIAG_VGRID_DENSITY = 3

Isopycnal bins for watermass analysis.

integer, public, parameter :: DIAG_VGRID_LAYER = 1

Model layers (k=1 bed, k=nz surface).

integer, public, parameter :: DIAG_VGRID_SIGMA = 7

Fixed sigma-levels: terrain-following fractional-depth output grid (target interface depth = sigma fraction * col_h).

integer, public, parameter :: DIAG_VGRID_SURFACE = 4

2D surface slice (no remap needed).

integer, public, parameter :: DIAG_VGRID_ZSTAR = 6

Fixed z*-levels: SSH-tracking stretched-depth output grid (each column’s target depths scale with col_h / resting depth).

integer, public, parameter :: DIAG_VGRID_Z_FIXED = 2

Fixed z-levels (e.g., 50 standard depths).

integer, private, parameter :: INITIAL_CAPACITY = 16

Abstract Interfaces

abstract interface

  • public subroutine diag_emit_proc(diag, ivar, t)

    Optional post-fire emit hook. Called by step once per cadence-fire AFTER the log line; used by the NetCDF writer to append a slice for the var that just fired. Kept abstract so the diag module stays free of NetCDF / I/O-server deps — rdb_ocean_diag_netcdf provides the concrete implementation.

    Arguments

    Type IntentOptional Attributes Name
    class(*), intent(inout) :: diag
    integer, intent(in) :: ivar
    real(kind=wp), intent(in) :: t

abstract interface

  • public subroutine diag_fill_proc(state_handle, buf)

    Fill routine signature. state_handle is polymorphic so this module stays decoupled from ocean_state_t (which itself composes ocean_diag_t). Implementations live in rdb_ocean_diag_fills and do a select type cast to the concrete ocean state inside the body. buf is the pre-allocated output buffer the manager owns.

    Arguments

    Type IntentOptional Attributes Name
    class(*), intent(in) :: state_handle
    real(kind=wp), intent(inout) :: buf(:,:,:)

abstract interface

  • public subroutine diag_remap_proc(state_handle, z_out, layer_buf, output_buf, is_extensive)

    Vertical-remap routine signature. Called by the manager after fill when a var’s output_vgrid is not LAYER. layer_buf is the manager-owned native-grid scratch the fill wrote into; output_buf is the remap target on the configured output vgrid (z-levels / isopycnals / 2D). State-side data needed for the remap (e.g. h_layer, bathy) is fetched via select type on state_handle.

    Conservation contract: is_extensive (forwarded by the manager from diag_var_t%is_extensive) selects the operator. .false. — INTENSIVE: the target value is the thickness-weighted average of the overlapping source layers. .true. — EXTENSIVE: the column integral is conservatively REDISTRIBUTED (Σ output == Σ layer_buf), for thickness- integrated quantities (hTr, h·KE, transports).

    Arguments

    Type IntentOptional Attributes Name
    class(*), intent(in) :: state_handle
    real(kind=wp), intent(in) :: z_out(:)
    real(kind=wp), intent(in) :: layer_buf(:,:,:)
    real(kind=wp), intent(inout) :: output_buf(:,:,:)
    logical, intent(in) :: is_extensive

Derived Types

type, public ::  diag_spec_t

One parsed entry of the &ocean_diag_nml diags selection list. Produced by parse_diag_spec; consumed by register_default_diags (canonical-default overrides / skips) and the derived-diagnostic orchestrator (apply_diag_selection). Unset attributes carry sentinels so the consumer falls back to the canonical default.

Components

Type Visibility Attributes Name Initial
integer, public :: coord = DIAG_COORD_UNSET

:layer/z/zstar/sigma output-vgrid override

real(kind=wp), public :: dt_out = -1.0_wp

:<n>s/m/h/d cadence override (<0 = unset)

character(len=64), public :: name = ""

diagnostic name (catalog or canonical)

logical, public :: off = .false.

:off => skip this diagnostic

integer, public :: time_op = DIAG_OP_UNSET

:instant/mean/max/min override

type, public ::  diag_var_t

Per-variable diagnostic record. Owned by ocean_diag_t.

Components

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

Wall time accumulated into this var’s window (s).

real(kind=wp), public :: dt_out = 3600.0_wp

Output cadence (s). Manager keeps a per-var counter and fires when the accumulated dt passes the threshold.

logical, public :: enabled = .true.

When .false. the dispatcher skips this var entirely — no fill_* kernel, no accumulator fold, no emit — so an unrequested diagnostic costs zero GPU work. Lets a run turn off individual default diagnostics it does not want. Default .true. ⇒ every registered var runs (bit-identical).

procedure(diag_fill_proc), public, pointer, nopass :: fill => null()
integer, public :: fire_count = 0

Number of times this var’s output_buffer has been refreshed (filled/folded/finalised + pulled host-ward) since registration. Incremented unconditionally in ocean_diag_step at the SAME point as the per-fire update self — independent of whether a NetCDF stream is open (nc_time_index stays 0 with diagnostics disabled or output suppressed, so it cannot serve this role). This is the generation the C ABI’s rdb_ocean_get_diagnostic_ptr returns: a Python Field re-checks it on access and knows output_buffer is already host-current the instant it changes (no separate refresh call needed — the pull above already happened synchronously).

logical, public :: has_missing = .false.

When .true. the remap fills target cells that overlap no water (below-bottom / pinched-out in a shallow column) with DIAG_MISSING_VALUE instead of 0, and the NetCDF writer tags the variable with a _FillValue / missing_value attribute. Set at register time for non-LAYER diagnostics when masking is enabled (&ocean_diag_nml mask_vanished_layers). Default .false. => below-bottom cells read 0 (bit-identical to the legacy writer).

logical, public :: is_extensive = .false.

.false. (default): field is INTENSIVE — per-unit-thickness quantity like temperature, salinity, velocity, density. When remapped to a non-LAYER vgrid, the column value is weight-averaged across overlapping source layers.

Read more…
real(kind=wp), public, allocatable :: layer_buffer(:,:,:)
character(len=128), public :: long_name = ""

CF-compliant long_name attribute.

type(diag_mask_t), public, allocatable :: mask

Optional region mask. When allocated, fold_sample multiplies each sample by mask%weight(i, j) before folding into the accumulator — cells outside the region contribute zero. Output buffer shape is unchanged (full domain with zeros outside the mask); scalar-aggregating reductions land with the Phase D budget plumbing or as a Phase C v2 follow-on.

integer, public :: n_accum = 0

Number of contributions in the current accumulator window.

character(len=64), public :: name = ""

Short netcdf variable name.

integer, public :: nc_time_dimid = -1
integer, public :: nc_time_index = 0

Number of slices already written for this var (1-indexed position of the NEXT write).

integer, public :: nc_time_varid = -1
integer, public :: nc_varid = -1
integer, public :: nc_z_dimid = -1

-1 for 2D vars; set for 3D vars when the stream is opened.

real(kind=wp), public, allocatable :: output_buffer(:,:,:)
integer, public :: output_vgrid = DIAG_VGRID_LAYER
procedure(diag_remap_proc), public, pointer, nopass :: remap => null()

Optional vertical-remap routine. Set at register time when output_vgrid /= DIAG_VGRID_LAYER; null otherwise.

integer, public :: source_loc = DIAG_LOC_CENTER
integer, public :: source_vgrid = DIAG_VGRID_LAYER
character(len=64), public :: standard_name = ""
integer, public :: stream_id = 0

ID of the output stream this var ships to (Phase 6 wires the I/O server stream table).

integer, public :: time_op = DIAG_OP_MEAN
character(len=32), public :: units = ""

Type-Bound Procedures

procedure, public, non_overridable :: bytes => diag_var_bytes

type, public ::  ocean_diag_nc_stream_t

NetCDF output stream state. Carried inline on ocean_diag_t so the diag module stays NetCDF-free (no use netcdf here); the actual file operations live in rdb_ocean_diag_netcdf which manipulates these fields.

Components

Type Visibility Attributes Name Initial
character(len=256), public :: filename = ""
logical, public :: is_open = .false.
integer, public :: ncid = -1
real(kind=real32), public, allocatable :: stage(:,:,:)

Host staging buffer for the fp64 -> fp32 conversion done immediately before nf90_put_var. Allocated by open_stream ONLY when xtype == NC_R4, sized to the largest registered variable’s output_buffer; unallocated (zero cost) on the default double-precision path.

integer, public :: x_dimid = -1
integer, public :: xtype = -1

Resolved NetCDF element type for the DATA variables of this stream (NC_WP / NC_R4 — see rdb_io_netcdf). -1 = unset; open_stream resolves it from &ocean_diag_nml output_precision (“double”, the default, => NC_WP => byte-identical output). Held as a bare integer so this module stays NetCDF-free; only rdb_ocean_diag_netcdf interprets it.

Read more…
integer, public :: y_dimid = -1

type, public ::  ocean_diag_t

Components

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

Wall time at last evaluation pass (s).

procedure(diag_emit_proc), public, pointer, nopass :: emit_post_fire => null()

Optional post-fire hook the manager calls after the log line. rdb_ocean_diag_netcdf::open_stream binds this to the NetCDF writer; unbound = log-only behaviour.

logical, public :: enabled = .true.

Master switch. Phase 6 reads from namelist.

type(hgrid_t), public :: grid

Cached grid (scalar-only struct, cheap to copy). Derived diagnostic fills read grid%nghost from here (and the per-cell spacing from state%metrics directly — design D5); they only see state_handle so the grid has to be reachable through the state composition.

logical, public :: is_init = .false.

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

integer, public :: n_rho_out = 0
integer, public :: n_sigma_out = 0
integer, public :: n_zstar_out = 0
type(ocean_diag_nc_stream_t), public :: nc_stream
integer, public :: nvars = 0

Live count of registered diagnostics.

integer, public :: nvars_max = 0

Capacity of vars(:). Grown via reallocation on register overflow.

integer, public :: nz_out = 0
logical, public :: on_device = .false.

.true. between enter_data and exit_data. The emit-time statistics reduction reads output_buffer where it actually lives: on the device via do concurrent ... reduce when mapped, on the host otherwise (unit tests that skip enter_data). A device read of an unmapped buffer under -gpu=mem:separate would return garbage silently, so this is not optional.

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

Output isopycnal bin edges (kg/m^3).

real(kind=wp), public, allocatable :: send_buf(:,:,:)
real(kind=wp), public, allocatable :: sigma_out(:)

Output sigma levels: cumulative fractions (0..1, shallow->deep).

type(diag_var_t), public, allocatable :: vars(:)
real(kind=wp), public, allocatable :: z_out(:)

Output z-levels (m, positive up; surface at index nz_out).

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

Output z* reference interface depths (m, positive-down; deepest = reference total depth H_ref). Per-column grid stretched by col_h / H_ref (SSH-tracking).

Type-Bound Procedures

procedure, public, non_overridable :: bytes => ocean_diag_bytes
procedure, public, non_overridable :: destroy => ocean_diag_destroy
procedure, public, non_overridable :: disable => ocean_diag_disable
procedure, public, non_overridable :: enter_data => ocean_diag_enter_data
procedure, public, non_overridable :: exit_data => ocean_diag_exit_data
procedure, public, non_overridable :: init => ocean_diag_init
procedure, public, non_overridable :: is_registered => ocean_diag_is_registered
procedure, public, non_overridable :: register => ocean_diag_register
procedure, public, non_overridable :: set_output_density_levels => ocean_diag_set_output_density_levels
procedure, public, non_overridable :: set_output_sigma_levels => ocean_diag_set_output_sigma_levels
procedure, public, non_overridable :: set_output_z_levels => ocean_diag_set_output_z_levels
procedure, public, non_overridable :: set_output_zstar_levels => ocean_diag_set_output_zstar_levels
procedure, public, non_overridable :: step => ocean_diag_step

Functions

public function parse_diag_spec(spec) result(specs)

Parse the &ocean_diag_nml diags selection string into structured entries. Entries are whitespace/comma-separated; within an entry, colon-separated attributes are self-identifying (order-free):

Read more…

Arguments

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

Return Value type(diag_spec_t), allocatable, (:)

private pure function diag_var_bytes(this) result(nbytes)

Counted allocatable footprint of ONE registered diagnostic (0 for every buffer that is unallocated).

Read more…

Arguments

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

Return Value integer(kind=int64)

private pure function lower_ascii(s) result(out)

ASCII lowercase a string (attribute matching is case-insensitive).

Arguments

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

Return Value character(len=len)

private pure function ocean_diag_bytes(this) result(nbytes)

Counted allocatable footprint of the diagnostics slot: the flat remap-level arrays, the host NetCDF staging buffer, AND the per-variable registry buffers (0 when unallocated). One arr_bytes term per array — add a term here when a new allocatable joins the type; new diag_var_t buffers go in diag_var_bytes.

Arguments

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

Return Value integer(kind=int64)

private pure function ocean_diag_is_registered(this, name) result(yes)

.true. iff a diagnostic named name is registered (enabled or not). Registration state only — says nothing about whether it will actually fire (see enabled); a disabled diagnostic is still registered and this returns .true. for it.

Arguments

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

Return Value logical


Subroutines

public pure subroutine diag_field_stats(buf, on_device, vmin, vmax, vmean, n_valid, n_total)

The [diag] console line’s min / max / mean for one diagnostic buffer, over the finite cells only.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: buf(:,:,:)
logical, intent(in) :: on_device

.true. when buf is device-resident, so the fused device reduction is the correct (and only correct) reader — a host read of a mapped buffer under -gpu=mem:separate is stale.

real(kind=wp), intent(out) :: vmin
real(kind=wp), intent(out) :: vmax
real(kind=wp), intent(out) :: vmean
integer, intent(out) :: n_valid
integer, intent(out) :: n_total

public pure subroutine diag_reduce_stats(buf, n1, n2, n3, vmin, vmax, vsum, n_valid)

Whole-array min / max / sum of a diagnostic buffer in ONE pass, over the FINITE cells only.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: buf(n1,n2,n3)
integer, intent(in) :: n1
integer, intent(in) :: n2
integer, intent(in) :: n3
real(kind=wp), intent(out) :: vmin
real(kind=wp), intent(out) :: vmax
real(kind=wp), intent(out) :: vsum
integer, intent(out), optional :: n_valid

Number of finite cells folded in. Optional so the pre-existing three-scalar call sites keep working unchanged.

private subroutine fill_and_remap(v, state_handle, z_out, rho_out, sigma_out, zstar_out)

Invoke the var’s fill (and remap if non-LAYER) so output_buffer holds the current sample. The accumulation / log emission step consumes output_buffer after this.

Read more…

Arguments

Type IntentOptional Attributes Name
type(diag_var_t), intent(inout) :: v
class(*), intent(in) :: state_handle
real(kind=wp), intent(in), allocatable :: z_out(:)
real(kind=wp), intent(in), allocatable :: rho_out(:)
real(kind=wp), intent(in), allocatable :: sigma_out(:)
real(kind=wp), intent(in), allocatable :: zstar_out(:)

private pure subroutine fill_buffer_impl(buf, val)

Device-side scalar fill. Used by reset_accumulator and (when idx_temperature / idx_salinity is unset) the manager could also seed output_buffer via this; today only the accumulator reset goes through here.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: buf(:,:,:)
real(kind=wp), intent(in) :: val

private subroutine finalise_accumulator(v)

Copy the accumulator into output_buffer. Called at cadence-fire BEFORE log / NetCDF emission consumes output_buffer.

Read more…

Arguments

Type IntentOptional Attributes Name
type(diag_var_t), intent(inout) :: v

private pure subroutine finalise_copy_impl(out, accum)

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: out(:,:,:)
real(kind=wp), intent(in) :: accum(:,:,:)

private pure subroutine finalise_scale_impl(out, accum, scale_factor)

Device-side out = accum * scale_factor. Used for MEAN finalise where scale_factor = 1/dt_accum.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: out(:,:,:)
real(kind=wp), intent(in) :: accum(:,:,:)
real(kind=wp), intent(in) :: scale_factor

private subroutine fold_sample(v, dt)

Combine the current output_buffer sample into accumulator per time_op.

Read more…

Arguments

Type IntentOptional Attributes Name
type(diag_var_t), intent(inout) :: v
real(kind=wp), intent(in) :: dt

private pure subroutine fold_sample_masked_impl(accum, out, weight, mask_nx, mask_ny, dt, time_op)

Masked fold — multiplies sample by weight(i, j) before folding. MAX/MIN treat weight == 0 as “don’t update” so masked-out cells keep their seed value. Loop bounds clip to whichever extent is smaller (accumulator vs mask) so an undersized mask doesn’t OOB.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: accum(:,:,:)
real(kind=wp), intent(in) :: out(:,:,:)
real(kind=wp), intent(in) :: weight(:,:)
integer, intent(in) :: mask_nx
integer, intent(in) :: mask_ny
real(kind=wp), intent(in) :: dt
integer, intent(in) :: time_op

private pure subroutine fold_sample_unmasked_impl(accum, out, dt, time_op)

Whole-buffer fold without a region mask. One do concurrent per op so the compiler can specialise — case-inside-loop blocks NVHPC device codegen.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: accum(:,:,:)
real(kind=wp), intent(in) :: out(:,:,:)
real(kind=wp), intent(in) :: dt
integer, intent(in) :: time_op

private subroutine ocean_diag_destroy(this)

Arguments

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

private subroutine ocean_diag_disable(this, name)

Turn OFF the registered diagnostic name so the dispatcher skips it entirely (no fill, no fold, no emit). Fail-loud if name matches no registered var — a typo must not silently leave a diagnostic running. Lists the registered names on abort.

Arguments

Type IntentOptional Attributes Name
class(ocean_diag_t), intent(inout) :: this
character(len=*), intent(in) :: name

private subroutine ocean_diag_enter_data(this)

Attach each registered var’s per-buffer allocatables to the device. Called by ocean_state_enter_data after register_default_diags has populated vars(:) — buffers are sized at register time, so the descriptors here are valid.

Read more…

Arguments

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

private subroutine ocean_diag_enter_data_impl(this)

Arguments

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

private subroutine ocean_diag_exit_data(this)

Detach in reverse order of enter_data. Idempotency-safe via is_init gate — repeated calls without intervening enter_data become no-ops once the manager is destroyed.

Arguments

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

private subroutine ocean_diag_exit_data_impl(this)

Arguments

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

private subroutine ocean_diag_init(this, grid)

Allocate an empty registry sized at INITIAL_CAPACITY slots. Subsequent register calls grow the array via doubling.

Arguments

Type IntentOptional Attributes Name
class(ocean_diag_t), intent(inout) :: this
type(hgrid_t), intent(in) :: grid

private subroutine ocean_diag_register(this, name, units, fill, n1, n2, n3, long_name, standard_name, time_op, dt_out, output_vgrid, remap, mask, is_extensive, has_missing)

Register a new diagnostic variable. Grows the registry via capacity doubling on overflow. Buffer allocation depends on output_vgrid: * LAYER (default): one output_buffer(n1, n2, n3) — fill writes directly into it. * Z_FIXED (or any non-LAYER target): two buffers — layer_buffer(n1, n2, n3) for the fill, plus output_buffer(n1, n2, this%nz_out) for the remapped result. Caller must have configured nz_out via set_output_z_levels first, and bind a remap proc. Caller binds fill to a routine that knows how to populate the layer-native buffer from the state handle.

Read more…

Arguments

Type IntentOptional Attributes Name
class(ocean_diag_t), intent(inout) :: this
character(len=*), intent(in) :: name
character(len=*), intent(in) :: units
procedure(diag_fill_proc) :: fill
integer, intent(in) :: n1
integer, intent(in) :: n2
integer, intent(in) :: n3
character(len=*), intent(in), optional :: long_name
character(len=*), intent(in), optional :: standard_name
integer, intent(in), optional :: time_op
real(kind=wp), intent(in), optional :: dt_out
integer, intent(in), optional :: output_vgrid
procedure(diag_remap_proc), optional :: remap
type(diag_mask_t), intent(in), optional :: mask
logical, intent(in), optional :: is_extensive
logical, intent(in), optional :: has_missing

private subroutine ocean_diag_set_output_density_levels(this, rho, ierr)

Configure the isopycnal (DENSITY) output grid — monotone-increasing target potential densities (kg/m³). Stored copy; must be called BEFORE any register with output_vgrid == DIAG_VGRID_DENSITY so the manager knows the output buffer shape (one cell per target).

Arguments

Type IntentOptional Attributes Name
class(ocean_diag_t), intent(inout) :: this
real(kind=wp), intent(in) :: rho(:)
integer, intent(out), optional :: ierr

private subroutine ocean_diag_set_output_sigma_levels(this, sigma, ierr)

Configure the terrain-following (SIGMA) output grid — cumulative sigma fractions (0..1, monotone shallow->deep). Stored copy; must be called BEFORE any register with output_vgrid == DIAG_VGRID_SIGMA so the manager knows the output buffer shape.

Arguments

Type IntentOptional Attributes Name
class(ocean_diag_t), intent(inout) :: this
real(kind=wp), intent(in) :: sigma(:)
integer, intent(out), optional :: ierr

private subroutine ocean_diag_set_output_z_levels(this, z, ierr)

Configure the fixed-z output grid. Stored copy; the original z array is not retained. Must be called BEFORE any register with output_vgrid == DIAG_VGRID_Z_FIXED so the manager knows the output buffer shape.

Arguments

Type IntentOptional Attributes Name
class(ocean_diag_t), intent(inout) :: this
real(kind=wp), intent(in) :: z(:)
integer, intent(out), optional :: ierr

Non-zero (OCEAN_STATUS_ERR_SETUP) when size(z) > NZ_STACK_MAX, when present; absent behaves as today (error stop). (F5 residual, P2.4)

private subroutine ocean_diag_set_output_zstar_levels(this, zstar, ierr)

Configure the SSH-tracking (ZSTAR) output grid — reference interface depths (m, positive-down, monotone shallow->deep; deepest = H_ref). Stored copy; must be called BEFORE any register with output_vgrid == DIAG_VGRID_ZSTAR so the manager knows the buffer shape.

Arguments

Type IntentOptional Attributes Name
class(ocean_diag_t), intent(inout) :: this
real(kind=wp), intent(in) :: zstar(:)
integer, intent(out), optional :: ierr

private subroutine ocean_diag_step(this, state_handle, dt, t)

Advance every registered variable. Behaviour by time_op:

Read more…

Arguments

Type IntentOptional Attributes Name
class(ocean_diag_t), intent(inout) :: this
class(*), intent(in) :: state_handle
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: t

private pure subroutine parse_cadence_attr(s, secs, ok)

Parse a cadence attribute <int><unit> (unit s/m/h/d) to seconds. ok=.false. if s is not a well-formed positive cadence.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: s
real(kind=wp), intent(out) :: secs
logical, intent(out) :: ok

private subroutine parse_one_spec_token(tok, s)

Parse a single name[:attr]... token into a diag_spec_t. Fails loud on an unrecognised attribute.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: tok
type(diag_spec_t), intent(out) :: s

private pure subroutine reset_accumulator(v)

Zero (MEAN), -huge (MAX), or +huge (MIN) the accumulator — so the first accumulate step seeds correctly. Runs on device via the flat-impl shim because v%accumulator is reached through the vars(:) array-of-derived-types indirection that NVHPC can’t follow inside a do concurrent.

Arguments

Type IntentOptional Attributes Name
type(diag_var_t), intent(inout) :: v