Meshing Guide

OpenFOAM blockMesh: Complete Tutorial

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.

By CFDpilot · Updated April 2026

blockMeshDict structure

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

Worked example: 2D channel flow

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.

Grading: refining near walls

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 patches

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));
    }
);

Running blockMesh

# 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.

Curved edges: arc and spline

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.

Cylindrical domain with O-grid topology

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.

Vertex layout

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
);

Arc edges

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.

5-block topology

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 patches

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)
        );
    }
);

Multi-direction wind studies

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.

Multi-block mesh: backward-facing step example

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.

Choosing cell counts and aspect ratio

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

FoamFile header for modern OpenFOAM versions

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.

Common blockMesh mistakes

Get your blockMesh setup reviewed — free

Upload your case and CFDpilot checks your block topology, cell counts, and grading for mesh quality issues.

Review my blockMesh →
Official documentation

Frequently Asked Questions

What is the correct vertex order for a hex block in blockMeshDict?

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.

How do I create a 2D simulation with blockMesh?

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.

What grading ratio should I use for wall refinement in blockMesh?

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.

Why does blockMesh produce negative volume cells?

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.

How do I create curved boundaries with blockMesh?

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.

How do I handle multiple blocks that share a face?

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.