Spatial meshes and FEM matrices for SPDE models, from point clouds to precision matrices in three lines.
library(tulpaMesh)
coords <- cbind(runif(200), runif(200))
mesh <- tulpa_mesh(coords, max_edge = 0.1)
fem <- fem_matrices(mesh, obs_coords = coords)
# fem$C (mass), fem$G (stiffness), fem$A (projection) ready for SPDESpatial models based on the SPDE approach (Lindgren, Rue & Lindstrom 2011) need a triangulated mesh and finite element matrices. tulpaMesh handles mesh generation, quality refinement, and FEM matrix assembly with no external dependencies beyond Rcpp and Matrix.
Common applications:
tulpa_mesh() - Constrained Delaunay from points,
formulas, or sf boundariestulpa_mesh_sphere() - Icosahedral geodesic meshes for
global-scale modelstulpa_mesh_1d() - Temporal meshes for space-time
SPDEtulpa_mesh_graph() - Network meshes for river/road
SPDEfem_matrices() - Mass (C), stiffness (G), and
projection (A) matricesfem_matrices(lumped = TRUE) - Diagonal mass for SPDE
Q-builderfem_matrices(parallel = TRUE) - Multithreaded assembly
via TBBfem_matrices(barrier = ...) - Barrier models (Bakka et
al. 2019)fem_matrices_p2() - Quadratic (P2) elements for higher
accuracyfem_matrices_nonstationary() - Spatially varying range
and variancemesh_quality() - Per-triangle angles, aspect ratio,
areamesh_summary() - Min/median/max quality statisticsplot() - Mesh visualization with optional quality
coloringmin_angle and
max_area parameterssubdivide_mesh() - Uniform 1-to-4 triangle
subdivisionsubset_mesh() - Extract submesh from triangle
indicesmesh_components() - Find disconnected componentsrefine_mesh() - Adaptive refinement from error
indicatorsas_tulpa_mesh() - Convert fmesher/INLA meshesbarrier_triangles() - Identify barrier regions from
polygonsInstall from CRAN:
install.packages("tulpaMesh")Development version:
# install.packages("pak")
pak::pak("gcol33/tulpaMesh")library(tulpaMesh)
set.seed(42)
coords <- cbind(runif(100), runif(100))
mesh <- tulpa_mesh(coords, min_angle = 25, max_edge = 0.15)
mesh_summary(mesh)
plot(mesh, color = "quality", vertex_col = "black")library(sf)
# Barrier polygon (e.g., land mass that field can't cross)
barrier <- st_polygon(list(rbind(c(3,3), c(7,3), c(7,7), c(3,7), c(3,3))))
bt <- barrier_triangles(mesh, st_sfc(barrier))
fem <- fem_matrices(mesh, barrier = bt)
# fem$G has zero stiffness across barrier trianglesglobe <- tulpa_mesh_sphere(subdivisions = 4)
globe
#> tulpa_mesh (sphere, radius = 1):
#> Vertices: 2562
#> Triangles: 5120
#> Edges: 7680
fem <- fem_matrices(globe, lumped = TRUE)
# Total surface area approximates 4*pi
sum(fem$va)library(fmesher)
fm <- fm_mesh_2d(loc = coords, max.edge = c(0.3, 0.6))
tm <- as_tulpa_mesh(fm) # direct conversion
fem <- fem_matrices(tm) # same C, G, A as fmesher“Software is like sex: it’s better when it’s free.” — Linus Torvalds
I’m a PhD student who builds R packages in my free time because I believe good tools should be free and open. I started these projects for my own work and figured others might find them useful too.
If this package saved you some time, buying me a coffee is a nice way to say thanks. It helps with my coffee addiction.
MIT (see the LICENSE.md file)
@software{tulpaMesh,
author = {Colling, Gilles},
title = {tulpaMesh: Constrained Delaunay Triangulation Meshes for Spatial SPDE Models},
year = {2026},
url = {https://CRAN.R-project.org/package=tulpaMesh},
doi = {10.32614/CRAN.package.tulpaMesh}
}