OpenPFC / From settings to a run

Turn a settings file into a runnable session

A configuration file should describe a computation, and the resulting computation should have an answer you can check.

Download runnable lesson (.zip)

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

The old json_read program printed raw JSON. This version uses the native settings loader and constructs a real spectral CPU session. JSON and TOML describe the same domain, method, backend and time interval. Physics remains a small visible callback.

Load the native schema

const auto settings = pfc::ui::load_settings_file(argv[1]);
const auto requested_domain = pfc::ui::from_json<pfc::Domain>(settings);
const auto requested_time = pfc::ui::from_json<pfc::Time>(settings);
const auto size = pfc::domain::get_size(requested_domain);
if (size[0] > 64 || size[1] > 64 || size[2] > 64 ||
    (requested_time.get_t1() - requested_time.get_t0()) /
            requested_time.get_dt() >
        10000)
  throw std::invalid_argument("Use at most 64 samples per axis and 10000 steps");
using Stack = pfc::sim::stacks::SpectralCPUStack;
auto session = pfc::ui::make_simulation_session<Stack>(settings, comm.rank(),
                                                       comm.size(), comm);
auto &u = session.stack().u();
auto &fft = session.stack().fft();
const auto &domain = u.domain();
const double origin = pfc::domain::get_origin(domain)[0];
const double length = size[0] * pfc::domain::get_spacing(domain)[0];
u.apply([&](double x, double, double) {
  return 1 + .2 * std::cos(2 * std::numbers::pi * (x - origin) / length);
});

load_settings_file recognizes .json and .toml and reports syntax errors. make_simulation_session interprets the domain and time schema and checks that method/backend selection matches the requested stack. Choosing "method":"fd" cannot silently turn a SpectralCPUStack into a finite-difference stack.

The domain uses size, lower and upper. On the periodic x axis, \(h_x=(\mathrm{upper}_x-\mathrm{lower}_x)/N_x\); the upper boundary is not a duplicated sample. The default interval \([-2,6)\) therefore has length 8.

Let the session own the clock and stack

For \(u_t=\Delta u\), start with \(u(x,0)=1+.2\cos[2\pi(x+2)/8]\) and apply backward Euler in Fourier space:

session.run([&](double, double interval) {
  fft.forward(u.vec(), modes);
  for (std::size_t i = 0; i < modes.size(); ++i)
    modes[i] /= 1 + interval * k2[i];
  fft.backward(modes, u.vec());
});

The callback receives the actual accepted interval. The default steps are .07, .07 and .06, ending at .2. A multiplier cached once for .07 would integrate the wrong final interval. The mean stays one and the mode’s amplitude is multiplied by \(\prod_m(1+k^2\Delta t_m)^{-1}\), where \(k=2\pi/8\).

Compare the two file formats

openpfc run model.cpp input.json --launcher=none

The companion TOML file describes the same computation. After building OpenPFC, run it from the repository root with:

./builds/release/examples/json_read examples/configuration/session/input.toml

Adjust builds/release to your build directory. The numerical regression launches the built executable with each file and checks identical complete fields and equivalent normalized settings. Source-based openpfc run currently validates JSON when snapshotting its input; TOML is supported by the library loader used by the compiled program.

Rank zero writes the parsed document to results/settings.json; every rank writes its owned values to results/field_rank_*.csv. There is no interactive stdin prompt. Change the bounds, timestep or backend token, then inspect either the result or the native validation message. This lesson demonstrates session construction, not every application-specific model.params schema.

Keep the source, JSON input and TOML input together. Continue with time-loop hooks or writing a result series.