Fields / Physical meaning
Write the physics in physical coordinates
A formula should describe the same field whether one rank or several store it.
Download runnable lesson (.zip)
Includes the model, inputs, required headers and a short run guide.
Suppose the initial condition is \(u(x,y,z)=1+x+y^2\). It contains no MPI rank and no local memory offset. An initialization routine should preserve that fact. The previous example 08 simulated four owners within one process; this refactor constructs one actual owned field per MPI rank.
The formula stays small
const auto domain = pfc::domain::create({16, 8, 2});
const auto partition = pfc::decomposition::create(domain, comm.size());
auto u = pfc::data::field_from_subdomain<double>(partition, comm.rank(), 0);
u.apply([](double x, double y, double) { return 1 + x + y * y; });domain describes the complete \(16\times8\times2\) grid with unit spacing. partition describes ownership. u stores only this rank’s values. apply bridges those descriptions by supplying physical coordinates to the lambda.
At global points \((4,2,0)\) and \((12,6,0)\) the answers must be
\[ \begin{gathered} u(4,2,0)=9, \\ u(12,6,0)=49. \end{gathered} \tag{1}\]
Those values do not depend on the process grid.
Who owns the point?
For this tiny demonstration, each rank checks its owned cells and a collective combines the answer:
for (const auto point : {pfc::Int3{4, 2, 0}, pfc::Int3{12, 6, 0}}) {
double local_value = 0.0;
int local_hits = 0;
u.for_each_owned([&](int i, int j, int k) {
const auto low = u.box().low;
if (pfc::Int3{i + low[0], j + low[1], k + low[2]} == point) {
local_value = u(i, j, k);
local_hits = 1;
}
});
double value = 0.0;
int hits = 0;
MPI_Allreduce(&local_value, &value, 1, MPI_DOUBLE, MPI_SUM, comm);
MPI_Allreduce(&local_hits, &hits, 1, MPI_INT, MPI_SUM, comm);
if (hits != 1 || value != 1 + point[0] + point[1] * point[1]) return 1;
if (comm.rank() == 0) {
std::cout << "u(" << point[0] << ',' << point[1] << ",0)=" << value
<< " owners=" << hits << '\n';
}
}The value reduction alone is not enough. A missing point could yield zero, and two owners could add values together. Reducing the number of owners as well requires that each requested point is represented exactly once.
This is an inspectable teaching probe, not an efficient general point-query service: scanning every cell is unnecessary for large repeated query sets. The example deliberately exposes the ownership decision and its invariant.
Repeat the experiment with another decomposition
openpfc run model.cpp input.json --ranks=2 --launcher=mpirunCompare this output with --launcher=none and four ranks. The source’s domain and formula stay fixed. Each run should report Equation 1 with owners=1. Those assertions are also part of the ordinary numerical checks.
Next, change the x spacing to one half while retaining the same global probe indices. The physical x coordinates halve. Predict why the first value becomes seven rather than nine, and update the reference using physical coordinates rather than copying the previous expected output.
This lesson initializes a field; it does not evolve a PDE or fill halos. For those operations continue with finite-difference heat flow. See the field factory and Field API for the underlying public operations. The complete C++ model and starting input are available with this lesson.