CHILmesh
2D mesh I/O, layer peel, smoothing, and quality analysis for triangular, quadrilateral, and mixed-element hydrodynamic meshes; C++ backend 8.6× to 14.7× faster than Python on full init.
Dominik Mattioli1†, Ethan Kubatko2
†Corresponding author | 1Unaffiliated | 2Ohio State University ()
MATLAB users. This Python library is the maintained successor to the original MATLAB code. The MATLAB code is no longer maintained; it is at
src/@CHILmesh/CHILmesh.mand on. To call the Python library from MATLAB, run
pip install chilmeshand use thepy.bridge, for examplepy.chilmesh.Mesh.read_from_fort14('ocean.14'). No MEX build is needed.
Table of Contents
- Status & Roadmap
- Why CHILmesh
- Installation
- Quick start
- Capabilities
- Performance
- Backends
- Examples and CLI
- Limitations
- Documentation
- Citation
- Contributing
- License
1. Status & Roadmap
Shipped: chilmesh 1.5.0 on PyPI (2026-10-02). The v1.x API is stable; downstream projects can pin chilmesh>=1.0,<2. Version 1.5.0 adds prebuilt C++ wheels (pip install "chilmesh[cpp]"), a size function for the FEM and angle-based smoothers, and an experimental GPU renderer (pip install "chilmesh[gpu]"). The full feature list is in Capabilities; release notes are in CHANGELOG.md.
- Now: a native
.chilcontainer format, built on a sandbox branch first (#201). - Next: a documentation site; a streaming GPU mesh view for live mesh generation (#167).
- Research: a medial-axis
skeletonize()(#223); size-controlled mesh cartograms (#219). - Later: one ecosystem with
and
.
2. Why CHILmesh
CHILmesh began in 2015-2017 at The Ohio State University as the data structure behind QuADMESH+, a layer-peel-driven indirect triangle-to-quadrilateral conversion heuristic for hydrodynamic models (Mattioli, MSc thesis, 2017; see Citation). The mesh generator needed three things no single tool provided: one adjacency model that treats triangles, quadrilaterals and mixed meshes identically; a concentric layer decomposition of the domain, from the boundary inward; and the ADCIRC and SMS file formats that coastal-ocean models read.
The Python library keeps that scope and adds the engineering the MATLAB code lacked. Seven adjacency tables are built once (docs/ARCHITECTURE.md), giving O(1) edge lookup, O(n) layer peel and O(log n) spatial queries. Every algorithm has one reference implementation in Python. The C++ backend reproduces it bit-for-bit, verified by 76 parametrized equivalence cases in tests/test_backend_equivalence.py. Terminology is fixed in docs/CONCEPTS.md: a layer is a concentric band of elements, distinct from a distance field, a medial axis or a skeleton ("vertex", "node" and "point" are interchangeable throughout this documentation).
3. Installation
pip install chilmesh # PyPI, pure Python
pip install "chilmesh[cpp]" # adds the prebuilt C++ backend
pip install "chilmesh[gpu]" # adds the experimental GPU renderer
uv pip install chilmesh # uv
pip install -e ".[dev]" # from source, with the test suite
The [cpp] extra installs chilmesh-cpp, a prebuilt binary wheel for CPython 3.10 to 3.14 on Linux x86_64, macOS x86_64 and arm64, and Windows x86_64. No compiler is needed. Check the result with chilmesh.backend_info(); it reports 'selected': 'cpp'. On any other platform, pip builds chilmesh-cpp from source, which needs a C++ toolchain and CMake. To build from a clone of this repository:
pip install ./src/chilmesh_cpp # or: bash scripts/build_cpp.sh
Python 3.10 or newer is required.
4. Quick start
import numpy as np
from chilmesh import Mesh
mesh = Mesh.read_from_fort14("ocean.14") # ADCIRC grid, boundaries preserved
mesh.smooth_mesh(method="fem", acknowledge_change=True)
quality, angles, stats = mesh.elem_quality() # per-element skew quality
elem = mesh.find_element(np.array([-75.2, 35.1])) # O(log n) point location
mesh.write_to_fort14("ocean_smoothed.14")
chilmesh.CHILmesh remains as an alias of Mesh. Four fixtures ship with the package: chilmesh.examples.{annulus, donut, block_o, structured}().
5. Capabilities
| Area | What ships | Entry points |
|---|---|---|
| File formats | ADCIRC fort.14 (lossless round trip, boundary records kept), fort.13 nodal attributes, fort.15 control file; SMS Aquaveo .2dm; Gmsh .msh 2.2 and 4.1; header-only summary() for fort.14, .2dm, fort.13, fort.15, .msh, .npy and .npz without loading the mesh |
Mesh.read_from_fort14, read_fort13, read_fort15, Mesh.read_from_2dm, Mesh.read_from_msh, chilmesh.summary |
| Topology | Triangular, quadrilateral and mixed meshes on one API; seven adjacency tables; concentric layer peel with per-layer OE, IE, OV, IV and boundary-edge IDs |
Mesh.peel_layers, mesh.layers |
| Quality | Skew quality and interior angles per element; per-edge Courant number and a CFL gate from depths and a time step | Mesh.elem_quality, courant_number, cfl_gate |
| Smoothing | Balendran direct FEM; Zhou-Shimada angle-based; ADMESH spring-based truss against a signed distance function; plain Laplacian. FEM, angle-based and truss accept an optional target size function. Boundary nodes fixed, topology unchanged, mixed meshes accepted | Mesh.smooth_mesh(method=...), Mesh.smooth |
| Mutation | 13 topology operations: split triangle, split edge, split boundary edge, swap edge, merge elements, remove vertex, collapse edge, insert vertex, move boundary node, split triangles, topological smoothing, local re-peel, layer diff | chilmesh.mutations |
| Boundary editing | Advancing-front element addition, boundary-loop removal, pinch-point detection in narrow channels | Mesh.add_advancing_front_element, Mesh.remove_boundary_loop, Mesh.pinch_points |
| Spatial queries | Point-in-element, radius search, k-nearest vertices, all O(log n) after a one-time index | Mesh.find_element, Mesh.find_elements_in_radius, Mesh.nearest_vertices |
| Geometry | Haversine distance, convex hull, Hausdorff distance, antimeridian-aware bounding boxes | chilmesh.geometry |
| Interop | ADMESH domain adapter and truss warm start; renumbering-tolerant node matching between two meshes with nodal-field deltas | Mesh.from_admesh_domain, chilmesh.admesh_warmstart, match_nodes |
| Plotting | Mesh, per-element quality map, matched-colormap quality histogram, layer paths; experimental GPU rendering through the [gpu] extra |
Mesh.plot, Mesh.plot_quality, Mesh.plot_quality_histogram, chilplotting.render_image(backend="gpu") |
6. Performance
The C++ backend full-initializes the 531,680-element ENPAC2003 mesh in 1.44 s against 12.30 s in Python, an 8.6× speedup; on the 98,365-element WNAT_Hagen mesh the ratio is 14.7× (1.65 s → 0.112 s). Reference workload: EasternPacific_ENPAC2003, 272,913 vertices, 531,680 elements, 75 layers, from the Valence registry. Medians of 3 runs on one machine at chilmesh 1.2.2; the algorithms have not changed since, and a 1.4.1 rerun on a second machine gave Python 11.89 s and C++ 0.803 s for full init.
| Stage | MATLAB (Octave 8.4) | Python | C++ | Rust |
|---|---|---|---|---|
| Fast init (adjacency, no peel) | 2.738 s | 6.454 s | 0.769 s | not exposed |
| Peel only | 12.771 s | 5.814 s | 0.669 s | not exposed |
| Full init (adjacency + peel) | 16.677 s | 12.300 s | 1.438 s | 11.98 s † |
| Quality (signed area) | 75 ms | 51 ms | 7 ms | 2 ms † |
Every backend runs the same operation on the same in-memory arrays; fort.14 parsing and rendering are excluded. All four resolve n_layers = 75, and Python and C++ layers are bit-identical. Octave builds adjacency 2.4× faster than Python through sparse() accumulation, and Python peels 2.2× faster than Octave. † Rust measured at chilmesh 1.4.1 on the second machine, where the same-machine controls were Python 11.89 s and C++ 0.803 s. Method, raw data and a 557-to-273k-vertex layer-parity catalog: docs/BENCHMARK.md.
Figure 1. EasternPacific_ENPAC2003 (272,913 vertices, 531,680 elements). plot_quality() renders per-element skew quality; plot_quality_histogram() draws the matched-colormap distribution beneath. Reproduce with python scripts/generate_enpac_showcase.py.
Figure 2. Distance is a scalar field; its ridge is the medial axis; the skeleton is a thinned discrete curve; layers are the concentric element bands CHILmesh peels. Definitions and algorithms: docs/CONCEPTS.md. Reproduce with python scripts/illustrate_mesh_concepts.py.
7. Backends
| Backend | Role | Status | How to get it |
|---|---|---|---|
| Python | Reference implementation; every other backend is validated against it | Default | pip install chilmesh |
| C++ (half-edge) | Accelerator, bit-identical output, 8.6× to 14.7× on full init | Recommended | pip install "chilmesh[cpp]" (prebuilt wheel), or pip install ./src/chilmesh_cpp (source build) |
| Rust (quad-edge) | Output-equivalent on all four fixtures (rust-equivalence CI); measured 2× to 5× slower than C++ on full init |
Frozen, not developed further (docs/RUST_EVALUATION.md) |
maturin build in src/chilmesh_core, not recommended |
| MATLAB | Original 2017 implementation | Archived | src/@CHILmesh/CHILmesh.m |
With CHILMESH_BACKEND unset, CHILmesh selects the fastest available backend in the order C++, Rust, Python. Force one with CHILMESH_BACKEND=python|cpp|rust and inspect the choice with chilmesh.backend_info():
import chilmesh
chilmesh.backend_info()
# After `pip install "chilmesh[cpp]"` or a source build of the C++ extension:
# {'available': ['cpp', 'python'], 'selected': 'cpp',
# 'versions': {'cpp': '0.6.0', 'python': '1.5.0'}}
chilmesh 1.5.x requires chilmesh-cpp>=0.6,<0.7; both version numbers change together when the extension API changes. Platform coverage is listed in Installation.
Parity is gated in CI. The cpp-equivalence and rust-equivalence jobs build each extension and run the equivalence suite on every pull request to main and every push to main. After each chilmesh-cpp release, a further CI job installs the published wheel from PyPI on Linux, macOS and Windows and runs the same suite.
8. Examples and CLI
python examples/01_quickstart.py # load, stats, plot
python examples/02_fort14_roundtrip.py # fort.14 read and write
python examples/03_smoothing.py # angle-based smoother
python examples/04_spatial_queries.py # find_element, radius search, k-nearest
chilmesh info mesh.fort.14 # vertex, element and layer counts
chilmesh summary mesh.fort.14 # header-only metadata, no full parse
chilmesh convert mesh.2dm mesh.fort.14 # format conversion
chilmesh smooth mesh.fort.14 -o out.fort.14 --method fem # smooth and write
chilmesh plot mesh.fort.14 -o mesh.png --quality # render
python -m chilmesh is equivalent; every subcommand takes --help.
9. Limitations
- Two-dimensional only. Vertices carry a z value for bathymetry, but all topology, quality and smoothing operate in the plane. Surface and volume meshes are out of scope.
- A plain install runs at Python speed.
pip install chilmeshhas no compiled code. Add the[cpp]extra for the C++ column of the table above. Prebuilt C++ wheels cover Linux x86_64, macOS x86_64 and arm64, and Windows x86_64 only; Linux on ARM builds from source. - The GPU renderer is experimental. The
[gpu]extra draws a static mesh and a per-element scalar. Streaming views and element picking are not available yet (#167). Off-screen rendering is tested; the interactive window is not. - Rust is frozen. It stays output-equivalent to Python but receives no new work. It exposes no separate fast-init or peel timing.
- Smoothing does not change topology. Quality gains come from vertex moves only; the ADMESH truss smoother is triangle-only. Topological repair is the separate mutation API.
- The size function can trade worst-element quality for size accuracy. On the 1.5.0 benchmark it lowers the mean size error by 4% to 20% but can lower the minimum aspect ratio, for example 0.614 → 0.453 on Test_Case_2 with FEM. Its two strength constants were tuned on the same meshes (
docs/benchmarks/size_smoothing.md). - The .2dm writer drops boundary records.
save('.2dm')thenread_from_2dmis lossless for geometry and topology but not for fort.14 boundary metadata (#228). - Benchmarks are single-machine medians of three runs. Absolute times are machine-dependent; ratios between backends are the reproducible quantity.
- The layer peel is not a medial axis, skeleton or distance transform.
docs/CONCEPTS.mddefines what the layers represent and what they do not.
10. Documentation
docs/API.md: API referencedocs/ARCHITECTURE.md: adjacency tables, complexities and the layerization boundarydocs/BENCHMARK.md: benchmark method and raw datadocs/CONCEPTS.md: distance field, medial axis, skeleton and layersdocs/RUST_EVALUATION.md: the measured case for freezing the Rust backendtests/TESTING.md: pytest markers, backend setup, parity testsexamples/: runnable scripts
11. Citation
Software
@software{mattioli_chilmesh,
author = {Mattioli, Dominik O. and Kubatko, Ethan J.},
title = {{CHILmesh}: a fast 2D mesh library for triangular,
quadrilateral, and mixed-element grids},
year = {2026},
publisher = {Zenodo},
version = {1.5.0},
doi = {10.5281/zenodo.20263853},
url = {https://github.com/domattioli/CHILmesh}
}
Original method
@mastersthesis{mattioli2017quadmesh,
author = {Mattioli, Dominik O.},
title = {{QuADMESH+}: A Quadrangular ADvanced Mesh Generator
for Hydrodynamic Models},
school = {The Ohio State University},
year = {2017},
url = {http://rave.ohiolink.edu/etdc/view?acc_num=osu1500627779532088}
}
CHILmesh is a by-product of a project funded by Aquaveo at The Ohio State University in 2015-2017.
12. Contributing
Issues and pull requests at github.com/domattioli/CHILmesh. Run pytest -v before opening a PR; markers and backend setup are in tests/TESTING.md.
13. License
Noncommercial and research use only. PolyForm Noncommercial License 1.0.0 with an additional No-AI/ML-training restriction; see LICENSE and docs/AI-USAGE.md. Commercial use, and use as training, fine-tuning or evaluation data for any model, require a separate written license from github.com/domattioli.
Metadata
Release files for chilmesh 1.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chilmesh-1.5.0.tar.gz | 290.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chilmesh-1.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 533.7 kB
Release files / chilmesh-1.5.0.tar.gz
| Download URL | chilmesh-1.5.0.tar.gz |
|---|---|
| Size | 290.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bde4bba617fa0b56e85a586e45f96b7cbe88b9598172d245c13597ddb8003c18
|
|
BLAKE2b-256 checksum How to use checksums |
58d1989337d890534f8c78d99afbfbe673dd6f8d1c5c85f42351754100fc5224
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / chilmesh-1.5.0-py3-none-any.whl
| Download URL | chilmesh-1.5.0-py3-none-any.whl |
|---|---|
| Size | 243.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d179b98bd770955b0585c652fc73df71a0deae8f88ad628a9128a97bf72ff7b5
|
|
BLAKE2b-256 checksum How to use checksums |
f56750d4a060ab89e52ff3584aa922f284a4ada1b4a6348587e6365acadfb3c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|