OpenFOAM Mesh Generation
Theory: meshing tools and wall treatment
Overview
OpenFOAM has no GUI mesher. How are meshes made?
By instructions in text dictionaries. The basic tool is blockMesh: write vertex coordinates, block (hexahedron) divisions and grading in system/blockMeshDict to get a structured mesh. For complex shapes, blockMesh builds a box-shaped background mesh and snappyHexMesh, given STL surfaces, finishes in three stages: (1) split cells finely at surfaces and chosen regions (castellatedMesh), (2) move cell points onto the surface (snap), and (3) extrude thin cell layers from walls (addLayers). Meshes from external meshers can also be converted. Whatever the method, finish with checkMesh to look for distorted cells (non-orthogonality, skewness) and negative volumes.
Grading, first cell and y+
r is the last-to-first cell ratio given in blockMesh simpleGrading, N the cell count, h the wall-to-centre length, Δy₁ the first cell thickness, u_τ friction velocity, C_f the channel friction coefficient (Dean's correlation) and ν kinematic viscosity. y+ is at the first cell centre.
A preference for hexahedra
As its name suggests, snappyHexMesh centres OpenFOAM meshing on hexahedra. OpenFOAM's finite volume method handles arbitrary polyhedra, but hexahedra need fewer cells for the same accuracy and, aligned with the flow, add less numerical diffusion. snappyHexMesh carves a hexahedral background mesh to the geometry, keeping most cells hexahedral even for complex shapes. Only cells along the surface become polyhedra, and their quality often sets the quality of the whole mesh.
Worked examples
Air ν = 1.5×10⁻⁵ m²/s; Dean Re is based on the full 50 mm channel height, not hydraulic diameter here. For r = 1, Δy₁ = h/N. Acceptable y+ depends on wall treatment; the buffer-layer warning is not a prohibition for every all-y+ function. Post-processing commands vary by release.
Example 1: 50 mm channel, air at 10 m/s (40 cells over the half-height)
Reynolds number 33,333, friction coefficient 0.0054, friction velocity 0.52 m/s, viscous length ν/u_τ about 29 µm.
| Grading r | First cell | y+ | Centre cell | Verdict |
|---|---|---|---|---|
| 1 | 625 µm | 10.8 | 0.62 mm | Buffer layer: suits neither |
| 10 | 158 µm | 2.7 | 1.58 mm | Slightly coarse |
| 40 | 58 µm | 1.0 | 2.3 mm | Good for wall resolution |
| 200 | 16 µm | 0.28 | 3.19 mm | Too fine at wall, coarse at centre |
For wall functions y+ ≈ 30 is a common target: a first cell of about 1.7 mm, or about 14 uniform cells over the half-height.
Forty uniform cells look fine and safe. Why 'suits neither'?
Because y+ ≈ 11 lies in the buffer layer, between the viscous sublayer (y+ < 5) and the log-law region (y+ > 30). Wall functions set wall shear stress assuming the first cell is in the log layer, so placing it in the buffer layer misjudges friction and heat transfer. Low-Reynolds models that resolve the wall need the first cell at y+ ≈ 1 or below, with several cells across the viscous layer, so y+ ≈ 11 is too coarse. OpenFOAM's kOmegaSST can be paired with treatments valid across all y+, such as nutUSpaldingWallFunction, but the buffer layer still carries the largest error. After solving, always map wall y+ with the yPlus utility (postProcess -func yPlus) and check it is in the intended range.
Example 2: snappyHexMesh refinement levels and layers (8 mm background)
| Level | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| Cell size | 8 mm | 4 mm | 2 mm | 1 mm | 0.5 mm |
Adding 5 layers with expansionRatio 1.2 and finalLayerThickness 0.5 (relative) at a level-3 (1 mm) wall gives, from the outside, 0.50, 0.42, 0.35, 0.29 and 0.24 mm: 1.79 mm total with a 0.24 mm first layer (y+ ≈ 4 for the Example 1 flow). For y+ ≈ 1, add layers or raise the wall refinement level.
Practical workflow
- Estimate friction velocity from flow conditions and set the first cell thickness for the chosen wall treatment.
- Build the background (or whole) mesh in blockMesh with the computed grading.
- Refine surfaces and key regions with snappyHexMesh and set layer count and thickness.
- Check non-orthogonality, skewness and negative volumes with checkMesh and fix trouble spots.
- After solving, check the y+ distribution and confirm results hold across two mesh refinements.
“One cell at 70° non-orthogonality”
checkMesh often reports just a few cells with maximum non-orthogonality above 70°. It is tempting to ignore a few, but pressure-equation convergence is dragged by such cells, and they often seed divergence. Increasing nNonOrthogonalCorrectors in fvSolution compensates, but fixing those cells is the sure cure. checkMesh -allGeometry -allTopology writes the offending cells as a cellSet; viewing them in ParaView quickly shows which feature or setting is to blame.
Common mistakes
Mistakes and fixes
| Mistake | Effect | Fix |
|---|---|---|
| First cell in the buffer layer | Friction and heat transfer errors | Aim for y+ ≈ 1 or ≥ 30 |
| Not checking y+ | Wall treatment and mesh mismatch | Map it with yPlus |
| Layers missing in places | Locally high y+ | Tune layer settings and quality limits |
| Leaving non-orthogonal cells | Pressure fails to converge | Locate via cellSet and fix |
| Holes or overlaps in STL | Inside and outside mixed | Check with surfaceCheck |
I'd like to learn related topics.
Related pages include introduction to OpenFOAM, OpenFOAM turbulence models, OpenFOAM parallel computing, structured grids and y+ (glossary).
Frequently asked questions
How are OpenFOAM meshes made?
Simple shapes are built from hexahedral blocks with blockMesh; complex shapes start from a blockMesh background mesh that snappyHexMesh cuts to the STL geometry, refines and adds wall layers to. Meshes from external tools (Gmsh, ANSYS Meshing, etc.) can be converted with gmshToFoam or fluentMeshToFoam. Finish by checking quality with checkMesh.
How do I size the first wall cell?
Choose y+ (wall distance divided by the viscous length) to suit the turbulence model's wall treatment: y+ ≈ 1 to resolve the wall, roughly 30–300 for wall functions. For air at 10 m/s in a 50 mm channel the viscous length is about 29 µm, so y+ ≈ 1 needs a first cell of about 58 µm (cell centre at about 29 µm).
snappyHexMesh layers aren't added properly.
Layer addition is skipped locally where there is no room to extrude along the geometry. Review the layer settings (number of layers, expansionRatio, finalLayerThickness) and the quality constraints (meshQualityControls), first reducing the layer count to see whether layers cover every surface. Confirm how many layers went onto each patch from the log or checkMesh output.
References and scope of verification
Numbers are educational reference calculations under the stated assumptions. Sources document theory and setup, not proof that these numerical values were reproduced in each package. Engineering decisions require validation for the actual conditions.