# Boundary conditions and halo/ghost cells

**Current implementation reviewed 2026-09-26; documentation repair only.** This guide
covers the single-grid GPU boundary behavior implemented by
[PatchBoundaryKernels](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/BC/Patches/PatchBoundaryKernels.cs),
[AdvectionGpu](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/Advection/AdvectionGpu.cs), and the
[pressure](/src/PolyCfd.Gpu/Projection/README) and
[diffusion](/src/PolyCfd.Gpu/Diffusion/README) operators. The
[boundary alignment plan](/docs/plans/boundary-condition-alignment) separates proposed
validation and numerical changes from these current contracts. No boundary algorithm
or runtime support changes with this documentation update.

## 1. Grid layout and storage

Pressure is cell-centered; U, V and W are face-centered along X, Y and Z respectively.
Their stored extents are `(Nx, Ny, Nz)`, `(Nx+1, Ny, Nz)`, `(Nx, Ny+1, Nz)` and
`(Nx, Ny, Nz+1)`. A normal boundary velocity lies on the physical domain face; a
neighboring tangential velocity lies half a cell inside. Coordinates and indices are
specified in [MAC grid conventions](/docs/MAC_GRID_CONVENTIONS).

[DeviceMacState](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/Core/DeviceMacState.cs) has dense velocity
interiors and **no velocity halo buffers**. Pressure has a separate packed halo with
six face planes and no edge/corner ghosts. A
[PressureWorkspace](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/Core/PressureWorkspace.cs) borrows a pressure
interior and owns only its halo. Host `MacState` still has per-field halo arrays;
that host layout does not imply GPU velocity storage. See [halo layout](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/docs/halo-layout.md).

## 2. Supported boundary descriptions

[PatchBcSet.ValidateSupported](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Core/BC/Patches/PatchBcSet.cs) accepts
six uniquely named faces in `XMin, XMax, YMin, YMax, ZMin, ZMax` order. Each face has
one velocity type/value and one pressure type/value. Pressure applies to the **whole
face**, independently of its velocity index lists. Independent pressure subregions
and Robin conditions are unsupported.

[PatchBuilder](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Core/BC/Patches/PatchBuilder.cs) normally selects only
the normal velocity samples on each face. A partial normal mask selects which of
those samples are directly prescribed. It does not introduce another boundary type
on the rest of the face: diffusion still uses the face's uniform ghost rule for
unselected normal rows and tangential rows. Tangential boundary values enter diffusion
through its inline stencil, not through additional tangential wall DOFs. Custom
index lists must not be interpreted as spatially varying inlet/outlet/wall regions.

Periodic partners are checked for reciprocal opposite faces separately for velocity
and pressure. The typed validator does not yet establish every required cross-field
periodicity or mask-compatibility rule. **BC-01 is planned** to consolidate setup
validation; AMR-02 will derive consumers from canonical domain periodicity. See the
[boundary alignment plan](/docs/plans/boundary-condition-alignment).

The standard [factory combinations](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Core/BC/Patches/PatchBcSetFactory.cs)
are:

| Physical use | Velocity descriptor | Pressure descriptor |
| --- | --- | --- |
| Stationary no-slip wall | `NoSlip` | `ZeroGradient` |
| Moving wall / prescribed inlet | `MovingWall` / `FixedValue` | `ZeroGradient` |
| Slip or symmetry plane | `Slip` / `SymmetryPlane` | `ZeroGradient` / `SymmetryPlane` |
| Pressure outlet | `ZeroGradient` | `FixedValue` |
| Periodic direction | `Periodic` on both faces | `Periodic` on both faces |

These physical combinations lower differently for transport, diffusion and projection.
A velocity `ZeroGradient` descriptor is not a guarantee of outflow-only transport or
of a nonreflecting open boundary.

## 3. Velocity enforcement

[ApplyVelocityBcs](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/BC/Patches/PatchBoundaryKernels.cs) writes the
selected stored velocity samples. No-slip writes zero; fixed value and moving wall
write the prescribed component. Slip/symmetry explicitly writes zero to the **normal**
component and leaves tangential samples unchanged. Zero-gradient performs no direct
velocity write. Periodic copies identify the duplicated normal end faces.

For standard domain patches, tangential no-slip/moving-wall conditions are enforced
by the diffusion stencil in [section 8](#8-bc-aware-diffusion-halo-free-stencil-computation).
Setting normal faces alone does not implement tangential inflow states for advection.
Embedded-wall velocity constraints use
[CutFaceProjectorGpu](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/Geometry/CutFaceProjectorGpu.cs) and are
separate from the domain faces.

## 4. Pressure values and correction equations

[Pressure halo filling](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/BC/Patches/PatchBoundaryKernels.cs) never
clamps an interior pressure unknown. For an adjacent cell value `pI`, a prescribed
physical-face value `pB` and a ghost-center value `pG`:

| Pressure type | Ghost relation |
| --- | --- |
| `ZeroGradient` / `SymmetryPlane` | `pG = pI` |
| `FixedValue` | `pG = 2*pB - pI`, hence `(pI+pG)/2 = pB` |
| `Periodic` | Copy the opposite-side interior cell |

`pB` is kinematic pressure at the physical domain face, not a ghost-center value.
This matches AMReX's distinction between boundary data and extrapolated ghost values;
its [linear-solver documentation](https://amrex-codes.github.io/amrex/docs_html/LinearSolvers.html#boundary-conditions)
also locates supplied Dirichlet data at the physical face even when a ghost cell is
used to store it. Copying that boundary value directly into PolyCFD's pressure halo
would change the discrete boundary location.

[PressureStencil](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/PressureStencil.cs) separates physical residuals
from homogeneous correction/error operators. PCG search directions and MG error
equations use zero prescribed values. Nonzero boundary forcing belongs to the
physical residual; it must not enter every application of the linear correction
operator.

For static incremental projection, the previous physical pressure enters the momentum
predictor, the solve starts with a zero pressure increment, and constant prescribed
pressure has zero prescribed increment. The increment is added to physical pressure;
its gauge and physical halos are then restored. The moving-geometry formulation still
uses full pressure. See [projection contracts](/src/PolyCfd.Gpu/Projection/README)
and the [physics guide](/docs/PHYSICS_GUIDE#2-one-time-step). Time-dependent prescribed
pressure is not established by this constant-value contract. Pressure gauges remain
per connected fluid region without a fixed-pressure anchor.

## 5. Advection and outlet limitations

[AdvectionGpu](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/Advection/AdvectionGpu.cs) applies stored velocity
boundary values before the forward sample. RK2 backtracing and interpolation receive
only per-axis periodic flags. Positions wrap by domain length on periodic axes;
nonperiodic positions and stencil indices clamp to the available samples. Periodic
stencil indices currently wrap by the stored component extent, including the duplicated
normal endpoint. Periodic seam accuracy needs explicit staggered-grid qualification.

The sampler does not receive the full wall/inlet type or a prescribed tangential
exterior state. Therefore clamping near an inlet, moving wall or outlet is not equivalent
to an AMReX `ext_dir` or direction-dependent boundary reconstruction. Geometry-aware
sampling excludes blocked faces and moves departure points out of solids; it does not
add those missing physical-domain exterior states.

MacCormack uses the forward field for its reverse pass and then applies the correction
and limiter; there is no separate boundary refresh of that forward field. Sources and
the pressure predictor occur before diffusion. The placement of boundary enforcement
across these stages, including zero viscosity, is part of the planned BC-02 qualification.

**Current outlet reversal behavior:** a pressure outlet neither clamps inward normal
velocity to zero nor prescribes a distinct incoming exterior state when flow reverses.
It retains the pressure condition and nonperiodic sampling above. No claim of a
qualified backflow treatment or no-inflow outlet is made. **BC-02 is planned** to choose
and validate an explicit reversal policy, coordinated with TRN-01 and before S2
acceptance. AMReX-Hydro's [advective boundary rules](https://amrex-fluids.github.io/amrex-hydro/docs_html/bcs.html#advective-bc-details)
operate on Godunov reconstructed states; adopting them requires a method-specific
derivation, not a clamp after pressure projection.

## 6. Boundary timing in a time step

[TimeIntegratorGpu.Step](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Gpu/Integration/TimeIntegratorGpu.cs) performs
advection (including its initial velocity enforcement), sources, the physical-pressure
predictor, optional diffusion, immersed-face constraints, and incremental projection.
Diffusion evaluates boundary neighbors inline and reapplies stored velocity conditions
at its exits. Projection reapplies velocity conditions before computing divergence and
after correcting velocity; the integrator applies them again at the end. Pressure
halos are filled for the pressure force, physical residuals, correction operators and
final physical pressure as required by each use.

Moving geometry has its own full-pressure projection and geometric continuity source,
with moving cut-face velocities imposed before and after projection. Its static
immersed-wall diffusion intercept coefficients are not rebuilt per step. The
[physics guide](/docs/PHYSICS_GUIDE) and operator READMEs specify these separate scopes.
A small linear-solver residual alone does not establish correct boundary momentum,
flux balance or accuracy after all stage enforcement.

## 7. Evidence and planned alignment

Existing tests pin useful current behavior; they do not qualify the planned BC changes:

| Current evidence | Tests |
| --- | --- |
| Pressure ghost relation and direct boundary writes | [PatchBoundaryKernelsTests](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/test/PolyCfd.Gpu.Tests/BC/PatchBoundaryKernelsTests.cs) |
| Independent nonzero Dirichlet pressure profile, axes and pressure offsets | [NonzeroPressureBoundaryTests](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/test/PolyCfd.Gpu.Tests/Numerics/Pcg/NonzeroPressureBoundaryTests.cs) |
| Incremental pressure/diffusion equilibrium and transient channel accuracy | [IncrementalProjectionTests](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/test/PolyCfd.Gpu.Tests/Projection/IncrementalProjectionTests.cs) |
| Independent diffusion rows, normal endpoints and partial masks | [DiffusionOperatorTests](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/test/PolyCfd.Gpu.Tests/Diffusion/DiffusionOperatorTests.cs), [DiffusionNormalEndpointTests](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/test/PolyCfd.Gpu.Tests/Diffusion/DiffusionNormalEndpointTests.cs) |
| Current periodic transport and limiter controls | [AdvectionSchemeTests](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/test/PolyCfd.Gpu.Tests/Advection/AdvectionSchemeTests.cs) |

The [boundary alignment plan](/docs/plans/boundary-condition-alignment) adds targeted
CUDA acceptance for setup compatibility, periodic seams, tangential inflow, outlet
reversal and stage consistency. Existing regression and performance limits stay in
force. Mixed pressure regions, general Robin conditions and higher-order boundary
closures remain separate numerical features.

Retain compact constant descriptors, borrowed device masks and inline velocity stencils
where appropriate. Alignment does not require persistent velocity halos or field-sized
boundary arrays. New repeated boundary evaluation and reductions must stay on the GPU;
measure any bounded host metadata under the [GPU execution policy](/docs/plans/gpu-resident-processing).

## 8. BC-aware diffusion (halo-free stencil computation)

The [Diffusion README](/src/PolyCfd.Gpu/Diffusion/README) is the implemented
contract for the row evaluator, solve method, effective residual and resource limits.
For `alpha = nu*dt` and `c = alpha/h^2`, each direction initially contributes two
neighbor shares to the diagonal of `(I-alpha*L)u = b`. Substituting the boundary ghost
moves its dependence on the center into that diagonal:

| Boundary neighbor | Ghost | Change to the base row |
| --- | --- | --- |
| Tangential no-slip/fixed/moving wall | `2*uWall-uI` | Add `c` to the diagonal and `2*c*uWall` to the effective RHS |
| Tangential slip/symmetry or zero-gradient | `uI` | Subtract `c` from the diagonal |
| Periodic | Opposite-side interior sample | Keep the neighbor; skip the duplicated endpoint along the component's normal axis |

A selected prescribed normal face lies on the wall and uses a held-value row instead
of this half-cell tangential relation. Its residual is the prescribed value minus the
current velocity. The effective RHS includes prescribed rows and nonzero wall forcing;
normalizing only by the stored pre-diffusion field would incorrectly treat a moving
wall driving a quiescent fluid as a zero-RHS problem.

Static immersed-wall coefficients place a viscous boundary at the SDF intercept. If
that intercept is distance `d` along a neighbor link of length `h`, the reconstructed
ghost is `uG = uWall*h/d - uI*(h-d)/d`; at `d=h/2` it reduces to the domain-wall
reflection. This changes the row's diagonal and wall forcing, separately from holding
blocked faces at wall velocity. The current
[coefficient builder](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/src/PolyCfd.Core/Geometry/ImmersedWallCoefficients.cs)
searches up to `2h`, falls back to `d=h` without a crossing, and by default also uses
`d=h` on wall links of an active unknown centered inside the solid. Resolved distances
have a default floor of `0.1h`. These are current approximation limits, not
general embedded-boundary accuracy guarantees. The moving path does not rebuild
these static intercept coefficients each step. See the
[diffusion contract](/src/PolyCfd.Gpu/Diffusion/README) and
[ImmersedWallDiffusionTests](https://github.com/hankbeasley/polycfd/blob/b7f5b9ba6f52355d9b196b586e4a6c8719d8feea/test/PolyCfd.Gpu.Tests/Diffusion/ImmersedWallDiffusionTests.cs).
