Query-based meshing on top of GMSH
meshql is a declarative, query-based tool for parametric mesh generation built on top of GMSH. It provides an intuitive API for creating structured and unstructured meshes with support for:
- Query-based selection: Use CadQuery-like selectors to target specific geometry entities
- Boundary layers: Automatic boundary layer generation for both 2D and 3D meshes
- Transfinite meshing: Automated structured meshing with intelligent edge/face grouping
- Physical groups: Easy boundary condition assignment with named groups
- Preprocessing: Advanced geometry splitting and partitioning for complex workflows
- Multiple formats: Export to GMSH, SU2, VTK, and other mesh formats
- Visualization: Built-in mesh and geometry visualization tools
meshql requires GMSH and CadQuery. Install them first:
# Install GMSH (required)
conda install -c conda-forge gmsh python-gmsh
# Install CadQuery (required for CAD geometry support)
conda install -c conda-forge -c cadquery cadquery=masterpip install meshqlpip install git+https://github.com/OpenOrion/meshql.git#egg=meshqlgit clone https://github.com/OpenOrion/meshql.git
cd meshql
pip install -e ".[gmsh]"import cadquery as cq
from meshql import GeometryQL
from meshql.utils.shapes import generate_naca4_airfoil
# Generate NACA 0012 airfoil coordinates
airfoil_coords = generate_naca4_airfoil("0012", num_points=40)
with GeometryQL.gmsh() as geo:
mesh = (
geo
.load(
cq.Workplane("XY")
.circle(20)
.polyline(airfoil_coords)
.close()
)
# Configure airfoil surface
.edges(type="interior")
.addPhysicalGroup("airfoil")
.addBoundaryLayer(
ratio=2,
size=0.00001,
num_layers=40,
)
.setMeshSize(0.01)
.end()
# Configure farfield
.edges(type="exterior")
.addPhysicalGroup("farfield")
.setMeshSize(3.0)
.end()
.generate(2)
.show("mesh")
)import cadquery as cq
from meshql import GeometryQL, Split
with GeometryQL.gmsh() as geo:
(
geo
.load(
cq.Workplane("XY").box(10, 10, 10).rect(2, 2).cutThruAll(),
preprocess=(Split, lambda split: (
split
.from_plane(angle=(90, 90, 0))
.from_plane(angle=(-90, 90, 0))
)),
)
.setTransfiniteAuto(max_nodes=50)
.generate(3)
.show("mesh", only_surface=True)
)import cadquery as cq
import numpy as np
from meshql import GeometryQL
from meshql.utils.shapes import generate_naca4_airfoil
airfoil_coords = generate_naca4_airfoil("0012", num_points=40) * 5 - np.array([2.5, 0])
with GeometryQL.gmsh() as geo:
(
geo
.load(
cq.Workplane("XY")
.box(10, 10, 10)
.faces(">Z")
.workplane(centerOption="CenterOfMass")
.polyline(airfoil_coords)
.close()
.cutThruAll()
)
.faces(type="interior")
.addPhysicalGroup("wing")
.addBoundaryLayer(size=0.001, ratio=1.5, num_layers=3)
.end()
.generate(2)
.show("mesh")
)Use intuitive CadQuery-style selectors to target geometry entities:
.faces(">Z") # Top faces
.edges(type="interior") # Interior edges
.solids() # All solids
.vertices() # All verticesAutomatic boundary layer generation for viscous flow simulations:
.addBoundaryLayer(
size=0.001, # First cell height
ratio=1.5, # Growth ratio
num_layers=10, # Number of layers
)Automated structured meshing with intelligent parameter selection:
.setTransfiniteAuto(
max_nodes=50, # Maximum nodes per edge
min_nodes=2, # Minimum nodes per edge
auto_recombine=True, # Recombine to quads/hexes
)Assign boundary conditions with named groups:
.faces(">Z")
.addPhysicalGroup("top_wall")
.end()
.faces("<Z")
.addPhysicalGroup("bottom_wall")
.end()Control mesh size and algorithms:
.setMeshSize(0.1) # Set mesh size
.setMeshAlgorithm2D("Delaunay") # 2D algorithm
.setMeshAlgorithm3D("Delaunay") # 3D algorithm
.refine() # Refine mesh
.recombine() # Recombine elementsSplit complex geometries for structured meshing:
preprocess=(Split, lambda split: (
split
.from_plane(angle=(90, 90, 0)) # Split by plane
.from_ratios([0.25, 0.5, 0.75]) # Split by ratios
.from_normals([(1, 0, 0), (0, 1, 0)]) # Split by normals
.from_edge(selector=">Z") # Split from edge
))Load existing meshes from various formats:
import meshly
mesh = meshly.Mesh(
vertices=vertices_array,
indices=indices_array,
markers={"boundary": [face_indices]}
)
geo.load(mesh)Export to multiple mesh formats:
.write("output.msh", dim=2) # GMSH format
.write("output.su2", dim=2) # SU2 format
.write("output.vtk", dim=2) # VTK formatBuilt-in visualization tools:
.show("mesh") # Show mesh
.show("mesh", only_surface=True) # Show surface only
.show("gmsh") # Open in GMSH GUIMore examples available in the examples directory:
- cube.ipynb: Basic structured meshing with boundary layers
- naca0012.ipynb: Airfoil meshing with boundary layers
- inviscid_wedge.ipynb: Inviscid flow simulation setup
- turbo.ipynb: Turbomachinery example with STEP import
- progression.ipynb: Mesh refinement progression
- Changelog: Version history and feature releases
git clone https://github.com/OpenOrion/meshql.git
cd meshql
make install# Install package in development mode with GMSH support
make install
# Build Python package
make build-package
# Run tests
make test
# Clean build artifacts
make clean
# Quick local build (for testing)
make buildgit clone https://github.com/OpenOrion/meshql.git
cd meshql
pip install -e ".[gmsh]"We welcome contributions! Please check out the Discord for discussions and collaboration.
- Discord: Join our community
- Issues: Report bugs or request features
Video tutorial available:
latest: https://www.youtube.com/watch?v=7skc6wjwoTk
v1: https://www.youtube.com/watch?v=ltbxRsuvaLw
MIT License - see LICENSE file for details.


