Mesh quality is the foundation of a stable OpenFOAM simulation. Poor mesh quality causes solver divergence, inaccurate results, and slow convergence — even when all numerical settings are correct. This guide explains how to read checkMesh output and what to do when metrics are out of range.
Run checkMesh on your case before starting the solver:
checkMesh
For a case with multiple time steps (after snappyHexMesh for example):
checkMesh -latestTime
The output reports mesh statistics and flags any issues with *** markers. Any line starting with *** is a warning that may affect solver stability.
A complete checkMesh report contains several sections. Here is what each section tells you:
Checking geometry...
Overall domain bounding box (-0.5 -0.5 0) (0.5 0.5 1)
Mesh has 3 geometric (non-empty/wedge) directions (1 1 1)
Boundary openness (1.4e-17 -3.2e-18 2.8e-17) OK.
Max cell openness = 2.7e-16 OK.
Max aspect ratio = 4.3 OK.
Min volume = 5.3e-09. Max volume = 2.1e-07. Cell volumes OK.
Mesh non-orthogonality Max: 38.5 average: 12.1
* Max skewness = 2.1, 2 highly skewed faces detected.
Mesh OK.
The * prefix indicates a note (not yet a fatal warning). Lines with *** are serious warnings. The final line "Mesh OK" or "Failed X mesh checks" gives the overall verdict. Every line starting with *** must be addressed before the solver is started.
Non-orthogonality measures the angle between the face normal vector and the vector connecting the two cell centres that share the face. A perfectly orthogonal mesh has 0° everywhere.
Fix in fvSolution:
SIMPLE // or PIMPLE
{
nNonOrthogonalCorrectors 2;
}
Also switch Laplacian schemes to corrected or limited 0.5:
laplacianSchemes
{
default Gauss linear limited 0.5;
}
The Laplacian operator discretisation in OpenFOAM is split into two parts: an orthogonal component (exact) and a non-orthogonal correction (approximation). When non-orthogonality is high, the correction term becomes large and must be iterated to convergence. Without correctors, the pressure equation is solved inaccurately, the velocity field develops spurious divergence, and the simulation becomes unstable.
The corrected Laplacian scheme applies the full non-orthogonal correction. The limited scheme applies a fraction (specified by the coefficient: 0.333 is conservative, 0.5 is standard, 1.0 is equivalent to corrected). Use limited 0.333 for very high non-orthogonality meshes where the full correction introduces its own instability.
Skewness measures how far the face interpolation point is from the line connecting the two cell centres. High skewness introduces interpolation error that can destabilise the pressure-velocity coupling.
Skewness above 4 in snappyHexMesh cases almost always comes from the surface layer addition step near complex or highly curved geometry. To fix:
addLayersControls
{
relativeSizes true;
layers
{
wall
{
nSurfaceLayers 3; // reduce if skewness is high
}
}
expansionRatio 1.2; // reduce from 1.3 for smoother grading
finalLayerThickness 0.3;
minThickness 0.1; // allow layer removal before creating skewed cells
featureAngle 130; // increase to smooth over sharper features
nRelaxIter 5;
nSmoothSurfaceNormals 1;
nSmoothNormals 3;
maxFaceThicknessRatio 0.5;
maxThicknessToMedialRatio 0.3;
}
The minThickness parameter is key: it allows snappyHexMesh to skip layer addition in regions where it would create poor quality cells, rather than forcing layers that result in high skewness.
For wall-bounded turbulent flows, the near-wall mesh resolution must match the wall treatment approach chosen in the turbulence model. Getting y+ wrong is one of the most common causes of inaccurate results in RANS simulations.
Requires y+ ≈ 1 at the first cell centre from the wall. Use this when you need accurate skin friction, heat transfer, or separation prediction. The boundary layer is fully resolved — no wall functions.
Wall BC: kLowReWallFunction, epsilonLowReWallFunction, nutLowReWallFunction
Requires y+ between 30 and 300 at the first cell centre. Wall functions model the boundary layer instead of resolving it. More economical for high-Re external flows where the boundary layer details are not the primary interest.
Wall BC: kqRWallFunction, epsilonWallFunction, nutkWallFunction
// Target y+ = 1 (low-Re)
// Δy = y+ * ν / u_τ
// u_τ = U_∞ * sqrt(C_f/2), C_f ≈ 0.058 * Re^(-0.2) for flat plate
// Example: U=10 m/s, L=1m, ν=1.5e-5 m²/s
// Re = 6.7e5 → C_f = 0.00315 → u_τ = 0.397 m/s → Δy = 3.8e-5 m
// Target y+ = 50 (wall functions)
// Δy = 50 * 1.5e-5 / 0.397 = 1.89e-3 m ≈ 1.9 mm
Use the yPlus function object to compute and write the y+ field during or after the simulation:
// Add to controlDict functions block
functions
{
yPlus
{
type yPlus;
libs (fieldFunctionObjects);
writeControl writeTime;
}
}
// Or run as post-processing:
simpleFoam -postProcess -func yPlus -latestTime
This writes a yPlus field to the time directory, which you can visualise in ParaView. If you see y+ below 1 with high-Re wall functions, or y+ above 5 with low-Re treatment, the mesh must be refined or coarsened near the wall.
High aspect ratio cells (very elongated) are common in boundary layer meshes and are acceptable as long as the primary flow direction is aligned with the long axis. However, high aspect ratio cells in shear layers or recirculation zones can cause stability issues. Keep aspect ratio below 1000 for most cases.
In wall-resolved boundary layer meshes, aspect ratios of 100–500 are routine and not problematic if the long axis aligns with the main flow direction. The same cell rotated 90° — long axis perpendicular to the wall — would be a serious problem. Check aspect ratio statistics alongside the flow topology to determine whether high values are acceptable in context.
// Typical boundary layer mesh: first cell at y+=1
// Δy = 3.8e-5 m (wall-normal)
// Δx = 0.01 m (streamwise, typical for external flow)
// Aspect ratio = 0.01 / 3.8e-5 ≈ 263 — acceptable if aligned with flow
Abrupt changes in cell volume across mesh interfaces (especially between refinement levels in snappyHexMesh) degrade accuracy and slow convergence. The volume ratio between adjacent cells should ideally stay below 3:1. snappyHexMesh controls this through nCellsBetweenLevels:
castellatedMeshControls
{
nCellsBetweenLevels 3; // buffer cells between refinement levels
refinementRegions
{
wake_region
{
mode inside;
levels ((0.1 2));
}
}
}
Setting nCellsBetweenLevels 3 instead of the default 1 inserts additional transition layers between refinement levels, smoothing the volume ratio gradient and improving both accuracy and convergence near refinement interfaces.
Here is a consolidated reference table for the key checkMesh metrics and what action each range requires:
Upload your case and CFDpilot evaluates non-orthogonality, skewness, y+, and aspect ratio — and flags what will cause divergence.
Analyse my mesh →Maximum non-orthogonality below 40° is excellent and requires no special numerical treatment. 40–70° is acceptable with nNonOrthogonalCorrectors 2 and a corrected Laplacian scheme. Above 70° the solver will struggle and convergence will be slow and noisy. Above 85° expect divergence regardless of numerical settings — improve the mesh. Also check the average: even if the maximum is 65°, an average above 30° indicates systemic mesh quality issues.
It depends on the wall treatment. For low-Re treatment (resolving the boundary layer with kLowReWallFunction), target y+ ≈ 1 at the first cell. For high-Re wall functions (nutkWallFunction, epsilonWallFunction), target y+ between 30 and 300. Avoid the buffer layer (5 < y+ < 30): wall functions are invalid there and the boundary layer is under-resolved. Use the yPlus function object to verify after running.
Run checkMesh from the case directory before starting the solver. For cases with multiple time directories (after snappyHexMesh), use checkMesh -latestTime. Read every line starting with *** — these are warnings that need attention before running. Key metrics: max non-orthogonality, max skewness, max aspect ratio, and cell volume range. A clean mesh reports "Mesh OK." at the end.
Yes, up to about 70–80° maximum. Add nNonOrthogonalCorrectors 2 to the SIMPLE or PIMPLE block, and set laplacianSchemes default Gauss linear corrected. These settings compensate numerically for the non-orthogonality error. Above 85°, numerical compensation becomes insufficient and the mesh needs improvement — either through surface smoothing in snappyHexMesh or by reconsidering the mesh generation parameters.
In snappyHexMesh cases, high skewness typically comes from layer addition near complex or highly curved geometry. To fix: reduce nSurfaceLayers, decrease expansion ratio from 1.3 to 1.15–1.2, increase featureAngle to smooth over sharp features, and set minThickness 0.1 to allow the mesher to skip layers where they would create skewed cells. Skewness above 4 at internal faces cannot be fixed numerically and requires mesh rebuild.
Use: delta_y = y_plus * nu / u_tau, where u_tau = U_inf * sqrt(Cf/2). For a flat plate, Cf ≈ 0.058 * Re^(-0.2). For a pipe, use the Darcy-Weisbach friction factor. Example: U=10 m/s, L=1m, air (nu=1.5e-5 m²/s): Re=6.7e5, Cf=0.00315, u_tau=0.397 m/s. For y+=1: delta_y = 1 * 1.5e-5 / 0.397 ≈ 38 microns. For y+=50 (wall functions): delta_y ≈ 1.9 mm.