Boundary Conditions
PDEBase.jl provides a type hierarchy for representing boundary conditions that arise during the parsing of a PDESystem. Understanding these types is essential for implementing discretization methods that handle boundaries correctly.
Type Hierarchy
AbstractBoundary
└── AbstractTruncatingBoundary
├── LowerBoundary
├── UpperBoundary
└── AbstractInterfaceBoundary
├── InterfaceBoundary
└── HigherOrderInterfaceBoundaryBoundary Types
AbstractBoundary
The root abstract type for all boundary conditions.
AbstractTruncatingBoundary
Abstract type for boundaries that "truncate" the computational domain - i.e., boundaries at the edges of the spatial domain.
LowerBoundary
Represents a boundary condition at the lower edge of a spatial domain (e.g., x = 0).
struct LowerBoundary <: AbstractTruncatingBoundary
u # Dependent variable this BC applies to
x # Independent variable (spatial dimension)
depvars # All dependent variables appearing in the BC equation
indvars # Independent variables in the BC
eq # The boundary condition equation
order # Order of the highest derivative in the BC
endExample: For u(t, 0) = 0, this would create a LowerBoundary for variable u at x = 0.
UpperBoundary
Represents a boundary condition at the upper edge of a spatial domain (e.g., x = 1).
struct UpperBoundary <: AbstractTruncatingBoundary
u # Dependent variable this BC applies to
x # Independent variable (spatial dimension)
depvars # All dependent variables appearing in the BC equation
indvars # Independent variables in the BC
eq # The boundary condition equation
order # Order of the highest derivative in the BC
endExample: For Dx(u(t, 1)) = 0 (Neumann BC), this would be an UpperBoundary with order = 1.
InterfaceBoundary
Represents an interface condition connecting two regions or variables. These are used for multi-domain problems where solutions must match at internal boundaries.
struct InterfaceBoundary{IsUpper_u, IsUpper_u2} <: AbstractInterfaceBoundary
u # First variable
u2 # Second variable
x # First interface coordinate
x2 # Second interface coordinate
eq # Interface condition equation
endThe type parameters IsUpper_u and IsUpper_u2 indicate whether each variable is at its upper boundary.
Key Assumptions:
- Variables in an interface BC must have the same argument signature
- They differ in only one variable (the interface variable)
- Interfaces are typically at the lower boundary of one domain and upper of another
HigherOrderInterfaceBoundary
For interface conditions involving derivatives (e.g., flux continuity).
struct HigherOrderInterfaceBoundary <: AbstractInterfaceBoundary
u # First variable
u2 # Second variable
x # First interface coordinate
x2 # Second interface coordinate
depvars # Dependent variables in the condition
indvars # Independent variables
eq # The interface equation
order # Derivative order
endBoundary Map Structure
During discretization, boundary conditions are organized into a nested dictionary structure:
boundarymap = Dict(
operation(u) => Dict(
x => [LowerBoundary(...), UpperBoundary(...)],
y => [LowerBoundary(...), UpperBoundary(...)],
t => [initial_condition, ...]
),
operation(v) => Dict(...)
)This structure maps each dependent variable to its boundaries for each independent variable.
Utility Functions
isupper(b::AbstractBoundary)
Returns true if the boundary is an upper boundary.
isupper(LowerBoundary(...)) # false
isupper(UpperBoundary(...)) # trueisinterface(b)
Returns Val(true) if the boundary is an interface boundary, Val(false) otherwise.
isinterface(InterfaceBoundary(...)) # Val(true)
isinterface(LowerBoundary(...)) # Val(false)isperiodic(b1::InterfaceBoundary, b2::InterfaceBoundary)
Checks if two interface boundaries form a periodic pair.
isperiodic(bc_left, bc_right) # true if periodicfilter_interfaces(bs)
Filters a collection of boundaries to return only interface boundaries.
interfaces = filter_interfaces(all_boundaries)haslowerupper(bs, x)
Checks if a collection of boundaries has both lower and upper boundaries for variable x.
has_lower, has_upper = haslowerupper(boundaries, x)has_interfaces(bmps)
Returns true if the boundary map contains any interface boundaries.
if has_interfaces(boundarymap)
# Handle multi-domain case
endgetvars(b::AbstractBoundary)
Returns the dependent variable and independent variable for a boundary.
u, x = getvars(boundary)flatten_vardict(bmps)
Flattens a nested boundary map dictionary into a single vector of boundaries.
all_boundaries = flatten_vardict(boundarymap)Parsing Boundary Conditions
The parse_bcs function (internal) processes the raw boundary conditions from the PDESystem:
- Generates matching rules for lower and upper boundaries
- Iterates through each boundary condition equation
- Determines which boundary (lower/upper/interface) each condition applies to
- Detects periodic boundaries (conditions that reference both lower and upper bounds)
- Constructs the appropriate boundary type
- Builds the boundary map
How Boundaries are Matched
A boundary condition is matched based on:
- Whether dependent variables are evaluated at domain boundaries
- The presence of derivatives and their orders
- Whether the condition spans multiple boundaries (interface/periodic)
Example of boundary matching:
# This is detected as a LowerBoundary at x = 0:
u(t, 0) ~ 0
# This is detected as an UpperBoundary at x = 1:
Dx(u(t, 1)) ~ 0
# This is detected as an InterfaceBoundary (periodic):
u(t, 0) ~ u(t, 1)Implementation Notes
When implementing a discretization:
- Check boundary validity in
check_boundarymap:
function PDEBase.check_boundarymap(bmap, v, disc::MyDiscretization)
for u in keys(bmap)
for x in keys(bmap[u])
if isempty(bmap[u][x])
error("Missing boundary condition for $u at $x")
end
end
end
end- Handle different boundary types in
discretize_equation!:
for bc in bcmap[operation(eqvar)][x]
if bc isa LowerBoundary
# Apply at first grid point
elseif bc isa UpperBoundary
# Apply at last grid point
elseif bc isa InterfaceBoundary
# Connect multiple regions
end
end- Separate initial conditions from spatial BCs:
# Initial conditions are boundaries on the time variable
ics = boundarymap[u][t]
# Spatial boundaries are on spatial variables
spatial_bcs = Dict(x => boundarymap[u][x] for x in indvars(v))PeriodicMap
The PeriodicMap type tracks which variables have periodic boundary conditions:
struct PeriodicMap
map # Dict tracking periodic dimensions for each variable
endThis is used to determine when to wrap grid indices in finite difference stencils.
See Also
- Developer Extension API for how boundaries integrate with discretization
- Discretization Workflow for the complete processing pipeline
- VariableMap for variable handling