Shared prefix of rdb_ocean_create_from_string AND
rdb_ocean_create_pending (P2.5): allocate a handle, parse +
validate the config, check dt_fixed. Does NOT touch
g_handle_live — callers set it themselves once they know which
of the two flows they are in (immediate complete_ocean_create vs
staying pending for geometry injection).
On success: c_handle/h are a valid, freshly allocated handle
(h%is_pending/h%is_initialised both still .false.).
On failure: the handle has ALREADY been destroyed (matches the F9
“destroy the whole handle on any failure” contract) — c_handle
is left as returned by handle_create/handle_destroy (the
caller must not touch it further) and h is null.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | text | |||
| type(c_ptr), | intent(out) | :: | c_handle | |||
| type(ocean_handle_t), | intent(out), | pointer | :: | h | ||
| integer(kind=c_int), | intent(out) | :: | status |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | private | :: | hstat | ||||
| integer, | private | :: | ierr |
subroutine build_pending_handle(text, c_handle, h, status) !! Shared prefix of `rdb_ocean_create_from_string` AND !! `rdb_ocean_create_pending` (P2.5): allocate a handle, parse + !! validate the config, check `dt_fixed`. Does NOT touch !! `g_handle_live` — callers set it themselves once they know which !! of the two flows they are in (immediate `complete_ocean_create` vs !! staying pending for geometry injection). !! !! On success: `c_handle`/`h` are a valid, freshly allocated handle !! (`h%is_pending`/`h%is_initialised` both still `.false.`). !! On failure: the handle has ALREADY been destroyed (matches the F9 !! "destroy the whole handle on any failure" contract) — `c_handle` !! is left as returned by `handle_create`/`handle_destroy` (the !! caller must not touch it further) and `h` is null. character(len=*), intent(in) :: text type(c_ptr), intent(out) :: c_handle type(ocean_handle_t), pointer, intent(out) :: h integer(c_int), intent(out) :: status integer :: ierr, hstat ! Bring the comm env up before ANYTHING touches it. The API is ! single-rank by design (compute_rank=0, compute_size=1), but ! "single-rank" is not "no MPI": in an RDB_ENABLE_MPI=ON build the ! setup path reaches `comm_world()`, and reaching it before MPI is ! initialised is not a soft failure -- ! ! *** The MPI_Comm_f2c() function was called before MPI_INIT was ! *** invoked. This is disallowed by the MPI standard. ! ! -- the process aborts. Both create entry points funnel through ! here, so this is the one place that needs it, and `comm_env_init` ! is idempotent, so paying it per create costs nothing. ! ! NOT a fix for being embedded in a host that has ALREADY called ! MPI_Init (mpi4py, a C driver of its own). `pic_mpi_init` calls ! `MPI_Init_thread` unconditionally and pic-mpi exposes no ! `MPI_Initialized` wrapper to guard on, so that case would still ! double-initialise. Closing it needs an upstream wrapper: the ! `no-mpi-in-rdb` rule means this file cannot ask MPI directly. call comm_env_init() c_handle = handle_create() hstat = handle_check(c_handle, h) if (hstat /= HANDLE_OK) then ! Unreachable in practice (we just created it), but never trust a ! resolve blindly — fail loud rather than dereference a null h. call handle_destroy(c_handle) h => null() status = int(OCEAN_STATUS_ERR_BAD_HANDLE, c_int) return end if call read_config_from_string(text, h%cfg, ierr=ierr) if (ierr /= OCEAN_STATUS_OK) then status = int(ierr, c_int) call handle_destroy(c_handle) h => null() return end if call validate_config(h%cfg, ierr=ierr) if (ierr /= OCEAN_STATUS_OK) then status = int(ierr, c_int) call handle_destroy(c_handle) h => null() return end if if (h%cfg%dt_fixed <= 0.0_wp) then status = int(OCEAN_STATUS_ERR_CONFIG_VALIDATE, c_int) call handle_destroy(c_handle) h => null() return end if status = int(OCEAN_STATUS_OK, c_int) end subroutine build_pending_handle