Simulation contracts¶
These interfaces define the reusable simulation lifecycle. Read the architecture, application pipeline, and 0.1 → 0.2 migration for ownership and control flow before using the exact declarations.
pfc::sim::SimulationDriver¶
Thin time loop over Time plus a physics step. See
include/openpfc/kernel/simulation/simulation_driver.hpp (pfc::sim::run).
pfc::Time¶
-
class Time
Public Functions
-
inline Time(const std::array<double, 3> &time)
Construct a new Time object with the specified time interval and default save interval.
The save interval is set to the same value as the time step.
- Parameters:
time – An array containing the start time, end time, and time step in that order
-
inline Time(const std::array<double, 3> &time, double saveat)
Construct a new Time object with the specified time interval and default save interval (euler integrator).
The save interval is set to the same value as the time step.
- Parameters:
time – An array containing the start time, end time, and time step in that order
-
inline double get_t0() const
Get the start time.
- Returns:
The start time
-
inline double get_t1() const
Get the end time.
- Returns:
The end time
-
inline double get_dt() const
Get the time step.
- Returns:
The time step
-
inline void set_dt(double dt)
Set the time step.
Sets a new time step size for adaptive time-stepping. The time step must be positive to ensure meaningful time integration.
This method enables runtime adjustment of the recommended / policy
dt. It does not change accepted simulation time; adaptive drivers must advance time via commit_attempt (or fixed-step next).See also
get_dt() - query the current time step
See also
set_increment() - fixed-dt reconstruction helper (not adaptive commit)
Note
Changing dt does not rewrite past accepted time. Use begin_attempt / commit_attempt for clipped attempts.
- Parameters:
dt – [in] The new time step size (must be > 0)
- Throws:
std::invalid_argument – If dt <= 0
- Post:
get_dt() returns the new dt value
- Post:
get_accepted_time() is unchanged
-
inline int get_increment() const
Get the current time increment.
- Returns:
Current increment
-
inline int get_step_count() const
Get the number of steps completed so far.
Self-documenting alternative to get_increment() for integrator authors querying progress (checkpoint/restart, adaptive step rejection logic, output scheduling): “increment” reads as a small delta, not a count, whereas “step count” states the intent directly. Returns the same underlying value as get_increment().
- Returns:
Current step count
-
inline void increment_step_success()
Record a successful adaptive step attempt.
Increments the accepted-attempt counter. Unlike next(), this does not advance simulation time; it only updates attempt statistics for adaptive time-stepping algorithms.
Note
When used with TimeStateGuard, call this before
commit()(or outside an uncommitted guard); otherwise the guard restores the counter on destruction.- Post:
get_accepted_steps() returns previous value + 1
-
inline void increment_step_rejection()
Record a rejected adaptive step attempt.
Increments the rejected-attempt counter. Unlike next(), this does not advance simulation time; it only updates attempt statistics.
Note
TimeStateGuard also restores accepted/rejected counters unless
commit()was called. Call this after an uncommitted guard’s scope ends (or outside the guard) so the rejection count is kept.- Post:
get_rejected_steps() returns previous value + 1
-
inline int get_accepted_steps() const
Get the number of accepted adaptive step attempts.
- Returns:
Accepted step count (starts at 0)
-
inline int get_rejected_steps() const
Get the number of rejected adaptive step attempts.
- Returns:
Rejected step count (starts at 0)
-
inline int get_stage() const
Get the current stage index within this time step.
Stage tracking is independent of get_increment(): stages range from
0toget_stage_count() - 1and describe progress within a single multi-stage step (e.g. RK2/RK4), while the increment counts completed time steps.- Returns:
Current stage index (0-based)
-
inline void set_stage(int stage)
Set the current stage index within this time step.
Multi-stage steppers should call this before computing each stage.
- Parameters:
stage – [in] New stage index (must satisfy
0 <= stage < get_stage_count())- Throws:
std::invalid_argument – If stage is out of range
-
inline int get_stage_count() const
Get the total number of stages in the current time step.
- Returns:
Stage count (always >= 1)
-
inline void set_stage_count(int stage_count)
Set the total number of stages for this time step.
Multi-stage steppers should call this once during initialization to configure how many stages each step has (e.g. 2 for RK2, 4 for RK4).
- Parameters:
stage_count – [in] New stage count (must be >= 1)
- Throws:
std::invalid_argument – If stage_count < 1
-
inline double get_accepted_time() const noexcept
Get the accepted simulation time (read-only)
Returns the stored accepted clock. Unchanged for the duration of an active attempt (
begin_attempt…commit_attempt/reject_attempt).- Returns:
Accepted simulation time
-
inline double get_current() const
Get the current (accepted) simulation time.
Returns the stored accepted time (
m_accepted_time), clamped tot1if needed. Prefer get_accepted_time for adaptive drivers; this alias preserves the historical name used by Simulator and writers.See also
get_accepted_time() - explicit accepted-time accessor
See also
get_increment() - get the number of steps taken
See also
next() - advance to next time step (fixed dt)
See also
done() - check if t_current >= t1
Note
Accepted time advances on next or commit_attempt only. set_dt does not rewrite this value.
- Returns:
Current simulation time, clamped to [t0, t1]
-
inline double get_saveat() const
Get the time interval for saving data.
- Returns:
The save interval
-
inline sim::steppers::RKIntegratorMethod method() const noexcept
Get the integrator method.
- Returns:
The integrator method
-
inline void set_method(sim::steppers::RKIntegratorMethod method) noexcept
Set the integrator method (JSON
simulator.integrator.method).
-
inline void set_increment(int increment)
Set the current time increment (fixed-
dtreconstruction helper).Sets the committed step count and reconstructs accepted time as
min(t0 + increment * dt, t1). This preserves restart / rewind helpers that assume a constantdt. It is not the adaptive commit path — use commit_attempt after a clipped attempt instead.The increment must be non-negative so that
get_current()stays in[t0, t1]anddone()/do_save()remain meaningful.- Parameters:
increment – The current time increment (number of completed steps from
t0, i.e. same convention as afternext()).- Throws:
std::invalid_argument – if
increment < 0- Post:
get_increment() == increment
- Post:
get_accepted_time() == min(t0 + increment * dt, t1)
-
inline void set_saveat(double saveat)
Set the time interval for saving data.
- Parameters:
saveat – The save interval
-
inline void next()
Advance to the next time step (fixed
dt)Advances accepted time by
dt(clamped tot1) and increments the step counter by 1. For adaptive clipped intervals, use begin_attempt / commit_attempt instead.See also
get_increment() - query current step number
See also
get_current() - accepted simulation time
See also
done() - check completion status
See also
commit_attempt() - advance by a clipped attempted interval
- Post:
get_increment() returns previous value + 1
- Post:
get_accepted_time() returns previous value + dt (clamped to t1)
-
inline double clip_attempt_dt(double candidate_dt) const
Clip a candidate step interval against terminal and output bounds.
Returns an attempted
dtsuch thataccepted_time + attempted_dtdoes not exceedt1. Whensaveat > 0, also does not pass the next output-alignment time without landing on it.Alignment uses the same tolerance family as do_save (
1e-9):next_save = ceil((accepted + 1e-9) / saveat) * saveat. When both the terminal bound and saveat constrain the step, the most restrictive (minimum) wins. Ifnext_save >= t1, only the terminal bound matters. Whensaveat <= 0, output-alignment clipping is skipped.- Parameters:
candidate_dt – [in] Proposed step size (must be > 0)
- Throws:
std::invalid_argument – If candidate_dt <= 0
- Returns:
Clipped attempted interval
-
inline bool attempt_active() const noexcept
Whether an attempt transaction is currently open.
-
inline double get_attempted_dt() const
Clipped interval for the active attempt.
- Throws:
std::logic_error – If no attempt is active
-
inline void begin_attempt(double candidate_dt)
Begin an attempt: clip candidate_dt and leave accepted time unchanged.
- Parameters:
candidate_dt – [in] Proposed step size (must be > 0)
- Throws:
std::logic_error – If an attempt is already active
std::invalid_argument – If candidate_dt <= 0 (via clip_attempt_dt)
- Post:
attempt_active() is true
- Post:
get_accepted_time() is unchanged
- Post:
get_attempted_dt() == clip_attempt_dt(candidate_dt)
-
inline void commit_attempt()
Commit the active attempt: advance accepted time by attempted_dt.
Does not auto-call increment_step_success (counters stay caller-owned).
- Throws:
std::logic_error – If no attempt is active
- Post:
get_accepted_time() advanced by the attempted interval (clamped to t1)
- Post:
get_increment() increased by 1
- Post:
attempt_active() is false
-
inline void reject_attempt()
Reject the active attempt without changing accepted time.
Does not auto-call increment_step_rejection (counters stay caller-owned).
- Throws:
std::logic_error – If no attempt is active
- Post:
get_accepted_time() and get_increment() unchanged
- Post:
attempt_active() is false
-
inline operator double() const
Conversion operator to retrieve the current time as a double value.
- Returns:
The current time as a double value
Friends
-
inline friend std::ostream &operator<<(std::ostream &os, const Time &t)
Overloaded stream insertion operator to print the Time object.
- Parameters:
os – The output stream
t – The Time object to be printed
- Returns:
The output stream
-
inline Time(const std::array<double, 3> &time)
pfc::FieldModifier¶
-
class FieldModifier
Subclassed by pfc::Constant, pfc::FileReader, pfc::RandomSeeds, pfc::SeedGrid, pfc::SingleSeed
Public Functions
-
inline void set_field_names(std::vector<std::string> names)
Declare every field this modifier may write (for Simulator checks).
-
inline const std::string &get_field_name() const
Get the name of the field this modifier operates on.
Returns the (first) field name set via
set_field_name(); sessions use it to look up the target field onSimulationStatebefore callingapply().See also
set_field_name()
Note
Returns “default” if field name was never explicitly set
-
inline virtual void set_mpi_comm(MPI_Comm)
Optional MPI communicator for modifiers that use collectives (MPI-IO, reductions). Default is a no-op; FileReader and app-local front-tracking BCs override.
-
inline virtual nlohmann::json checkpoint_state() const
Restart state for this modifier, or null for a stateless modifier.
Sessions capture this on every rank when publishing a checkpoint. Stateful implementations must return the same state on each rank and override restore_checkpoint_state, rejecting null when their state is required.
-
inline virtual void restore_checkpoint_state(const nlohmann::json &state)
Restore saved state; the default accepts only a stateless record.
-
inline virtual void apply(const SimulationContext &simulation_context, pfc::field::FieldOutput<double> field, const Domain &domain, const Box3i &box, double time)
Apply the field modification with explicit simulation context.
The default implementation ignores the context and calls
apply(field, domain, box, time). Override this when the modifier needssimulation_context.mpi_comm().
-
virtual void apply(pfc::field::FieldOutput<double> field, const Domain &domain, const Box3i &box, double time = 0.0) = 0
Apply the modification to a host field over
box(pure virtual)- Parameters:
field – Mutable host view with
boxvoxel count (x-fastest, halo 0). Astd::vector<double>lvalue orField::output()converts implicitly; for a deviceFielduseapply_field_modifier(kernel/simulation/apply_field_modifier.hpp), which brackets the host mirror.domain – Global geometry (origin, spacing, size)
box – Local owned index box
time – Current simulation time (typically 0 for ICs)
-
virtual ~FieldModifier() = default
Destructor for the FieldModifier class.
The destructor is declared as default, allowing proper destruction of derived classes.
-
inline void set_field_names(std::vector<std::string> names)
pfc::ResultsWriter¶
-
class ResultsWriter
Subclassed by pfc::FileResultsWriter
Public Functions
-
ResultsWriter() = default
Default-construct a writer.
File sinks take a path pattern on
FileResultsWriter/BinaryWriter/VTKWriter. Non-file sinks need no dummy filename.
-
virtual ~ResultsWriter() noexcept(false) = default
Virtual destructor.
Marked noexcept(false) to allow subclasses to throw on MPI cleanup failures (fail-closed policy).
-
inline virtual void set_geometry(const std::array<double, 3> &origin, const std::array<double, 3> &spacing)
Configure the physical geometry of the grid.
set_domain()says how many cells there are and who owns which; this says how big a cell is and where the grid starts. Formats that record physical coordinates need both — a.vtiwhoseSpacingdoes not match the run’sdxrenders at the wrong scale in ParaView, and every length measured off it is wrong by the same factor.The default is a deliberate no-op so that index-only sinks (
BinaryWriter, in-memory writers) need not implement it. Override it wherever the file format has a place to put the numbers.See also
pfc::apply_writer_domain - forwards
Domaingeometry to both calls- Parameters:
origin – [in] Physical coordinate of global grid index (0, 0, 0)
spacing – [in] Physical cell size along each axis
-
virtual MPI_Status write(int increment, pfc::field::FieldView<std::complex<double>> data) = 0
Write a complex-valued field to file at specified time step.
Writes the local portion of a complex field view (complex doubles) to the output file. Useful for storing Fourier coefficients or k-space data.
See also
FFT::forward() - produces the complex hat from the real field
Note
Complex field size is typically ~50% of real field size (r2c symmetry).
- Parameters:
increment – [in] Time step or frame number
data – [in] Local complex field data
- Returns:
MPI_Status Information about the write operation
-
ResultsWriter() = default