API reference
Documentation for Bramble.jl's public API.
Utilities
Linear algebra backends
Bramble.backend — Function
backend(Wₕ::AbstractSpaceType) -> AbstractBackendReturns the computational backend associated with the space Wₕ.
Bramble.ExecutionPolicy — Type
ExecutionPolicyAbstract supertype for backend execution policies.
Directs grid operations and form assembly to execute either sequentially via Serial or across threads via Parallel.
Bramble.Serial — Type
Serial() <: ExecutionPolicySequential execution policy.
Directs grid operations and form assembly to execute via single-threaded loops. This is the default execution policy.
See also: Parallel, ExecutionPolicy.
Bramble.Parallel — Type
Parallel() <: ExecutionPolicyMultithreaded execution policy.
Directs grid operations and form assembly to execute across CPU threads via static partitioning. Execution is unconditional: no per-call size thresholds are imposed. For workloads dominated by small, frequently repeated calls, use Serial.
See also: Serial, ExecutionPolicy.
Bramble.execution_policy — Function
execution_policy(backend::Backend{<:Any, <:Any, EP}) -> EP
execution_policy(::Type{<:Backend{<:Any, <:Any, EP}}) -> EPReturn the ExecutionPolicy instance (Serial or Parallel) configured for backend.
execution_policy(Ωₕ::AbstractMeshType) -> ExecutionPolicyReturn the ExecutionPolicy (Serial or Parallel) of the Backend associated with mesh Ωₕ.
Bramble.vector — Function
vector(backend::Backend{VT}, n::Integer) -> VTAllocate an uninitialized vector of length n using vector type VT configured in backend.
Arguments
backend: Target backend instance.n: Number of vector elements.
Throws
ErrorException: If neitherVT(undef, n)norVT(n)succeeds.
Bramble.matrix — Function
matrix(backend::Backend{<:Any, MT}, n::Integer, m::Integer) -> MTAllocate a matrix of dimensions n × m using matrix type MT configured in backend.
For dense matrix types, allocates uninitialized storage via MT(undef, n, m). For sparse matrix types (SparseMatrixCSC), allocates an empty sparse matrix via spzeros(T, Ti, n, m).
Arguments
backend: Target backend instance.n: Number of rows.m: Number of columns.
Throws
ErrorException: IfMTcannot be constructed with dimensions(n, m).
Bramble.vector_type — Function
vector_type(backend::Backend{VT}) -> Type{VT}
vector_type(::Type{<:Backend{VT}}) -> Type{VT}Return the vector type VT configured for backend.
Bramble.matrix_type — Function
matrix_type(backend::Backend{<:Any, MT}) -> Type{MT}
matrix_type(::Type{<:Backend{<:Any, MT}}) -> Type{MT}Return the matrix type MT configured for backend.
Bramble.backend_types — Function
backend_types(backend::Backend{VT, MT, EP}) -> Tuple{Type, Type{VT}, Type{MT}, Type{Backend{VT, MT, EP}}}
backend_types(::Type{<:Backend{VT, MT, EP}}) -> Tuple{Type, Type{VT}, Type{MT}, Type{Backend{VT, MT, EP}}}Return a 4-tuple containing (eltype(VT), VT, MT, Backend{VT, MT, EP}).
Bramble.backend_eye — Function
backend_eye(backend::Backend, n::Integer) -> AbstractMatrixConstruct an $n \times n$ identity matrix matching the matrix type configured in backend.
Bramble.backend_zeros — Function
backend_zeros(backend::Backend, n::Integer) -> AbstractMatrixConstruct an $n \times n$ zero matrix matching the matrix type configured in backend.
Bramble.metal_backend — Function
metal_backend(::Type{T} = Float32; policy::ExecutionPolicy = Serial()) -> BackendConstruct a Metal GPU Backend backed by Metal.jl arrays.
Requires using Metal in the caller environment. Apple Silicon GPUs support Float32 and Float16, but do not support 64-bit floating point arithmetic.
Arguments
T: Floating-point element type (Float32orFloat16, default:Float32).
Keywords
Throws
ErrorException: IfMetal.jlis not loaded.
Geometry
Sets and intervals
Bramble.interval — Function
interval(x::Number, y::Number) -> CartesianProduct{1, T}
interval(X::CartesianProduct{1}) -> CartesianProduct{1, T}Construct a 1D CartesianProduct representing the closed interval $[x, y]$.
Inputs are converted to floating-point representation with promoted element type T.
Arguments
x: Lower endpoint.y: Upper endpoint.
Throws
ArgumentError: Ifx > y.
Examples
using Bramble
I = interval(0.0, 1.0)
first(I) == 0.0 && last(I) == 1.0
# output
trueBramble.point — Function
point(Ωₕ::AbstractMeshType, idx)Return the coordinate point at index idx (linear integer, tuple (i, j), or CartesianIndex):
- For 1D meshes: scalar coordinate $x_i$.
- For nD meshes: coordinate tuple $(x_{i_1}, \dots, x_{i_D})$.
Direct indexing Ωₕ[idx] delegates to point(Ωₕ, idx).
Bramble.box — Function
box(a::Number, b::Number) -> CartesianProduct{1, T}
box(a::NTuple{D}, b::NTuple{D}) -> CartesianProduct{D, T}Construct a CartesianProduct from two opposing corner points a and b.
Interval bounds for each dimension i are defined by $[\min(a_i, b_i), \max(a_i, b_i)]$.
LinearAlgebra.:× — Function
×(X::CartesianProduct{D1}, Y::CartesianProduct{D2}) -> CartesianProduct{D1 + D2}Compute the Cartesian tensor product of sets X and Y.
The resulting set has embedding dimension D1 + D2 with promoted scalar coordinate type.
×(W₁::AbstractSpaceType, W₂::AbstractSpaceType) -> CompositeGridSpaceConstruct the Cartesian product space of W₁ and W₂.
Chaining products associatively flattens them into a flat CompositeGridSpace{N}, matching mathematical product space conventions (e.g. W₁ × W₂ × W₃ -> CompositeGridSpace{3}). To explicitly construct hierarchical (nested) composite spaces, call CompositeGridSpace directly (e.g. CompositeGridSpace(Vh, Qh)).
Bramble.dim — Function
dim(Wₕ::AbstractSpaceType) -> Int
dim(::Type{<:AbstractSpaceType}) -> IntReturns the spatial dimension of the mesh associated with the function space Wₕ.
Bramble.topo_dim — Function
topo_dim(X::CartesianProduct{D}) -> IntReturn the topological dimension of X, defined as the embedding dimension D minus the number of collapsed dimensions.
topo_dim(Ω::Domain) -> IntReturn the topological dimension of Domain Ω.
topo_dim(Ωₕ::AbstractMeshType{D}) -> IntReturn the topological dimension of Ωₕ.
The topological dimension counts the number of coordinate axes with more than one point, identifying degenerate or collapsed dimensions (such as manifolds or boundaries embedded in higher-dimensional ambient space).
Base.extrema — Method
extrema(X::CartesianProduct, i::Integer) -> Tuple{T, T}
extrema(X::CartesianProduct{1}) -> Tuple{T, T}
extrema(X::CartesianProduct{D}) -> NTuple{D, Tuple{T, T}}Return component interval endpoint pairs (min, max) for index i or all dimensions.
Bramble.center — Function
center(X::CartesianProduct{D, T}) -> NTuple{D, T}Compute the geometric center point of CartesianProduct X.
center(Ω::Domain) -> NTupleCompute the geometric center point of Domain Ω.
Bramble.projection — Function
projection(X::CartesianProduct, i::Integer) -> CartesianProduct{1}Extract the i-th coordinate dimension of X as a 1D CartesianProduct.
projection(Ω::Domain, i::Integer) -> CartesianProduct{1}Extract the i-th coordinate dimension of domain Ω as a 1D CartesianProduct.
Bramble.is_collapsed — Function
is_collapsed(a::Number, b::Number) -> Bool
is_collapsed(X::CartesianProduct) -> Bool
is_collapsed(X::CartesianProduct, i::Integer) -> BoolCheck whether an interval endpoint pair, a Cartesian set, or a coordinate dimension i is degenerate (min ≈ max).
For an $n$-dimensional CartesianProduct X, is_collapsed(X) returns true if any coordinate dimension is collapsed (any(X.collapsed)), equivalently when the topological dimension is strictly less than the spatial embedding dimension D. is_collapsed(X, i) queries whether the i-th coordinate axis is degenerate.
Arguments
a,b: Scalar interval endpoints.X: Cartesian product set.i: Coordinate dimension index (1 <= i <= D).
Throws
BoundsError: Ifi < 1ori > D.
is_collapsed(Ω::Domain) -> Bool
is_collapsed(Ω::Domain, i::Integer) -> BoolReturn whether the underlying geometric set of domain Ω is collapsed across any dimension, or along coordinate dimension i.
is_collapsed(Ωₕ::AbstractMeshType) -> BoolWhether Ωₕ has no interval to refine or measure a spacing over — a Mesh1D built over a single point. A MeshnD is never collapsed as a whole: each axis is its own Mesh1D and may be collapsed individually, which is handled per axis rather than at this level, so the default here is false.
Bramble.point_type — Function
point_type(X::CartesianProduct{1, T}) -> Type{T}
point_type(X::CartesianProduct{D, T}) -> Type{NTuple{D, T}}
point_type(::Type{<:CartesianProduct{1, T}}) -> Type{T}
point_type(::Type{<:CartesianProduct{D, T}}) -> Type{NTuple{D, T}}Return the coordinate point representation type for a point in X.
point_type(Ω::Domain) -> Type
point_type(::Type{<:Domain{SetType}}) -> TypeReturn the coordinate point representation type of Domain Ω.
Bramble.boundary_symbols — Function
boundary_symbols(Ω::Domain) -> Tuple{Vararg{Symbol}}
boundary_symbols(X::CartesianProduct) -> Tuple{Vararg{Symbol}}
boundary_symbols(D::Integer) -> Tuple{Vararg{Symbol}}Return the canonical coordinate-aligned boundary symbols for dimension D or domain Ω:
- 1D $[x_1, x_2]$:
(:xmin, :xmax) - 2D $[x_1, x_2] \times [y_1, y_2]$:
(:xmin, :xmax, :ymin, :ymax) - 3D $[x_1, x_2] \times [y_1, y_2] \times [z_1, z_2]$:
(:xmin, :xmax, :ymin, :ymax, :zmin, :zmax)
Legacy viewpoint symbols (:left, :right, :bottom, :top, :front, :back) remain supported as backward-compatible aliases across the boundary marker interface.
Throws
ErrorException: If dimensionD > 3.
Bramble.set — Function
set(X::CartesianProduct) -> CartesianProductIdentity accessor returning the geometric set X.
set(Ω::Domain) -> CartesianProductReturn the geometric set defining Domain Ω.
set(Ωₕ::AbstractMeshType) -> AbstractSetTypeReturn the underlying geometric set of the domain over which mesh Ωₕ is defined.
Markers and domains
Bramble.markers — Function
markers(space_set::CartesianProduct, pairs::Pair...) -> DomainMarkers
markers(space_set::CartesianProduct, time_set::CartesianProduct{1}, pairs::Pair...) -> DomainMarkersConstruct a DomainMarkers collection from label => identifier pairs.
Arguments
space_set: Geometric spatial set.time_set: Optional 1D temporal interval for time-dependent boundary conditions.pairs: Vararg sequence oflabel => identifierpairs where identifier is aSymbol,NTuple{N, Symbol}, or predicateFunction.
Examples
using Bramble
I = interval(0.0, 1.0)
m = markers(I, :left_boundary => :left, :internal => x -> 0.2 < x < 0.8)
length(symbols(m)) == 1 && length(conditions(m)) == 1
# output
truemarkers(Ω::Domain) -> DomainMarkersReturn the DomainMarkers collection associated with domain Ω.
markers(Ωₕ::AbstractMeshType) -> MeshMarkersReturn the MeshMarkers dictionary associated with mesh Ωₕ.
Bramble.domain — Function
domain(X::CartesianProduct) -> Domain
domain(X::CartesianProduct, markers::DomainMarkers) -> Domain
domain(X::CartesianProduct, pairs::Pair...) -> Domain
domain(space_set::CartesianProduct, time_set::CartesianProduct{1}, pairs::Pair...) -> DomainConstruct a computational Domain from a CartesianProduct set and optional markers.
When no markers are supplied, defaults to a :boundary marker covering all boundaries of X.
Arguments
X: Underlying geometric set.markers: ExplicitDomainMarkerscontainer.pairs: Variable sequence oflabel => identifierpairs.space_set: Spatial bounding set.time_set: 1D temporal interval for time-dependent boundary conditions.
Examples
using Bramble
Ω = domain(interval(0.0, 1.0))
dim(Ω) == 1 && eltype(Ω) === Float64
# output
trueBramble.labels — Function
labels(Ω::Domain)Return a tuple of the Symbol label of every marker in domain Ω.
Zero-allocation; see label_identifiers.
Meshes
Mesh types and constructors
Bramble.AbstractMeshType — Type
AbstractMeshType{D}Abstract supertype for all mesh types in Bramble. The type parameter D represents the spatial dimension of the mesh (1, 2, or 3).
All concrete mesh types must implement the AbstractMeshType interface, including:
eltype,dim,topo_dim,indices,backend,markerspoints,point,half_points,half_pointspacing,half_spacing,forward_spacing
Type parameters
D: Spatial dimension (1, 2, or 3)
Related types
- Meshes are created from a
Domainusing themeshfunction. - See
MeshMarkersfor marker management on meshes.
Bramble.Mesh1D — Type
Mesh1D{BT, CI, VT, T} <: AbstractMeshType{1}One-dimensional grid discretizing a 1D CartesianProduct interval.
Stores grid point coordinates pts, underlying geometric interval set, semantic markers markers, Cartesian indices indices, and computational backend backend. Also precomputes and caches cell centers (half_pts), cell measures (half_spacings), and backward spacings (spacings).
Fields
set: 1D geometricCartesianProductinterval over which the mesh is defined.markers:MeshMarkersdictionary mapping symbols toBitVectorindicators.indices:CartesianIndices{1}of the grid points.backend: Linear algebraBackendfor memory management and operations.pts: Coordinate vector storing grid points $x_i$ for $i = 1, \dots, N$.half_pts: Precomputed cell centers (midpoints) $x_{i+1/2}$ for $i = 1, \dots, N+1$.half_spacings: Precomputed cell widths (control volume measures) $h_{i+1/2}$ for $i = 1, \dots, N$.spacings: Precomputed backward grid spacings $h_i = x_i - x_{i-1}$, with $h_1 = x_2 - x_1$.collapsed: Boolean flag indicating whether the interval is degenerate (a single point).
See also: MeshnD, mesh, AbstractMeshType.
Bramble.MeshnD — Type
MeshnD{D, BT, CI, SM, T} <: AbstractMeshType{D}Structured multi-dimensional tensor-product mesh for spatial dimensions $D \in \{2, 3\}$.
Constructed as a Cartesian product of 1D submeshes (Mesh1D). Coordinate points are evaluated on demand from the tensor-product submeshes.
Type parameters
D: Spatial dimension (2 or 3).BT <: Backend: Computational linear algebra backend.CI <: CartesianIndices{D}: Cartesian index space.SM <: Tuple: Tuple of 1D submeshes (Mesh1D).T: Coordinate element type (Float64,Float32, etc.).
Fields
set: Multi-dimensional geometricCartesianProductdomain.markers:MeshMarkersdictionary mapping symbols toBitVectorindicators.indices: Multi-dimensionalCartesianIndices{D}for the grid.backend: Linear algebraBackend.submeshes: Tuple ofDMesh1Dobjects along each coordinate axis.
Examples
# Create a 2D mesh with 20×30 grid points
X = domain(interval(0, 1) × interval(0, 2))
Ωₕ = mesh(X, (20, 30), (true, false))
# Access submeshes
x_mesh = Ωₕ(1) # 1D mesh along x-axis
y_mesh = Ωₕ(2) # 1D mesh along y-axis
# Query a specific point coordinate
point(Ωₕ, (10, 15)) # returns (x₁₀, y₁₅)Bramble.MeshMarkers — Type
const MeshMarkers = Dict{Symbol, BitVector}Dictionary mapping semantic marker symbols to boolean indicator vectors across mesh points.
For each label, a BitVector indicates whether the corresponding mesh point satisfies the marker.
Bramble.mesh — Function
mesh(Wₕ::AbstractSpaceType) -> AbstractMeshTypeReturns the underlying mesh object associated with the function space Wₕ.
Bramble.submeshes — Function
submeshes(Ω::Domain, npts, unif, backend) -> NTuple{D, Mesh1D}Create the component 1D submeshes for a tensor-product grid.
Generates a tuple of D independent Mesh1D objects corresponding to each coordinate axis of Ω.
Arguments
Points and spacings
Bramble.npoints — Function
npoints(Ωₕ::AbstractMeshType) -> Int
npoints(Ωₕ::AbstractMeshType, ::Type{Tuple}) -> NTuple{D, Int}Return the total number of points in Ωₕ. When passing Tuple as the second argument, returns a tuple with the number of points along each dimension.
Bramble.points — Function
points(Ωₕ::AbstractMeshType) -> Union{Vector, NTuple}Return the coordinates of the mesh points:
- For 1D meshes (
Mesh1D): returns a coordinate vectorVector{T}of length $N_x$. - For nD meshes (
MeshnD): returns anNTuple{D, Vector{T}}containing the 1D coordinate vectors along each axis.
See also: point.
Bramble.half_points — Function
half_points(Ωₕ::AbstractMeshType)Return the precomputed cell centers (half-points) for each coordinate axis:
\[x_{i+1/2} = \frac{x_i + x_{i+1}}{2}, \quad i = 1, \dots, N-1.\]
Bramble.half_point — Function
half_point(Ωₕ::AbstractMeshType, idx)Return the cell center (half-point) coordinate corresponding to index idx.
Bramble.spacing — Function
spacing(Ωₕ::AbstractMeshType, idx)
spacing(Ωₕ::AbstractMeshType, idx, dim::Int)Return the backward spacing $h_i = x_i - x_{i-1}$ at index idx (for $i=1$, returns $x_2 - x_1$). For nD meshes, returns a tuple of backward spacings along each axis; passing dim queries only that axis directly, without building and discarding the other D - 1 components.
Bramble.forward_spacing — Function
forward_spacing(Ωₕ::AbstractMeshType, idx)
forward_spacing(Ωₕ::AbstractMeshType, idx, dim::Int)Return the forward spacing $h_{i+1} = x_{i+1} - x_i$ at index idx (for $i=N$, returns $x_N - x_{N-1}$). For nD meshes, returns a tuple of forward spacings along each axis; passing dim queries only that axis directly, without building and discarding the other D - 1 components.
Bramble.half_spacing — Function
half_spacing(Ωₕ::AbstractMeshType, idx)Return the cell width (half-spacing) at index idx.
Bramble.spacings — Function
spacings(Ωₕ::Mesh1D) -> AbstractVectorReturn the cached vector of backward spacings, where spacings(Ωₕ)[i] is spacing(Ωₕ, i). Recomputed by set_points! whenever the grid points change.
spacings(Ωₕ::MeshnD{D}) -> NTuple{D, AbstractVector}Return the per-axis backward spacings as an NTuple{D} of vectors, where spacings(Ωₕ)[d][i] is spacing(Ωₕ(d), i).
See also: half_spacings, cell_measures.
Bramble.forward_spacings — Function
forward_spacings(Ωₕ::Mesh1D) -> AbstractVectorReturn the forward spacings of Ωₕ, where forward_spacings(Ωₕ)[i] is forward_spacing(Ωₕ, i). Unlike spacings, this is not cached: it is spacing's vector read one index ahead, computed lazily on iteration.
See also: forward_spacing_for_derivative.
Bramble.half_spacings — Function
half_spacings(Ωₕ::AbstractMeshType)Return the cell widths (half-spacings) along each axis:
\[h_{i+1/2} = \frac{h_i + h_{i+1}}{2}.\]
Bramble.hₘₐₓ — Function
hₘₐₓ(Ωₕ::AbstractMeshType) -> RealReturn the maximum diagonal stepsize across all cells in the mesh:
\[h_{\max} = \max_{\mathbf{i}} \| (h_{1, i_1}, \dots, h_{D, i_D}) \|_2.\]
Bramble.hₘᵢₙ — Function
hₘᵢₙ(Ωₕ::AbstractMeshType) -> RealReturn the diagonal of the smallest cell in the mesh, the counterpart of hₘₐₓ:
- In 1D: $\min_i (x_i - x_{i-1})$.
- In nD:
\[h_{\min} = \min_{\mathbf{i}} \| (h_{1, i_1}, \dots, h_{D, i_D}) \|_2.\]
This is a diagonal rather than an edge length, so that hₘₐₓ and hₘᵢₙ measure the same kind of quantity. For the smallest extent along one coordinate, query that submesh directly: hₘᵢₙ(Ωₕ(i)).
Bramble.stepsize — Function
stepsize(Ωₕ::AbstractMeshType) -> Union{Real, NTuple{D, Real}}
stepsize(Ωₕ::AbstractMeshType, d::Integer) -> RealReturn the constant stepsize for a uniform mesh:
- In 1D: returns scalar $h = x_2 - x_1$.
- In nD: returns a tuple $(h_1, \dots, h_D)$ of stepsizes along each coordinate axis.
- When
dis specified: returns the stepsize along dimensiond.
Throws an ArgumentError if the mesh is not uniform.
See also: is_uniform, spacing.
Bramble.locate_cell — Function
locate_cell(Ωₕ::AbstractMeshType{1}, x::Real) -> Int
locate_cell(Ωₕ::AbstractMeshType{D}, x) -> CartesianIndex{D}Locate the cell containing continuous coordinate x:
- For 1D meshes: returns integer index
i \in 1:N-1such that $x_i \le x \le x_{i+1}$ (clamped to the domain boundaries). - For nD meshes: returns a
CartesianIndex{D}locating the bounding cell along each dimension.
Examples
Ωₕ = mesh(domain(interval(0.0, 1.0)), 11) # h = 0.1
locate_cell(Ωₕ, 0.35) # returns 4 (interval [0.3, 0.4])Bramble.normal_vector — Function
normal_vector(Ωₕ::AbstractMeshType{D}, symbol::Symbol) -> NTuple{D, Float64}
normal_vector(::Val{D}, symbol::Symbol) -> NTuple{D, Float64}Return the outward unit normal vector (as an NTuple{D, Float64}) associated with a standard boundary facet label (:xmin, :xmax, :ymin, :ymax, :zmin, :zmax) or legacy viewpoint alias (:left, :right, :bottom, :top, :front, :back).
Conventions
- 1D:
:xmin,:left$\to (-1.0)$:xmax,:right$\to (+1.0)$
- 2D:
:xmin,:left$\to (-1.0, 0.0)$:xmax,:right$\to (+1.0, 0.0)$:ymin,:bottom$\to (0.0, -1.0)$:ymax,:top$\to (0.0, +1.0)$
- 3D:
:xmin,:back$\to (-1.0, 0.0, 0.0)$:xmax,:front$\to (+1.0, 0.0, 0.0)$:ymin,:left$\to (0.0, -1.0, 0.0)$:ymax,:right$\to (0.0, +1.0, 0.0)$:zmin,:bottom$\to (0.0, 0.0, -1.0)$:zmax,:top$\to (0.0, 0.0, +1.0)$
See also: boundary_symbols.
Bramble.cell_measure — Function
cell_measure(Ωₕ::AbstractMeshType, idx) -> RealReturn the control volume (length, area, or volume) of the cell centered at index idx:
\[\operatorname{meas}(\square_{\mathbf{i}}) = \prod_{d=1}^D h_{d, i_d+1/2}.\]
Bramble.cell_measures — Function
cell_measures(Ωₕ::MeshnD{D}) -> NTuple{D, AbstractVector}Return the per-axis cell widths as an NTuple{D} of vectors. The measure of an individual cell is the product of its per-axis widths; see cell_measure.
Bramble.is_uniform — Function
is_uniform(Ωₕ::AbstractMeshType; tol = 1e-10) -> BoolCheck whether the mesh has uniform spacing (within numerical tolerance tol).
Mesh indexing and boundaries
Bramble.indices — Function
indices(Ωₕ::AbstractMeshType) -> CartesianIndicesReturn the CartesianIndices associated with the points of mesh Ωₕ.
Bramble.boundary_indices — Function
boundary_indices(idxs::CartesianIndices{D}) -> NTuple{2D, CartesianIndices{D}}
boundary_indices(Ωₕ::AbstractMeshType{D}) -> NTuple{2D, CartesianIndices{D}}Return all boundary facets of a CartesianIndices domain or mesh Ωₕ as a tuple of CartesianIndices.
Bramble.interior_indices — Function
interior_indices(indices::CartesianIndices{D}) -> CartesianIndices{D}
interior_indices(Ωₕ::AbstractMeshType{D}) -> CartesianIndices{D}Compute the CartesianIndices representing the interior of a domain or mesh, excluding all boundary points. Dimensions with a length of one or less remain unchanged.
Bramble.is_boundary_index — Function
is_boundary_index(idxs::CartesianIndices{D}, idx) -> Bool
is_boundary_index(Ωₕ::AbstractMeshType, idx) -> BoolDetermine whether index idx lies on the boundary of idxs or mesh Ωₕ.
Bramble.index_in_marker — Function
index_in_marker(Ωₕ::AbstractMeshType, label::Symbol) -> BitVectorReturn the BitVector indicator associated with marker label in mesh Ωₕ.
If label is not directly found in the mesh markers, its coordinate-aligned or viewpoint boundary alias (e.g. :xmin ↔ :left in 2D, :xmin ↔ :back in 3D) is consulted if available.
Mesh adaptation and mutation
Bramble.iterative_refinement! — Function
iterative_refinement!(Ωₕ::AbstractMeshType, [domain_markers::DomainMarkers]) -> AbstractMeshTypeRefine the mesh Ωₕ in-place by halving each existing cell (inserting new points at midpoints). If domain markers are supplied, they are re-evaluated onto the refined grid points.
Without domain_markers, any custom marker Ωₕ carries beyond :boundary/:interior has no domain here to re-derive it from, so this throws an ArgumentError rather than silently dropping it. Pass domain_markers (the same ones the mesh was built with, or equivalent) to keep them.
Bramble.change_points! — Function
change_points!(Ωₕ::AbstractMeshType, [domain_markers::DomainMarkers], pts) -> AbstractMeshTypeUpdate the coordinates of mesh Ωₕ in-place using new point coordinates in pts, recalculating all cached half-points and cell spacings.
Bramble.set_points! — Function
set_points!(Ωₕ::Mesh1D, pts::AbstractVector) -> NothingOverride the grid coordinates in Ωₕ. Recalculates cached spacings, half_points, and half_spacings.
Grid spaces
Function spaces
Bramble.ScalarGridSpace — Type
ScalarGridSpace(mesh::MType, weights::SpaceWeights{D, VT})
ScalarGridSpace{D, T, VT, MType}(mesh::MType, weights::SpaceWeights{D, VT})Represents a function space for scalar fields defined on a mesh.
A ScalarGridSpace pairs the mesh with the precomputed weight vectors (SpaceWeights) its discrete inner products need.
Fields
mesh::MType: the underlying mesh of the grid space.weights::SpaceWeights{D, VT}: precomputed inner product weight vectors.
Discrete inner products
The weights object stores vectors for different discrete $L^2$ inner products on the space of grid functions. They are defined as follows:
- :innerₕ: The standard discrete $L^2$ inner product, weighted by the cell measure $|\square_k|$.
- 1D case:
\[(u_h, v_h)_h = \sum_{i=1}^{N_x} |\square_{i}| u_h(x_i) v_h(x_i)\]
- 2D case:
\[(u_h, v_h)_h = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y} |\square_{i,j}| u_h(x_i,y_j) v_h(x_i,y_j)\]
- 3D case:
\[(u_h, v_h)_h = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y}\sum_{l=1}^{N_z} |\square_{i,j,l}| u_h(x_i,y_j,z_l) v_h(x_i,y_j,z_l)\]
Here, $|\cdot|$ denotes the measure of the set (length, area, or volume). See cell_measure for details.
- :inner₊, :inner₊ₓ, :inner₊ᵧ, :inner₊₂: Modified discrete $L^2$ inner products, weighted by a mix of forward/backward spacings ($h_k$) and cell widths ($h_{k+1/2}$).
- 1D case (
:inner₊):
\[(u_h, v_h)_+ = \sum_{i=1}^{N_x} h_{i} u_h(x_i) v_h(x_i)\]
- 2D case (
:inner₊ₓ,:inner₊ᵧ):
\[(u_h, v_h)_{+x} = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y} h_{x,i} h_{y,j+1/2} u_h(x_i,y_j) v_h(x_i,y_j)\]
\[(u_h, v_h)_{+y} = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y} h_{x,i+1/2} h_{y,j} u_h(x_i,y_j) v_h(x_i,y_j)\]
- 3D case (
:inner₊ₓ,:inner₊ᵧ,:inner₊₂):
\[(u_h, v_h)_{+x} = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y}\sum_{l=1}^{N_z} h_{x,i} h_{y,j+1/2} h_{z,l+1/2} u_h(x_i,y_j,z_l) v_h(x_i,y_j,z_l)\]
\[(u_h, v_h)_{+y} = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y}\sum_{l=1}^{N_z} h_{x,i+1/2} h_{y,j} h_{z,l+1/2} u_h(x_i,y_j,z_l) v_h(x_i,y_j,z_l)\]
\[(u_h, v_h)_{+z} = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y}\sum_{l=1}^{N_z} h_{x,i+1/2} h_{y,j+1/2} h_{z,l} u_h(x_i,y_j,z_l) v_h(x_i,y_j,z_l)\]
Bramble.CompositeGridSpace — Type
CompositeGridSpace(spaces::Tuple)
CompositeGridSpace{N}(spaces::Spaces) where {N, Spaces <: Tuple}
CompositeGridSpace{N, Spaces}(spaces::Spaces) where {N, Spaces <: Tuple}A CompositeGridSpace represents a grid space formed by composing N individual sub-spaces. It is immutable and stack-allocatable, wrapping a tuple of spaces.
Fields
spaces::Spaces: the tuple of constituent sub-spaces.
Bramble.gridspace — Function
gridspace(Ωₕ::AbstractMeshType{D}) -> ScalarGridSpace{D}Constructs a ScalarGridSpace defined on the mesh Ωₕ, precomputing the inner product weights listed in ScalarGridSpace.
Scratch memory is supplied explicitly by callers through in-place mutating operators (such as D₋ₓ!(vₕ, uₕ)), avoiding hidden internal vector buffers.
gridspace(Ωₕ::AbstractMeshType, ::Val{N}) where N -> CompositeGridSpace{N}
gridspace(Ωₕ::AbstractMeshType, N::Int) -> CompositeGridSpace{N}Constructs a vector function space with N components on mesh Ωₕ. The underlying scalar space and its weights are computed once and shared across components.
N == 1 yields the ScalarGridSpace itself rather than a one-component composite, for both spellings. The element interface is uniform either way: uₕ(1) and components(uₕ) work on a scalar element.
The Val form is always type stable. The Int form is stable wherever N is a literal or otherwise constant-foldable, and returns a small Union when N is only known at run time; prefer Val on hot paths.
Bramble.vector_gridspace — Function
vector_gridspace(Ωₕ::AbstractMeshType, [N = dim(Ωₕ)]) -> CompositeGridSpaceConvenience constructor for a vector grid space on mesh Ωₕ. If N is omitted, it defaults to the spatial dimension of the mesh (dim(Ωₕ)).
Space properties and degrees of freedom
Bramble.ndofs — Function
ndofs(Wₕ::AbstractSpaceType) -> Int
ndofs(Wₕ::AbstractSpaceType, ::Type{Tuple}) -> NTuple{N, Int}Returns the total number of degrees of freedom (DOFs) in the function space Wₕ.
On a ScalarGridSpace, ndofs(Wₕ, Tuple) is the grid's shape — one entry per spatial dimension (Nₓ, Nᵧ, ...). On a CompositeGridSpace, it is instead one entry per component, each that component's own (scalar) DOF count — unrelated to spatial dimension, and not a shape a prod should be taken over. Code that does not know in advance which kind of space it was given should reach for one of the two unambiguous forms instead: npoints(mesh(Wₕ), Tuple) for the grid shape, or map(ndofs, spaces(Wₕ)) for the per-component counts. Mixing them up is not hypothetical: src/space/operators/difference.jl's _grid_dims avoids ndofs(Wₕ, Tuple) for exactly this reason, after a 3-component 4×6 space addressed 13824 slots into 72 and segfaulted under an @inbounds engine.
Bramble.weights — Function
weights(Wₕ::ScalarGridSpace) -> SpaceWeights
weights(Wₕ::ScalarGridSpace, ::Innerh) -> AbstractVector
weights(Wₕ::ScalarGridSpace, ::Innerplus) -> NTuple{D, AbstractVector}
weights(Wₕ::ScalarGridSpace, ::InnerProductType, i::Int) -> AbstractVectorReturns the precomputed weight vectors for discrete inner products.
The weights are diagonal matrices (stored as vectors) used in computing discrete $L^2$ inner products. They represent cell measures or staggered grid spacings.
Methods
weights(Wₕ)- Returns the fullSpaceWeightsstructweights(Wₕ, Innerh())- Returns weights for standard $L^2$ inner product (cell volumes)weights(Wₕ, Innerplus())- Returns tuple of weights for modified inner products (all directions)weights(Wₕ, Innerplus(), i)- Returns weights for modified inner product in directioniweights(Wₕ, Innerh(), i)- Same asweights(Wₕ, Innerh()); the cell measures do not depend on a direction, soiis accepted and ignored for interface symmetry
Examples
Wₕ = gridspace(Ωₕ)
# Get all weights
w = weights(Wₕ) # Returns SpaceWeights{D, VT}
# Get standard L² weights
w_h = weights(Wₕ, Innerh()) # Vector of cell volumes
# Get modified inner product weights for x-direction
w_plus_x = weights(Wₕ, Innerplus(), 1) # Vector for x-direction
# Use in inner product
result = dot(uₕ.data, w_h, vₕ.data) # Weighted inner productDefined for a ScalarGridSpace only, the same rule normₕ/norm₊ follow: a composite grid space's leaves can have different meshes and therefore different weights, so there is no single vector that could correctly answer for the whole composite. A CompositeGridSpace raises a MethodError; take a scalar component of it with components first.
See also: SpaceWeights, Innerh, Innerplus, innerₕ
Bramble.spaces — Function
spaces(Wₕ::AbstractSpaceType) -> TupleReturns the constituent subspace(s) of Wₕ as a tuple.
Bramble.space — Function
space(Wₕ::AbstractSpaceType) -> AbstractSpaceTypeReturns the function space Wₕ itself.
space(uₕ::VectorElement) -> AbstractSpaceTypeReturns the grid space associated with VectorElement uₕ.
space(sd::Semidiscretization)Return the test space the semidiscretisation was built on.
Bramble.ncomponents — Function
ncomponents(Wₕ::AbstractSpaceType) -> Int
ncomponents(::Type{<:AbstractSpaceType}) -> IntReturns the number of field components of the function space (e.g. 1 for scalar, D for vector).
Vector elements and grid functions
Bramble.VectorElement — Type
VectorElement(data::VT, space::S)
VectorElement{S, T, VT}(data::VT, space::S)Represents a grid function (a vector) that belongs to a specific function space.
This is a wrapper that bundles the raw numerical data (the vector data) with its parent space. The space provides the essential context, such as the underlying mesh and associated operators. By subtyping AbstractVector, a VectorElement can be used just like a regular Julia vector in most operations.
Fields
data::VT: the raw vector data containing the degrees of freedom.space::S: the parent function space to which this vector belongs.
Bramble.element — Function
element(Wₕ::AbstractSpaceType) -> VectorElement
element(Wₕ::AbstractSpaceType, α::Number) -> VectorElementReturns a VectorElement for grid space Wₕ with uninitialized components. If α is provided, the components are initialized to α.
element(Wₕ::AbstractSpaceType, ::Type{T}) -> VectorElementReturns a VectorElement for grid space Wₕ holding coefficients of type T, with uninitialized components.
The coefficients of a grid function and the coordinates of the mesh under it are two different things, and this is where they part company. element(Wₕ) takes its type from the backend, which is the mesh's own; this takes whatever is asked for, and the container follows through similar, so a Vector backend gives a Vector{T} and a device array gives a device array of T.
The case this exists for is automatic differentiation: a ForwardDiff.Dual grid function over an ordinary Float64 mesh, so that the geometry is not differentiated along with the field. See Rₕ, which uses it to give back an element of whatever type the restricted function returns.
element(Wₕ::AbstractSpaceType, v::AbstractVector) -> VectorElementReturns a VectorElement for a grid space Wₕ with the same coefficients as v.
Base.parent — Method
parent(uₕ::VectorElement) -> AbstractVectorReturns the coefficient vector containing the degrees of freedom of VectorElement uₕ.
Base.reshape — Method
reshape(uₕ::VectorElement)Reshapes the flat coefficient vector of uₕ into a multidimensional array that matches the logical layout of the grid points.
- For a scalar space, this returns a D-dimensional array.
- For an N-component vector space, it returns an N-tuple of arrays, one for each component.
This zero-argument form is specific to VectorElement and does not conflict with reshape(A, dims); it does shadow Base.reshape(A) = reshape(A, ())'s 0-dimensional result for this type specifically, in favor of the shape a grid function actually has. Replaces the former to_matrix outright (gpena/Bramble.jl#73).
Bramble.components — Function
components(uₕ::VectorElement) -> TupleReturns an NTuple of VectorElement views, one per leaf of uₕ's space, depth-first — the same leaf a matching-index uₕ(i) returns, regardless of nesting.
components(op::Union{TrialFunction, TestFunction}) -> Tuple
components(op::LazyOp, N::Integer) -> Tuple
components(op::LazyOp, space::AbstractSpaceType) -> TupleReturns an NTuple of components of the symbolic trial or test function, suitable for tuple destructuring: (u, v) = components(p).
Bramble.component_range — Function
component_range(Wₕ::CompositeGridSpace, i::Int) -> UnitRange{Int}Returns the degree-of-freedom index range for the i-th leaf of composite space Wₕ, numbered depth-first (see leaf_spaces_offsets) — the same numbering uₕ(i) and dirichlet_components use, so it agrees with them regardless of how deeply Wₕ nests.
Bramble.component_ranges — Function
component_ranges(Wₕ::CompositeGridSpace) -> NTuple{N, UnitRange{Int}}N is the number of scalar leaves underneath Wₕ, counting through any nesting.
Returns the degree-of-freedom ranges for every leaf of Wₕ, depth-first — see component_range.
Base.:* — Method
f::Function * uₕ::VectorElement -> VectorElement
uₕ::VectorElement * f::Function -> VectorElementProject the continuous function f onto uₕ's own space and scale uₕ pointwise by it (gpena/Bramble.jl#197): Rₕ(space(uₕ), f) .* uₕ.
A plain Function has no meaning as a grid function on its own – a form built from innerₕ(f, v) restricts it first through source_function/form's own lowering, but f * uₕ outside a form (or a continuous spatial condition multiplying a grid function, (x -> x[1] < 1) * uₕ) had no operator to reach that with:
julia> (x -> x[1] < 1) * uₕ
ERROR: MethodError: no method matching *(::Function, ::VectorElement)now restricts f to space(uₕ) and multiplies elementwise, so the result is an ordinary VectorElement, usable anywhere one is – including as a SourceFunction-lowered term inside another form.
Restriction and averaging operators
Bramble.Rₕ — Function
Rₕ(Wₕ::AbstractSpaceType, f; markers = ()) -> VectorElementStandard nodal restriction operator. Evaluates f at the grid points of mesh(Wₕ) and returns the result as a VectorElement.
Arguments
Wₕ::AbstractSpaceType: grid space on which to restrictf.f: function of one grid point. It receives a scalar on a 1D mesh and anNTuple{D}on aD-dimensional one, never anSVector.
Keywords
markers::NTuple{N,Symbol}: restrict evaluation to the named marked regions, leaving every other entry zero.
Examples
Rₕ(Wₕ, x -> sin(x)) # 1D: x is a Float64
Rₕ(Wₕ, x -> sin(x[1]) * x[2]) # 2D: x is a Tuple{Float64,Float64}
# Vector-valued spaces:
Rₕ(Vₕ, (f₁, f₂)) # one function per component
Rₕ(Vₕ, x -> (f₁(x), f₂(x))) # one function returning all componentsPrefer x -> (f₁(x), f₂(x)) when components share computation, as it evaluates once per grid point on a space whose components share one mesh, whereas (f₁, f₂) always evaluates each component function separately. On a heterogeneous composite — components built over different meshes — the single-function form gives up that advantage, since there is no grid point shared by every component to evaluate it at only once.
Bramble.Rₕ! — Function
Rₕ!(uₕ::VectorElement, f; markers = ()) -> VectorElementIn-place version of the restriction operator Rₕ. Evaluates f at the grid points and writes the result into uₕ. Returns uₕ.
Arguments
uₕ::VectorElement: pre-allocated element to write into.f: function of one grid point. It receives a scalar on a 1D mesh and anNTuple{D}on aD-dimensional one, never anSVector.
Keywords
markers::NTuple{N,Symbol}: restrict evaluation to the named marked regions, leaving every other entry zero. Several markers act as a union.
Examples
Rₕ!(uₕ, x -> sin(x)) # 1D: x is a Float64
Rₕ!(uₕ, x -> sin(x[1]) * cos(x[2])) # 2D: x is a Tuple{Float64,Float64}
# only the points carrying the :left marker; the rest stay zero
Rₕ!(uₕ, x -> 1.0; markers = (:left,))For an N-component element either shape of f works and both give the same result; the single vector-valued function is evaluated once per grid point when every component shares the same mesh, whereas the tuple always evaluates each component function separately. On a heterogeneous composite — components built over different meshes — the single-function form is instead re-evaluated once per component, since there is no grid point shared by every component to evaluate it at only once:
Rₕ!(uₕ, (f₁, f₂)) # one function per component
Rₕ!(uₕ, x -> (f₁(x), f₂(x))) # one function returning all componentsBramble.avgₕ — Function
avgₕ(Wₕ::AbstractSpaceType, f; quad_points = AVG_QUAD_POINTS, markers = ()) -> VectorElementReturns a VectorElement with the average of function f with respect to the cell_measure of mesh(Wₕ) around each grid point.
Each cell average is a tensor-product Gauss-Legendre rule with quad_points points per direction, exact for polynomials of degree 2 * quad_points - 1.
Arguments
Wₕ::AbstractSpaceType: grid space on which to averagef.f: function of one grid point. Receives coordinates as a scalar on 1D meshes or anNTuple{D}onD-dimensional meshes, never anSVector.
Keywords
quad_points::Union{Integer, Val}: points per direction, per cell. Defaults toVal(AVG_QUAD_POINTS).markers::NTuple{N, Symbol}: restrict evaluation to the named marked regions, leaving every other entry zero.
Examples
avgₕ(Wₕ, x -> sin(x))
avgₕ(Wₕ, x -> sin(x[1]) * x[2]; quad_points = Val(4))Bramble.avgₕ! — Function
avgₕ!(uₕ::VectorElement, f; quad_points = AVG_QUAD_POINTS, markers = ()) -> VectorElementIn-place version of the averaging operator avgₕ. Returns uₕ.
Evaluates the tensor-product Gauss-Legendre cell average of f and writes the result into uₕ.
Arguments
uₕ::VectorElement: pre-allocated element to write into.f: function of one grid point. Receives coordinates as a scalar on 1D meshes or anNTuple{D}onD-dimensional meshes.
Keywords
quad_points::Union{Integer, Val}: points per direction, per cell. Defaults toVal(AVG_QUAD_POINTS). Using aValallows compile-time specialization of the quadrature nodes and weights without boxing.markers::NTuple{N, Symbol}: restrict evaluation to the named marked regions, leaving every other entry zero.
Interpolation between grid spaces
Moving a grid function from one mesh to another — the piecewise (multi)linear interpolant, named after Rₕ/Rₕ!'s own Xₕ/Xₕ! convention. One name, πₕ, with methods that dispatch tells apart by what they are given rather than by different names:
πₕ(Wₕ, uₕ)andπₕ!(dest, src)— the numeric operator, interpolating a grid function's values onto another space's mesh.interpolate_atis the single-point building block both are written in terms of, andinterpolation_matrixis the same interpolant as a sparse matrix rather than applied pointwise.πₕ(uₕ)— the symbolic source, wrapping a grid function's interpolant as an AST leaf, composable withD₋ₓ/M₋ₓ/... insideinnerₕ. For the known side of a linear form.
See the operators tutorial for the numeric side and the pattern this exists for: a heterogeneous composite space whose leaves live on different meshes.
Bramble.interpolate_at — Function
interpolate_at(uₕ::VectorElement, x)The piecewise (multi)linear interpolant of uₕ at the physical point x, using uₕ's own mesh.
Locates the cell of mesh(space(uₕ)) containing x (locate_cell) and blends the $2^D$ grid values at that cell's corners, weighted by x's relative position within it (the standard bilinear/trilinear construction), exact for any affine function of the coordinates and correct on a non-uniform mesh, since it reads the mesh's own point coordinates rather than assuming a fixed step. locate_cell clamps which cell a point outside the mesh is read against to the boundary cell, but the relative position x is weighted by is not itself clamped, so a point outside the mesh is a linear extrapolation along that boundary cell's own slope, not a constant hold of the boundary value.
This is the building block both πₕ!/πₕ (below, the numeric operator) and the one-argument, symbolic πₕ use: x -> interpolate_at(uₕ, x) is itself a valid source function, usable anywhere one is accepted, including directly as Rₕ's own argument: πₕ(Wₕ, src) is Rₕ applied to this one function, not a separate mechanism. Rₕ(Wₕ, f) restricts an arbitrary continuous f; when f happens to be another grid function's own interpolant, restricting it is interpolating it, which is why πₕ generalises Rₕ for the case the source is discrete rather than a closed-form function.
Bramble.πₕ! — Function
πₕ!(dest::VectorElement, src::VectorElement) -> VectorElementFills dest with the piecewise (multi)linear interpolant of src, sampled at dest's own mesh points, providing the in-place numeric interpolation operator named after the Rₕ!/ avgₕ! convention.
interpolate_at(src, ·) is a genuine function of a physical point (evaluable anywhere, not only at src's own grid points), so this is equivalent to Rₕ!(dest, x -> interpolate_at(src, x)). dest and src may be built over entirely different meshes. Rₕ! handles evaluating the interpolant at each of dest's grid points, following dest's backend execution_policy.
Every call here re-locates, via locate_cell, which cell of src's mesh each of dest's points falls in. Interpolating repeatedly between the same two meshes (a time loop transferring a coefficient between two composite leaves, say) should build that once instead: see the interpolation_matrix-based method below, following the same "build the pattern once" shape allocate_system_matrix/assemble! already use.
πₕ!(dest::VectorElement, P::SparseMatrixCSC, src::VectorElement) -> VectorElementFills dest via a precomputed interpolation_matrix P instead of re-locating each destination point's cell: parent(dest) .= P * parent(src), computed in place via mul! (zero allocations, once P and dest already exist).
P must be interpolation_matrix(space(dest), space(src)) (or an equal-shape matrix built the same way) – a mismatched size throws the usual DimensionMismatch from mul!. Repeated interpolation between the same two meshes should build P once and reuse it here every subsequent call, exactly as allocate_system_matrix/assemble! split the sparsity pattern (expensive, built once) from refilling values (cheap, every step):
P = interpolation_matrix(space(dest), space(src))
for step in 1:nsteps
Rₕ!(src, coefficient_at(step))
πₕ!(dest, P, src) # no locate_cell search, zero allocations
endBramble.πₕ — Function
πₕ(Wₕ::ScalarGridSpace, src::VectorElement) -> VectorElementEvaluates Rₕ(Wₕ, x -> interpolate_at(src, x)); see πₕ!. Distinguished by multiple dispatch from the one-argument symbolic wrapper πₕ(uₕ) in form/operators/interpolation.jl. The element type is promoted from Wₕ's and src's own, so interpolating a Dual-valued src yields a Dual-valued result on an undifferentiated Wₕ.
πₕ(uₕ::VectorElement) -> LazyOpThe interpolant of uₕ, as a symbolic source term; usable anywhere a source is, including inside another operator: innerₕ(D₋ₓ(πₕ(uₕ)), D₋ₓ(v)) differentiates the interpolated field the same way D₋ₓ differentiates any other source, innerₕ(M₋ₓ(πₕ(uₕ)), v) averages it, and so on. This enables a coupled form to evaluate a leaf's grid function on a different leaf's mesh.
Built as source_function(x -> interpolate_at(uₕ, x), Val(D)): a SourceFunction's own local_stencil evaluates its function at the current point of whichever mesh is being walked, so uₕ can originate from another leaf without special handling; the interpolation occurs once per point inside interpolate_at, where ordinary source function calls occur.
Bramble.interpolation_matrix — Function
interpolation_matrix(Wdest::ScalarGridSpace, Wsrc::ScalarGridSpace) -> SparseMatrixCSCThe piecewise (multi)linear interpolant of πₕ/interpolate_at as a sparse matrix P rather than applied pointwise: P * parent(src) ≈ parent(πₕ(Wdest, src)) for any src::VectorElement over Wsrc. P is ndofs(Wdest) × ndofs(Wsrc), generally rectangular (since Wdest and Wsrc are built over different meshes), with at most $2^D$ nonzero entries per row: the corner weights of the source cell locate_cell places that destination point in.
Unlike D₋ₓ(Wₕ) and the other operator matrices, this is always a SparseMatrixCSC, regardless of either space's own backend matrix_type. Those matrices are built from shift (a fixed diagonal offset generalized via Kronecker products), but which source cell a destination point falls in has no such regular structure across two independent meshes: it is genuinely sparse and irregular, assembled directly from locate_cell rather than composed from a handful of shifts. Converting the result to another matrix type, where that is meaningful, is left to the caller.
Difference, jump and average operators
The finite difference, the jump and the average, per coordinate and over every coordinate at once. See the operators tutorial.
The unscaled differences (diff₋ₓ and its siblings) are the plain, undivided differences these are built from. They are reached as Bramble.diff₋ₓ rather than brought into scope by using Bramble: they have no form-layer node, so they cannot appear inside a bilinear form, and in a form the undivided forward difference is spelled jumpₓ, which says which of the two is meant.
Bramble.diff₋ₓ — Function
diff₋ₓ(arg)The backward unscaled difference along the x direction, $u_{i} - u_{i-1}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for backward_difference(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.diff₋ₓ! — Function
diff₋ₓ!(vₕ, uₕ)The backward unscaled difference of uₕ along the x direction, $u_{i} - u_{i-1}$, written into vₕ.
The in-place form of diff₋ₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(diff₋ₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_difference!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.diff₋ᵧ — Function
diff₋ᵧ(arg)The backward unscaled difference along the y direction, $u_{i} - u_{i-1}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for backward_difference(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.diff₋ᵧ! — Function
diff₋ᵧ!(vₕ, uₕ)The backward unscaled difference of uₕ along the y direction, $u_{i} - u_{i-1}$, written into vₕ.
The in-place form of diff₋ᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(diff₋ᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_difference!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.diff₋₂ — Function
diff₋₂(arg)The backward unscaled difference along the z direction, $u_{i} - u_{i-1}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for backward_difference(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.diff₋₂! — Function
diff₋₂!(vₕ, uₕ)The backward unscaled difference of uₕ along the z direction, $u_{i} - u_{i-1}$, written into vₕ.
The in-place form of diff₋₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(diff₋₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_difference!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.diff₋ₕ — Function
diff₋ₕ(arg)The backward unscaled difference of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, diff₋ₕ(uₕ) is (backward_difference(uₕ, Val(1)), backward_difference(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for backward_difference.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
Bramble.diff₊ₓ — Function
diff₊ₓ(arg)The forward unscaled difference along the x direction, $u_{i+1} - u_i$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for forward_difference(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.diff₊ₓ! — Function
diff₊ₓ!(vₕ, uₕ)The forward unscaled difference of uₕ along the x direction, $u_{i+1} - u_i$, written into vₕ.
The in-place form of diff₊ₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(diff₊ₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_difference!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.diff₊ᵧ — Function
diff₊ᵧ(arg)The forward unscaled difference along the y direction, $u_{i+1} - u_i$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for forward_difference(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.diff₊ᵧ! — Function
diff₊ᵧ!(vₕ, uₕ)The forward unscaled difference of uₕ along the y direction, $u_{i+1} - u_i$, written into vₕ.
The in-place form of diff₊ᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(diff₊ᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_difference!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.diff₊₂ — Function
diff₊₂(arg)The forward unscaled difference along the z direction, $u_{i+1} - u_i$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for forward_difference(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.diff₊₂! — Function
diff₊₂!(vₕ, uₕ)The forward unscaled difference of uₕ along the z direction, $u_{i+1} - u_i$, written into vₕ.
The in-place form of diff₊₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(diff₊₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_difference!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.diff₊ₕ — Function
diff₊ₕ(arg)The forward unscaled difference of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, diff₊ₕ(uₕ) is (forward_difference(uₕ, Val(1)), forward_difference(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for forward_difference.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
Bramble.D₋ₓ — Function
D₋ₓ(arg)The backward finite difference along the x direction, $\frac{u_{i} - u_{i-1}}{h_i}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for backward_finite_difference(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
D₋ₓ(op::LazyOp{D}) where D
D₊ₓ(op::LazyOp{D}) where D
D₋ᵧ(op::LazyOp{D}) where D
D₊ᵧ(op::LazyOp{D}) where D
D₋₂(op::LazyOp{D}) where D
D₊₂(op::LazyOp{D}) where DSymbolic finite difference operators in specified coordinate directions (x, y, z).
Bramble.D₋ₓ! — Function
D₋ₓ!(vₕ, uₕ)The backward finite difference of uₕ along the x direction, $\frac{u_{i} - u_{i-1}}{h_i}$, written into vₕ.
The in-place form of D₋ₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(D₋ₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_finite_difference!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.D₋ᵧ — Function
D₋ᵧ(arg)The backward finite difference along the y direction, $\frac{u_{i} - u_{i-1}}{h_i}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for backward_finite_difference(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.D₋ᵧ! — Function
D₋ᵧ!(vₕ, uₕ)The backward finite difference of uₕ along the y direction, $\frac{u_{i} - u_{i-1}}{h_i}$, written into vₕ.
The in-place form of D₋ᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(D₋ᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_finite_difference!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.D₋₂ — Function
D₋₂(arg)The backward finite difference along the z direction, $\frac{u_{i} - u_{i-1}}{h_i}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for backward_finite_difference(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.D₋₂! — Function
D₋₂!(vₕ, uₕ)The backward finite difference of uₕ along the z direction, $\frac{u_{i} - u_{i-1}}{h_i}$, written into vₕ.
The in-place form of D₋₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(D₋₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_finite_difference!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.∇₋ₕ — Function
∇₋ₕ(arg)The backward finite difference of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, ∇₋ₕ(uₕ) is (backward_finite_difference(uₕ, Val(1)), backward_finite_difference(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for backward_finite_difference.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
∇₋ₕ(op::LazyOp{D}) where DSymbolic backward gradient operator.
∇₋ₕ(ops::Tuple)Applies the backward gradient component-wise to a tuple of scalar symbolic functions (e.g. the velocity components (u1, u2) of a composite space). Returns a tuple of gradient tuples, one per component.
Bramble.D₊ₓ — Function
D₊ₓ(arg)The forward finite difference along the x direction, $\frac{u_{i+1} - u_i}{h_i}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for forward_finite_difference(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.D₊ₓ! — Function
D₊ₓ!(vₕ, uₕ)The forward finite difference of uₕ along the x direction, $\frac{u_{i+1} - u_i}{h_i}$, written into vₕ.
The in-place form of D₊ₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(D₊ₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_finite_difference!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.D₊ᵧ — Function
D₊ᵧ(arg)The forward finite difference along the y direction, $\frac{u_{i+1} - u_i}{h_i}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for forward_finite_difference(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.D₊ᵧ! — Function
D₊ᵧ!(vₕ, uₕ)The forward finite difference of uₕ along the y direction, $\frac{u_{i+1} - u_i}{h_i}$, written into vₕ.
The in-place form of D₊ᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(D₊ᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_finite_difference!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.D₊₂ — Function
D₊₂(arg)The forward finite difference along the z direction, $\frac{u_{i+1} - u_i}{h_i}$. The unscaled difference is not divided by the grid spacing; the finite difference is.
Alias for forward_finite_difference(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.D₊₂! — Function
D₊₂!(vₕ, uₕ)The forward finite difference of uₕ along the z direction, $\frac{u_{i+1} - u_i}{h_i}$, written into vₕ.
The in-place form of D₊₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(D₊₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_finite_difference!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.∇₊ₕ — Function
∇₊ₕ(arg)The forward finite difference of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, ∇₊ₕ(uₕ) is (forward_finite_difference(uₕ, Val(1)), forward_finite_difference(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for forward_finite_difference.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
∇₊ₕ(op::LazyOp{D}) where DSymbolic forward gradient operator.
∇₊ₕ(ops::Tuple)Applies the forward gradient component-wise to a tuple of scalar symbolic functions, as ∇₋ₕ does. Returns a tuple of gradient tuples, one per component.
The forward difference over the averaged spacing, which is the one that satisfies the discrete summation-by-parts identity $(\textrm{Dstar}_{+x} u_h, v_h)_h = -(u_h, D_{-x} v_h)_{+x}$ for grid functions vₕ vanishing on the boundary.
Bramble.Dstar₊ₓ — Function
Dstar₊ₓ(arg)The forward difference of uₕ along the x direction over the averaged spacing, $\frac{u_{i+1} - u_i}{(h_i + h_{i+1})/2}$.
Alias for forward_star_difference(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. The last point along x is truncated to zero.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Dstar₊ₓ(op::LazyOp{D}) where D
Dstar₊ᵧ(op::LazyOp{D}) where D
Dstar₊₂(op::LazyOp{D}) where DSymbolic starred forward differences in the coordinate directions.
Bramble.Dstar₊ₓ! — Function
Dstar₊ₓ!(vₕ, uₕ)The forward difference of uₕ along the x direction over the averaged spacing, $\frac{u_{i+1} - u_i}{(h_i + h_{i+1})/2}$, written into vₕ.
The in-place form of Dstar₊ₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dstar₊ₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_star_difference!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.Dstar₊ᵧ — Function
Dstar₊ᵧ(arg)The forward difference of uₕ along the y direction over the averaged spacing, $\frac{u_{i+1} - u_i}{(h_i + h_{i+1})/2}$.
Alias for forward_star_difference(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. The last point along y is truncated to zero.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.Dstar₊ᵧ! — Function
Dstar₊ᵧ!(vₕ, uₕ)The forward difference of uₕ along the y direction over the averaged spacing, $\frac{u_{i+1} - u_i}{(h_i + h_{i+1})/2}$, written into vₕ.
The in-place form of Dstar₊ᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dstar₊ᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_star_difference!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.Dstar₊₂ — Function
Dstar₊₂(arg)The forward difference of uₕ along the z direction over the averaged spacing, $\frac{u_{i+1} - u_i}{(h_i + h_{i+1})/2}$.
Alias for forward_star_difference(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. The last point along z is truncated to zero.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.Dstar₊₂! — Function
Dstar₊₂!(vₕ, uₕ)The forward difference of uₕ along the z direction over the averaged spacing, $\frac{u_{i+1} - u_i}{(h_i + h_{i+1})/2}$, written into vₕ.
The in-place form of Dstar₊₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dstar₊₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_star_difference!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.Dstar₊ₕ — Function
Dstar₊ₕ(arg)The starred forward difference of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, Dstar₊ₕ(uₕ) is (forward_star_difference(uₕ, Val(1)), forward_star_difference(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for forward_star_difference.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
The centered difference, over the span its stencil covers. It reproduces the derivative of an affine function exactly on any grid, and is skew-symmetric in innerₕ for grid functions vanishing on the boundary.
Bramble.Dcₓ — Function
Dcₓ(arg)The centered difference of uₕ along the x direction, $\frac{u_{i+1} - u_{i-1}}{h_i + h_{i+1}}$.
Alias for centered_difference(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. The first and last points along x are truncated to zero, so the mesh needs at least three points along x and an ArgumentError is thrown when it has fewer.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Dcₓ(op::LazyOp{D}) where D
Dcᵧ(op::LazyOp{D}) where D
Dc₂(op::LazyOp{D}) where DSymbolic centered differences in the coordinate directions.
Bramble.Dcₓ! — Function
Dcₓ!(vₕ, uₕ)The centered difference of uₕ along the x direction, $\frac{u_{i+1} - u_{i-1}}{h_i + h_{i+1}}$, written into vₕ.
The in-place form of Dcₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dcₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for centered_difference!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.Dcᵧ — Function
Dcᵧ(arg)The centered difference of uₕ along the y direction, $\frac{u_{i+1} - u_{i-1}}{h_i + h_{i+1}}$.
Alias for centered_difference(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. The first and last points along y are truncated to zero, so the mesh needs at least three points along y and an ArgumentError is thrown when it has fewer.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.Dcᵧ! — Function
Dcᵧ!(vₕ, uₕ)The centered difference of uₕ along the y direction, $\frac{u_{i+1} - u_{i-1}}{h_i + h_{i+1}}$, written into vₕ.
The in-place form of Dcᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dcᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for centered_difference!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.Dc₂ — Function
Dc₂(arg)The centered difference of uₕ along the z direction, $\frac{u_{i+1} - u_{i-1}}{h_i + h_{i+1}}$.
Alias for centered_difference(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. The first and last points along z are truncated to zero, so the mesh needs at least three points along z and an ArgumentError is thrown when it has fewer.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.Dc₂! — Function
Dc₂!(vₕ, uₕ)The centered difference of uₕ along the z direction, $\frac{u_{i+1} - u_{i-1}}{h_i + h_{i+1}}$, written into vₕ.
The in-place form of Dc₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dc₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for centered_difference!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.Dcₕ — Function
Dcₕ(arg)The centered difference of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, Dcₕ(uₕ) is (centered_difference(uₕ, Val(1)), centered_difference(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for centered_difference.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
Dcₕ(op::LazyOp{D}) where D
Dstar₊ₕ(op::LazyOp{D}) where D
∇ₕ(op::LazyOp{D}) where DThe vector forms: every direction at once, as a D-tuple of nodes. In one dimension there is only one direction, so the node itself is returned rather than a one-element tuple, as ∇₋ₕ and ∇₊ₕ already do.
The cross-weighted centered difference, the same two one-sided differences weighted by the opposite spacings. It reproduces the derivative of a quadratic exactly on any grid, and so is second order on a non-uniform one where Dcₓ is first.
Bramble.Dₕₓ — Function
Dₕₓ(arg)The cross-weighted centered difference of uₕ along the x direction, the backward differences at $x_{i+1}$ and $x_i$ weighted by $h_i$ and $h_{i+1}$.
Alias for cross_weighted_difference(arg, Val(1)). Second order on a non-uniform grid, where Dcₓ is first. arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. Unlike Dcₓ, the first and last points along x are not truncated: with no neighbour on the far side, each collapses to the one-sided difference the near side still gives, D₊ₓ at the first point and D₋ₓ at the last. The mesh still needs at least three points along x, and an ArgumentError is thrown when it has fewer.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Dₕₓ(op::LazyOp{D}) where D
Dₕᵧ(op::LazyOp{D}) where D
Dₕ₂(op::LazyOp{D}) where DSymbolic cross-weighted centered differences in the coordinate directions.
Bramble.Dₕₓ! — Function
Dₕₓ!(vₕ, uₕ)The cross-weighted centered difference of uₕ along the x direction, the backward differences at $x_{i+1}$ and $x_i$ weighted by $h_i$ and $h_{i+1}$, written into vₕ.
The in-place form of Dₕₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dₕₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for cross_weighted_difference!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.Dₕᵧ — Function
Dₕᵧ(arg)The cross-weighted centered difference of uₕ along the y direction, the backward differences at $x_{i+1}$ and $x_i$ weighted by $h_i$ and $h_{i+1}$.
Alias for cross_weighted_difference(arg, Val(2)). Second order on a non-uniform grid, where Dcᵧ is first. arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. Unlike Dcᵧ, the first and last points along y are not truncated: with no neighbour on the far side, each collapses to the one-sided difference the near side still gives, D₊ᵧ at the first point and D₋ᵧ at the last. The mesh still needs at least three points along y, and an ArgumentError is thrown when it has fewer.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.Dₕᵧ! — Function
Dₕᵧ!(vₕ, uₕ)The cross-weighted centered difference of uₕ along the y direction, the backward differences at $x_{i+1}$ and $x_i$ weighted by $h_i$ and $h_{i+1}$, written into vₕ.
The in-place form of Dₕᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dₕᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for cross_weighted_difference!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.Dₕ₂ — Function
Dₕ₂(arg)The cross-weighted centered difference of uₕ along the z direction, the backward differences at $x_{i+1}$ and $x_i$ weighted by $h_i$ and $h_{i+1}$.
Alias for cross_weighted_difference(arg, Val(3)). Second order on a non-uniform grid, where Dc₂ is first. arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement. Unlike Dc₂, the first and last points along z are not truncated: with no neighbour on the far side, each collapses to the one-sided difference the near side still gives, D₊₂ at the first point and D₋₂ at the last. The mesh still needs at least three points along z, and an ArgumentError is thrown when it has fewer.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.Dₕ₂! — Function
Dₕ₂!(vₕ, uₕ)The cross-weighted centered difference of uₕ along the z direction, the backward differences at $x_{i+1}$ and $x_i$ weighted by $h_i$ and $h_{i+1}$, written into vₕ.
The in-place form of Dₕ₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(Dₕ₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for cross_weighted_difference!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.∇ₕ — Function
∇ₕ(arg)The cross-weighted centered difference of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, ∇ₕ(uₕ) is (cross_weighted_difference(uₕ, Val(1)), cross_weighted_difference(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for cross_weighted_difference. The centered counterpart of ∇₋ₕ and ∇₊ₕ, built from Dₕₓ rather than from the one-sided differences.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
Jumps across an interface, $\llbracket u \rrbracket = u_{i+1} - u_i$. There is one of these rather than a forward and a backward pair: a jump belongs to the interface between two cells, not to a direction of travel across it.
Bramble.jumpₓ — Function
jumpₓ(arg)The jump across the interfaces along the x direction, $\\llbracket u \\rrbracket = u_{i+1} - u_i$.
Alias for jump(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
The last point along x has no forward neighbour and is treated as though it were zero, as in diff₊ₓ.
jumpₓ(op::LazyOp{D}) -> JumpNode
jumpᵧ(op::LazyOp{D}) -> JumpNode
jump₂(op::LazyOp{D}) -> JumpNodeSymbolic jumps across the interfaces along coordinate directions $x$, $y$, and $z$.
Bramble.jumpₓ! — Function
jumpₓ!(vₕ, uₕ)The in-place form of jumpₓ: writes the jump into vₕ and returns it, allocating nothing. vₕ and uₕ must belong to the same space and must not be the same object.
Bramble.jumpᵧ — Function
jumpᵧ(arg)The jump across the interfaces along the y direction, $\\llbracket u \\rrbracket = u_{i+1} - u_i$.
Alias for jump(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
The last point along y has no forward neighbour and is treated as though it were zero, as in diff₊ᵧ.
Bramble.jumpᵧ! — Function
jumpᵧ!(vₕ, uₕ)The in-place form of jumpᵧ: writes the jump into vₕ and returns it, allocating nothing. vₕ and uₕ must belong to the same space and must not be the same object.
Bramble.jump₂ — Function
jump₂(arg)The jump across the interfaces along the z direction, $\\llbracket u \\rrbracket = u_{i+1} - u_i$.
Alias for jump(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
The last point along z has no forward neighbour and is treated as though it were zero, as in diff₊₂.
Bramble.jump₂! — Function
jump₂!(vₕ, uₕ)The in-place form of jump₂: writes the jump into vₕ and returns it, allocating nothing. vₕ and uₕ must belong to the same space and must not be the same object.
Bramble.jumpₕ — Function
jumpₕ(arg)The jump of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, jumpₕ(uₕ) is (jump(uₕ, Val(1)), jump(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for jump.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
jumpₕ(op::LazyOp{D})Symbolic jumps across every coordinate direction simultaneously. Returns a JumpNode in 1D, or a NTuple{D, JumpNode} in higher dimensions.
Averages of a point with its neighbour.
Bramble.M₋ₓ — Function
M₋ₓ(arg)The backward average along the x direction, $\frac{u_{i-1} + u_{i}}{2}$.
Alias for backward_average(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
M₋ₓ(op::LazyOp{D}) where D
M₊ₓ(op::LazyOp{D}) where D
M₋ᵧ(op::LazyOp{D}) where D
M₊ᵧ(op::LazyOp{D}) where D
M₋₂(op::LazyOp{D}) where D
M₊₂(op::LazyOp{D}) where DSymbolic averaging operators in specified coordinate directions (x, y, z).
Bramble.M₋ₓ! — Function
M₋ₓ!(vₕ, uₕ)The backward average of uₕ along the x direction, $\frac{u_{i-1} + u_{i}}{2}$, written into vₕ.
The in-place form of M₋ₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(M₋ₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_average!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.M₋ᵧ — Function
M₋ᵧ(arg)The backward average along the y direction, $\frac{u_{i-1} + u_{i}}{2}$.
Alias for backward_average(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.M₋ᵧ! — Function
M₋ᵧ!(vₕ, uₕ)The backward average of uₕ along the y direction, $\frac{u_{i-1} + u_{i}}{2}$, written into vₕ.
The in-place form of M₋ᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(M₋ᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_average!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.M₋₂ — Function
M₋₂(arg)The backward average along the z direction, $\frac{u_{i-1} + u_{i}}{2}$.
Alias for backward_average(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.M₋₂! — Function
M₋₂!(vₕ, uₕ)The backward average of uₕ along the z direction, $\frac{u_{i-1} + u_{i}}{2}$, written into vₕ.
The in-place form of M₋₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(M₋₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for backward_average!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.M₋ₕ — Function
M₋ₕ(arg)The backward average of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, M₋ₕ(uₕ) is (backward_average(uₕ, Val(1)), backward_average(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for backward_average.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
M₋ₕ(op::LazyOp{D}) where DSymbolic backward spatial averaging operator tuple.
Bramble.M₊ₓ — Function
M₊ₓ(arg)The forward average along the x direction, $\frac{u_{i} + u_{i+1}}{2}$.
Alias for forward_average(arg, Val(1)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.M₊ₓ! — Function
M₊ₓ!(vₕ, uₕ)The forward average of uₕ along the x direction, $\frac{u_{i} + u_{i+1}}{2}$, written into vₕ.
The in-place form of M₊ₓ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(M₊ₓ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_average!(vₕ, uₕ, Val(1)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.M₊ᵧ — Function
M₊ᵧ(arg)The forward average along the y direction, $\frac{u_{i} + u_{i+1}}{2}$.
Alias for forward_average(arg, Val(2)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.M₊ᵧ! — Function
M₊ᵧ!(vₕ, uₕ)The forward average of uₕ along the y direction, $\frac{u_{i} + u_{i+1}}{2}$, written into vₕ.
The in-place form of M₊ᵧ: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(M₊ᵧ!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_average!(vₕ, uₕ, Val(2)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.M₊₂ — Function
M₊₂(arg)The forward average along the z direction, $\frac{u_{i} + u_{i+1}}{2}$.
Alias for forward_average(arg, Val(3)). arg is a mesh, a grid space or a VectorElement: the first two give the operator as a sparse matrix, the third applies it and returns a VectorElement.
Accepts a grid function of a scalar or of a composite grid space. On a composite one the operator is applied to each component in turn, and the result is the composite grid function whose components are those results.
Bramble.M₊₂! — Function
M₊₂!(vₕ, uₕ)The forward average of uₕ along the z direction, $\frac{u_{i} + u_{i+1}}{2}$, written into vₕ.
The in-place form of M₊₂: it allocates nothing, where the allocating form allocates its result. Returns vₕ, so it composes: normₕ(M₊₂!(vₕ, uₕ)).
vₕ and uₕ must be grid functions of the same space, and must not be the same object, as every stencil reads neighbours of the target coordinate; aliasing them would read values that have already been overwritten.
Alias for forward_average!(vₕ, uₕ, Val(3)). Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter.
Bramble.M₊ₕ — Function
M₊ₕ(arg)The forward average of arg along every coordinate, as a tuple with one entry per spatial dimension. On a one-dimensional mesh it returns that single entry rather than a one-tuple.
For a 2D space, M₊ₕ(uₕ) is (forward_average(uₕ, Val(1)), forward_average(uₕ, Val(2))). arg is a mesh, a grid space or a VectorElement, as for forward_average.
Accepts a grid function of a scalar or of a composite grid space, componentwise on the latter: each entry of the tuple is then itself a composite grid function.
M₊ₕ(op::LazyOp{D}) where DSymbolic forward spatial averaging operator tuple.
Inner products and norms
Bramble.innerₕ — Function
innerₕ(uₕ::VectorElement, vₕ::VectorElement; markers = ()) -> RealReturns the discrete $L^2$ inner product of the grid functions uₕ and vₕ, weighting each point by its cell measure.
- 1D case
\[(\textrm{u}_h, \textrm{v}_h)_h \vcentcolon = \sum_{i=1}^N |\square_{i}| \textrm{u}_h(x_i) \textrm{v}_h(x_i)\]
- 2D case
\[(\textrm{u}_h, \textrm{v}_h)_h \vcentcolon = \sum_{i=1}^{N_x} \sum_{j=1}^{N_y} |\square_{i,j}| \textrm{u}_h(x_i,y_j) \textrm{v}_h(x_i,y_j)\]
- 3D case
\[(\textrm{u}_h, \textrm{v}_h)_h \vcentcolon = \sum_{i=1}^{N_x} \sum_{j=1}^{N_y} \sum_{l=1}^{N_z} |\square_{i,j,l}| \textrm{u}_h(x_i,y_j) \textrm{v}_h(x_i,y_j)\]
On a CompositeGridSpace it is the inner product of the product space, the sum of the component-wise products:
\[(\textrm{u}_h, \textrm{v}_h)_h = \sum_{c=1}^{N_c} (\textrm{u}_h^{(c)}, \textrm{v}_h^{(c)})_h\]
which is the only meaning it can have, so there is nothing ambiguous about accepting one. The two grid functions must have the same number of components.
markers restricts the sum to the union of the labelled regions' points (a masked sum of the same cell measures, not a surface integral; see the note above _combined_mask).
innerₕ(left::LazyOp{D}, right::LazyOp{D}; markers = ()) -> LazyOp{D}Constructs a symbolic $L^2$ inner product between left and right: a LinearProduct (source × test) if left is source-only (_is_source_only: a source, or a source wrapped in differences/averages/shifts/jumps/restrictions/scales, never a trial function), or a BilinearProduct (trial × test) otherwise.
This applies specifically when left is a LazyOp: a bare Function/Number/VectorElement is unconditionally a source, so those overloads build a LinearProduct directly. When left arrives already wrapped (πₕ(uₕ) or D₋ₓ(πₕ(uₕ))), this check ensures the correct linear AST node is constructed.
markers restricts the assembled term to the union of the labelled regions: a mask on which grid points it contributes to at all.
Bramble.inner₊ — Function
inner₊(uₕ::VectorElement, vₕ::VectorElement) -> Real
inner₊(uₕ::VectorElement, vₕ::VectorElement, ::Type{Tuple}) -> Tuple
inner₊(uₕ::NTuple{D, VectorElement}, vₕ::NTuple{D, VectorElement}) -> RealReturns the discrete modified $L^2$ inner product of the grid functions uₕ and vₕ.
If the Tuple argument is given, it returns a D-tuple of all $\textrm{inner}_{x_i,+}$ applied to its input arguments, where D is the topological dimension of the mesh associated with the elements.
If NTuples of VectorElement are passed as input arguments, it returns the sum of all inner products $(\textrm{u}_h[i],\textrm{v}_h[i])_{+x_i}$.
For VectorElements, the definition is given by
- 1D case
\[(\textrm{u}_h, \textrm{v}_h)_+ \vcentcolon = \sum_{i=1}^{N_x} h_{i} \textrm{u}_h(x_i) \textrm{v}_h(x_i)\]
- 2D case
\[(\textrm{u}_h, \textrm{v}_h)_+ \vcentcolon = (\textrm{u}_h, \textrm{v}_h)_{+x} + (\textrm{u}_h, \textrm{v}_h)_{+y}\]
- 3D case
\[(\textrm{u}_h, \textrm{v}_h)_+ \vcentcolon = (\textrm{u}_h, \textrm{v}_h)_{+x} + (\textrm{u}_h, \textrm{v}_h)_{+y} + (\textrm{u}_h, \textrm{v}_h)_{+z}.\]
See the definitions of inner₊ₓ, inner₊ᵧ, and inner₊₂ for more details.
Defined for grid functions of a ScalarGridSpace only, and for tuples whose entries are such grid functions. A grid function of a composite grid space raises a MethodError; take a scalar component of it with components first, which is itself a scalar grid function and is accepted.
inner₊(left::LazyOp{1}, right::LazyOp{1}; markers = ())
inner₊(left::NTuple{D,LazyOp{D}}, right::NTuple{D,LazyOp{D}}; markers = ()) where DConstructs a symbolic modified $L^2_+$ inner product between left and right.
markers restricts the sum as it does for innerₕ.
inner₊(left::BackwardDifference{D,Dim}, right::BackwardDifference{D,Dim}) -> LazyOp{D}inner₊ of two backward differences taken along the same direction, which is the weight the product carries: InnerPlus{Dim}.
In one dimension there is only one direction, so inner₊(left, right) already answers. Above one dimension a bare inner₊ of two operators names no direction, and the weights are directional: the direction is read off the nodes. This is what makes inner₊(D₋ₓ(u), D₋ₓ(v)) mean what it reads as.
Backward differences only, as everywhere inner₊ meets a difference: the weights are those of the summation-by-parts identity, which pairs them with a backward difference.
Constructs a LinearProduct (source × test) if left is source-only (_is_source_only), or a BilinearProduct otherwise, matching innerₕ.
markers restricts the sum as it does for innerₕ.
inner₊(left::LazyOp{D}, right::LazyOp{D}) -> LazyOp{D}Symbolic inner₊ of two operators neither of which names a direction.
In one dimension there is only one direction to name, so this is the product, with weight InnerPlus{1}. Above one dimension the weights are directional and nothing here supplies the direction, so it throws an ArgumentError.
markers restricts the sum as it does for innerₕ.
inner₊(left::LazyOp{D}, right::BackwardDifference{D,Dim}) -> LazyOp{D}
inner₊(left::BackwardDifference{D,Dim}, right::LazyOp{D}) -> LazyOp{D}inner₊ where one side is a backward difference and the other is not: the difference names the direction, so the product carries InnerPlus{Dim}.
This is what inner₊(u, D₋ₓ(v)) means: the common form, and the one the coupled pressure-velocity terms are written in, inner₊(p, D₋ₓ(v[1])) with p a symbolic scalar field. It is not restricted to indexed leaves: a plain TrialFunction reads the direction off the difference just as an IndexedTrialFunction does.
Backward differences only, as everywhere inner₊ meets a difference.
A LinearProduct if left is source-only (_is_source_only), a BilinearProduct otherwise, matching innerₕ.
markers restricts the sum as it does for innerₕ.
inner₊(left::BackwardDifference{D,Dim1}, right::BackwardDifference{D,Dim2}) where {D,Dim1,Dim2}Rejects inner₊ of two backward differences taken along different directions.
Each side names a direction and they disagree, so there is no one weight the product carries. This also has to be written out rather than left to dispatch: with a difference on either side, the two single-sided methods above tie, and the pair would be an ambiguity rather than an error the caller can read.
inner₊(left::NTuple{N,<:Tuple}, right::NTuple{N,<:Tuple}) where NVector-field inner₊: sums per-component inner products. Used when left and right are tuples of gradient tuples, e.g. inner₊(∇₋ₕ(u), ∇₋ₕ(v)) where u = (u1, u2) is a velocity tuple. Each element pair (left[k], right[k]) is a D-tuple of LazyOp (a gradient), which dispatches to the existing inner₊(::NTuple{D,LazyOp}, ::NTuple{D,LazyOp}).
This overload is intentionally restricted to NTuple{N,<:Tuple} so it does not interfere with inner₊(NTuple{D,VectorElement}, NTuple{D,VectorElement}) handled by the @generated method in inner_product.jl. That restriction is what separates this file's symbolic family from that file's numeric one; it is asserted in test/form/inner_products.jl, testset "Symbolic and numeric families stay apart".
markers restricts the whole sum, not each component separately, as it does for innerₕ.
Bramble.inner₊ₓ — Function
inner₊ₓ(uₕ::VectorElement, vₕ::VectorElement; markers = ()) -> RealReturns the discrete modified $L^2$ inner product of the grid functions uₕ and vₕ associated with the first variable.
For VectorElements, it is defined as
- 1D case
\[(\textrm{u}_h, \textrm{v}_h)_+ \vcentcolon = \sum_{i=1}^{N_x} h_{i} \textrm{u}_h(x_i) \textrm{v}_h(x_i)\]
- 2D case
\[(\textrm{u}_h, \textrm{v}_h)_{+x} \vcentcolon = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y} h_{x,i} h_{y,j+1/2} \textrm{u}_h(x_i,y_j) \textrm{v}_h(x_i,y_j)\]
- 3D case
\[(\textrm{u}_h, \textrm{v}_h)_{+x} \vcentcolon = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y}\sum_{l=1}^{N_z} h_{x,i} h_{y,j+1/2} h_{z,l+1/2} \textrm{u}_h(x_i,y_j,z_l) \textrm{v}_h(x_i,y_j,z_l).\]
Defined for grid functions of a ScalarGridSpace only. A grid function of a composite grid space is rejected at dispatch; take a scalar component of it with components first, which is itself a scalar grid function and is accepted.
markers restricts the sum as it does for innerₕ (a masked sum, not a surface integral).
inner₊ₓ(left::LazyOp{D}, right::LazyOp{D}) where D
inner₊ᵧ(left::LazyOp{D}, right::LazyOp{D}) where D
inner₊₂(left::LazyOp{D}, right::LazyOp{D}) where DConstructs directional modified $L^2_+$ inner products in x, y, and z directions.
A LinearProduct if left is source-only (_is_source_only), a BilinearProduct otherwise, exactly as innerₕ decides.
markers restricts the sum as it does for innerₕ.
Bramble.inner₊ᵧ — Function
inner₊ᵧ(uₕ::VectorElement, vₕ::VectorElement; markers = ()) -> RealReturns the discrete modified $L^2$ inner product of the grid functions uₕ and vₕ associated with the second variable, the $y$ direction.
For VectorElements, it is defined as
- 2D case
\[(\textrm{u}_h, \textrm{v}_h)_{+y} \vcentcolon = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y} h_{x,i} h_{y,j+1/2} \textrm{u}_h(x_i,y_j) \textrm{v}_h(x_i,y_j)\]
- 3D case
\[(\textrm{u}_h, \textrm{v}_h)_{+y} \vcentcolon = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y}\sum_{l=1}^{N_z} h_{x,i+1/2} h_{y,j} h_{z,l+1/2} \textrm{u}_h(x_i,y_j,z_l) \textrm{v}_h(x_i,y_j,z_l).\]
Defined for grid functions of a ScalarGridSpace only. A grid function of a composite grid space is rejected at dispatch; take a scalar component of it with components first, which is itself a scalar grid function and is accepted.
markers restricts the sum as it does for innerₕ (a masked sum, not a surface integral).
Bramble.inner₊₂ — Function
inner₊₂(uₕ::VectorElement, vₕ::VectorElement; markers = ()) -> RealReturns the discrete modified $L^2$ inner product of the grid functions uₕ and vₕ associated with the z variable
\[(\textrm{u}_h, \textrm{v}_h)_{+z} \vcentcolon = \sum_{i=1}^{N_x}\sum_{j=1}^{N_y}\sum_{l=1}^{N_z} h_{x,i+1/2} h_{y,j+1/2} h_{z,l} \textrm{u}_h(x_i,y_j,z_l) \textrm{v}_h(x_i,y_j,z_l).\]
Defined for grid functions of a ScalarGridSpace only. A grid function of a composite grid space is rejected at dispatch; take a scalar component of it with components first, which is itself a scalar grid function and is accepted.
markers restricts the sum as it does for innerₕ (a masked sum, not a surface integral).
Bramble.normₕ — Function
normₕ(uₕ::VectorElement) -> RealReturns the discrete $L^2$ norm of the grid function uₕ, defined as
\[\Vert \textrm{u}_h \Vert_h \vcentcolon = \sqrt{(\textrm{u}_h, \textrm{u}_h)_h}\]
On a CompositeGridSpace it is the norm of the product space, which follows from the inner product there: the square root of the sum of the components' squared norms.
Bramble.norm₁ₕ — Function
norm₁ₕ(uₕ::VectorElement) -> RealReturns the discrete version of the standard $H^1$ norm of VectorElement uₕ.
\[\Vert \textrm{u}_h \Vert_{1h} \vcentcolon = \sqrt{ \Vert \textrm{u}_h \Vert_h^2 + \Vert \nabla_h \textrm{u}_h \Vert_h^2 }\]
Built from the squared quantities directly: taking normₕ and snorm₁ₕ and squaring them back up would compute two square roots only to undo them.
Defined for grid functions of a ScalarGridSpace only. A grid function of a composite grid space is rejected at dispatch; take a scalar component of it with components first, which is itself a scalar grid function and is accepted.
Bramble.snorm₁ₕ — Function
snorm₁ₕ(uₕ::VectorElement) -> RealReturns the discrete $H^1$ seminorm of the grid function uₕ,
\[|\textrm{u}_h|_{1h} \vcentcolon = \Vert \nabla_h \textrm{u}_h \Vert_+\]
so that snorm₁ₕ(uₕ) == norm₊(∇₋ₕ(uₕ)) in one, two and three dimensions. The argument is the grid function itself; the backward gradient is taken internally, and without materialising it, so this allocates nothing.
Defined for grid functions of a ScalarGridSpace only. A grid function of a composite grid space is rejected at dispatch; take a scalar component of it with components first, which is itself a scalar grid function and is accepted.
Bramble.norm₊ — Function
norm₊(uₕ::VectorElement) -> Real
norm₊(uₕ::NTuple{D, VectorElement}) -> RealReturns the discrete modified $L^2$ norm of the grid function uₕ. It also accepts an NTuple of VectorElements.
For VectorElements uₕ, it is defined as
\[\Vert \textrm{u}_h \Vert_+ = \sqrt{(\textrm{u}_h,\textrm{u}_h)_+}.\]
and for NTuples of VectorElements it returns
\[\Vert \textrm{u}_h \Vert_+ \vcentcolon = \sqrt{ \sum_{i=1}^D(\textrm{u}_h[i],\textrm{u}_h[i])_{+,x_i}}.\]
Defined for grid functions of a ScalarGridSpace only, and for tuples whose entries are such grid functions. A grid function of a composite grid space raises a MethodError; take a scalar component of it with components first, which is itself a scalar grid function and is accepted.
Forms
Linear and bilinear forms, their assembly, and the boundary conditions applied to an assembled system. See the forms tutorial.
Building a form
Bramble.form — Function
form(Wₕ, f) -> LinearFormConstruct a LinearForm over the test space Wₕ using the linear expression f.
Construction resolves the AST once and runs simplify_ast over it – factoring common scalings, combining like terms, and eliding zero-scaled ones – before it is stored. Grid partitioning for parallel assembly is determined from the resolved AST during assembly (see _colour_strides).
Every SourceFunction reachable from the simplified AST – a source term built directly from a plain function, f(x), rather than a VectorElement or a Ref – is then sampled once against its own leaf's space and lowered to a SourceVector (gpena/Bramble.jl#197): a brand-new closure passed as a source used to force a full recompilation of the assembly pipeline (~11ms measured) on every distinct closure, since Julia gives it its own type; assembling against the fixed SourceVector shape instead means that cost is paid once, here, not on every later assemble!/assemble call. This changes what a raw closure that captures mutable state does: it is evaluated once, now, not re-evaluated on later assemblies. VectorElement and Ref coefficients are unaffected – see the note on _lower_sources in form/common.jl for why, and for the documented alternative (update_coefficients!) a source meant to keep varying should use instead.
Examples
# 1D linear form: l(v) = (f, v)
l = form(Wₕ, v -> innerₕ(fₕ, v))form(Wₕ, Vₕ, f) -> BilinearFormConstruct a BilinearForm over the trial space Wₕ and the test space Vₕ from the bilinear expression f (a function of trial and test arguments (u, v)). The AST is resolved once and run through simplify_ast – factoring common scalings, combining like terms, and eliding zero-scaled ones – before it is stored.
Examples
# a(u, v) = (∇₋ₕu, ∇₋ₕv)₊
a = form(Wₕ, Wₕ, (u, v) -> inner₊ₓ(D₋ₓ(u), D₋ₓ(v)))
# a coupled system, one term per block
a = form(Vₕ, Vₕ, (u, v) -> inner₊ₓ(D₋ₓ(u(1)), D₋ₓ(v(1))) + innerₕ(u(2), v(1)))Assembling
assemble allocates its result; the mutating forms refill one that already exists, which is what a time loop wants. allocate_system_matrix builds a matrix's sparsity pattern once so that assemble! can refill it without allocating.
Bramble.assemble — Function
assemble(form::LinearForm; dirichlet = nothing, dirichlet_components = nothing) -> AbstractVectorAssemble the system vector of the LinearForm, applying the boundary values dirichlet carries on the regions it names. dirichlet accepts a label => f Pair, a Tuple of such Pairs, or constraints from dirichlet_constraints – a bare label or Tuple of labels has no values to write and raises an error (see _normalize_dirichlet for every accepted form). dirichlet_components restricts which leaf components of a composite test space they bind to (see dirichlet_bc!).
Runs serially or across threads following test_space(form)'s backend execution_policy: Serial (the default) or Parallel. assemble_parallel! always threads regardless of the backend policy.
assemble(form::BilinearForm; dirichlet = nothing, dirichlet_components = nothing) -> SparseMatrixCSCAllocate a matrix with the form's sparsity pattern and assemble into it.
Call this once, then assemble into what it returns. Building the sparsity pattern is the larger part of the work (at 250,000 degrees of freedom it is 9,700 us and 52 MB against 1,500 us and zero allocations to refill the matrix), and the pattern does not change between assemblies. A time loop or Newton iteration benefits from preallocating the pattern once:
A = assemble(a) # once: pattern, allocation and initial fill
for step in 1:nsteps
Rₕ!(cₕ, coefficient_at(step)) # modified in-place
assemble!(A, a) # or assemble_parallel!(A, a)
endRuns serially or across threads following form.trial_space's backend execution_policy: Serial by default. Optional dirichlet applies boundary conditions to the matrix – a label Symbol, a Tuple of labels, a label => f Pair (values ignored on the matrix side), or constraints from dirichlet_constraints; see _normalize_dirichlet for every accepted form. dirichlet_components restricts which leaf components of a composite trial space they bind to (see dirichlet_bc!).
assemble(a::BilinearForm, l::LinearForm; dirichlet = nothing, dirichlet_components = nothing, symmetrize::Bool = false) -> (A, F)Assemble both the matrix and the vector in one call, applying the same dirichlet constraints to each – the common case of building a system to solve A \ F against. a and l should share the same test space; boundary values are read from dirichlet once rather than once per form.
symmetrize = true additionally restores symmetry in A after dirichlet_bc! (which only clears rows, not the columns) and updates F to match, via symmetrize!. Requires dirichlet to name at least one label – there is nothing to symmetrize against otherwise.
A, F = assemble(a, l; dirichlet = :boundary => sol, symmetrize = true)
u = A \ Fis the one-call equivalent of assembling a and l separately and calling symmetrize! by hand.
Bramble.assemble! — Function
assemble!(b::AbstractVector, form::LinearForm; dirichlet = nothing,
dirichlet_components = nothing) -> AbstractVectorRefill b with the assembled form and return it with zero allocations (0 bytes).
assemble! uses the pre-resolved form.ast stored directly inside the form.
Live coefficients
- Grid functions: the stored AST retains references to source
VectorElementstorage. Mutating values in-place (Rₕ!(uₕ, ...)orparent(uₕ) .= ...) between steps automatically updates the assembled vector without needing to rebuild the form. - Dynamic scalars: plain numbers work directly for constant scalars. To update a scalar dynamically across loop iterations, wrap it in a
Ref(val)(e.g.α = Ref(1.0); l = form(Wₕ, v -> α * innerₕ(uₕ, v))). Mutatingα[] = new_valevaluates live during assembly with 0 allocations.
Arguments
b: Vector to refill, with lengthndofs(test_space(form)).form: Linear form to assemble.
Keywords
dirichlet: Boundary values to impose after assembly, and the labels to impose them on, together – alabel => fPair, aTupleof suchPairs, or constraints fromdirichlet_constraints(default:nothing; see_normalize_dirichletfor every accepted form).dirichlet_components: Restricts which leaf components of a compositetest_space(form)the labels bind to (seedirichlet_bc!; default:nothing, targeting all leaves).
Runs serially or across threads following test_space(form)'s backend execution_policy: Serial or Parallel. assemble_parallel! forces threaded execution regardless of backend policy.
See also assemble, assemble_parallel!, and evaluate!.
assemble!(A::SparseMatrixCSC, form::BilinearForm; dirichlet = nothing, dirichlet_components = nothing) -> SparseMatrixCSCAssemble the BilinearForm into the preallocated sparse matrix A, allocating nothing (0 bytes).
Runs serially or across threads following form.trial_space's backend execution_policy: Serial (the default) or Parallel. assemble_parallel! is a separate, lower-level entry point that always threads, ignoring the backend's policy.
assemble! uses the pre-resolved form.ast stored directly inside the form.
Live coefficients
- Grid functions: the stored AST retains references to source
VectorElementstorage. Mutating values in-place (Rₕ!(cₕ, ...)orparent(cₕ) .= ...) between steps automatically updates the matrix entries with 0 allocations. - Dynamic scalars: plain numbers work directly for constant scalars. To update a scalar dynamically across loop iterations, wrap it in a
Ref(val)(e.g.β = Ref(1.0); a = form(Wₕ, Wₕ, (u, v) -> innerₕ(β * D₋ₓ(u), D₋ₓ(v)))). Mutatingβ[] = new_valevaluates live during assembly with 0 allocations.
Bramble.assemble_parallel! — Function
assemble_parallel!(b::AbstractVector, form::LinearForm) -> AbstractVectorRefill b with the assembled form across threads and return it, regardless of test_space(form)'s backend execution policy. Unlike assemble!, does not apply Dirichlet conditions.
assemble_parallel!(A::SparseMatrixCSC, form::BilinearForm) -> SparseMatrixCSCRefill A with the assembled form across threads and return it, regardless of form.trial_space's backend policy. A must already carry the correct sparsity pattern from allocate_system_matrix or a previous assemble. Unlike assemble!, does not apply dirichlet_labels.
Colouring on the test side ensures thread safety when updating stored matrix values concurrently.
Bramble.allocate_system_matrix — Function
allocate_system_matrix(form::BilinearForm, ast = resolve_form_ast(form)) -> SparseMatrixCSCBuild the sparse matrix a BilinearForm assembles into: the appropriate size, correct sparsity pattern, and stored zeros throughout.
The pattern follows from the stencil rather than coefficient values, remaining invariant while the mesh and expression structure are unchanged. Preallocating the matrix once outside loops allows zero-allocation in-place assembly:
A = allocate_system_matrix(a)
for step in 1:nsteps
assemble!(A, a) # refills values in-place with zero allocations
endOnly the structure is preallocated here; all stored entries are zero until assemble! fills them.
Bramble.evaluate! — Function
evaluate!(scratch::AbstractVector, form::LinearForm, vₕ::VectorElement) -> NumberEvaluate form at vₕ, assembling into scratch rather than into a newly allocated vector, and return the resulting contracted scalar value.
Useful when both the assembled vector and the scalar value are needed (such as a Newton step requiring the residual vector and its norm). scratch is overwritten and contains the right-hand side upon return.
For the scalar value alone, form(vₕ) fuses the contraction into the assembly sweep in a single pass.
Examples
l = form(Wₕ, v -> innerₕ(fₕ, v))
scratch = zeros(ndofs(Wₕ))
for step in 1:nsteps
Rₕ!(fₕ, source_at(step)) # modified in-place
value = evaluate!(scratch, l, uₕ)
endJacobian sparsity
For a Newton residual built from a BilinearForm with a live nonlinear coefficient (see the nonlinear Poisson example), jacobian_pattern reads the Jacobian's sparsity pattern directly off the form's AST, without AD tracing. ast_sparsity_detector wraps it as an ADTypes.AbstractSparsityDetector, ready to hand AutoSparse directly (requires ADTypes.jl).
Bramble.jacobian_pattern — Function
jacobian_pattern(a::BilinearForm, coefficient_dependencies::Function...) -> SparseMatrixCSC{Bool}Sparsity pattern of the Jacobian of a Newton residual A(u) * u - F, where a is the BilinearForm that assembles A(u) (e.g. diffusion_matrix in the nonlinear Poisson example) and each function in coefficient_dependencies names, symbolically, the stencil operator one of a's live coefficients was itself computed from – written the same way a form term names an operator, as a function of the trial placeholder. If a's diffusion coefficient was built as αvals = α.(M₋ₕ(uₕ)), pass U -> M₋ₕ(U).
Derived entirely from a's AST and each dependency's own stencil reach – no AD tracing, no coefficient values needed. A safe superset of the exact pattern (a pointwise nonlinear α can never narrow the reach its argument already has), suitable for ADTypes.KnownJacobianSparsityDetector:
a = form(Wₕ, Wₕ, (U, V) -> inner₊(αvals * ∇₋ₕ(U), ∇₋ₕ(V))) # αvals = α.(M₋ₕ(uₕ))
pattern = jacobian_pattern(a, U -> M₋ₕ(U))
sparse_ad = AutoSparse(AutoForwardDiff();
sparsity_detector = KnownJacobianSparsityDetector(pattern),
coloring_algorithm = GreedyColoringAlgorithm())Composite trial/test spaces
A dependency may also name a different leaf, the same way a form term does – U -> U(2) for a coefficient that is component 2's own value (no stencil op, as in the coupled reaction-diffusion example's v_c), or U -> M₋ₕ(U(2)) for one built from a stencil op applied to that other component. nothing named (U -> M₋ₕ(U), no (k)) means the coefficient depends on this block's own trial leaf, exactly like the non-composite case above. Every dependency still applies to every block the walk visits, whichever leaf it names – a safe superset stays safe however many blocks end up seeing an entry they did not strictly need.
# a = form(Vₕ, Vₕ, (p, q) -> inner₊(∇₋ₕ(p(1)), ∇₋ₕ(q(1))) + innerₕ(v_c * p(1), q(1)) +
# inner₊(∇₋ₕ(p(2)), ∇₋ₕ(q(2))) - innerₕ(u_c * p(2), q(2)))
pattern = jacobian_pattern(a, U -> U(2), U -> U(1)) # block (1,1) reads U(2), (2,2) reads U(1)Bramble.ast_sparsity_detector — Function
ast_sparsity_detector(a::BilinearForm, coefficient_dependencies::Function...)An ADTypes.AbstractSparsityDetector that supplies jacobian_pattern(a, coefficient_dependencies...) directly as a Newton residual's Jacobian sparsity, in place of one detected by tracing:
sparse_ad = AutoSparse(AutoForwardDiff();
sparsity_detector = ast_sparsity_detector(a, U -> M₋ₕ(U)),
coloring_algorithm = GreedyColoringAlgorithm())Requires ADTypes.jl; call using ADTypes before calling this function.
Time-dependent problems and the SciML stack
semidiscretize applies the method of lines to a spatial BilinearForm and a source LinearForm, producing the system M uₕ' = F(t) - A uₕ as a callable with the (du, u, p, t) signature a time stepper expects. Dirichlet conditions become algebraic rows of a singular mass matrix, so the result is an index-1 differential-algebraic system needing only the boundary data g, never its time derivative (see the heat equation example).
Nothing in this group needs a weak dependency except the last three, which name their results the way SciMLBase does: ode_function and ode_problem hand the semidiscretisation to OrdinaryDiffEq, and linear_problem hands a steady system to LinearSolve with its factorisations and preconditioners. All three require SciMLBase.jl.
Bramble.semidiscretize — Function
semidiscretize(a::BilinearForm, l::LinearForm; kwargs...) -> SemidiscretizationSemidiscretise M u_h' = F(t) - A u_h from the spatial form a and the source l, both posed on the same test space.
Write a as the steady problem is written: assemble(a) is A, so the steady state of the returned system solves A u_h = F.
Keywords
mass:BilinearFormdefiningM(default:innerₕ(u, v), the discreteL²inner product, which is diagonal).dirichlet: constrained labels and, where they carry values, the values – every formassembleaccepts, including time-dependent constraints fromdirichlet_constraints(Ωₕ, I, :label => (x, t) -> ...)(default:nothing).dirichlet_components: leaf components of a composite space the labels bind to (default:nothing, all leaves).state:VectorElementthe currentuis copied into before each assembly, for forms whose coefficients read it (default:nothing).update_coefficients!: called with the currenttbefore each assembly, for coefficients that vary in time –t -> Rₕ!(fₕ, x -> f(x, t))for a time-dependent source, ort -> (α[] = t)for a scalarRef(default:nothing).reassemble: refillAat every step, for an operator whose coefficients change withtoru(default:false).
Time dependence of the Dirichlet values is detected by arity, exactly as dirichlet_constraints validates it: conditions accepting (x, t) are evaluated at each step, conditions accepting (x) are not.
Examples
Ωₕ = mesh(domain(interval(0.0, 1.0)), 101)
Wₕ = gridspace(Ωₕ)
I = interval(0.0, 1.0)
fₕ = Rₕ(Wₕ, x -> 1.0)
a = form(Wₕ, Wₕ, (u, v) -> inner₊(∇₋ₕ(u), ∇₋ₕ(v)))
l = form(Wₕ, v -> innerₕ(fₕ, v))
bcs = dirichlet_constraints(Ωₕ, I, :boundary => (x, t) -> 0.0)
sd = semidiscretize(a, l; dirichlet = bcs)See also ode_function, ode_problem, jacobian!.
Bramble.Semidiscretization — Type
Semidiscretization{...}Method-of-lines semidiscretisation of M u_h' = F(t) - A u_h, callable with the (du, u, p, t) signature a time stepper expects.
Built by semidiscretize; read back with mass_matrix and operator_matrix.
Fields
operator: spatialBilinearForm, assembled intoA.source: sourceLinearForm, assembled intoF(t)at each step.space: the test space both forms share.operator_matrix:A, witheₖrows on the constrained degrees of freedom.mass_matrix:M, with zero rows on the constrained degrees of freedom.source_vector: the reusableFbuffer, refilled in place.constraints: how the source's boundary values are reached – one ofNoConstraints,LabelsOnly,StaticConstraintsorTimeDependentConstraints.labels: constrained boundary labels.components: leaf components the labels bind to, ornothingfor all.state:VectorElementreceivingubefore assembly, ornothing.update_coefficients: callable invoked withtbefore assembly, ornothing.reassemble:Val(true)to refillAat every step.
Bramble.mass_matrix — Function
mass_matrix(sd::Semidiscretization) -> SparseMatrixCSCReturn the constant mass matrix M, whose constrained rows are zero.
Bramble.operator_matrix — Function
operator_matrix(sd::Semidiscretization) -> SparseMatrixCSCReturn the assembled spatial operator A, whose constrained rows are eₖ.
Bramble.jacobian! — Function
jacobian!(J, sd::Semidiscretization, u, p, t) -> JFill J with ∂/∂u (F(t) - A u) = -A and return it.
Exact whenever the operator's coefficients do not read u – that is, whenever sd was built without state. With a state the operator also varies with u, the term -(∂A/∂u) u is missing, and this is a Picard linearisation rather than a Jacobian: pass jacobian = nothing to ode_function and let the solver build it by sparse automatic differentiation instead, seeded from jacobian_pattern.
See also jacobian_prototype.
Bramble.jacobian_prototype — Function
jacobian_prototype(sd::Semidiscretization) -> SparseMatrixCSCReturn a matrix carrying the sparsity of ∂/∂u (F(t) - A u), for a solver to use as its Jacobian cache.
This is the pattern of the assembled operator, Dirichlet rows included, which is what the system's Jacobian has – jacobian_pattern answers the different question of what a Newton residual's Jacobian looks like when the form's coefficients depend on the solution.
See also jacobian!.
Bramble.ode_function — Function
ode_function(sd::Semidiscretization; kwargs...) -> ODEFunction
ode_function(a::BilinearForm, l::LinearForm; kwargs...) -> ODEFunctionWrap a semidiscretisation as an ODEFunction carrying its mass matrix, Jacobian and sparsity, ready for OrdinaryDiffEq.
The two-form method builds the Semidiscretization first, forwarding every keyword to semidiscretize.
Keywords
jacobian:jacobian!(the default) to hand the solver the exact-A, ornothingto let it build one by automatic differentiation fromjac_prototype.jac_prototype: sparsity for the solver's Jacobian cache (default:jacobian_prototype(sd)).
The resulting system is a differential-algebraic one whenever any Dirichlet label is constrained, since those rows of the mass matrix are zero. Solve it with a method that admits a singular mass matrix – FBDF, QNDF, Rodas5P, RadauIIA5 – not an explicit one.
A Rosenbrock method (Rodas5P, Rosenbrock23) also needs ∂f/∂t, which it builds by differentiating through t. That works when the only time dependence is the Dirichlet data, since those values reach the assembled vector as its element type. An update_coefficients! hook writing into a Float64 VectorElement – the usual t -> Rₕ!(fₕ, x -> f(x, t)) – cannot take a ForwardDiff.Dual time, and the solve fails on the first step with a time-gradient error. Either pass Rodas5P(autodiff = AutoFiniteDiff()), or use a BDF method, which needs no ∂f/∂t at all. FBDF and QNDF are unaffected either way.
Requires SciMLBase.jl; call using SciMLBase (or any package that loads it, such as OrdinaryDiffEq) before calling this function.
See also ode_problem, linear_problem.
Bramble.ode_problem — Function
ode_problem(sd::Semidiscretization, u₀, I::CartesianProduct{1}; kwargs...) -> ODEProblem
ode_problem(a::BilinearForm, l::LinearForm, u₀, I; kwargs...) -> ODEProblemBuild the ODEProblem stepping sd over the time domain I, from the initial condition u₀ – a VectorElement or a plain vector. I may equally be a (t₀, t₁) tuple.
u₀ is copied, never mutated, and the copy is made consistent with the Dirichlet rows at t₀ (see dirichlet_bc!).
Keywords are those of ode_function; the two-form method also forwards to semidiscretize.
Requires SciMLBase.jl.
Examples
sd = semidiscretize(a, l; dirichlet = bcs)
prob = ode_problem(sd, Rₕ(Wₕ, x -> sinpi(x[1])), interval(0.0, 1.0))
sol = solve(prob, FBDF())See also ode_function, semidiscretize.
Bramble.linear_problem — Function
linear_problem(a::BilinearForm, l::LinearForm; kwargs...) -> LinearProblemAssemble a and l into the LinearProblem that LinearSolve.solve takes, so the steady system reaches the factorisations, iterative solvers and preconditioners in that stack without being assembled by hand first.
Keywords
dirichlet,dirichlet_components: asassembletakes them.symmetrize: restore symmetry after imposing the conditions (default:false; seesymmetrize!).
Requires SciMLBase.jl, which defines LinearProblem; LinearSolve itself is needed only to solve the result.
Examples
using LinearSolve, IncompleteLU
prob = linear_problem(a, l; dirichlet = bcs)
sol = solve(prob, KrylovJL_GMRES())See also assemble, ode_problem.
Caching a coefficient-dependent assembly by element type
A Newton residual generic over T (Float64 on a plain call, ForwardDiff.Dual while an AD backend's sparse Jacobian sweep is probing it) cannot preallocate one matrix the way a Picard loop can. type_cached_assemble! gives the sparsity pattern a place to live per element type it is ever reached at instead, so only the very first call at a given type pays for it.
Bramble.type_cached_assemble! — Function
type_cached_assemble!(build, cache::AbstractDict, uₕ::VectorElement;
dirichlet = nothing, dirichlet_components = nothing) -> SparseMatrixCSCAssembles a coefficient-dependent BilinearForm into a matrix whose sparsity pattern is built once per distinct element type uₕ is ever passed at, rather than on every call – the fix diffusion_matrix-style Newton residuals in the nonlinear worked examples name and deliberately defer, since assemble/allocate_system_matrix rebuild the whole matrix, pattern included, every time otherwise.
build(uₕ) is called once for each element type uₕ is ever seen at, and must return (a, refill!): the BilinearForm to assemble, built around whatever live coefficient buffer(s) it needs (see the forms tutorial), and a one-argument function refill!(uₕ) updating those buffers from the current uₕ – called on every invocation, cache hit or miss, so a later call at an already-seen type still sees the new guess rather than the one build first saw.
build itself should be a named function defined once, not a closure literal written inside whatever function calls type_cached_assemble! — the same reason the Picard loop in poisson_nonlinear.jl builds its own form once, outside the loop, rather than on every iteration: a do ... end block re-literalized on every call allocates a new closure each time, which is exactly the cost this function exists to avoid paying more than once.
function build_diffusion(uₕ)
Mu = element(Wₕ, eltype(uₕ)) # scratch for M₋ₓ!'s own output
αvals = element(Wₕ, eltype(uₕ))
a = form(Wₕ, Wₕ, (U, V) -> inner₊(αvals * ∇₋ₕ(U), ∇₋ₕ(V)))
refill!(uₕ) = begin
M₋ₓ!(Mu, uₕ) # in place: `M₋ₓ(uₕ)` alone would allocate a fresh result
αvals .= α.(Mu)
end
return a, refill!
end
cache = Dict()
diffusion_matrix(uₕ) = type_cached_assemble!(
build_diffusion, cache, uₕ; dirichlet = :boundary)refill! reaches for M₋ₓ! rather than the non-mutating M₋ₓ/M₋ₕ deliberately: the latter allocates a fresh result every call (the same similar-based cost every allocating stencil operator has), which would silently reintroduce an O(n) allocation this function's whole point is to stop paying repeatedly. M₋ₓ! alone covers the 1D case above; a D-dimensional coefficient needs one scratch buffer and one M₋ₓ!/M₋ᵧ!/M₋₂! call per direction, the same way poisson_nonlinear.jl's own nonlinear_series builds a D-dimensional coefficient tuple.
cache is shared across an entire Newton (or Picard) loop, one Dict per residual: the first call at a given type pays build's own cost plus allocate_system_matrix's; every later call at that same type pays only assemble!'s refill plus a small, fixed dictionary/dynamic-dispatch overhead fetching the cached entry back out (a few KB, independent of ndofs) – not the O(ndofs) pattern rebuild a cache miss (or no cache at all) pays every time.
Not thread-safe: cache is a plain, unlocked Dict, sized for the one-cache-per-residual usage above. A form assembled from more than one task needs a lock or a per-task cache, the same as any other shared mutable Dict.
Dirichlet conditions
Bramble.DirichletConstraint — Type
DirichletConstraint = DomainMarkersType alias for Dirichlet boundary constraint storage.
Bramble.dirichlet_constraints — Function
dirichlet_constraints(input, [I::CartesianProduct{1}], pairs::Pair...) -> DomainMarkersCreate Dirichlet boundary constraints.
Each pair is of the form :label => func, where :label identifies the boundary region and func defines the Dirichlet values. If the optional time domain I is provided, func must be a time-dependent function func(x, t); this is checked by arity, since nothing about func itself can be evaluated at construction time.
input can be a CartesianProduct, a Domain, an AbstractMeshType, a ScalarGridSpace, or a CompositeGridSpace from which the mesh is extracted. The :label must match a label in the mesh definition.
dirichlet_constraints(X::CartesianProduct, f::Function) -> DomainMarkersCreate a single Dirichlet boundary constraint with function f under the :boundary label.
Bramble.dirichlet_bc! — Function
dirichlet_bc!(A::AbstractMatrix, Ωₕ::AbstractMeshType, labels::Symbol...) -> AbstractMatrixApply Dirichlet boundary conditions to matrix A based on marked regions in the mesh Ωₕ.
For each index i associated with the given Dirichlet labels, this function:
- Sets all elements in the
i-th row ofAto zero. - Sets the diagonal element
A[i, i]to one.
dirichlet_bc!(A::AbstractMatrix, space::CompositeGridSpace, labels::Symbol...; components = nothing) -> AbstractMatrixApply Dirichlet boundary conditions to matrix A on the regions named by labels, restricted to the leaf components specified in components (1-based positions in leaf_spaces_offsets(space), following the depth-first ordering used by u(1)/u(2) addressing). components = nothing (the default) applies to every leaf component.
This allows coupled systems to constrain selected fields while leaving others unconstrained (for example, prescribing velocity while leaving pressure free in a Stokes problem):
Wₕ = vector_gridspace(Ωₕ, Val(2)) # 1: velocity, 2: pressure
dirichlet_bc!(A, Wₕ, :left, :right; components = 1) # velocity onlySuccessive calls with different labels/components pairs compose cleanly.
dirichlet_bc!(v::AbstractVector, Ωₕ::AbstractMeshType, bcs::ConstraintMarkers, labels::Symbol...) -> AbstractVectorWrite Dirichlet values into v at the points marked by labels.
bcs may be unevaluated constraints or time-evaluated constraints (see ConstraintMarkers). Only the marked entries are modified, with complexity proportional to the boundary cardinality.
dirichlet_bc!(u::AbstractVector, sd::Semidiscretization, t::Number) -> uWrite sd's Dirichlet values at time t into u and return it.
An index-1 differential-algebraic system needs its initial condition to satisfy the algebraic rows already: a u disagreeing with g(x, 0) on the boundary is inconsistent, and a stiff solver either rejects it or absorbs it into the first step. ode_problem applies this to a copy of the initial condition it is handed.
Bramble.symmetrize! — Function
symmetrize!(A::AbstractMatrix, F::AbstractVector, Wₕ::CompositeGridSpace, labels::Symbol...; components = nothing)Symmetrize a coupled linear system leaf space by leaf space.
The counterpart of the composite dirichlet_bc!: each leaf's marker mask is read at that leaf's offset into the global system without allocating full-system masks. leaf_spaces_offsets returns a tuple, enabling loop unrolling and type stability.
Takes the same components keyword as composite dirichlet_bc!, restricting which leaf components labels binds to (1-based positions in leaf_spaces_offsets(Wₕ)). components = nothing (the default) applies to every leaf.
Structural properties
Whether a BilinearForm is symmetric, or symmetric positive semi-definite, by construction — a cheap, symbolic check on its expression, answered before any matrix is assembled.
LinearAlgebra.issymmetric — Method
issymmetric(a::BilinearForm) -> BoolWhether a is symmetric by construction: innerₕ(L(u), L(v)), or a sum or scaling of such terms, with the same L written once and applied to both the trial and test argument.
Purely structural: this walks a's expression and never assembles a matrix. It is also conservative: a term that happens to produce a symmetric matrix through some other route answers false, the same as one that is not symmetric at all.
This describes the unconstrained operator. dirichlet_bc! zeros a row without touching its column, so a matrix assembled with dirichlet is not symmetric even when issymmetric(a) is true, until symmetrize! restores it. true here is a claim about a's expression, not about whatever matrix a particular call to assemble produced.
Examples
a = form(Wₕ, Wₕ, (u, v) -> inner₊ₓ(D₋ₓ(u), D₋ₓ(v)))
issymmetric(a) # true: the same D₋ₓ on both sides
b = form(Wₕ, Wₕ, (u, v) -> inner₊(u, D₋ₓ(v)))
issymmetric(b) # false: different operators either side
issymmetric(Matrix(assemble(a))) # true
issymmetric(Matrix(assemble(a; dirichlet = :boundary))) # false: rows zeroed, columns notLinearAlgebra.isposdef — Method
isposdef(a::BilinearForm) -> BoolWhether a is symmetric positive semi-definite by the same LᵀWL construction issymmetric checks: true only when, in addition, every scaling along the way is by a positive number, which is what keeps that positivity from being flipped or collapsed.
Purely structural, like issymmetric, and for the same reason conservative: this does not prove positive-definite (which also needs L to have trivial kernel), only that the assembled matrix is symmetric positive semi-definite, enough to make cholesky worth attempting first rather than a general factorization.
Describes the unconstrained operator, exactly as issymmetric does: a matrix assembled with dirichlet needs symmetrize! after dirichlet_bc! before either symmetry or positive-definiteness holds of it, isposdef(a) being true notwithstanding.
Exporters
Writing a mesh and its grid functions to a file a viewer can open. See the VTK export tutorial and the PGFPlots export tutorial.
Bramble.export_vtk — Function
export_vtk(filename::AbstractString, Ωₕ::AbstractMeshType, fields::Pair...) -> Vector{String}
export_vtk(filename::AbstractString, uₕ::VectorElement, name::AbstractString = "u") -> Vector{String}Write Ωₕ, and any number of named fields over it, to a VTK rectilinear grid file (.vtr).
Each entry in fields is name => data, where data is a VectorElement over a grid space on Ωₕ (scalar or composite) or a plain array already shaped like the grid. The second method is a shorthand for a single field, named "u" unless told otherwise.
A 1D mesh gets a degenerate second axis rather than being refused: VTK has no dedicated 1D grid type, but a rectilinear grid one point deep in y opens and renders correctly.
Requires WriteVTK.jl; call using WriteVTK before calling this.
Examples
using Bramble, WriteVTK
Ωₕ = mesh(domain(interval(0.0, 1.0) × interval(0.0, 1.0)), (20, 20), (true, true))
Wₕ = gridspace(Ωₕ)
uₕ = Rₕ(Wₕ, x -> sin(x[1]) * x[2])
export_vtk("solution", Ωₕ, "u" => uₕ) # writes solution.vtr
export_vtk("solution", uₕ) # the same field, named "u"Bramble.export_pgfplots — Function
export_pgfplots(filename::AbstractString, Ωₕ::AbstractMeshType{1}, fields::Pair...) -> String
export_pgfplots(filename::AbstractString, Ωₕ::AbstractMeshType{2}, field::Pair) -> String
export_pgfplots(filename::AbstractString, uₕ::VectorElement, name::AbstractString = "u") -> StringWrite grid data to a plain-text table, laid out the way pgfplots reads it directly: no external package needed, since the format is just whitespace-separated numbers.
On a 1D mesh, any number of named fields become columns: x name₁ name₂ ..., one row per grid point, readable with \addplot table {file.dat} or \addplot table[y=name] {file.dat} to pick one column by name. A composite VectorElement expands into one column per component, named name_1, name_2, and so on.
On a 2D mesh, exactly one field is written as x y z triples (pgfplots' surf/mesh format has no way to encode more than one value per point) with a blank line after each run of constant x. That blank line is what tells \addplot3[surf] table {file.dat} where one row of the grid ends and the next begins; without it the same numbers plot as a shredded zigzag instead of a surface. A composite element is refused with a message saying so, rather than silently writing one of its components.
data in a field pair can be a VectorElement, or a plain array already shaped like the grid (a vector in 1D, a vector or a (nx, ny) matrix in 2D).
A 3D mesh is refused: \addplot3[surf] plots a height field over a 2D domain, not a true 3D volume, so a 3D mesh has no faithful representation in this format. See export_vtk.
Examples
using Bramble
Ωₕ = mesh(domain(interval(0.0, 1.0)), 33, true)
Wₕ = gridspace(Ωₕ)
uₕ = Rₕ(Wₕ, sin)
export_pgfplots("curve", Ωₕ, "u" => uₕ) # writes curve.dat
Ω2 = mesh(domain(interval(0.0, 1.0) × interval(0.0, 1.0)), (20, 20), (true, true))
W2 = gridspace(Ω2)
v2 = Rₕ(W2, x -> sin(x[1]) * x[2])
export_pgfplots("surface", Ω2, "u" => v2) # writes surface.dat, for \addplot3[surf]