Mesh#
Grid closure of polygons#
mesh of a domain with polygon markers fits the grid to the polygons with _grid_closure (internal), the closure described on the Meshes API page. Lines within max(1e-10, 64ε) L of each other merge (L the polygons' extent), so crossings and vertices sit on nodes up to that tolerance.
The oblique edges must share one slope magnitude; that is necessary but not sufficient. A closure that grows past a number of lines proportional to the input throws an ArgumentError, "unbounded or too fine for this grid", and a polygon small relative to its offset from the origin may hit it, as the rounding of the closure then exceeds the merge tolerance.
The nodes are then marked by their signed distance s to the boundary: on it when |s| <= τ, inside when s < -τ, outside when s > τ, with τ = max(√ε hₘᵢₙ, 10³ ε L) + 4 ε M and M the largest vertex coordinate magnitude.
Grid fit of a level set#
mesh of a domain with a level-set marker :D => levelset(φ) hands the submeshes' points as seeds to _levelset_grid, which returns the fitted lines and the polygon inscribed in the curve φ = 0 whose vertices are the boundary nodes. Two paths, chosen by the crossing map of the curve (_levelset_class): when its orbits are finite, _grid_closure closes the seeds (the closure path, every seed kept); otherwise periodic orbits of the map are closed by least squares (the orbit path). Both end in the same post-check, which refuses a grid that does not fit.
_close_grid! returns that polygon with the new indices, and _set_markers! (what set_markers! calls, with one fitted polygon per region marker) labels the nodes by it: a vertex is a boundary node, every other node is inside or outside by the parity of the polygon edges above it, linear in the nodes. The polygon is never stored on the mesh, whose type carries no function, and φ stays on the domain. The labels are not recomputed from φ: snapping the curve to the nearest nodes mislabels a few vertices. The same reason makes set_markers!, change_points! and iterative_refinement! throw an ArgumentError on a level-set marker, since there is no polygon to label by. A domain takes one level-set marker, and none beside a polygon marker.
Ghost meshes#
A GhostMesh is a physical mesh together with one ghost point beyond each node of the facets it names. It wraps two meshes: the physical one, and a padded one of the same kind (Mesh1D or MeshnD) with one extra point at each named facet end, at \(x_1 - h_2\) below and \(x_n + h_n\) above, the values spacing(Ωₕ, 2) and forward_spacing(Ωₕ, n) already hold. The padded mesh is a real mesh with submeshes of its own, so every operator family and the assembly walk read the ghosts with no change.
The wrapper forwards the accessors to the padded mesh: points, spacings and indices are the padded ones, npoints(G, Tuple) is the padded size and G(i) the padded axis submesh. It overrides the queries that depend on where the faces are, so that a facet means the physical facet nodes: boundary_indices, index_in_marker, the surface weights and masks, and the geometric markers. A facet label (:xmin, :boundary, an alias, or a label of the physical mesh) is an index-derived stored marker on those nodes, never a predicate of position, so the labels survive change_points! and the mesh type carries no function.
A ghost has a number among 1:nghosts(G): the named facets in the order xmin, xmax, ymin, ymax, zmin, zmax, each facet's ghosts in the column-major order of its nodes. ghost_index and boundary_node convert between a facet node and its ghost number in O(1). A padded point outside along two or more axes (a 2D corner, a 3D edge or corner) is absent: it is not a node, has no ghost number and is never a degree of freedom, and is_absent tells it.
The padded points follow change_points! of the physical mesh through the version stamp of its submeshes, and are recomputed on the access that finds them stale. iterative_refinement! of the physical mesh changes the point count, which a ghost mesh cannot follow: its points, spacings, submeshes and cell lookup then throw an ArgumentError, while its counts and index sets keep the sizes it was built with. A new space is needed. gridspace of a GhostMesh is refused, because it would weigh the ghost and absent points as nodes: a space with ghosts is built from the physical mesh with gridspace(Ωₕ; ghosts) (see Spaces).
Bramble.AbstractMeshType
source
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
See
MeshMarkersfor marker management on meshes.
Base.eltype — Tuple{Bramble.AbstractMeshType}
source
eltype(Ωₕ::AbstractMeshType) -> Type
eltype(::Type{<:AbstractMeshType}) -> Type
Return the floating-point coordinate element type of the points in Ωₕ.
Bramble.half_spacings
source
half_spacings(Ωₕ::AbstractMeshType)
Return the cell widths (half-spacings) along each axis:
Bramble.is_collapsed — Tuple{Bramble.AbstractMeshType}
source
is_collapsed(Ωₕ::AbstractMeshType) -> Bool
Whether Ωₕ 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.markers! — Tuple{Bramble.AbstractMeshType, Any}
source
markers!(Ωₕ::AbstractMeshType, mesh_markers::MeshMarkers) -> Nothing
Override the markers dictionary in Ωₕ. Used internally during mesh refinement and by set_points! when it changes the point count, where the old dictionary is sized for the old grid and is replaced outright rather than merged into. The label table and the marker word matrix are rebuilt with it.
The mesh stores copies of the BitVectors, never mesh_markers's own: set_markers! re-evaluates a mesh's labels in place, and a BitVector shared with another mesh or dictionary would change under it.
Bramble.set — Tuple{Bramble.AbstractMeshType}
source
set(Ωₕ::AbstractMeshType) -> AbstractSetType
Return the underlying geometric set of the domain over which mesh Ωₕ is defined.
Bramble.set_indices! — Tuple{Bramble.AbstractMeshType, Any}
source
set_indices!(Ωₕ::AbstractMeshType, indices::CartesianIndices) -> Nothing
Override the grid indices in Ωₕ. Used internally during mesh refinement.
Bramble.generate_indices — Tuple{Int64}
source
generate_indices(pts::Int) -> CartesianIndices{1}
generate_indices(pts::NTuple{D, Int}) -> CartesianIndices{D}
Return the CartesianIndices of a mesh with pts[i] points in each direction.
For scalar input (Int), returns 1D CartesianIndices. For tuple input, returns multi-dimensional CartesianIndices.
Base.axes — Tuple{Bramble.AbstractMeshType}
source
Base.axes(Ωₕ::AbstractMeshType)
Base.axes(Ωₕ::AbstractMeshType, d::Integer)
Return the axes of the mesh's CartesianIndices.
Base.firstindex — Tuple{Bramble.AbstractMeshType{1}}
source
Base.firstindex(Ωₕ::AbstractMeshType)
Base.firstindex(Ωₕ::AbstractMeshType, d::Integer)
Return the first valid index of Ωₕ.
Base.iterate — Union{Tuple{Bramble.AbstractMeshType{1}}, Tuple{Bramble.AbstractMeshType{1}, Any}}
source
Base.iterate(Ωₕ::AbstractMeshType, [state])
Iterate over all grid points of Ωₕ, returning coordinates point(Ωₕ, idx) for each index.
Base.lastindex — Tuple{Bramble.AbstractMeshType{1}}
source
Base.lastindex(Ωₕ::AbstractMeshType)
Base.lastindex(Ωₕ::AbstractMeshType, d::Integer)
Return the last valid index of Ωₕ.
Base.length — Tuple{Bramble.AbstractMeshType}
source
Base.length(Ωₕ::AbstractMeshType) -> Int
Return the total number of points in Ωₕ, matching npoints(Ωₕ).
Base.size — Tuple{Bramble.AbstractMeshType}
source
Base.size(Ωₕ::AbstractMeshType) -> NTuple{D, Int}
Base.size(Ωₕ::AbstractMeshType, d::Integer) -> Int
Return the tuple of point counts along each spatial dimension, matching npoints(Ωₕ, Tuple).
Bramble._check_surface_is_thin — Union{Tuple{D}, Tuple{Bramble.AbstractMeshType{D}, Any}} where D
source
_check_surface_is_thin(Ωₕ::AbstractMeshType{D}, mask) -> Nothing
Refuse a face set that is not a surface on this mesh.
An axis carrying both its faces and at most two points puts every grid point on the set, so it has dimension D rather than D - 1 and no (D-1)-dimensional weight is defined on it. It fires on a 2x2 mesh of the square asked for :boundary, and on any mesh too coarse to separate the two ends of an axis; it never fires with three or more points on every axis.
Bramble._face_mask — Union{Tuple{N}, Tuple{D}, Tuple{Val{D}, NTuple{N, Symbol}}} where {D, N}
source
_face_mask(::Val{D}, labels::NTuple{N, Symbol}) -> NTuple{D, NTuple{2, Bool}}
The face set labels names, as a per-axis (min, max) mask.
:boundary is every face. Everything else has to name a whole coordinate face; see inner_Γ for why a general marked set is a different computation.
Bramble._marked_face_mask — Union{Tuple{D}, Tuple{Bramble.AbstractMeshType{D}, Any}} where D
source
_marked_face_mask(Ωₕ::AbstractMeshType{D}, marked_at) -> NTuple{D, NTuple{2, Bool}}
The coordinate faces a marked point set covers, as a per-axis (min, max) mask, read off the marked points rather than a label's name. marked_at(k) says whether the point with linear index k is marked.
A face is selected when every point of its relative interior is marked: the points off the edges and corners it shares with its neighbours. A marker that leaves the corners out (x -> x[1] == 1 && 0 < x[2] < 1) therefore still selects its face. A face with no relative interior (two points or fewer on a transverse axis) needs all its points marked; in 1D a face is a single point and its own interior.
Bramble._surface_mask — Union{Tuple{N}, Tuple{D}, Tuple{Bramble.AbstractMeshType{D}, NTuple{N, Symbol}}, Tuple{Bramble.AbstractMeshType{D}, NTuple{N, Symbol}, String}} where {D, N}
source
_surface_mask(Ωₕ::AbstractMeshType{D}, labels::NTuple{N, Symbol}, caller = "inner_Γ") -> NTuple{D, NTuple{2, Bool}}
The coordinate faces labels cover on the mesh Ωₕ, as a per-axis (min, max) mask. A label is read from its definition: one that markers(Ωₕ) holds is read off its marked points, the union over every such label going through _marked_face_mask; any other label is a face name, resolved by _face_of_symbol. The alias fallback of index_in_marker is not used: it answers a face name with the aliased marker's own points whenever they all lie on that face, and they can be only part of it, while a face name here means the whole coordinate face.
Throws an ArgumentError, prefixed with caller, when the markers select no face, or when one of their marked points lies on no selected face. "On a face" is index arithmetic, never a weight, so the check reads no spacing and does not scalar-index a device mesh.
Bramble._surface_weight — Union{Tuple{D}, Tuple{Bramble.AbstractMeshType{D}, Any, CartesianIndex{D}}} where D
source
_surface_weight(Ωₕ::AbstractMeshType{D}, mask, I::CartesianIndex{D}) -> Real
The lumped surface weight ω(I) of the face set mask, zero off the surface.
Computed from the mesh's live half_spacings on every call: there is no vector to store, key by label set, or stamp with a mesh version, so SpaceWeights (space/scalar_gridspace.jl) needs no fourth family and the staleness question does not arise here.
Bramble.locate_cell
source
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.
x outside [x_1, x_N] is silently clamped to the boundary cell – locate_cell never throws and never signals that x was out of range, so a caller that assumes the returned cell means x was inside the mesh inherits that assumption unknowingly. This is exactly what interpolate_at's outside keyword (gpena/Bramble.jl#223) exists to make an explicit, checked choice about one layer up, rather than leaving to a bare cell lookup with no way to say "no" – a new caller of locate_cell directly should decide its own out-of-range policy the same way, not assume this one already did.
In a hot loop, pass x as a Tuple or an SVector: both are stack-allocated and go through the zero-cost ntuple-based methods below, while a Vector allocates wherever it is built.
Examples
using Bramble: locate_cell
Ωₕ = mesh(domain(interval(0.0, 1.0)), 11) # h = 0.1
locate_cell(Ωₕ, 0.35) # returns 4 (interval [0.3, 0.4])
locate_cell(Ωₕ, 5.0) # returns 10 (the last cell) -- no error, x = 5.0 is well outside [0, 1]
Bramble.stepsize — Tuple{Bramble.AbstractMeshType{1}}
source
stepsize(Ωₕ::AbstractMeshType) -> Union{Real, NTuple{D, Real}}
stepsize(Ωₕ::AbstractMeshType, d::Integer) -> Real
Return 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.MeshMarkers
source
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.
The dictionary markers(Ωₕ) returns is a read-only view; see there for how to change labels.
Bramble.__process_condition! — Tuple{Any, Any, Any}
source
__process_condition!(mesh_marker::BitVector, identifier, Ωₕ::AbstractMeshType) -> Nothing
Core logic for evaluating a function-based marker predicate across all mesh points.
Reads coordinates once through host_points rather than point(Ωₕ, idx) per point, so identifier – an arbitrary user predicate that must keep running on the host – is evaluated against a host-resident array whether Ωₕ itself is device-backed or not (gpena/Bramble.jl#309).
Bramble._condition_point — Tuple{AbstractVector, CartesianIndex{1}}
source
_condition_point(pts, idx) -> Union{Real, NTuple}
Read the coordinate at idx from pts, host_points(Ωₕ)'s return value.
A Mesh1D gives a Vector and idx is a CartesianIndex{1}; a MeshnD gives the per-axis NTuple{D, Vector} and idx is a CartesianIndex{D}, assembled here into the coordinate tuple the same way point(Ωₕ::MeshnD, idx) does.
Bramble._ensure_geometric_markers! — Tuple{Dict{Symbol, BitVector}, Bramble.AbstractMeshType}
source
_ensure_geometric_markers!(mesh_markers::MeshMarkers, Ωₕ::AbstractMeshType) -> Nothing
Seed :boundary and :interior from the mesh's own geometry, preserving any existing custom definitions registered under those names.
If a pre-existing custom marker with the same name disagrees with the geometric boundary, a warning is issued because downstream operators (restrict_to) assume geometric semantics, unless warn_marker_mismatch is false, for a caller that has deliberately redefined the label and does not want to be told so on every mesh built from it.
Bramble._init_mesh_markers — Tuple{Bramble.AbstractMeshType, Union{Bramble.DomainMarkers, Bramble.EvaluatedDomainMarkers}}
source
_init_mesh_markers(Ωₕ::AbstractMeshType, domain_markers) -> MeshMarkers
Internal helper function to construct and initialize the MeshMarkers dictionary.
Allocates BitVector storage initialized to false for every symbol, tuple, and condition label defined in domain_markers, a DomainMarkers or the EvaluatedDomainMarkers that Ω(t) of a time-dependent domain holds, and for the three labels of each polygon and level-set marker.
Bramble._mark_indices! — Tuple{AbstractVector{Bool}, Any, Any}
source
_mark_indices!(marker_set::AbstractVector{Bool}, linear_indices, indices_to_mark) -> Nothing
Utility function to update a boolean marker vector.
Sets entries to true at the linear positions corresponding to indices_to_mark.
Bramble._marker_id — Tuple{Bramble.AbstractMeshType, Symbol}
source
_marker_id(Ωₕ::AbstractMeshType, label::Symbol) -> Int
The column of label in _marker_words(Ωₕ). Reads the label exactly as stored: no boundary alias is resolved, unlike index_in_marker.
Throws
KeyError:Ωₕhas no markerlabel.
Bramble._marker_table — Tuple{Dict{Symbol, BitVector}, Int64}
source
_marker_table(mesh_markers::MeshMarkers, npts::Int) -> Tuple{Dict{Symbol, Int}, Matrix{UInt64}}
The label-to-column table of mesh_markers, labels sorted, and the word matrix holding each label's BitVector chunks in its column, cld(npts, 64) words per column.
Bramble._marker_words
source
_marker_words(Ωₕ::AbstractMeshType) -> Matrix{UInt64}
The marker word matrix of Ωₕ (or of its state): word × label, column _marker_id(Ωₕ, label) holding the chunks of markers(Ωₕ)[label].
Bramble._set_markers_conditions! — Tuple{Dict{Symbol, BitVector}, Tuple, Any}
source
_set_markers_conditions!(mesh_markers::MeshMarkers, conditions, Ωₕ::AbstractMeshType) -> Nothing
Iterate through all function-based markers and evaluate them across the mesh.
Bramble._set_markers_regions! — Tuple{Dict{Symbol, BitVector}, Any, Tuple, Any}
source
_set_markers_regions!(mesh_markers::MeshMarkers, regions, fitted::Tuple, Ωₕ::AbstractMeshType) -> Nothing
Mark the interior, boundary and exterior nodes of every polygon and level-set marker in regions. fitted holds, per region, the polygon mesh inscribed in a level-set curve (nothing for a polygon marker, which labels by its own polygon).
Bramble._set_markers_symbols! — Tuple{Dict{Symbol, BitVector}, Any, Any}
source
_set_markers_symbols!(mesh_markers::MeshMarkers, symbols, Ωₕ::AbstractMeshType) -> Nothing
Process markers identified by predefined symbols (:left, :top, etc.) or collections of symbols.
Bramble.boundary_symbol_to_cartesian — Tuple{CartesianIndices{1, R} where R<:Tuple{OrdinalRange{Int64, Int64}}}
source
boundary_symbol_to_cartesian(indices::CartesianIndices{1}) -> NamedTuple
boundary_symbol_to_cartesian(indices::CartesianIndices{2}) -> NamedTuple
boundary_symbol_to_cartesian(indices::CartesianIndices{3}) -> NamedTuple
Map canonical boundary symbols (:xmin, :xmax, :ymin, :ymax, :zmin, :zmax) and legacy viewpoint aliases (:left, :right, :top, :bottom, :front, :back) to their corresponding CartesianIndices on the mesh boundary.
Returns
A NamedTuple with boundary symbols as keys and CartesianIndices as values.
1D has
:xmin,:xmax,:left,:right2D has
:xmin,:xmax,:ymin,:ymax,:left,:right,:bottom,:top3D has all six faces,
:xmin,:xmax,:ymin,:ymax,:zmin,:zmax,:back,:front,:left,:right,:bottom,:top
Examples
faces = boundary_symbol_to_cartesian(CartesianIndices((1:3, 1:4)))
faces.xmin == CartesianIndices((1:1, 1:4)) && faces.ymax == CartesianIndices((1:3, 4:4)) &&
faces.left === faces.xmin && faces.top === faces.ymax
# output
true
See also: boundary_symbol_to_dict, set_markers!.
Bramble.boundary_symbol_to_dict — Tuple{CartesianIndices}
source
boundary_symbol_to_dict(indices::CartesianIndices) -> Dict{Symbol, CartesianIndices}
Return a dictionary connecting the facet labels of a set to the corresponding CartesianIndices.
See also: boundary_symbol_to_cartesian.
Bramble.process_label_for_mesh! — Tuple{Any, Dict{Symbol, BitVector}, Any}
source
process_label_for_mesh!(npts, markers_mesh::MeshMarkers, set_labels) -> Nothing
Initialize boolean indicator vectors for a collection of marker labels within markers_mesh.
For each label in set_labels, assigns a BitVector of length npts initialized to false.
Arguments
npts: Total number of grid points in the mesh.markers_mesh:MeshMarkersdictionary modified in-place.set_labels: Collection ofSymbollabels to initialize.
Bramble.set_markers! — Tuple{Bramble.AbstractMeshType, Any}
source
set_markers!(Ωₕ::AbstractMeshType, domain_markers; warn_marker_mismatch = true) -> Nothing
Evaluate domain markers onto mesh points, creating BitVector indicators for each label.
Supports five classes of domain markers:
Symbol markers, the predefined boundary labels (
:left,:right, etc.).Tuple markers, the unions of boundary symbols.
Function markers, the boolean predicates
x -> Bool.Polygon markers
:L => polygon(V), which split the nodes of a 2D mesh intoL(inside),L_boundaryandL_exterior(outside); seemarkersfor the boundary tolerance.Level-set markers
:L => levelset(φ), which give the same three labels. Their labels come from the polygon thatmeshinscribes in the curve \(φ = 0\) when it fits the grid, soset_markers!alone refuses a level-set marker (below).
Also seeds the default geometric markers :boundary and :interior if not already defined.
When domain_markers holds the labels Ωₕ already has, at its current point count, they are re-evaluated into the stored BitVectors rather than into new ones, so a BitVector read from markers before the call holds the new set after it. A changed label set builds the markers afresh.
Arguments
Ωₕ: Target mesh whosemarkersfield is populated.domain_markers: aDomainMarkersor the evaluated markers ofΩ(t), holding the semantic boundary or regional labels.
Keywords
warn_marker_mismatch::Bool = true: whether to warn when a custom:boundary/:interiormarker disagrees with the mesh's own geometric definition. The custom marker is kept either way; set tofalseto silence the warning for an intentional redefinition (seemesh).
Throws
ArgumentError: ifdomain_markersholds a level-set marker, whose labels onlymeshcan set: the grid lines and the inscribed polygon are fitted together, and neither is kept. This coverschange_points!anditerative_refinement!of such a mesh too.
Examples
Ω = domain(interval(0, 1) × interval(0, 1),
:inlet => :left,
:outlet => :right,
:walls => (:top, :bottom),
:obstacle => x -> norm(x .- 0.5) < 0.2)
Ωₕ = mesh(Ω, (20, 20), (true, true))
# markers(Ωₕ) contains BitVectors for :inlet, :outlet, :walls, :obstacle, :boundary, :interior
See also: DomainMarkers, MeshMarkers.
Bramble.Mesh1DState
source
Mesh1DState{BT, CI, VT, T, WT} <: AbstractMeshType{1}
The immutable state of a Mesh1D: its geometry, version, uniformity flag and marker words, held as plain arrays and isbits values only (gpena/Bramble.jl#437), so it can cross a task boundary (Polyester.@batch) where a mutable mesh holding a Dict cannot.
_walk_mesh(Ωₕ) returns it. It implements every geometric accessor a Mesh1D does (points, point, spacing, half_spacing, cell_measure, npoints, _mesh_version, is_uniform, ...), reading the same arrays: a state taken before set_points! with an unchanged point count still aliases the mutated arrays, and only its version tells it is stale. The marker Dict stays on the Mesh1D (markers); the state carries the same bits as one word matrix (_marker_words), column _marker_id(Ωₕ, label) per label.
Fields
set: the 1DCartesianProductinterval the mesh discretises.indices:CartesianIndices{1}of the grid points.backend: the linear algebraBackend.pts: grid points \(x_i\), \(i = 1, \dots, N\).half_pts: cell boundaries \(x_{i+1/2}\), \(i = 1, \dots, N+1\).half_spacings: cell widths \(h_{i+1/2}\), \(i = 1, \dots, N\).spacings: backward spacings \(h_i = x_i - x_{i-1}\), with \(h_1 = x_2 - x_1\).collapsed: whether the interval is degenerate (a single point).version: the mesh version when this state was built (gpena/Bramble.jl#221); see_mesh_version.uniform: the default-toleranceis_uniformanswer for these points, computed whenever the points change (gpena/Bramble.jl#332).words: one column ofBitVectorchunks per marker label, aMatrix{UInt64}on a mesh; its typeWTis a parameter so a rebuilt state can hold another array type (_batch_rebuild).uid: an identity unique to the owning mesh, kept across its mutations.
Base.copy — Tuple{Bramble.Mesh1D}
source
Base.copy(Ωₕ::Mesh1D) -> Mesh1D
Create a copy of mesh Ωₕ. The copy is shallow with respect to immutable fields (set, indices, backend, collapsed), but deep with respect to mutable data fields (pts, half_pts, half_spacings, spacings, markers). The copy keeps the version and gets an identity of its own.
Base.show — Tuple{IO, Bramble.Mesh1D}
source
Base.show(io::IO, Ωₕ::Mesh1D) -> Nothing
Custom display for Mesh1D objects with detailed mesh summary, domain information, and markers.
Bramble._launch_half_points! — NTuple{4, Any}
source
_launch_half_points!(x::AbstractVector, pts, n::Int, dev) -> Nothing
Fills x (length n + 1) with the mesh's half points: the boundary entries x[1] = pts[1] and x[n + 1] = pts[n], and the interior midpoints x[i] = (pts[i] + pts[i - 1]) / 2, via a KernelAbstractions.@kernel launch on dev. The device counterpart of half_points!'s CPU loop.
Throws
ErrorException: noKernelAbstractionsextension is loaded (_throw_no_ka_mesh_kernel).
Bramble._launch_half_spacing! — NTuple{4, Any}
source
_launch_half_spacing!(x::AbstractVector, h, n::Int, dev) -> Nothing
Fills x (length n) with the mesh's half spacings: the boundary entries x[1] = h[1] / 2 and x[n] = h[n] / 2, and the interior x[i] = (h[i] + h[i + 1]) / 2, via a KernelAbstractions.@kernel launch on dev. The device counterpart of half_spacing!'s CPU loop.
Throws
ErrorException: noKernelAbstractionsextension is loaded (_throw_no_ka_mesh_kernel).
Bramble._launch_nonuniform_mesh1d_metrics! — NTuple{6, Any}
source
_launch_nonuniform_mesh1d_metrics!(half_pts, spacings, half_spacings, pts, n::Int, dev) -> Nothing
Fills a non-uniform mesh's three derived arrays – spacings, half_pts and half_spacings – in a single KernelAbstractions.@kernel launch on dev, over 1:(n + 1) work items, from pts alone: spacings[i] = pts[i] - pts[i - 1] (pts[2] - pts[1] at i = 1); half_pts[i] = (pts[i] + pts[i - 1]) / 2 for 2 <= i <= n, with boundary entries pts[1] at i = 1 and pts[n] at i = n + 1; and half_spacings[i] = spacing(i) / 2 at i = 1 or i = n, and, with back = pts[i] - pts[i - 1] and fwd = pts[i + 1] - pts[i], half_spacings[i] = (back + fwd) / 2 elsewhere – the same association half_spacing! uses on its own already-rounded spacings, not the cheaper two-point difference (pts[i + 1] - pts[i - 1]) / 2, so a device mesh matches the CPU one bit for bit – the fusion of _launch_spacing!, _launch_half_points! and _launch_half_spacing! into one kernel for a non-uniform device mesh (gpena/Bramble.jl#305). Each thread reads only its own 3-point local stencil of pts, so spacings never has to be written to device memory before half_spacings can be computed from it.
Throws
ErrorException: noKernelAbstractionsextension is loaded, so there is no device kernel to reach (_throw_no_ka_mesh_kernel).
Bramble._launch_refine_indices! — NTuple{4, Any}
source
_launch_refine_indices!(new_points::AbstractVector, old_points, N_old::Int, dev) -> Nothing
Fills new_points (length 2 * N_old - 1) with the refined mesh: new_points[2i - 1] = old_points[i] copies each old point to its odd slot, and new_points[2i] = (old_points[i] + old_points[i + 1]) / 2 inserts the midpoint at the even slot for i < N_old, via a KernelAbstractions.@kernel launch on dev. The device counterpart of _refine_indices_fill!'s CPU loop.
Throws
ErrorException: noKernelAbstractionsextension is loaded (_throw_no_ka_mesh_kernel).
Bramble._launch_spacing! — NTuple{4, Any}
source
_launch_spacing!(x::AbstractVector, pts, n::Int, dev) -> Nothing
Fills x (length n) with the backward spacings of pts: the boundary entry x[1] = pts[2] - pts[1], and x[i] = pts[i] - pts[i - 1] elsewhere, via a KernelAbstractions.@kernel launch on dev. The device counterpart of spacing!'s CPU loop.
Throws
ErrorException: noKernelAbstractionsextension is loaded (_throw_no_ka_mesh_kernel).
Bramble._launch_uniform_mesh1d_init! — NTuple{9, Any}
source
_launch_uniform_mesh1d_init!(pts, half_pts, spacings, half_spacings, a, b, h, n::Int, dev) -> Nothing
Fills all four of a uniform mesh's arrays – pts, half_pts, spacings and half_spacings – in a single KernelAbstractions.@kernel launch on dev, over 1:(n + 1) work items: pts[i] = a + (i - 1) * h except pts[n] = b, spacings[i] = h, half_spacings[i] = h / 2 at i = 1 or i = n and h elsewhere, and half_pts[i] = a at i = 1, b at i = n + 1, and the midpoint formula in between. The last point is b itself, since a + (n - 1) * h can miss it by an ulp (gpena/Bramble.jl#695). Every entry comes from a, b, h and n alone, with no read of pts itself – the fusion of the uniform point fill, _launch_spacing!, _launch_half_points! and _launch_half_spacing! into one kernel for a uniform device mesh (gpena/Bramble.jl#303).
Throws
ErrorException: noKernelAbstractionsextension is loaded, so there is no device kernel to reach (_throw_no_ka_mesh_kernel).
Bramble._mesh_version — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT}
source
_mesh_version(Ωₕ::AbstractMeshType) -> Int
The monotone counter set_points! bumps every in-place point mutation (gpena/Bramble.jl#221) – what a ScalarGridSpace's SpaceWeights records at construction and every discrete inner product/norm re-checks against, to catch weights computed from a mesh that has since been mutated underneath them.
A Mesh1D reads the counter stored in its Mesh1DState. A MeshnD has no coordinates of its own – only submeshes, each independently mutated by change_points!(Ωₕ::MeshnD, ...) – so its version is the sum of theirs: strictly increasing whenever any one axis changes, regardless of which, with no second counter of its own to keep in sync. A state answers the version it was built at.
Bramble._rebackend — Union{Tuple{F}, Tuple{Bramble.Mesh1D, Bramble.Backend, F}} where F
source
_rebackend(Ωₕ::Mesh1D, be::Backend, f) -> Mesh1D
A mesh over the same markers, label table, version and uniformity flag as Ωₕ, on backend be, with f applied to each of its four arrays (Array for a host mirror, identity to share them), and an identity of its own.
Bramble._seed_mesh1d_rng! — Tuple{Any}
source
_seed_mesh1d_rng!(seed) -> Nothing
Seed the package-local RNG _generate_random_points! can draw from, and switch it onto that RNG instead of Random.default_rng() (gpena/Bramble.jl#320). A device backend's kernel launches (for metrics, fused init, and other work) consume draws from the global RNG as a side effect of launching, so seeding Random.default_rng() alone does not make a host and device build of the "same" non-uniform mesh agree.
Call this immediately before building one or more non-uniform Mesh1Ds that must reproduce the same interior coordinates for a given seed regardless of what any kernel launch does to the global stream, then call _unseed_mesh1d_rng! once done. Code that never calls this is unaffected: _generate_random_points! keeps drawing from Random.default_rng() exactly as it always did, so an existing Random.seed!(N) call immediately before a non-uniform mesh build keeps controlling it.
Bramble._unseed_mesh1d_rng! — Tuple{}
source
_unseed_mesh1d_rng!() -> Nothing
Switch _generate_random_points! back onto Random.default_rng(), undoing _seed_mesh1d_rng!. Call this once code that opted into the package-local RNG is done building its non-uniform mesh(es), so no later, unrelated code silently keeps reading from it.
Bramble._walk_mesh — Tuple{Bramble.Mesh1D}
source
_walk_mesh(Ωₕ::AbstractMeshType) -> AbstractMeshType
The immutable state the assembly walk reads Ωₕ through: a Mesh1D's stored Mesh1DState, or for a MeshnD a MeshnDState built on demand from its submeshes' states. A state is its own walk state.
Bramble.backward_spacings_for_derivative — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT}
source
backward_spacings_for_derivative(Ωₕ::Mesh1D) -> AbstractVector
Return a vector h with h[i] ==spacing_for_derivative(Ωₕ, i) for every i > 1. Entry 1 holds spacing(Ωₕ, 1), which no backward stencil reads; the scalar spacing_for_derivative(Ωₕ, 1) is zero.
Bramble.forward_spacing_for_derivative — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT, Any}
source
forward_spacing_for_derivative(Ωₕ::Mesh1D, idx) -> eltype(Ωₕ)
Return the spacing that a forward finite difference divides by at idx, which is forward_spacing(Ωₕ, idx) everywhere except the last point, where the difference has no stencil and this is zero.
See also: spacing_for_derivative, spacings.
Bramble.forward_spacings — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT}
source
forward_spacings(Ωₕ::Mesh1D) -> AbstractVector
Return 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, a new vector each call, of the same array type as spacings(Ωₕ).
See also: forward_spacing_for_derivative.
Bramble.forward_spacings_for_derivative — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT}
source
forward_spacings_for_derivative(Ωₕ::Mesh1D) -> AbstractVector
Return a vector h with h[i] ==forward_spacing_for_derivative(Ωₕ, i) for every i < npoints(Ωₕ), as a view onto cached spacings. The last entry is omitted because the forward difference has no stencil at the right boundary.
Bramble.host_half_spacings — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT}
source
host_half_spacings(Ωₕ::Mesh1D) -> Vector
Return half_spacings(Ωₕ) as a host-resident Array, in one bulk transfer regardless of where Ωₕ's storage lives, the same way host_spacings does for spacings (gpena/Bramble.jl#307).
On a host-backed mesh, half_spacings(Ωₕ) already is an Array, so this returns it directly with no copy; only a device-backed mesh pays the one bulk Array(...) transfer.
See also: host_spacings, half_spacings, half_spacing.
Bramble.host_points — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT}
source
host_points(Ωₕ::Mesh1D) -> Vector
Return points(Ωₕ) as a host-resident Array, in one bulk transfer regardless of where Ωₕ's storage lives, the same way host_spacings does for spacings (gpena/Bramble.jl#308).
point(Ωₕ, i) throws outright on a device-backed mesh rather than scalar-reading it one point at a time. Call this once instead to bring every coordinate to the host in a single transfer, then index the Array it returns as many times as needed – locate_cell's non-uniform search does exactly that.
On a host-backed mesh, points(Ωₕ) already is an Array, so this returns it directly with no copy – host_points(Ωₕ) === points(Ωₕ); only a device-backed mesh pays the one bulk Array(...) transfer.
See also: host_spacings, points, point.
Bramble.host_spacings — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT}
source
host_spacings(Ωₕ::Mesh1D) -> Vector
Return spacings(Ωₕ) as a host-resident Array, in one bulk transfer regardless of where Ωₕ's storage lives (gpena/Bramble.jl#94).
spacing(Ωₕ, i) and forward_spacing(Ωₕ, i) read spacings(Ωₕ) one element at a time, which a device-backed mesh (Metal.jl, ...) refuses outright: its scalar- indexing guard throws before a per-point loop over either accessor gets anywhere. Call this once instead to bring every spacing to the host in a single transfer, then index the Array it returns as many times as needed – stencil_matrix.jl's dense stencil-matrix builder does exactly that.
On a host-backed mesh, spacings(Ωₕ) already is an Array, so this returns it directly with no copy; only a device-backed mesh pays the one bulk Array(...) transfer.
See also: spacings, spacing, forward_spacing.
Bramble.locate_cell — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT, Real}
source
locate_cell(Ωₕ::Mesh1D, x::Real) -> Int
See locate_cell's generic docstring (mesh/queries.jl) for the contract: the largest node index i with pts[i] <= x, clamped to 1:n-1 – exactly what searchsortedlast on the point array answers, and what this method must keep answering, uniform or not.
A uniform host-backed mesh reads its own end points pts[1] and pts[n], an O(1) read, and takes a first estimate clamp(floor(Int, (x - pts[1]) / h) + 1, 1, n - 1) with h = (pts[n] - pts[1]) / (n - 1); it then walks against pts until pts[idx] <= x < pts[idx + 1], one step at most in practice. That is exactly searchsortedlast, with no allocation, whatever the points are: change_points! accepts points that are uniform only within tolerance and need not lie on the domain's set, so neither the set nor a closed form a + (i - 1) * h may stand in for them (gpena/Bramble.jl#495).
A uniform device-backed mesh refuses scalar reads (gpena/Bramble.jl#308). While its version is still 0 its points are the ones _mesh built from the set, so it answers with no array read: the same estimate on the set's endpoints a, b, then one correction step against the closed-form coordinates of that estimate's neighbouring nodes (a + idx * h, a + (idx - 1) * h), since (x - a) / h rounds to either side of an integer at a node coordinate – worse in Float32 (Metal's only type) than in Float64. Once set_points! has replaced its points (version above 0), it searches host_points(Ωₕ) as a non-uniform device mesh does: one bulk transfer, exact.
A point at or past either endpoint, ±Inf included, returns the boundary cell before any division, and NaN returns n - 1 as the search does, so no path throws.
A non-uniform mesh has no formula to fall back on and searches host_points(Ωₕ) instead of the raw, possibly device-resident points(Ωₕ).
Bramble.spacing_for_derivative — Tuple{Union{Bramble.Mesh1D{BT}, Bramble.Mesh1DState{BT}} where BT, Any}
source
spacing_for_derivative(Ωₕ::Mesh1D, idx) -> eltype(Ωₕ)
Return the spacing that a backward finite difference divides by at idx, which is spacing(Ωₕ, idx) everywhere except the first point, where the difference has no stencil and this is zero.
See also: forward_spacing_for_derivative, spacings.
Bramble.MeshnDState
source
MeshnDState{D, BT, CI, SM, T, WT} <: AbstractMeshType{D}
The immutable walk state of a MeshnD (gpena/Bramble.jl#437), built on demand by _walk_mesh: the set, parent indices and backend, the tuple of its submeshes' Mesh1DStates, its marker word matrix, its version (the sum of the submeshes') and its identity. Plain arrays and isbits values only. It answers every geometric accessor a MeshnD does, through its submesh states. The word matrix's type WT is a parameter so a rebuilt state can hold another array type (_batch_rebuild).
Base.copy — Union{Tuple{Bramble.MeshnD{D, BT, CI} where {BT<:Bramble.Backend, CI<:(CartesianIndices{D, R} where R<:NTuple{D, OrdinalRange{Int64, Int64}})}}, Tuple{D}} where D
source
Base.copy(Ωₕ::MeshnD{D}) -> MeshnD{D}
Create a copy of mesh Ωₕ. The copy is shallow with respect to immutable fields (set, indices, backend), but deep with respect to mutable data fields (submeshes, markers). The copy and each of its submeshes get an identity of their own.
Base.show — Union{Tuple{D}, Tuple{IO, Bramble.MeshnD{D, BT, CI} where {BT<:Bramble.Backend, CI<:(CartesianIndices{D, R} where R<:NTuple{D, OrdinalRange{Int64, Int64}})}}} where D
source
Base.show(io::IO, Ωₕ::MeshnD) -> Nothing
Custom display for MeshnD objects with detailed mesh summary, domain information, and markers.
Bramble._grid_closure — Tuple{Union{Bramble.LevelSet, Bramble.Polygon}, Any, Any}
source
_grid_closure(P, xs, ys) -> (Vector{T}, Vector{T})
Close the grid lines xs and ys (sorted, end points included) over the polygon P, or a tuple of polygons and level sets, so that every crossing of a grid line with \(\partial P\) is a grid node. T is the floating-point type of xs and ys; the vertices are converted to T first.
The end points and the vertex coordinates are lines first, then the lines the closure computes: a grid line crossing an oblique edge, or the curve \(\varphi = 0\) of a level set (_crossings, between the end-point lines), adds the crossing's other coordinate as a line, until no line is new. The remaining entries of xs and ys are seeds. Lines closer than the merge tolerance \(\max(10^{-10}, 64\varepsilon) L\) (\(L\) the polygons' extent, \(\varepsilon\) the larger epsilon of T and of the vertices' type, whose rounding a vertex carries; with no vertices, \(L\) is the larger span of the end-point lines) are one line. A seed that meets a vertex or end-point line, or whose lines meet those of a vertex or an earlier seed, within the tolerance but not exactly, is dropped and the closure restarts without it: the seed gives way, never the vertex or the line computed from it. Two lines that both come from the end points and vertices and lie within the tolerance are one line. Polygon detail finer than the tolerance is merged, as is rounding, since floating point cannot tell the two apart. Every vertex coordinate is a line, and every crossing is on a node, up to the merge tolerance.
The oblique edges must share one slope magnitude, a necessary condition only: with equal slopes the closure may still grow without bound, or beyond the cap. A level set closes only when the orbits of its crossing map are finite; otherwise the cap is reached.
Throws
ArgumentError: if the oblique edges do not share one slope magnitude, or if the closure exceeds a number of lines proportional to the input (the grid closure of a polygon or level set is unbounded or too fine for this grid).
Bramble._mesh — Union{Tuple{D}, Tuple{Bramble.Domain, NTuple{D, Int64}, NTuple{D, Bool}, Any}} where D
source
_mesh(Ω::Domain, npts::NTuple{D, Int}, unif::NTuple{D, Bool}, backend) -> MeshnD{D}
Internal constructor for multi-dimensional tensor-product mesh MeshnD.
Builds the 1D submeshes along each axis and combines them into a MeshnD. Collapsed dimensions (degenerate single-point intervals) are forced to a point count of 1.
Arguments
Bramble.cell_measures — Union{Tuple{Union{Bramble.MeshnD{D, BT, CI} where {BT<:Bramble.Backend, CI<:(CartesianIndices{D, R} where R<:NTuple{D, OrdinalRange{Int64, Int64}})}, Bramble.MeshnDState{D, BT, CI} where {BT<:Bramble.Backend, CI<:(CartesianIndices{D, R} where R<:NTuple{D, OrdinalRange{Int64, Int64}})}}}, Tuple{D}} where D
source
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.host_points — Union{Tuple{Union{Bramble.MeshnD{D, BT, CI} where {BT<:Bramble.Backend, CI<:(CartesianIndices{D, R} where R<:NTuple{D, OrdinalRange{Int64, Int64}})}, Bramble.MeshnDState{D, BT, CI} where {BT<:Bramble.Backend, CI<:(CartesianIndices{D, R} where R<:NTuple{D, OrdinalRange{Int64, Int64}})}}}, Tuple{D}} where D
source
host_points(Ωₕ::MeshnD{D}) -> NTuple{D, Vector}
Return the per-axis host_points(Ωₕ(i)) of each submesh, one bulk transfer per axis regardless of where that axis's storage lives (gpena/Bramble.jl#308).
Bramble.submeshes — Tuple{Bramble.Domain, Any, Any, Any}
source
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
Bramble.print_mesh_domain_info — Tuple{Bramble.PrettyPrinter, Bramble.CartesianProduct}
source
print_mesh_domain_info(pp::PrettyPrinter, set::CartesianProduct) -> Nothing
Print the domain information for a mesh.
Bramble.print_mesh_header — Tuple{Bramble.PrettyPrinter, String, Int64, Type, Any}
source
print_mesh_header(pp::PrettyPrinter, mesh_type::String, D::Int, T::Type, npts) -> Nothing
Print a styled header for mesh objects.
Bramble.print_mesh_markers — Tuple{Bramble.PrettyPrinter, Dict{Symbol, BitVector}}
source
print_mesh_markers(pp::PrettyPrinter, mesh_markers::MeshMarkers) -> Nothing
Print marker information and labeled point counts for a mesh.
Bramble.print_mesh_spacing_info — Tuple{Bramble.PrettyPrinter, Bool, Any}
source
print_mesh_spacing_info(pp::PrettyPrinter, uniform::Bool, hmax) -> Nothing
print_mesh_spacing_info(pp::PrettyPrinter, uniform::Tuple{Vararg{Bool}}, hmax) -> Nothing
Print mesh spacing information and maximum cell diagonal.
Bramble.print_mesh_summary — Tuple{Bramble.PrettyPrinter, Any, Int64, Bool}
source
print_mesh_summary(pp::PrettyPrinter, npts, topodim::Int, collapsed::Bool) -> Nothing
Print a summary line for mesh properties (number of points, topology).