OpenPFC / Learn by doing

A few lines. A mathematical idea. An answer you can check.

Start with a derivative. Follow it into a heat equation. Change the numerical method and discover what changes in the answer.

These lessons are invitations to experiment with C++ and OpenPFC. Each one develops a small mathematical argument, shows the actual implementation, and gives you a way to decide whether the answer is right. Read the source, change a parameter, and try to predict the result before running it.

Start with a question

New to OpenPFC? Follow your first six experiments: one guided route from a physical grid to three ways of solving the heat equation. Each chapter has a complete runnable download and a next step.

For a first C++ program, start by giving a grid physical meaning. Then let that grid carry a field and a numerical question.

Begin with How accurately can we differentiate?. Second-order differences, fourth-order differences, and Fourier derivatives act on the same smooth function. A refinement plot makes their different behaviour visible.

Then follow one heat equation through three implementations: neighbouring values, Fourier modes, and an exact exponential propagator. The geometry and initial condition stay fixed, so each change in the answer has a numerical explanation.

Title Description
Give the grid a physical meaning Create a domain, map an index to a coordinate and distinguish samples from box boundaries.
How accurately can we differentiate? Watch finite differences converge and a Fourier derivative reach roundoff.
Heat flow from neighbouring values Build a conservative stencil and make the forward-Euler stability limit visible.
A stencil has moments, units and a boundary contract Normalize a three-dimensional Sobel stencil and check what its smoothing does to an edge and a polynomial.
Same heat, different derivative Replace a stencil by Fourier differentiation and find the time error left behind.
Let each wave decay exactly Use the Fourier symbol to integrate linear heat flow with an exponential.
One box, many owners Divide a global domain among the actual MPI ranks and check that no cell is lost or counted twice.
A hot spot spreads without losing heat Initialize a periodic Gaussian with a callable, evolve it spectrally, and distinguish time error from an unresolved initial field.
From an index to a memory offset Understand inclusive boxes, local origins and x-fastest array storage before writing a stencil.
A field is more than a vector Give values physical coordinates and distinguish owned cells from halo-padded storage.
Write the physics in physical coordinates Initialise a decomposed field with a lambda and locate two values without guessing their owner.
Trust an FFT by checking its coefficients Compare every distributed Fourier coefficient against a global direct DFT, then reconstruct the original field.
Use polar coordinates to define a Cartesian field Initialize a smooth angular pattern with a small callable, handle the undefined angle at the origin, and preserve owned fields across MPI ranks.
Same transform, different ownership Use the public host FFT contract with either a local FFTW transform or distributed HeFFTe.
An FFT index is not a wave number Give a Fourier mode physical units, preserve its Nyquist convention, and build heat-flow multipliers.
One derivative, two algorithms A cotangent matrix and an FFT differentiate the same periodic interpolant, including its ringing near corners.
What smoothness buys you Four functions, four convergence stories: finite regularity, a smooth bump, analyticity and an exactly represented wave.
A curve hidden between the samples Reconstruct three discrete signals with cardinal sinc functions and distinguish interpolation from recovering an unknown original.
An FFT reads the memory you give it Keep a known field intact while moving between host vectors, device buffers and a distributed Fourier layout.
A wave changes speed Follow a Gaussian through a variable transport speed, compare leapfrog with exact characteristics, and expose the two-level startup choice.
Where a timestep is allowed to live Build Runge–Kutta stability functions from public tableaus and compare the multistep root-locus families from Trefethen’s program 25.
Where should the samples go? Compare equally spaced and Chebyshev nodes without changing the function or polynomial degree.
The geometry behind an interpolation error See how the product of distances to the nodes shapes polynomial approximation on the real and complex planes.
Differentiate a cosine-mapped grid Compare a Chebyshev collocation matrix with coefficients obtained through OpenPFC LocalFFT, including both endpoints.
Smoothness still controls convergence Repeat the four regularity experiments on a nonperiodic Chebyshev grid and see when a polynomial becomes exact.
An integral is a boundary-value problem Recover four definite integrals by solving for an antiderivative with OpenPFC GMRES.
A boundary condition changes an equation Solve three Chebyshev Poisson problems with OpenPFC GMRES: homogeneous, lifted and mixed boundary data.
Let the field determine its own forcing Solve a nonlinear boundary-value problem using OpenPFC Newton–GMRES, then check it with a scalar root and an analytic solution.
Does the endpoint occupy a sample? Before choosing a stencil or an FFT, decide what the endpoints mean. Five samples on a periodic interval and five samples including both endpoints are different grids, even when their physical bounds are identical.
What does a domain query measure? A helper name is not a substitute for its mathematical definition. OpenPFC can report a domain’s point count, dimensionality, bounds, extent and physical_volume. These quantities answer related but different questions.
Let the compiler read the geometry An origin and a spacing are both triples of real numbers, but swapping them changes the physics. OpenPFC gives them separate types so the function signature can express what each triple means.
Different spacings, one coordinate map A rectangular grid need not have cubic cells. With an origin \(o\) and spacing \(h\), the coordinate of index \((i,j,k)\) is
Let the error choose the next step Compare memoryless and PI control, keep rejected candidates separate, and land on a known change in decay rate.
Compare one step with two half steps Build adaptive exponential evolution from a linear Fourier symbol, a cubic reaction and explicit rollback.
A clock should advance only once Separate proposed steps, rejected attempts, accepted time and output boundaries.
Let another solver own the loop Export a borrowed host field, negotiate an interval and impose new data at the accepted endpoint.
Two fields, one Runge–Kutta stage Bind a runtime field group to RK4 and verify that both variables see the same intermediate state.
Two equations, two derivative requests, one sweep Compose two native gradient evaluators and check every coupled rate against a known Fourier mode.
Stop, rebuild and continue the same trajectory Publish a field checkpoint, restore into new fields and a new clock, and compare against an uninterrupted run.
Write the expression once, inspect the generated C++ Use bounded symbolic generation to share a repeated source term while keeping the model, derivatives and scatter contracts visible.
A process has a rank and a lifetime Start MPI once, query its communicator and turn rank-local values into a global answer.
A simulation owns its session Use C++ member declaration order to release private communication before MPI finalizes.
Measure a region with a defined boundary Time actual local work, accumulate laps and reduce a maximum without confusing it with an end-to-end benchmark.
Give measured work a name Record real Euler updates with nested scopes, a custom scalar metric and a collective JSON export.
A halo exchange inside a measured frame Separate warmup, synchronized frame time and communication regions in the existing halo profiler.
Find the energies of a harmonic trap Build a Fourier Hamiltonian with the native local FFT and watch its lowest energies converge to odd integers.
Turn a halo exchange into a byte rate Count the face bytes that leave each rank, time the exchange alone and state what the resulting GB/s does and does not measure.
A periodic potential splits eigenvalues Track the eleven lowest periodic Mathieu eigenvalues as the strength of a cosine potential changes.
A grid has its own spectrum Distinguish physical Dirichlet eigenmodes from large grid-scale eigenvalues and the timestep restriction they create.
A zero weight is a constraint Solve the fifth positive Airy eigenmode without dividing by the zero coordinate at the centre of the grid.
An asymmetric potential reshapes a membrane mode Build a tensor-product eigenproblem with a steep potential and inspect its four lowest modes.
Solve a square without a giant Kronecker matrix Apply a Chebyshev tensor Laplacian through native GMRES, then check an exactly representable manufactured solution.
A wave number can make a solve delicate Keep the tensor discretization and add a Helmholtz shift that changes the spectrum and conditioning.
Let the boundary drive the interior Eliminate prescribed edge values into the right-hand side and solve a homogeneous interior Laplace equation.
A contour turns an integral into a sum Use complex arithmetic and a shifted trapezoidal rule to approximate reciprocal gamma, while keeping poles and branch choices explicit.
Use a hierarchy to correct different scales Solve a manufactured three-dimensional Dirichlet Poisson problem with native geometric multigrid preconditioning.
A pulse meets the end of an interval Use an even extension and a local FFT to evolve a wave on Chebyshev points.
Let a wave spread across a square Apply one-dimensional FFT-based Chebyshev derivatives along rows and columns.
A wave tank has two kinds of edge Combine a periodic Fourier direction with a Chebyshev direction and reflecting Neumann walls.
Let dispersion evolve exactly between stages Solve KdV with Fourier differentiation, an integrating factor and OpenPFC’s complex-state RK4.
Lift the boundary before evolving the interior Follow Allen–Cahn dynamics with a Chebyshev operator, fixed endpoint values and native forward Euler.
A changing wall must enter the next derivative Impose time-dependent Allen–Cahn endpoint values explicitly after each accepted Euler step.
Keep the field compact and the neighbours separate Solve the same periodic heat problem with a contiguous owned core and six independent face halos.
Write an initial condition as a function Use a stateful functor and two lambdas to fill real distributed fields from physical coordinates.
Let configuration choose an initial condition Extend the native FieldModifierCatalog with an affine field while keeping the catalog local to the program.
Turn a settings file into a runnable session Use native JSON/TOML loading and the spectral session factory, then evolve a mode with the actual clock intervals.
An implicit solve can be a division Solve periodic heat flow with backward Euler and compare its unconditional linear stability with its time error.
Let the driver tell the step how far to go Observe the native time-loop hooks and use the actual final interval in an implicit spectral heat update.
Many local bricks, one binary field Write and read a distributed field collectively, then verify the global x-fastest file layout.
A result series remembers geometry and time Write padded fields through SnapshotSeries and inspect VTK pieces, raw arrays, the manifest and XDMF.
Let a conserved field separate into phases Express Cahn–Hilliard physics as two spectral symbols and one local cubic, then advance it with native ETD1.
Build the clamped ends into the trial function Compose native Chebyshev differentiation with a weighted polynomial representation for a fourth-order boundary problem.
A square can sing the same note in two shapes Build a clamped plate from one-dimensional Chebyshev operators and use symmetry to distinguish repeated vibration modes.
A tiny real part decides whether a disturbance grows Use clamped Chebyshev operators for a complex generalized eigenproblem, then distinguish a small residual from a resolved stability prediction.
Eigenvalue dots do not tell the whole story Measure spectral sensitivity with small singular values of a complex harmonic oscillator, using column rotations rather than normal equations.
Meet at the centre of a disk Combine signed Chebyshev radii and angular Fourier modes, then check disk vibration shapes against Bessel functions.
A cubic needs room in Fourier space Run the existing nonlinear Fourier course model, derive its sampling condition and separate implementation checks from PDE accuracy.
A small residual can leave a visible error Solve a polar Poisson equation with native FFTs and GMRES, then use an exact separated solution to explain radial convergence.
Recover velocity from one scalar field Use vorticity, a Fourier Poisson solve and an exact two-dimensional vortex to understand the retained flow prototype.
Compare grids without changing the time question Use the retained spatial-refinement driver to compare normalized Fourier modes at a fixed timestep and inspect a separate time control.
Measure time accuracy with a nonlinear field Hold the grid fixed, activate nonlinear mode interactions and interpret a guarded observed-order estimate from the retained IFRK4 driver.
Check flux on the field you actually represent Integrate a Fourier velocity over closed boxes, reconstruct its missing conjugate modes and distinguish spectral identities from nodal quadrature.
Couple vorticity to a magnetic potential Inspect two-field incompressible MHD, check an exact magnetic mode and use an aligned state to test the Lorentz sign.
Let a three-dimensional vortex change its shape Use the installed Fourier velocity projector and rotational tendency, then interpret energy and shell diagnostics in the retained 3-D Taylor–Green driver.
Count what leaves the boundary Use a nonperiodic summation-by-parts diffusion operator, distinguish derivative from physical flux and close the weighted mass balance.
Let layers choose two different averages Homogenize a graded elastic cell with six periodic FFT solves, then check the tensor and local shear against an independent layered solution.
Solve a weak equation on free nodes Compose Q1 finite elements, nodal multigrid and CG, then separate algebraic error from a finite-element discretization error you can derive.
No matching items

Read An FFT index is not a wave number to connect array indices, physical units and the Nyquist derivative convention.

Follow a spreading Gaussian pulse to distinguish conserved discrete mass, spatial sampling and time-stepping accuracy.

Learn what a stencil’s weights mean by normalizing a Sobel kernel and checking its polynomial moments.

Compose two derivative requests in one sweep and use an exact coupled mode to distinguish spatial error from stage errors. Then generate a small pointwise law, inspect its shared expression, and verify its explicit source.

Compare host and device FFT buffers, keeping ownership and normalization fixed while changing the backend.

For a diagnostic challenge, put a halo exchange inside a measured frame and distinguish warmup, collective waiting and per-rank records.

Connect the nonlinear course to an alias-free cubic model, then separate sampling, cutoff and time-refinement questions.

Begin the flow challenges by recovering velocity from vorticity and checking an exact two-dimensional vortex.

Track a nonperiodic boundary by counting what leaves the interval, then check the weighted mass plus its accepted outward flux.

Recover a macroscopic elastic law by homogenizing a graded layered cell, and derive its arithmetic and harmonic shear averages.

Compose Q1 finite elements and nodal multigrid, then separate the linear solver residual from the continuum discretization error.

Make your first change

Install a compatible OpenPFC SDK and CLI using the installation guide. Run openpfc doctor once in your compiler/MPI environment. The CLI guide explains SDK selection when it is not discovered automatically.

From the OpenPFC checkout:

cd examples/differentiation/periodic
openpfc run model.cpp input.json --launcher=none

Edit input.json to change the grid. Edit model.cpp to change the function. The CLI compiles the model, preserves an attempt directory, and prints its location. CSV files are in that attempt’s results/. Changing the input alone reuses the compiled executable.

The heat lessons share a small header for inputs and diagnostics. Their commands add --source-root .. so that the CLI also preserves that header. Each chapter gives the complete command. For MPI-capable chapters, invoke the CLI once with --ranks=2 --launcher=mpirun instead of --launcher=none. The complete-domain polynomial references and geometry-only programs use the documented single-process command.

Build a model you can configure and inspect

Write initial patterns as callables, then select one through a native catalog. A JSON/TOML session assembles the clock and stack while keeping the physics callback visible. Compare implicit heat with the other heat methods, then follow driver hook ordering.

Give the computed array a durable form with collective binary I/O and a geometry-aware result series. The Cahn–Hilliard lesson brings state declaration, spectral symbols and a nonlinear local law together.

Where next?

The geometry sequence explains owned boxes, memory offsets, padded fields, and physical-coordinate initialization. Then use a polar-defined pattern to give those Cartesian points a different mathematical interpretation. For the transform itself, compare complex coefficients and local versus distributed layouts.

The book-inspired sequence continues with cardinal interpolation, matrix and FFT differentiation, and four regularity classes. The latter includes the complete resolution presets for Trefethen’s programs 1, 2, and 7, alongside the hat and smooth-profile comparison of programs 4 and 5.

Follow a variable-speed wave, then ask which timesteps are stable. On a finite interval, begin with where to place polynomial samples and work through cosine-mapped differentiation, integration as an ODE, boundary equations, and a nonlinear solve.

Watch a pulse reflect on an interval, spread across a square, and travel through a mixed Fourier–Chebyshev tank. Then separate linear dispersion from nonlinear interaction in KdV. The Allen–Cahn pair compares fixed lifted boundaries with an explicitly driven endpoint. For another MPI storage choice, return to the heat equation with a compact core and separate halos.

The coverage inventory lists every existing executable and all forty book programs, including those still awaiting a teaching chapter.

To build a complete simulation workflow, investigate adaptive step rejection, exponential step doubling, output-time alignment, two fields at one RK stage, and checkpoint restart. The host orchestrator shows how another solver can own the loop. Start parallel work with MPI ownership and finish with structured diagnostics.

For operator and boundary problems, begin with harmonic-trap energies and a periodic Mathieu potential. Then distinguish physical and grid-scale eigenmodes before examining the Airy constraint and a perturbed membrane. The square Poisson, Helmholtz, and boundary-driven Laplace sequence shares one native tensor-action GMRES solve. Geometric multigrid introduces a native hierarchy on uniform grids. For a different use of complex arithmetic, try a contour integral. Continue to fourth-order problems with a clamped beam and twenty-five square-plate modes. Both build on native Chebyshev differentiation; the plate uses symmetry to explain repeated vibration frequencies. The Orr–Sommerfeld problem reuses the clamped operators to ask whether a small flow disturbance grows. Its complex generalized eigenproblem separates matrix residuals from resolution and stability. For the sensitivity behind that distinction, examine pseudospectra of a complex oscillator. Small singular values show where a small perturbation can move an eigenvalue. The final book pair moves to a disk: vibration modes explain signed radii and angular multiplicity, and Poisson’s equation uses an exact reference to distinguish a tiny residual from radial approximation error.

The existing examples catalogue also includes adaptive integration, Cahn–Hilliard evolution, custom initial conditions, coupled fields, and output. Those remain useful next steps after these lessons. GPU execution has its own application/build path; the source-model CLI used here currently qualifies CPU execution.