One ocean simulation: config + the P2.4 setup/step/teardown
engine (god-state + grid + geothermal + bc_source + decomp +
sea-ice params — rdb_ocean_engine). state/grid below are
thin views onto engine%state/engine%grid kept ONLY so the
P2 accessor bodies (h%state%..., h%grid%...) did not all
need touching when this handle was rewired onto the shared
engine (P2.4): state is POINTER-associated to engine%state
right after engine_enter_data (stable for the handle’s
lifetime — engine is a component of this heap-allocated,
pointer-accessed handle, never copied/reallocated); grid is a
ONE-TIME VALUE COPY of engine%grid right after engine_setup
(safe because grid metadata is read-only after setup — no
kernel ever mutates nx_phys/dx/… mid-run). The standalone
sf slot from P1 is gone: engine_setup now fully configures
engine%state%surface_flux from the namelist (P2.4 closes the
“API surface flux is a separate, minimally-seeded object” gap),
so rdb_ocean_get/set_*_flux read/write h%state%surface_flux
directly, same as the driver.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| type(config_t), | public | :: | cfg | ||||
| logical, | public | :: | device_mapped | = | .false. |
True between |
|
| type(ocean_engine_t), | public | :: | engine |
Not declared |
|||
| type(hgrid_t), | public | :: | grid | ||||
| logical, | public | :: | host_is_current | = | .true. |
P2 lazy-sync gate ( |
|
| logical, | public | :: | is_initialised | = | .false. |
True once create() has completed device mapping. |
|
| logical, | public | :: | is_pending | = | .false. |
P2.5: true from |
|
| integer, | public | :: | magic | = | 0 | ||
| integer, | public | :: | n_inner | = | 0 |
Barotropic fast-loop substep count, mirrored from
|
|
| type(ocean_state_t), | public, | pointer | :: | state | => | null() | |
| real(kind=wp), | public | :: | t_current | = | 0.0_wp |
type :: ocean_handle_t !! One ocean simulation: config + the P2.4 setup/step/teardown !! engine (god-state + grid + geothermal + bc_source + decomp + !! sea-ice params — `rdb_ocean_engine`). `state`/`grid` below are !! thin views onto `engine%state`/`engine%grid` kept ONLY so the !! P2 accessor bodies (`h%state%...`, `h%grid%...`) did not all !! need touching when this handle was rewired onto the shared !! engine (P2.4): `state` is POINTER-associated to `engine%state` !! right after `engine_enter_data` (stable for the handle's !! lifetime — `engine` is a component of this heap-allocated, !! pointer-accessed handle, never copied/reallocated); `grid` is a !! ONE-TIME VALUE COPY of `engine%grid` right after `engine_setup` !! (safe because grid metadata is read-only after setup — no !! kernel ever mutates `nx_phys`/`dx`/... mid-run). The standalone !! `sf` slot from P1 is gone: `engine_setup` now fully configures !! `engine%state%surface_flux` from the namelist (P2.4 closes the !! "API surface flux is a separate, minimally-seeded object" gap), !! so `rdb_ocean_get/set_*_flux` read/write `h%state%surface_flux` !! directly, same as the driver. integer :: magic = 0 type(config_t) :: cfg type(ocean_engine_t) :: engine !! Not declared `target` — component attributes may not include !! `target` (illegal syntax) — but this needs none: `h` is only !! ever reached through a `pointer` variable (`c_f_pointer` off !! the opaque `c_ptr` handle), and a pointer's entire pointee, !! subobjects included, is a valid pointer-association target !! for as long as that association persists. `h%state => !! h%engine%state` below relies on exactly this. type(hgrid_t) :: grid type(ocean_state_t), pointer :: state => null() integer :: n_inner = 0 !! Barotropic fast-loop substep count, mirrored from !! `engine%n_inner` once at create — kept as a separate field !! only because the C ABI's OWN stricter "n_inner must resolve !! to >= 1" contract (P1) is enforced here, not inside the !! shared `engine_setup` (which tolerates n_inner < 1 the same !! way `driver_run_ocean` does — see `ocean_engine_t`'s !! docstring). real(wp) :: t_current = 0.0_wp logical :: is_initialised = .false. !! True once create() has completed device mapping. `step`/ !! `get_*` reject a handle that never got this far. logical :: is_pending = .false. !! P2.5: true from `rdb_ocean_create_pending` until !! `rdb_ocean_create_finalize` completes (success or failure — !! failure destroys the whole handle, per the F9 rollback !! contract). The `rdb_ocean_stage_*` geometry-injection calls !! require this; `is_initialised` (above) still gates every !! step/getter/setter exactly as before P2.5. logical :: device_mapped = .false. !! True between `ocean_state_enter_data` and `ocean_state_exit_data` !! — tells `destroy` whether an exit_data pairing is owed. logical :: host_is_current = .true. !! P2 lazy-sync gate (`docs/ocean_python_api_plan.md`, !! `06_python_surface_design.md` D3.4): true iff the HOST copies of !! the API-exposed prognostic/forcing arrays match the (possibly !! more advanced) device copies. `rdb_ocean_step` clears it !! unconditionally — it does not sync; `rdb_ocean_refresh_host` !! is the only thing that sets it back to true (via a leaf-array !! `!$acc update self`, never the aggregate `state`). Starts `true`: !! right after `create()` the host seed IS what `enter_data` copied !! to the device, so the two agree with nothing to refresh yet. end type ocean_handle_t