blockMesh is OpenFOAM's built-in structured mesh generator. It creates hexahedral meshes from a dictionary of vertices, blocks, and patch definitions. For simple geometries — channels, pipes, boxes — it produces high-quality orthogonal meshes ideal for accurate RANS and LES simulations.
The file lives at system/blockMeshDict and has five main sections:
FoamFile { ... }
scale 1; // scale factor applied to all coordinates
vertices ( ... ); // list of (x y z) points
blocks ( ... ); // hex blocks referencing vertex indices
edges ( ... ); // curved edges (optional)
boundary ( ... ); // named patches with face lists
A 2D channel of length 1m, height 0.1m, depth 0.01m (one cell in z for 2D):
scale 1;
vertices
(
(0 0 0) // 0
(1 0 0) // 1
(1 0.1 0) // 2
(0 0.1 0) // 3
(0 0 0.01) // 4
(1 0 0.01) // 5
(1 0.1 0.01) // 6
(0 0.1 0.01) // 7
);
blocks
(
hex (0 1 2 3 4 5 6 7) // vertex order: bottom face, then top face
(100 40 1) // cells in x, y, z
simpleGrading (1 4 1) // grading: uniform x, 4:1 bias toward top in y, uniform z
);
The vertex order for a hex block follows a specific convention: vertices 0-3 form the bottom face in counterclockwise order, vertices 4-7 form the top face directly above them.
simpleGrading specifies a uniform expansion ratio along each axis. A ratio of 4 means the last cell is 4x larger than the first. To refine near both walls (symmetric grading):
simpleGrading
(
1 // x: uniform
( // y: two-section grading
(0.5 0.5 4) // bottom half: 50% of cells, 4:1 grading toward centre
(0.5 0.5 0.25) // top half: 4:1 grading away from centre (= 1/4)
)
1 // z: uniform
)
boundary
(
inlet
{
type patch;
faces ((0 4 7 3)); // face defined by 4 vertices, outward normal
}
outlet
{
type patch;
faces ((1 2 6 5));
}
bottomWall
{
type wall;
faces ((0 1 5 4));
}
topWall
{
type wall;
faces ((3 7 6 2));
}
frontAndBack
{
type empty; // 2D simulation: no flux through z faces
faces ((0 3 2 1) (4 5 6 7));
}
);
# From the case directory:
blockMesh
# Check the mesh:
checkMesh
If blockMesh reports "FOAM FATAL ERROR" about vertex ordering, the face normals are likely pointing inward. Reverse the vertex order in the problematic face.
The edges section allows you to define curved boundaries rather than straight lines between vertices. This is essential for pipe cross-sections and any geometry with circular features:
edges
(
// arc: define a circular arc via a point on the arc
arc 1 2 (1.0 0.0707 0.0) // arc from vertex 1 to 2, passing through this point
// spline: polyline through intermediate points
spline 0 1
(
(0.25 0.02 0)
(0.50 0.035 0)
(0.75 0.02 0)
)
);
For a circular pipe cross-section, eight vertices arranged in two squares (inner and outer) with arc edges produce an O-grid block topology — the standard approach for pipe flow blockMesh cases.
A full cylinder (common for wind studies, atmospheric boundary layer simulations, and multi-direction cases) uses a 5-block O-grid: one central square block surrounded by four curved blocks. This avoids the singularity at the cylinder axis that a single wedge block would create.
Define the inner square half-side as s = R * 0.35 — this ratio keeps the cell aspect ratio reasonable between the core and outer ring. The outer ring vertices sit at the cardinal points of the cylinder:
// R = cylinder radius, H = height, s = R * 0.35 (inner square half-side)
vertices
(
// inner square — bottom (z=0)
(-s -s 0) // 0
( s -s 0) // 1
( s s 0) // 2
(-s s 0) // 3
// inner square — top (z=H)
(-s -s H) // 4
( s -s H) // 5
( s s H) // 6
(-s s H) // 7
// outer cylinder — bottom, cardinal points
( 0 -R 0) // 8 south
( R 0 0) // 9 east
( 0 R 0) // 10 north
(-R 0 0) // 11 west
// outer cylinder — top
( 0 -R H) // 12
( R 0 H) // 13
( 0 R H) // 14
(-R 0 H) // 15
);
Each arc is defined by the two outer vertices it connects and a point on the arc. For a cylinder of radius R, the midpoint of each 90° arc is at R/√2 from the origin:
edges
(
// bottom arcs (z=0) — point on arc at 45° between vertices
arc 8 9 ( 0.707R -0.707R 0)
arc 9 10 ( 0.707R 0.707R 0)
arc 10 11 ( -0.707R 0.707R 0)
arc 11 8 ( -0.707R -0.707R 0)
// top arcs (z=H) — same x,y, different z
arc 12 13 ( 0.707R -0.707R H)
arc 13 14 ( 0.707R 0.707R H)
arc 14 15 ( -0.707R 0.707R H)
arc 15 12 ( -0.707R -0.707R H)
);
Replace 0.707R with the actual value: for R=3000 m, use 2121.
blocks
(
// nR = radial cells, nC = core cells, nZ = vertical cells
// center square block
hex (0 1 2 3 4 5 6 7) (nC nC nZ) simpleGrading (1 1 1)
// south block
hex (8 9 1 0 12 13 5 4) (nR nC nZ) simpleGrading (1 1 1)
// east block
hex (9 10 2 1 13 14 6 5) (nR nC nZ) simpleGrading (1 1 1)
// north block
hex (10 11 3 2 14 15 7 6) (nR nC nZ) simpleGrading (1 1 1)
// west block
hex (11 8 0 3 15 12 4 7) (nR nC nZ) simpleGrading (1 1 1)
);
Cell count guidance: for a 6000 m diameter wind domain with H=250 m, start with nR=30, nC=20, nZ=15. Add radial grading to stretch cells toward the outer boundary: replace the radial 1 with 4 to cluster cells near the buildings and coarsen toward the cylinder wall.
boundary
(
outerRim
{
type patch;
faces
(
(8 9 13 12)
(9 10 14 13)
(10 11 15 14)
(11 8 12 15)
);
}
top
{
type patch;
faces
(
(4 5 6 7)
(12 13 5 4)
(13 14 6 5)
(14 15 7 6)
(15 12 4 7)
);
}
ground
{
type wall;
faces
(
(0 3 2 1)
(8 0 1 9)
(9 1 2 10)
(10 2 3 11)
(11 3 0 8)
);
}
);
The main advantage of this topology for ABL wind simulations: the mesh never changes between directions. To rotate the wind by 15°, change only the flowDir vector in your 0/U boundary condition — snappyHexMesh runs once. Use inletOutlet on the outerRim patch so the solver handles inflow and outflow faces automatically without specifying which side is inlet.
More complex geometries require multiple hex blocks sharing faces. Here is a backward-facing step with two blocks:
vertices
(
(0 0 0) // 0 — inlet bottom
(0.5 0 0) // 1 — step corner
(2 0 0) // 2 — outlet bottom
(2 0.1 0) // 3 — outlet top
(0.5 0.1 0) // 4 — step top
(0 0.1 0) // 5 — inlet top
// repeat at z = 0.01 for 2D:
(0 0 0.01) // 6
(0.5 0 0.01) // 7
(2 0 0.01) // 8
(2 0.1 0.01) // 9
(0.5 0.1 0.01) // 10
(0 0.1 0.01) // 11
);
blocks
(
// Block 0: upstream section
hex (0 1 4 5 6 7 10 11) (50 20 1) simpleGrading (1 1 1)
// Block 1: downstream section (after step)
hex (1 2 3 4 7 8 9 10) (150 20 1) simpleGrading (1 1 1)
);
The shared face between the two blocks (vertices 1, 4, 10, 7) automatically creates a conformal mesh interface — no special treatment required.
Cell count in each direction should respect the aspect ratio constraint. Cells that are too elongated cause numerical diffusion and increased discretisation error:
# Estimating cell count for a channel
# Length L=1m, height H=0.1m, target aspect ratio 10:1 in body
# Wall cell height delta_y = 1e-3m (for y+=50 at U=10 m/s)
# Grading ratio = H_top / H_wall = 0.05 / 0.001 = 50
# → use multi-section grading, not simpleGrading
Modern OpenFOAM versions (v2006+, v9+) require or accept the full FoamFile header in blockMeshDict:
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object blockMeshDict;
}
scale 1;
// OpenFOAM v2012+ syntax: named vertices
vertices
(
name inlet_bottom (0 0 0)
name outlet_bottom (1 0 0)
// etc.
);
Named vertices (available in OpenFOAM v2012+) make blockMeshDict significantly more readable and less error-prone, especially for geometries with many blocks. Vertex names replace integer indices throughout the blocks and boundary sections.
empty patches for 2D cases — all z-direction faces must be typed empty for 2D simulations.scale — if your geometry is in mm, set scale 0.001 to convert to metres.0/ boundary files.Upload your case and CFDpilot checks your block topology, cell counts, and grading for mesh quality issues.
Review my blockMesh →Vertices 0-3 form the bottom face in counterclockwise order when viewed from outside (below) the block. Vertices 4-7 form the top face directly above the corresponding bottom vertices in the same order. The right-hand rule applied to each face should give an outward-pointing normal. Getting this wrong produces FOAM FATAL ERROR about inverted faces or negative cell volumes.
Set one cell in the z-direction in the block cell count — e.g., (100 40 1). Then set all z-normal patches (front and back faces) to type empty in the boundary section. OpenFOAM treats empty patches as directions with no flux, reducing the simulation to 2D. The z-depth must be non-zero but its absolute value does not affect incompressible flow results.
For wall functions (y+ 30-300), a simpleGrading ratio of 4-10 is typically sufficient. For low-Re treatment (y+ near 1), the required ratio is much larger and you should use multi-section grading instead. Keep the single-section grading ratio below 20 — above this, the mesh quality (aspect ratio and non-orthogonality) degrades significantly.
Negative volume cells result from incorrect vertex ordering in a block — the block is effectively inside-out. Visualise the mesh immediately after blockMesh in ParaView and look for the inverted cells using the checkMesh output. Reversing the vertex order of the problematic block (swap vertices 0-3 with 4-7, or reverse the sequence within each face) usually resolves it.
Use the edges section with arc (for circular arcs) or spline (for polyline approximations). For an arc, specify the two vertex indices and a point on the arc: arc v1 v2 (x y z). For a pipe cross-section, use an O-grid topology with eight arc edges connecting the inner and outer block boundaries. This is the standard blockMesh approach for circular geometries.
Blocks that reuse the same vertex indices on a shared face automatically create a conformal mesh interface — no special syntax is needed. Simply ensure the two blocks reference exactly the same four vertex indices on the shared face in compatible orientation. For non-conformal block interfaces (different cell counts on each side), use the mergePatchPairs section to merge the patches after meshing.