# C API Baseline

Last updated: 2026-05-26

This note records the first MuPRO C ABI skeleton, runtime bridge, runtime context skeleton, and optional FFT runtime capability. The goal is to establish a stable external boundary before Python, CLI/TUI, or solver-specific host integrations depend on internal Fortran modules.

## Scope

Implemented in PR-014:

- Public header: `library/c_api/include/mupro.h`
- Opaque runtime handle: `mupro_runtime_t`
- Opaque TDGL handle: `mupro_tdgl_t`
- Status object: `mupro_status_t`
- Runtime create/destroy and size setup
- TDGL create/destroy and parameter setup
- TDGL solve function signature with pointer, shape, and stride metadata
- C unit smoke test: `unit.c_api`
- C example: `examples/c_api_tdgl`

Implemented in PR-015:

- Runtime size setup now calls a Fortran `bind(C)` bridge into `mod_base_size::size_setup`.
- Runtime size query reads back the Fortran global size state and checks it against the C handle cache.
- Runtime MPI setup is explicit through `mupro_runtime_initialize_mpi`.
- Runtime MPI query returns the rank/process discovered by Fortran `mod_base_mpi::mpi_setup`.
- Runtime destroy finalizes MPI only when MuPRO initialized MPI itself; it does not call the legacy `mpi_exit` path because that routine stops the process.

Implemented in PR-016:

- Added `library/runtime` with `mod_mupro_runtime` and `mod_mupro_backend`.
- Added `type_mupro_RuntimeContext`, including size, MPI, FFT placeholder, backend, setup state, and legacy-global-state flag.
- Added `type_mupro_BackendContext` with backend kind, device ID, and device-memory ownership flag.
- Added `mupro_runtime_setup`, `mupro_runtime_finalize`, size setup/query, and MPI initialize/finalize routines.
- Moved the C ABI Fortran bridge to call `mod_mupro_runtime` instead of directly owning `mod_base_size` and `mod_base_mpi` interactions.
- Added `unit.runtime` for runtime context size/MPI lifecycle coverage.

Implemented in PR-017:

- FFT is now an optional runtime capability under `mod_mupro_runtime`.
- `runtime_setup` still does not initialize FFT by default.
- Added `mupro_runtime_setup_fft`, `mupro_runtime_ensure_fft`, and `mupro_runtime_finalize_fft`.
- Added minimal `mupro_fft_finalize` in `L0_FFT` to release the current legacy FFT plans, work buffers, and k-vector allocation.
- `unit.runtime` now verifies that FFT is not set up implicitly, can be explicitly set up, can be finalized, and can be set up again.
- Adjusted the `L0_FFT` include path to avoid stale per-module `.mod` files shadowing the main build module output.

Implemented in PR-021:

- Added an optional TDGL C adapter compiled when `MUPRO_BUILD_TDGL=ON`.
- `mupro_tdgl_setup` requests runtime-owned FFT setup before calling the Fortran TDGL setup path.
- `mupro_tdgl_solve` calls the existing Fortran `type_TDGLContext%solve` path when the adapter is enabled.
- The default build without `MUPRO_BUILD_TDGL` keeps the stable C skeleton and returns `MUPRO_STATUS_NOT_SETUP` for solve.
- Added `integration.c_api_tdgl_adapter` for TDGL-enabled builds.

Non-goal for PR-014:

- No numerical TDGL solve through C yet.
- No direct C ABI ownership of FFT plans yet.
- No Python binding or GPU backend.

Non-goal for PR-015:

- No multiple-runtime support. The current bridge still writes the legacy Fortran global size/MPI state, so the process should be treated as having one active MuPRO runtime.
- No FFT plan lifecycle ownership yet.

Non-goal for PR-016:

- No TDGL numerical backend connection through C yet.
- No GPU/backend dispatch implementation beyond backend metadata.

Non-goal for PR-017:

- No C ABI FFT API surface yet; FFT remains an internal runtime capability for solver adapters.
- No FFT algorithm change.
- No GPU FFT backend implementation.

Non-goal for PR-021:

- No TDGL numerical algorithm change.
- No public C FFT API. FFT remains solver-requested through the runtime bridge.
- No benchmark-backed production correctness claim for TDGL yet.

## Status Contract

Every C ABI function returns `mupro_status_t`.

The C status codes mirror the current Fortran status foundation:

| Code | Meaning |
| --- | --- |
| `MUPRO_STATUS_SUCCESS` | Call succeeded. |
| `MUPRO_STATUS_NULL_POINTER` | Required pointer argument was null. |
| `MUPRO_STATUS_INVALID_SHAPE` | Shape or stride metadata is invalid. |
| `MUPRO_STATUS_NOT_SETUP` | Required runtime or solver setup is missing, or the backend is not wired yet. |
| `MUPRO_STATUS_LICENSE` | Reserved for license validation failures. |
| `MUPRO_STATUS_NUMERICAL` | Numerical or allocation failure. |
| `MUPRO_STATUS_INVALID_ARGUMENT` | Non-shape argument is invalid. |

## Array Layout

TDGL field arrays use the solver's Fortran column-major layout.

Logical shape order:

```text
[component, z, y, x]
```

This matches the current Fortran TDGL fields:

```fortran
real(kind=rdp) :: field(3, Rn3, Rn2, Rn1)
```

Strides are measured in `double` elements, not bytes. A contiguous Fortran field has:

```text
shape  = [3, Rn3, Rn2, Rn1]
stride = [1, 3, 3*Rn3, 3*Rn3*Rn2]
```

The C `mupro_tdgl_solve` implementation validates pointer, shape, and stride metadata. Without `MUPRO_BUILD_TDGL`, it returns `MUPRO_STATUS_NOT_SETUP` because the TDGL backend is not present in that build. With `MUPRO_BUILD_TDGL=ON`, it currently requires contiguous Fortran-column-major strides and routes the call through the Fortran TDGL adapter.

## Runtime Bridge

`mupro_runtime_set_size` is now the C-side owner for writing the legacy Fortran global size state. It stores the requested size in the C handle and calls a narrow Fortran bridge into `mod_mupro_runtime`. The runtime module then calls `size_setup`. `mupro_runtime_get_size` reads the Fortran global state back through `mod_mupro_runtime` and fails with `MUPRO_STATUS_NUMERICAL` if the Fortran state no longer matches the C handle.

`mupro_runtime_initialize_mpi` is the C-side owner for initializing MPI through `mod_mupro_runtime`, which delegates to the existing `mpi_setup` implementation. The runtime records whether MPI was already initialized before MuPRO entered; `mupro_runtime_destroy` only finalizes MPI when MuPRO owns that initialization.

The current runtime context remains a compatibility adapter over legacy globals. It is suitable for one active runtime per process, not for independent concurrent runtime handles.

## Optional FFT Capability

FFT belongs to runtime ownership, but it is not a mandatory runtime resource. `mupro_runtime_setup` leaves `context%fft%is_setup` false. Spectral solvers should call `mupro_runtime_setup_fft` or `mupro_runtime_ensure_fft` when they need FFT plans; finite-difference, finite-volume, and finite-element paths should not pay this setup cost or inherit an FFT dependency unless they request it.

The current implementation wraps the existing `L0_FFT` global FFT state. `mupro_runtime_finalize_fft` releases the legacy FFT plans and arrays before MPI finalization. This is still single-runtime compatible, not independent multi-runtime FFT ownership.

TDGL C adapter setup calls the runtime FFT setup bridge lazily. This keeps finite-difference, finite-volume, finite-element, and non-spectral users from paying FFT setup cost unless a solver asks for it.

## TDGL Adapter

The TDGL adapter is build-time optional:

- Default L0/L1 development build: C TDGL setup stores validated parameters, and solve returns `MUPRO_STATUS_NOT_SETUP`.
- `MUPRO_BUILD_TDGL=ON`: C TDGL setup requests runtime FFT and calls Fortran TDGL setup; C TDGL solve calls Fortran TDGL solve.

The adapter uses the current legacy global runtime/FFT state. It should be treated as one active MuPRO runtime and one active TDGL adapter context per process until solver internals stop depending on global state.

The C field arrays must use:

```text
shape  = [3, Rn3, Rn2, Rn1]
stride = [1, 3, 3*Rn3, 3*Rn3*Rn2]
```

`G_6x6` is interpreted as a C row-major 6x6 matrix and copied into the Fortran TDGL context before setup.

## Downstream Linking

The public ABI in `mupro.h` is C-compatible, but the current static SDK archive contains Fortran and C++ objects. Downstream CMake consumers should enable `C`, `CXX`, and `Fortran` languages before `find_package(mupro)`. The installed package now fails early if CXX is not enabled, because otherwise C-only examples can reach link time without the C++ runtime required by the expression-evaluation object code.

## Next ABI Work

The next ABI/runtime slice should add concrete MPI smoke and benchmark-linked TDGL/FFT validation before expanding broader solver coverage.
