Bramble.jl GitHub

Geometry#

Sets and intervals#

is_collapsed, point_type and set are private; see the geometry internals page.

Bramble.interval

source
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: If x > y.

Examples

using Bramble
I = interval(0.0, 1.0)
first(I) == 0.0 && last(I) == 1.0

# output
true

Bramble.point

source
point(Ωₕ::AbstractMeshType, idx)

Return the coordinate point at index idx (linear integer, tuple (i, j), or CartesianIndex).

  • For 1D meshes, the scalar coordinate \(x_i\).

  • For nD meshes, the coordinate tuple \((x_{i_1}, \dots, x_{i_D})\).

Direct indexing Ωₕ[idx] delegates to point(Ωₕ, idx).

Bramble.box

source
box(a::Number, b::Number) -> CartesianProduct{1, T}
box(a::Tuple{Vararg{Number, D}}, b::Tuple{Vararg{Number, D}}) -> CartesianProduct{D, T}

Construct a CartesianProduct from two opposing corner points a and b.

The corners may mix element types: every coordinate is floated and all axes are promoted to one type T, so box((0.0f0, 0.0), (1.0f0, 0.1)) is a CartesianProduct{2, Float64}. Homogeneous corners keep their type.

Interval bounds for each dimension i are defined by \([\min(a_i, b_i), \max(a_i, b_i)]\).

LinearAlgebra.:×

source
×(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) -> CompositeGridSpace

Construct 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)).

Ghost spaces (GhostGridSpace) combine with ghost spaces only, C₁ × C₂, into a flat composite whose closure is block diagonal over the components; a product with a plain space refuses with an ArgumentError, and a nested composite with a ghost component is refused where it is used (assembly/ghost_composite.jl).

×(C₁::GhostGridSpace, C₂::GhostGridSpace) -> CompositeGridSpace

The composite of two ghost spaces, flat as for plain spaces: C₁ × C₂ × C₃ has three components. Its degrees of freedom are those of C₁ then those of C₂, and its closure is block diagonal, each component constrained by its own relations, so forms on it assemble a vector problem with per-component relations (see GhostGridSpace). Relations coupling the components, such as a traction, are given on the product of the unconstrained ghost spaces, constrained(C₁ × C₂, relations), whose closure is solved for all of them at once. Bramble.padded_space and Bramble.closure give the padded composite and the closure, and lift the padded grid functions of a solution.

A ghost space and a plain one do not combine: the product refuses with an ArgumentError. Forms couple the components only when they ghost the same facets of meshes of one size.

Bramble.dim

source
dim(Wₕ::AbstractSpaceType) -> Int
dim(::Type{<:AbstractSpaceType}) -> Int

Returns the spatial dimension of the mesh associated with the function space Wₕ.

Bramble.topo_dim

source
topo_dim(X::CartesianProduct{D}) -> Int

Return the topological dimension of X, defined as the embedding dimension D minus the number of collapsed dimensions.

topo_dim(Ω::Domain) -> Int

Return the topological dimension of Domain Ω.

topo_dim(Ωₕ::AbstractMeshType{D}) -> Int

Return 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 — Tuple{Bramble.CartesianProduct, Integer}

source
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

source
center(X::CartesianProduct{D, T}) -> NTuple{D, T}

Compute the geometric center point of CartesianProduct X.

center(Ω::Domain) -> NTuple

Compute the geometric center point of Domain Ω.

Bramble.projection

source
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.boundary_symbols

source
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 set X.

  • 1D \([x_1, x_2]\) gives (:xmin, :xmax)

  • 2D \([x_1, x_2] \times [y_1, y_2]\) gives (:xmin, :xmax, :ymin, :ymax)

  • 3D \([x_1, x_2] \times [y_1, y_2] \times [z_1, z_2]\) gives (: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.

boundary_symbols(Ω::Domain) -> Tuple{Vararg{Symbol}}

Return the canonical coordinate-aligned boundary symbols for domain Ω.

Boundary symbols#

The canonical names mean the same face in every dimension; the viewpoint aliases do not (:left is :xmin in 1D and 2D, :ymin in 3D):

  • 1D: :xmin (:left), :xmax (:right)

  • 2D: :xmin (:left), :xmax (:right), :ymin (:bottom), :ymax (:top)

  • 3D: :xmin (:back), :xmax (:front), :ymin (:left), :ymax (:right), :zmin (:bottom), :zmax (:top)

boundary_symbols lists the canonical names for a given dimension:

boundary_symbols(2)
(:xmin, :xmax, :ymin, :ymax)
2D boundary facets :ymax (:top) :ymin (:bottom) :xmin (:left) :xmax (:right) 3D boundary facets :zmax (:top) :ymax (:right) :xmax (:front) :ymin (:left) :xmin (:back) :zmin (:bottom)

Collapsed sets#

A dimension is collapsed when its interval is degenerate (\(a = b\)), which is how a surface or interface embedded in a higher-dimensional space is written. dim is the embedding dimension and topo_dim the topological one; they differ only for a collapsed set. Bramble.is_collapsed answers per axis; it is private, hence the prefix:

line_in_2d = interval(0.0, 1.0) × point(0.0)

dim(line_in_2d), topo_dim(line_in_2d),
Bramble.is_collapsed(line_in_2d, 1), Bramble.is_collapsed(line_in_2d, 2)
(2, 1, false, true)

Related constructors: interval(0, 2) converts integer bounds to Float64, point(0.5) is the degenerate interval \([0.5, 0.5]\), and box(1.5, 0.2) sorts its bounds.

Markers and domains#

Bramble.markers

source
markers(space_set::CartesianProduct, pairs::Pair...) -> DomainMarkers
markers(space_set::CartesianProduct, time_set::CartesianProduct{1}, pairs::Pair...) -> DomainMarkers

Construct a DomainMarkers collection from label => identifier pairs.

Arguments

  • space_set: Geometric spatial set.

  • time_set: Optional 1D temporal interval for time-dependent boundary conditions. With it, every predicate Function takes (x, t). markers(...)(t) fixes (x, t) predicates at t and keeps spatial x predicates, so it also applies to markers built without it.

  • pairs: Vararg sequence of label => identifier pairs where identifier is a Symbol, a tuple (NTuple{N, Symbol}), AbstractVector{Symbol} or AbstractSet{Symbol} of boundary symbols, a predicate Function (x -> Bool, or (x, t) -> Bool with time_set), or, on a 2D space_set, a Polygon built by polygon or a LevelSet built by levelset (whose φ takes x alone, with or without time_set). A pair :L => polygon(V) gives a mesh the labels L, L_boundary and L_exterior: the nodes inside the polygon, on its boundary and outside it. A node is on the boundary when its distance to the boundary is at most \(\max(\sqrt{\varepsilon} h_{\min}, 10^3 \varepsilon L) + 4 \varepsilon M\), with \(h_{\min}\) the smallest mesh spacing, \(L\) the polygon's extent, \(M\) the largest coordinate magnitude of the polygon's vertices, and \(\varepsilon\) the larger machine epsilon of the mesh's points and the polygon's vertices. A pair :L => levelset(φ) gives the same three labels, with φ < 0 inside.

Throws

  • ArgumentError: if an identifier is of any other kind (for example a String, a Dict or a callable object that is not a Function), names a symbol that is not a boundary of space_set, or, with time_set, is a predicate that does not accept (x, t). Also if a polygon marks a set that is not 2D, has a vertex outside space_set, is labelled :boundary or :interior, or one of its three labels is another marker's. Likewise for a level set, whose φ must also return a plain real number at the centre of space_set: an AbstractFloat, an Integer that is not a Bool, a Rational or an AbstractIrrational. Any other Real, a ForwardDiff.Dual among them, is refused: the level-set geometry is not differentiated.

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
true
markers(Ω::Domain) -> DomainMarkers

Return the DomainMarkers collection associated with domain Ω.

markers(Ωₕ::AbstractMeshType) -> MeshMarkers

Return the MeshMarkers dictionary associated with mesh Ωₕ.

The dictionary and its BitVectors are read-only views of the mesh's labels. Change labels with markers! or set_markers!, which update the marker state assembly reads; an edit made in place is not seen by assembly.

Bramble.domain

source
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...) -> Domain

Construct 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: Explicit DomainMarkers container.

  • pairs: Variable sequence of label => identifier pairs.

  • space_set: Spatial bounding set.

  • time_set: 1D temporal interval for time-dependent boundary conditions. Predicate identifiers then take (x, t), and an x -> Bool predicate throws an ArgumentError.

Examples

using Bramble
Ω = domain(interval(0.0, 1.0))
dim(Ω) == 1 && eltype(Ω) === Float64

# output
true

Bramble.labels

source
labels(Ω::Domain)

Return a tuple of the Symbol label of every marker in domain Ω.

Zero-allocation; see label_identifiers.

Polygons#

polygon(V) is exported. signed_distance, polygon_spaces and lift are public and not exported; using Bramble: polygon_spaces, lift brings the last two in by name. A polygon is a marker value: :P => polygon(V) in a domain labels the nodes of a 2D mesh :P (interior), :P_boundary and :P_exterior. The vertices are validated with exact predicates for orientation, folding back and crossing: at least three, no zero-length edge, an area above rounding level, a simple boundary. A vertex in the middle of a straight edge is kept. See Meshes for the grid fitted to a polygon and Grid spaces for polygon_spaces.

Bramble.polygon

source
polygon(V::AbstractVector{<:NTuple{2, Real}}) -> Polygon{T}

Construct the simple polygon whose boundary visits the vertices V in order and returns from the last to the first. V may run clockwise or counter-clockwise; the result is stored counter-clockwise. Coordinates are converted to the promoted floating-point type T.

A vertex in the middle of a straight edge is kept as a vertex.

The vertices take no derivative: ForwardDiff and Enzyme both throw. Build the mesh of a polygon domain outside a function Enzyme differentiates, since a mesh built inside it fails; data and coefficients on the resulting space differentiate under ForwardDiff and Enzyme reverse mode.

The area is zero up to rounding when |2A| ≤ 8nεL², with 2A the shoelace sum, n the vertex count, ε the epsilon of the coordinate type and L the larger side of the bounding box.

Throws

  • ArgumentError: if V has fewer than three vertices, a coordinate is not finite, two consecutive vertices coincide (the last and the first included), the polygon has zero area up to rounding, or its boundary crosses or touches itself (a bowtie, an edge folding back on the previous one).

Bramble.signed_distance

source
signed_distance(P::Polygon, x) -> Real

Signed Euclidean distance from the point x = (x₁, x₂) to the boundary of P: negative inside, zero on the boundary, positive outside.

The distance is the minimum over the edges of the exact distance from x to the edge; the sign comes from the crossing number of a horizontal ray from x, so non-convex polygons and reflex vertices are handled.

Level sets#

levelset(φ) is exported. It wraps a function φ(x) as a 2D marker value: :D => levelset(φ) in a domain labels the nodes of a 2D mesh :D (where φ < 0), :D_boundary (on the curve φ = 0) and :D_exterior (where φ > 0). φ must return a real number, not a Bool, and need not be a signed distance. The labels follow the rules of a polygon's: a 2D set, no reserved label, no clash with another marker's labels. With a time_set, φ still takes x alone.

Bramble.levelset

source
levelset(φ) -> LevelSet

Wrap the function φ(x), x = (x₁, x₂), as a 2D level-set marker. In a domain, :D => levelset(φ) gives a 2D mesh the labels D (nodes where φ < 0), D_boundary (nodes on the curve φ = 0) and D_exterior (nodes where φ > 0).

φ must return a plain real number: an AbstractFloat, an Integer that is not a Bool, a Rational or an AbstractIrrational. Any other Real, a ForwardDiff.Dual among them, is refused with an ArgumentError, as the level-set geometry is not differentiated: a parameter of φ takes no derivative through the mesh. Enzyme gives no reliable derivative of such a parameter either, so build the mesh outside a function Enzyme differentiates. φ need not be a signed distance; a mesh samples each grid line at 129 points and bisects every sign change, so φ only has to change sign across the curve. Two roots on a line closer than its sample spacing are missed. With a time_set, φ still takes x alone.

Examples

using Bramble
L = levelset(p -> hypot(p[1], p[2]) - 1)
Ω = domain(interval(-1.25, 1.25) × interval(-1.25, 1.25), :D => L)
labels(Ω)

# output
(:D, :D_boundary, :D_exterior)

See also: polygon, domain.