External coupling¶
OpenPFC can run as a library inside a host solver (FEM, another PDE code, or a custom orchestrator). The stable surface is small on purpose.
Runnable sketch: examples/coupling/host-orchestrator/model.cpp.
The accompanying Quarto lesson
explains the field lifetime, coordinate offset and complete accepted-state check.
What is stable¶
Piece |
Header |
Role |
|---|---|---|
|
|
Non-owning host export: name, |
|
same |
Build a handle from |
|
|
Free-function time loop the host can own or call step-by-step |
|
|
dt negotiation: the host proposes a step; OpenPFC clips to |
|
|
Opt-in |
|
|
Restart owned by OpenPFC; coordinate checkpoints with the host clock via |
FieldHandle is a read export. Write a source or boundary back through
SimulationState (typically get_field<double>(name).apply(...)) or a
FieldModifier-shaped adapter apply(SimulationState&, double) as in the
example header. Do not treat FieldView as mutable.
pfc::coupling::FieldHandle is not pfc::FieldHandle<T> (the typed index
inside SimulationState).
What is not stable¶
Device-resident fields (
CUDASpace/HIPSpace): this export is host-only.Gen-1
Model/Simulatoraccessors.Stage buffers, FFT plans, operator caches, stepper rollback scratch.
Halo cells: the handle’s
owned_boxis the owned index box; ghost values need a halo exchange after the host writes owned cells.Changing the MPI rank map or global grid between checkpoint and restore.
dt negotiation¶
The host owns the outer loop. Before an attempt:
const double t0 = time.get_accepted_time();
time.begin_attempt(std::min(host_proposed_dt, time.get_dt()));
const double dt = time.get_attempted_dt();
// ... host physics / OpenPFC step ...
source.apply(state, t0 + dt); // This sketch prescribes the new endpoint values.
time.commit_attempt();
time.increment_step_success();
begin_attempt uses clip_attempt_dt, which never lengthens the proposal.
The sketch imposes field values rather than adding a time-integrated source.
Choose the application time according to the actual coupling algorithm. Reject with
time.reject_attempt() if the host step fails.
Restart coordination¶
Use one OpenPFC bundle, not a second host-side dump of the same field:
{
"restart_from": "results/ckpt/step_10",
"checkpoint": { "every": 10, "directory": "results/ckpt" }
}
CheckpointService::restore_from_config loads fields, accepted time,
increment, result counter, and method identity. The host must restart at the
same accepted time; mismatch of grid or method is a hard error. See
checkpoint_publish.md.
Communicator isolation¶
If the host already uses MPI_COMM_WORLD for its own halo or reductions,
duplicate before constructing OpenPFC exchanges:
pfc::mpi::communicator world;
auto isolated = world.duplicate();
Pass MPI_Comm(isolated) into stacks, CheckpointService, and
BinaryReader. Destroying the wrapper frees the duplicate.