| Title: | Constrained Delaunay Triangulation Meshes for Spatial 'SPDE' Models |
|---|---|
| Description: | Generate constrained Delaunay triangulation meshes for use with stochastic partial differential equation (SPDE) spatial models (Lindgren, Rue and Lindstroem 2011 <doi:10.1111/j.1467-9868.2011.00777.x>). Provides automatic mesh generation from point coordinates with boundary constraints, Ruppert refinement for mesh quality, finite element method (FEM) matrix assembly (mass, stiffness, projection), barrier models, spherical meshes via icosahedral subdivision, and metric graph meshes for network geometries. Built on the 'CDT' header-only C++ library (Amirkhanov 2024 <https://github.com/artem-ogre/CDT>). Designed as the mesh backend for the 'tulpa' Bayesian hierarchical modelling engine but usable standalone for any spatial triangulation task. |
| Authors: | Gilles Colling [aut, cre, cph] (ORCID: <https://orcid.org/0000-0003-3070-6066>), Artem Amirkhanov [ctb, cph] (CDT library (src/cdt/, MPL-2.0)), William C. Lenthe [ctb, cph] (Geometric predicates (src/cdt/predicates.h, BSD-3-Clause)) |
| Maintainer: | Gilles Colling <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.2 |
| Built: | 2026-05-31 15:20:39 UTC |
| Source: | https://github.com/gcol33/tulpamesh |
Generic function to convert mesh objects from other packages into
tulpa_mesh objects. Currently supports fm_mesh_2d objects from
the fmesher package.
as_tulpa_mesh(x, ...) ## S3 method for class 'fm_mesh_2d' as_tulpa_mesh(x, ...) ## S3 method for class 'inla.mesh' as_tulpa_mesh(x, ...)as_tulpa_mesh(x, ...) ## S3 method for class 'fm_mesh_2d' as_tulpa_mesh(x, ...) ## S3 method for class 'inla.mesh' as_tulpa_mesh(x, ...)
x |
Object to convert. |
... |
Additional arguments (currently unused). |
A tulpa_mesh object.
Determines which mesh triangles fall inside barrier regions (e.g., coastlines, rivers, lakes). A triangle is marked as a barrier if its centroid falls inside any barrier polygon.
barrier_triangles(mesh, barriers)barrier_triangles(mesh, barriers)
mesh |
A |
barriers |
An sf/sfc object defining barrier regions, or a list of N x 2 coordinate matrices (each defining a closed polygon). |
A logical vector of length n_triangles. TRUE indicates
the triangle is inside a barrier region.
Computes the finite element mass (C) and stiffness (G) matrices from a triangular mesh, plus the projection matrix (A) that maps mesh vertices to observation locations.
fem_matrices( mesh, obs_coords = NULL, barrier = NULL, parallel = FALSE, lumped = FALSE )fem_matrices( mesh, obs_coords = NULL, barrier = NULL, parallel = FALSE, lumped = FALSE )
mesh |
A |
obs_coords |
Observation coordinates (N x 2 matrix). If NULL, the projection matrix A is the identity (observations at mesh nodes). |
barrier |
Optional logical vector of length |
parallel |
Logical. If |
lumped |
Logical. If |
A list with sparse matrices (dgCMatrix class from Matrix package):
C: consistent mass matrix (n_vertices x n_vertices)
G: stiffness matrix (n_vertices x n_vertices)
A: projection matrix (n_obs x n_vertices)
n_mesh: number of mesh vertices
C0: (only if lumped = TRUE) diagonal lumped mass matrix
va: (only if lumped = TRUE) vertex areas (numeric vector)
ta: (only if lumped = TRUE) triangle areas (numeric vector)
Computes weighted FEM matrices for non-stationary SPDE models where the range and variance parameters vary spatially. The weights are per-vertex kappa(s) and tau(s) values, interpolated within each triangle using the element-average approximation.
fem_matrices_nonstationary(mesh, kappa, tau)fem_matrices_nonstationary(mesh, kappa, tau)
mesh |
A |
kappa |
Numeric vector of length |
tau |
Numeric vector of length |
A list with sparse matrices:
Ck: kappa²-weighted mass matrix
Gk: kappa²-weighted stiffness matrix
Ct: tau²-weighted mass matrix
C: unweighted consistent mass matrix
G: unweighted stiffness matrix
C0: lumped (diagonal) mass matrix
n_mesh: number of mesh vertices
Computes finite element matrices using 6-node quadratic triangular elements. Each triangle edge gets a midpoint node, giving 6 basis functions per triangle instead of 3. Quadratic elements provide better approximation accuracy with fewer mesh nodes.
fem_matrices_p2(mesh)fem_matrices_p2(mesh)
mesh |
A |
A list with:
C: consistent mass matrix (n_total x n_total sparse)
G: stiffness matrix (n_total x n_total sparse)
n_mesh: total number of nodes (vertices + midpoints)
n_vertices: number of original mesh vertices
n_midpoints: number of added midpoint nodes
vertices: N x 2 matrix of all node coordinates
triangles6: M x 6 connectivity matrix
(columns: v0, v1, v2, mid01, mid12, mid20)
Finds connected components of a mesh via triangle adjacency (two triangles are connected if they share an edge).
mesh_components(mesh)mesh_components(mesh)
mesh |
A |
An integer vector of length n_triangles giving the
component ID (1, 2, ...) for each triangle.
Access or assign a coordinate reference system to a tulpa_mesh
or tulpa_mesh_graph object. The CRS is stored as metadata and
propagated through mesh operations.
mesh_crs(x) set_crs(x, value)mesh_crs(x) set_crs(x, value)
x |
A |
value |
A CRS specification: an integer EPSG code, a PROJ string,
a WKT string, an |
mesh_crs() returns the CRS (an sf::crs object or NULL).
set_crs() returns the mesh with CRS attached.
Computes quality metrics for each triangle in a mesh: minimum angle, maximum angle, aspect ratio, and area.
Computes per-triangle quality metrics: minimum angle, maximum angle, aspect ratio, and area. Useful for identifying degenerate triangles that may cause numerical issues in SPDE models.
mesh_quality(mesh) mesh_quality(mesh)mesh_quality(mesh) mesh_quality(mesh)
mesh |
A |
A data.frame with one row per triangle and columns:
min_angle: minimum interior angle (degrees)
max_angle: maximum interior angle (degrees)
aspect_ratio: ratio of circumradius to twice the inradius
(1 for equilateral, larger for worse quality)
area: triangle area
A data.frame with one row per triangle and columns:
min_angle: minimum interior angle (degrees)
max_angle: maximum interior angle (degrees)
aspect_ratio: longest edge / shortest edge (1 = equilateral)
area: triangle area
set.seed(42) mesh <- tulpa_mesh(cbind(runif(50), runif(50))) q <- mesh_quality(mesh) summary(q)set.seed(42) mesh <- tulpa_mesh(cbind(runif(50), runif(50))) q <- mesh_quality(mesh) summary(q)
Prints min/median/max of mesh quality metrics.
Prints a summary of mesh quality metrics.
mesh_summary(mesh) mesh_summary(mesh)mesh_summary(mesh) mesh_summary(mesh)
mesh |
A |
Invisible data.frame of per-triangle quality metrics.
Invisibly returns the quality data.frame.
Draws the mesh using base R graphics: edges as line segments, vertices as points. Optionally colors triangles by a quality metric.
## S3 method for class 'tulpa_mesh' plot( x, color = NULL, border = "grey50", vertex_col = NULL, vertex_cex = 0.5, palette = grDevices::hcl.colors, n_colors = 100L, main = "tulpa_mesh", ... )## S3 method for class 'tulpa_mesh' plot( x, color = NULL, border = "grey50", vertex_col = NULL, vertex_cex = 0.5, palette = grDevices::hcl.colors, n_colors = 100L, main = "tulpa_mesh", ... )
x |
A |
color |
Optional per-triangle numeric vector to color triangles
(e.g., output of |
border |
Edge color. Default |
vertex_col |
Vertex point color. Default |
vertex_cex |
Vertex point size. Default 0.5. |
palette |
Color palette function for triangle fill. Default
|
n_colors |
Number of colors in palette. Default 100. |
main |
Plot title. |
... |
Additional arguments passed to |
The tulpa_mesh object x, returned invisibly. Called for
the side effect of producing a plot.
Refines triangles where error indicators exceed a threshold by inserting their centroids as new vertices and re-triangulating. Designed for iterative solve-refine-re-solve workflows where error indicators come from an SPDE solver (e.g., tulpa's posterior variance).
refine_mesh( mesh, indicators, threshold = NULL, fraction = NULL, max_iter = 1L, min_area = 0 )refine_mesh( mesh, indicators, threshold = NULL, fraction = NULL, max_iter = 1L, min_area = 0 )
mesh |
A |
indicators |
Numeric vector of per-triangle error indicators
(length |
threshold |
Triangles with indicator above this value are refined.
Default: |
fraction |
Alternative to |
max_iter |
Maximum number of refine-retriangulate iterations. Default 1 (single refinement pass). |
min_area |
Minimum triangle area below which triangles are never refined, regardless of indicator. Default 0 (no limit). |
A new tulpa_mesh object with refined triangulation.
Splits each triangle into 4 sub-triangles by inserting edge midpoints, then re-triangulates. Useful for multi-resolution workflows where a coarser mesh needs uniform refinement.
subdivide_mesh(mesh)subdivide_mesh(mesh)
mesh |
A |
A new tulpa_mesh object with approximately 4x as many triangles.
Creates a new tulpa_mesh containing only the specified triangles,
with vertex indices remapped.
subset_mesh(mesh, triangles)subset_mesh(mesh, triangles)
mesh |
A |
triangles |
Integer vector of triangle indices to keep, or a
logical vector of length |
A new tulpa_mesh object containing only the selected
triangles and their vertices.
Generates a constrained Delaunay triangulation from point coordinates, optionally with boundary constraints. The resulting mesh can be used directly with tulpa's SPDE spatial fields.
tulpa_mesh( coords, data = NULL, boundary = NULL, max_edge = NULL, cutoff = 0, extend = 0.1, min_angle = NULL, max_area = NULL, max_steiner = 10000L ) ## S3 method for class 'tulpa_mesh' print(x, ...)tulpa_mesh( coords, data = NULL, boundary = NULL, max_edge = NULL, cutoff = 0, extend = 0.1, min_angle = NULL, max_area = NULL, max_steiner = 10000L ) ## S3 method for class 'tulpa_mesh' print(x, ...)
coords |
A matrix or data.frame with columns x and y, or a formula
like |
data |
Optional data.frame for formula evaluation. |
boundary |
Optional boundary specification: a matrix of boundary vertex coordinates (N x 2), an sf polygon, or NULL for convex hull. |
max_edge |
Maximum edge length. A single value or a vector of two
values |
cutoff |
Minimum distance between mesh vertices. Points closer than this are merged. Default 0 (no merging). |
extend |
Numeric extension factor beyond the boundary. Default 0.1 (10% of domain diameter). Set to 0 for no extension. |
min_angle |
Minimum angle (degrees) for Ruppert refinement. If
specified, Steiner points are inserted at circumcenters of triangles
with angles below this threshold. Theoretical maximum is ~20.7 degrees;
values up to 30 usually work. Default |
max_area |
Maximum triangle area for refinement. Triangles larger
than this are refined regardless of angle quality. Default |
max_steiner |
Maximum number of Steiner points to insert during Ruppert refinement. Default 10000. |
x |
A |
... |
Additional arguments (ignored). |
A tulpa_mesh object with components:
vertices: N x 2 matrix of vertex coordinates
triangles: M x 3 integer matrix of vertex indices (1-based)
edges: K x 2 integer matrix of edge vertex indices (1-based)
n_vertices: number of mesh vertices
n_triangles: number of triangles
n_edges: number of edges
n_input_points: number of original input points
The tulpa_mesh object x, returned invisibly.
# Simple mesh from random points set.seed(42) coords <- cbind(runif(50), runif(50)) mesh <- tulpa_mesh(coords) print(mesh) # Mesh with Ruppert refinement (min angle 25 degrees) mesh_refined <- tulpa_mesh(coords, min_angle = 25)# Simple mesh from random points set.seed(42) coords <- cbind(runif(50), runif(50)) mesh <- tulpa_mesh(coords) print(mesh) # Mesh with Ruppert refinement (min angle 25 degrees) mesh_refined <- tulpa_mesh(coords, min_angle = 25)
Generates a 1D mesh (interval partition) for temporal components of spatio-temporal SPDE models. Returns 1D FEM matrices (tridiagonal mass and stiffness) that can be combined with 2D spatial FEM matrices via Kronecker products in tulpa's space-time Q-builder.
tulpa_mesh_1d(knots, boundary = NULL, n_extend = 3L) ## S3 method for class 'tulpa_mesh_1d' print(x, ...)tulpa_mesh_1d(knots, boundary = NULL, n_extend = 3L) ## S3 method for class 'tulpa_mesh_1d' print(x, ...)
knots |
Numeric vector of mesh knot locations (e.g., time points). Will be sorted and deduplicated. |
boundary |
Two-element numeric vector |
n_extend |
Number of extra knots to add beyond each boundary, spaced at the median knot interval. Default 3. |
x |
A |
... |
Additional arguments (ignored). |
A tulpa_mesh_1d object with components:
knots: sorted numeric vector of mesh knot locations
n: number of knots
C: consistent mass matrix (tridiagonal, n x n sparse)
G: stiffness matrix (tridiagonal, n x n sparse)
C0: lumped (diagonal) mass matrix
The tulpa_mesh_1d object x, returned invisibly.
Builds a 1D FEM mesh along the edges of a spatial network (roads, rivers, coastlines). Each edge is discretized into segments with P1 linear elements. Junction nodes where edges meet are shared. Based on the Whittle-Matern SPDE formulation on metric graphs (Bolin, Simas & Wallin, 2024).
tulpa_mesh_graph(edges, max_edge = NULL, snap_tolerance = 1e-08) ## S3 method for class 'tulpa_mesh_graph' print(x, ...)tulpa_mesh_graph(edges, max_edge = NULL, snap_tolerance = 1e-08) ## S3 method for class 'tulpa_mesh_graph' print(x, ...)
edges |
A list of N x 2 coordinate matrices, each defining a polyline edge of the network. Or an sf object with LINESTRING geometries. |
max_edge |
Maximum segment length along edges. Edges longer
than this are subdivided. Default |
snap_tolerance |
Distance below which endpoints are snapped
to the same junction node. Default |
x |
A |
... |
Additional arguments (ignored). |
A tulpa_mesh_graph object with components:
vertices: N x 2 matrix of node coordinates
segments: M x 2 integer matrix of segment connectivity (1-based)
n_vertices, n_segments: counts
C: consistent mass matrix (tridiagonal-block sparse)
G: stiffness matrix (tridiagonal-block sparse)
C0: lumped (diagonal) mass matrix
degree: integer vector of node degrees (junction = degree > 2)
The tulpa_mesh_graph object x, returned invisibly.
Generates a geodesic mesh on the unit sphere by recursive subdivision of an icosahedron. Optionally refines around data locations using stereographic projection and local Delaunay triangulation.
tulpa_mesh_sphere(subdivisions = 3L, coords = NULL, radius = 1)tulpa_mesh_sphere(subdivisions = 3L, coords = NULL, radius = 1)
subdivisions |
Number of recursive icosahedral subdivisions. Each level quadruples the triangle count: 0 = 20 triangles, 1 = 80, 2 = 320, 3 = 1280, 4 = 5120, 5 = 20480. Default 3. |
coords |
Optional matrix of lon/lat coordinates (degrees) to insert into the mesh. Points are projected to the sphere and added as additional vertices via local re-triangulation. |
radius |
Sphere radius. Default 1 (unit sphere). For Earth, use 6371 (km). |
A tulpa_mesh object with components:
vertices: N x 3 matrix of (x, y, z) Cartesian coordinates
on the sphere surface
triangles: M x 3 integer matrix of vertex indices (1-based)
edges: K x 2 integer matrix of edge vertex indices (1-based)
lonlat: N x 2 matrix of (longitude, latitude) in degrees
n_vertices, n_triangles, n_edges: counts
n_input_points: number of original input points
sphere: list with radius and subdivisions