Restart cadence + per-slot checkpoint registry for the ocean dyn-core. Each prognostic-owning slot registers its arrays at birth; the manager walks the registry to do the I/O, so new state-carrying slots join by registering rather than editing here.
Contract (MPI-native):
1. FULL local arrays — every registered array is written/read at
its TOTAL local extent (interior + ghosts). Physical wall
ghosts are genuine owned boundary state (nothing rebuilds them),
so an interior-only write breaks the bit-exact gate; periodic/
fold/halo seam ghosts are redundant but harmless to save.
2. Decomposition + grid metadata (px, py, dims, i/j_start, nz,
nghost, vcoord, tracer set) live in the file; resume requires
the SAME decomposition and error-stops on mismatch. Cross-rank
redistribution is an offline tool, never in-Fortran.
3. Global scalars (time, step, outer_step_count) are rank-0’s.
4. A registered field is REQUIRED by default (missing on read is
FATAL); mark genuinely optional entries optional=.true..
Device sync: device_mapped arrays (default) are pulled with
!$acc update self before the host NetCDF write; host-only state
(device_mapped=.false.) is skipped so update self is never
issued on an unmapped array. The read path runs BEFORE
ocean_state_enter_data, writing the host interior so the
subsequent enter_data carries values up to the device.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | public, | parameter | :: | RESTART_SCHEMA_VERSION | = | 1 |
Bumped on any non-back-compatible on-disk field-set/layout change. Written as a global attribute, validated on read. |
| integer, | private, | parameter | :: | MAX_RESTART_ENTRIES | = | 256 |
Fixed cap on registered fields; bump if a slot family exceeds it. |
Restart-manager lifecycle marker. The registry is built per-run
by ocean_state_build_restart_registry (it holds live pointers
into the freshly allocated slots), not stored here. Cadence is
driven by the driver off cfg%restart_interval/cfg%output_dir.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| logical, | public | :: | is_init | = | .false. |
True between |
| procedure, public, non_overridable :: destroy => ocean_restart_destroy | |
| procedure, public, non_overridable :: init => ocean_restart_init |
One registered checkpoint field. Holds a pointer to the slot’s
host array (or host scalar) plus the interior extents needed to
slice owned cells. Exactly one of p0 / p2 / p3 is associated
(per rank: 0 = host scalar, 2/3 = array).
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| logical, | public | :: | device_mapped | = | .true. |
.true. => write path pulls host-ward via |
|
| logical, | public | :: | found | = | .false. |
PR-2 (bt-rem-from-av-rem review): set by |
|
| integer, | public | :: | ng | = | 0 |
Ghost width (interior starts at ng+1 in x and y). |
|
| integer, | public | :: | nk | = | 0 |
Third-dim extent for rank-3 fields (no vertical ghosts — layers/interfaces are all owned). |
|
| integer, | public | :: | nx_phys | = | 0 |
Owned-cell extents in x, y. |
|
| integer, | public | :: | ny_phys | = | 0 |
Owned-cell extents in x, y. |
|
| logical, | public | :: | optional | = | .false. |
.true. => read path warns-and-seeds if absent. Default .false. => a missing field is FATAL. |
|
| real(kind=wp), | public, | pointer | :: | p0 | => | null() |
Host scalar (rank-0 persistent state, e.g. Chapman eta_old). |
| real(kind=wp), | public, | pointer | :: | p2(:,:) | => | null() |
Host array for rank-2 fields (incl. ghosts). |
| real(kind=wp), | public, | pointer | :: | p3(:,:,:) | => | null() |
Host array for rank-3 fields (incl. ghosts). |
| integer, | public | :: | rank | = | 0 |
0 (host scalar), 2, or 3. |
|
| character(len=64), | public | :: | tag | = | "" |
NetCDF variable name (unique within the file). |
Append-only registry of checkpoint fields. Slots call
register_2d / register_3d from their (or the state’s) init.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| type(restart_entry_t), | public | :: | entries(MAX_RESTART_ENTRIES) | ||||
| integer, | public | :: | n | = | 0 |
| procedure, public, non_overridable :: clear => registry_clear | |
| procedure, public, non_overridable :: entry_found => registry_entry_found | |
| procedure, public, non_overridable :: register_2d => registry_register_2d | |
| procedure, public, non_overridable :: register_3d => registry_register_3d | |
| procedure, public, non_overridable :: register_scalar => registry_register_scalar |
Was tag actually present in the file the last time this
registry was passed to ocean_restart_read_local? .false.
before any read, and .false. for an unknown tag (a caller
typo is a silent cold-seed, not a crash — callers that care
should assert the tag exists via a successful register_*
first). See restart_entry_t%found’s docstring for why this is
not the same question as optional.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(restart_registry_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | tag |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_restart_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_restart_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(restart_registry_t), | intent(inout) | :: | this |
Register a rank-2 owned field. arr is the full (ghosted)
host array; the owned slice is (ng+1:ng+nx_phys, ng+1:ng+ny_phys).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(restart_registry_t), | intent(inout) | :: | this | |||
| character(len=*), | intent(in) | :: | tag | |||
| real(kind=wp), | intent(in), | target | :: | arr(:,:) | ||
| integer, | intent(in) | :: | ng | |||
| integer, | intent(in) | :: | nx_phys | |||
| integer, | intent(in) | :: | ny_phys | |||
| logical, | intent(in), | optional | :: | optional | ||
| logical, | intent(in), | optional | :: | device_mapped |
Register a rank-3 owned field. The vertical extent is taken
from size(arr,3) (layers or interfaces — both fully owned).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(restart_registry_t), | intent(inout) | :: | this | |||
| character(len=*), | intent(in) | :: | tag | |||
| real(kind=wp), | intent(in), | target | :: | arr(:,:,:) | ||
| integer, | intent(in) | :: | ng | |||
| integer, | intent(in) | :: | nx_phys | |||
| integer, | intent(in) | :: | ny_phys | |||
| logical, | intent(in), | optional | :: | optional | ||
| logical, | intent(in), | optional | :: | device_mapped |
Register a host scalar (rank-0 persistent state, e.g. the Chapman
eta_old_chapman_* corner values). Host-only — never device-
mapped, so the write path skips update self for it.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(restart_registry_t), | intent(inout) | :: | this | |||
| character(len=*), | intent(in) | :: | tag | |||
| real(kind=wp), | intent(in), | target | :: | scal | ||
| logical, | intent(in), | optional | :: | optional |