OpenPFC / Build a simulation

A simulation owns its session

Use C++ member declaration order to release private communication before MPI finalizes.

Download runnable lesson (.zip)

Includes the model, inputs, required headers and a short run guide.

A simulation object can own MPI for a standalone executable. Its members must express the dependency order. This refactors the class-owned MPI example into a session whose teardown is also observable.

Declare owners before their dependants

struct Simulation {
  pfc::mpi::environment environment;
  pfc::mpi::communicator world;
  pfc::mpi::communicator private_comm = world.duplicate();
};

C++ constructs members in declaration order and destroys them in reverse order. The environment is created first, the WORLD wrapper second, and the duplicated communicator last. Teardown frees the private communicator before the environment finalizes MPI. Reordering an initializer list would not change this rule; declaration order is what matters.

{
  Simulation simulation;
  rank = simulation.private_comm.rank();
  ranks = simulation.private_comm.size();
  MPI_Allreduce(&rank, &sum, 1, MPI_INT, MPI_SUM, simulation.private_comm);
} // private_comm and world are destroyed before environment finalizes MPI.
int finalized = 0;
MPI_Finalized(&finalized); // This query is permitted after MPI_Finalize.
if (rank == 0)
  std::cout << "ranks,rank_sum,finalized\n"
            << ranks << ',' << sum << ',' << finalized << '\n';

MPI_Finalized is one of the MPI queries permitted after finalization. We use it to verify the end of the session, not to perform more communication. The rank and rank sum were captured while the communicator was valid.

Make ownership visible at the call site

openpfc run model.cpp input.json --launcher=none
openpfc run model.cpp input.json --ranks=2 --launcher=mpirun

The empty input is unused. The root CSV includes finalized=1 after the session block ends, alongside the independently checkable rank sum. Tests exercise both one and two ranks. This small object is a standalone runtime owner; an application embedding OpenPFC after its own MPI_Init must borrow the host runtime instead. See the communicator implementation for owned duplicate cleanup and the host orchestrator for the corresponding field exchange seam.

The complete C++ model and starting input are available with this lesson.