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: Ifx > 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)
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 predicateFunctiontakes(x, t).markers(...)(t)fixes(x, t)predicates attand keeps spatialxpredicates, so it also applies to markers built without it.pairs: Vararg sequence oflabel => identifierpairs where identifier is aSymbol, a tuple (NTuple{N, Symbol}),AbstractVector{Symbol}orAbstractSet{Symbol}of boundary symbols, a predicateFunction(x -> Bool, or(x, t) -> Boolwithtime_set), or, on a 2Dspace_set, aPolygonbuilt bypolygonor aLevelSetbuilt bylevelset(whoseφtakesxalone, with or withouttime_set). A pair:L => polygon(V)gives a mesh the labelsL,L_boundaryandL_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φ < 0inside.
Throws
ArgumentError: if an identifier is of any other kind (for example aString, aDictor a callable object that is not aFunction), names a symbol that is not a boundary ofspace_set, or, withtime_set, is a predicate that does not accept(x, t). Also if a polygon marks a set that is not 2D, has a vertex outsidespace_set, is labelled:boundaryor: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 ofspace_set: anAbstractFloat, anIntegerthat is not aBool, aRationalor anAbstractIrrational. Any otherReal, aForwardDiff.Dualamong 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: ExplicitDomainMarkerscontainer.pairs: Variable sequence oflabel => identifierpairs.space_set: Spatial bounding set.time_set: 1D temporal interval for time-dependent boundary conditions. Predicate identifiers then take(x, t), and anx -> Boolpredicate throws anArgumentError.
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: ifVhas 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)