Ghost cells, halo exchange, virtual topologies
After this lecture, you can
MPI_SendrecvMost stencil-based solvers (diffusion, wave equation, lattice Boltzmann) are nearest-neighbour. Each process owns a subdomain plus a layer of ghost cells that mirror its neighbour’s data.
After every time step, ranks exchange the boundary layer – the halo exchange.
A finite-difference discretization of the 1D diffusion equation:
\[ c_i(t + \Delta t) = c_i(t) + \frac{\Delta t \, D}{\Delta x^2} \big(c_{i-1} + c_{i+1} - 2 c_i\big) \]
Figure 1: Serial baseline: one process owns the whole grid. The highlighted three-point stencil shows that each update reads both neighbours.
Spread \(N\) data points over \(s\) MPI processes. Process of rank \(r\) owns indices \(rN/s\) through \((r{+}1)N/s - 1\), plus one ghost cell on each side.
Figure 2: Ghost cells (dashed, amber) hold copies of the neighbouring rank’s edge cells; the highlighted stencil shows why the boundary update needs them.
Sendrecv
Figure 3: Each rank sends its last interior cell to the right neighbour’s ghost while receiving into its own left ghost – Sendrecv pairs both halves, avoiding deadlock.
For periodic boundary conditions, the first and last process exchange with each other (rank 0 ↔︎ rank \(s{-}1\)).
The same idea, generalized to 2D and to a vector of populations \(f_{i,kl}\) at each lattice site:
Figure 4: The D2Q9 lattice: every node \((k, l)\) stores nine populations \(f_0 \dots f_8\) – one rest population, four axis-aligned, four diagonal.
Figure 5: Each rank owns a tile plus a one-cell ghost frame (dashed). Only populations pointing across a boundary must be communicated: \(f_1, f_5, f_8\) cross the right edge (to rank 1), \(f_2, f_5, f_6\) the top edge (to rank 2). The diagonal populations land in the corners.
Figure 6: Sweep 1 exchanges in \(x\) (columns, including corners); sweep 2 exchanges in \(y\) (rows, which now carry the corner data). No diagonal message needed.
This is the standard trick for stencil halo exchange.
In 2D and 3D, computing neighbour ranks by hand is error-prone. MPI provides a Cartesian communicator:
Figure 7: A 3×3 periodic Cartesian topology: each rank gets grid coordinates, and MPI_Cart_shift returns the neighbour ranks of (here) rank 4 in each direction – including the periodic wrap-around.
For \(s\) processes and an \(L \times L\) grid:
The 2D tiling has smaller communication volume per rank when \(s\) is large – but is more complex (corner exchange).
MPI_Dims_create will pick a balanced factorization for you.
The same idea generalizes to 3D – each rank owns a box and exchanges six faces.
Sendrecv for halo exchange – never Send then Recv in lockstep across ranks (deadlock risk)Sendrecv if you used a periodic Cartesian communicatorSendrecv directly on Kokkos device views (after Kokkos::fence())HPC: Parallelization on GPUs and Accelerators