Open-source project
inducer/meshpy avatar
inducer/meshpy

MeshPy: calling Triangle and TetGen from Python for finite element meshes

2D/3D simplicial mesh generator interface for Python (Triangle, TetGen, gmsh)

591 stars112 forksC++NOASSERTION

At a glance

What is it?
MeshPy is a Python interface to the Triangle and TetGen mesh generators, with both C++ tools bundled in slightly modified form, so a Python simulation code can build a quality triangular or tetrahedral mesh without shelling out to a separate binary. gmsh support lives in a separate project.
Who is it for?
Adopt MeshPy when you are writing a Python finite element or simulation tool and need quality 2D triangular or 3D tetrahedral meshes without maintaining a separate mesh generator installation, and use gmsh_interop instead if you need gmsh specifically. Before you commit, read the LICENSE file, because the repository licence field is a custom one GitHub cannot classify, and note the last push was on 2026-10-19 with release v2026.1.1 on 2026-09-02.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A wrapper around two well-regarded C++ generators

MeshPy is described as a 2D and 3D simplicial mesh generator interface for Python, and the README puts it as offering quality triangular and tetrahedral mesh generation for Python. Meshes of this type are mainly used in finite-element simulation codes, but also have many other applications ranging from computer graphics to robotics.

The important design decision is what the package actually is. It provides Python interfaces to two well-regarded mesh generators: Triangle by J. Shewchuk and TetGen by Hang Si. Both are included in the package in slightly modified versions.

That last clause is the one to pay attention to. The generators are compiled into the Python extension, so there is no external binary to install, no PATH to configure, and no version skew between the wrapper and the tool it wraps. The cost is that you are shipping a compiled artifact with the generators inside it, and the modifications to the upstream sources live in a patches/ directory in the repository, which is worth reading if you care about exactly what differs from upstream.

The primary language of the repository is C++, and the package metadata lists Programming Language:: C++ alongside Python. The build is meson-based, with meson-python as the backend, and pybind11 for the bindings, which is the standard route to exposing a C++ library to Python without writing a Python C API wrapper by hand.

The licence situation needs a moment. The repository licence field shows a custom licence that GitHub cannot classify, and the pyproject.toml points at the LICENSE file through license-files rather than naming a standard identifier, so the file is the thing to read.

What you get and what lives in another repository

The division of labour is clean, and the README states it directly. For an interface to gmsh, by Christophe Geuzaine and Jean-Francois Remacle, see gmsh_interop, which is a separate project under the same organisation.

So there are two axes of choice. Within MeshPy, Triangle handles two-dimensional triangular meshes and TetGen handles three-dimensional tetrahedral meshes, and both are the classic, well-documented generators in their fields. Outside MeshPy, gmsh is a different generator with a different feature set, and reaching it means taking a dependency on gmsh_interop rather than on MeshPy.

That split matters more than it looks. Mesh generators differ in what they can represent: which boundary segments and holes they accept, how they constrain regions, how they refine, and what the output mesh means. A tool that generates a mesh for a particular kind of simulation will often have settled on one generator's conventions, and the boundary markers, region tags and file formats are generator-specific.

The examples directory makes this concrete. It holds dedicated scripts for the different generators and the different capabilities: test_triangle.py, test_triangle_refine.py, test_triangle_with_specified_points.py, test_tetgen.py and test_tetgen_2.py for the core cases, test_tri_quadratic.py and test_tri_pml.py for the more specialised ones, tri-boundary-markers.py and tri-refinement-spec.py for the boundary and refinement features, tet-size-control.py for size control, and box-in-box.py and test_tet_torus.py for geometry cases. There are also geometry and I/O examples, including airfoil3d.py, nico_mesh.py, mesh_ply.py and write_dolfin.py, which is a hint that interoperability with other simulation toolchains is a first-class concern rather than an afterthought.

The naming of the files is itself a useful index to what the library can do, and it is a better index than the README, which is short.

Getting the package, and the documentation that explains the generators

There is no install command given in the project documentation, so rather than invent one, the supported routes are the ones the README lists. The package index is on PyPI under the name MeshPy, the documentation is hosted at http://documen.tician.de/meshpy, the home page is at https://mathema.tician.de/software/meshpy, and the source is on GitHub with discussions enabled.

What the packaging metadata does tell you is the shape of the installation. The project name is meshpy, the version is 2026.1.1, and requires-python is 3.10 or newer. The runtime dependency list is short: numpy and nothing else, which is notable for a package that bundles two C++ generators. The optional groups are for docs, with furo and sphinx, and for test, with mako, pytest and ruff.

The versioning scheme is worth noting because it is unusual. The version number reads like a calendar version, 2026.1.1, and the recent releases follow the same pattern with v2026.1 and v2025.1.1. A year-based scheme for a scientific library signals that breaking changes are expected on an annual cadence rather than on every commit, which is a reasonable convention for something that wraps native code.

One practical consequence of the calendar scheme is that you pin a specific year, and the tools behind it change with it. If your simulation code has been validated against a particular mesh, the year in the version is the first thing to record.

Where the boundary of a wrapper sits

There are three limitations that follow from the architecture rather than from any missing feature.

The first is that the generators are old and the wrapper does not change that. Triangle and TetGen are well-regarded and widely used, but they are the same code that was wrapped, and MeshPy's value is the Python interface, not a better algorithm. If you need a feature the underlying generator does not have, no amount of Python will produce it, and the fixes usually belong upstream in C++.

The second is that the slightly modified versions create a maintenance question. Patches are applied to upstream sources and carried in the repository. That is a reasonable way to add Python bindings, but it means that when upstream Triangle or TetGen changes, somebody has to reconcile the patches. For a package with a maintainer who has been doing this for a long time, that is handled, and for a fork or a vendored copy, it is a cost you inherit.

The third is that you do not get the generators' own tools. Triangle and TetGen ship command line programs with their own options and their own file formats, and this interface is for the library API, not for the tools. A workflow built around the command line versions, with their output file conventions, does not automatically transfer.

There is also the practical matter of the extension. The package is built with meson-python and pybind11, so an installation compiles the bundled C++ sources. That means a build toolchain is needed at install time on platforms without wheels, and a source build on an unusual architecture is a different experience from a pure Python install.

How it compares with running the generators yourself

The realistic alternative is to call the generator binaries directly from your Python code with subprocess, and the difference in approach is worth being clear about.

The subprocess route has the appeal of simplicity. You invoke the binary, pass it a polygon or a polyhedron description, and parse the output mesh. There is no build step, no compiled extension to ship, and the generator is whatever version your system has. The costs are the ones that come with file formats and process boundaries: a temporary directory for input and output, a parser for the generator's format, error handling for a non-zero exit, and the loss of any structured return value, since everything comes back as text on disk.

The MeshPy route removes all of that. The mesh comes back as Python objects, the geometry and the mesh live in the same process, and the numeric data is numpy arrays rather than text to be parsed. For a code that generates a mesh for every run of a simulation, that is the difference between a file format in the hot path and none.

The third option is gmsh, whether through the command line, through its own Python API, or through the gmsh_interop project. The difference in approach is that gmsh is a much larger and more general tool: it does meshes in one, two and three dimensions, handles geometry files, and covers CAD-like workflows. MeshPy is narrow on purpose, covering the two generators whose Python interfaces are the ones you want inside a scientific code.

So the decision is about scope. If your geometry is a polygon or a tetrahedron described in code, and you want a numpy mesh out, MeshPy is the smallest thing that does it. If your geometry comes from a CAD file or a complex solid, gmsh is the tool, and gmsh_interop is the bridge for that case.

Editorial conclusion

Adopt MeshPy when you are writing a Python finite element or simulation tool and need quality 2D triangular or 3D tetrahedral meshes without maintaining a separate mesh generator installation, and use gmsh_interop instead if you need gmsh specifically. Before you commit, read the LICENSE file, because the repository licence field is a custom one GitHub cannot classify, and note the last push was on 2026-10-19 with release v2026.1.1 on 2026-09-02.

Frequently asked questions

What is MeshPy and which mesh generators does it wrap?

MeshPy offers quality triangular and tetrahedral mesh generation for Python through interfaces to Triangle by J. Shewchuk and TetGen by Hang Si, with both tools included in the package in slightly modified versions. A separate project, gmsh_interop, provides the interface to gmsh.

What are the Python and build requirements for MeshPy?

requires-python is 3.10 or newer, and the only runtime dependency is numpy. The build backend is meson-python with pybind11, numpy and wheel in the build requirements, so the bundled C++ generators are compiled at install time.

What kind of meshes does MeshPy generate and what are they used for?

It generates 2D triangular and 3D tetrahedral meshes, which are mainly used in finite-element simulation codes, and also apply to computer graphics and robotics. The examples directory includes scripts for refinement, boundary markers, size control, quadratic elements and writing output for other tools.

How do I get MeshPy and its documentation?

The package index is on PyPI under the name MeshPy, the documentation is at http://documen.tician.de/meshpy, and the home page is at https://mathema.tician.de/software/meshpy. The source and discussions are on GitHub, and the repository also carries a CITATION.cff and a Zenodo DOI for the latest release.

Official sources

  1. inducer/meshpy on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/inducer-meshpy.svg)](https://hysenlabs.com/projects/inducer-meshpy)

Was this useful?