Numerics Guide

OpenFOAM fvSchemes: Choosing Discretisation Schemes

The fvSchemes dictionary controls how OpenFOAM discretises differential operators in time and space. The wrong scheme choice is one of the leading causes of divergence and inaccurate results. This guide covers the most important choices for each operator type.

By CFDpilot · Updated April 2026

Structure of fvSchemes

ddtSchemes      { ... }   // time derivative
gradSchemes     { ... }   // gradient (grad)
divSchemes      { ... }   // divergence (div) — convective terms
laplacianSchemes { ... }  // Laplacian (diffusion)
interpolationSchemes { ... }
snGradSchemes   { ... }   // surface-normal gradient

ddtSchemes: time derivative

ddtSchemes
{
    default     steadyState;   // steady-state: simpleFoam, rhoSimpleFoam
    // or:
    default     Euler;         // 1st-order implicit, unconditionally stable
    default     backward;      // 2nd-order implicit, more accurate
    default     CrankNicolson 0.9; // blended CN (0=Euler, 1=pure CN)
}

For most transient cases: start with Euler for stability, switch to backward or CrankNicolson 0.9 once the simulation is running cleanly.

divSchemes: convective terms — the most important choice

The convection scheme for momentum (div(phi,U)) has the largest impact on both stability and accuracy:

divSchemes
{
    default         none;

    // Most stable — 1st order, bounded, always safe to start with:
    div(phi,U)      Gauss upwind;

    // Best for production RANS — 2nd order, bounded:
    div(phi,U)      Gauss linearUpwind grad(U);

    // High accuracy for LES — unbounded, only for well-resolved meshes:
    div(phi,U)      Gauss linear;

    // Turbulence scalars — upwind is sufficient and safe:
    div(phi,k)      Gauss upwind;
    div(phi,epsilon) Gauss upwind;
    div(phi,omega)  Gauss upwind;
}

Never use Gauss linear for RANS — it is unbounded and will diverge on realistic meshes. Reserve it for LES with fine, structured meshes.

gradSchemes: gradient computation

gradSchemes
{
    default         Gauss linear;   // standard, 2nd order

    // For high non-orthogonality, limit the gradient:
    grad(U)         cellLimited Gauss linear 1;
    grad(p)         cellLimited Gauss linear 1;
}

cellLimited prevents gradient overshoots in distorted cells — essential for meshes with non-orthogonality above 60°.

laplacianSchemes: diffusion

laplacianSchemes
{
    default         Gauss linear corrected;     // best for orthogonal meshes
    // For non-orthogonal meshes:
    default         Gauss linear limited corrected 0.5;
    // For highly non-orthogonal meshes (>70°):
    default         Gauss linear uncorrected;
}

The interpolation (linear) and non-orthogonal correction (corrected, limited, uncorrected) are separate. On poor meshes, corrected can amplify errors — use limited 0.333 as a safe middle ground.

Recommended fvSchemes for simpleFoam (production RANS)

ddtSchemes      { default steadyState; }

gradSchemes
{
    default         Gauss linear;
    grad(U)         cellLimited Gauss linear 1;
}

divSchemes
{
    default         none;
    div(phi,U)      Gauss linearUpwind grad(U);
    div(phi,k)      Gauss upwind;
    div(phi,epsilon) Gauss upwind;
    div(phi,omega)  Gauss upwind;
    div((nuEff*dev2(T(grad(U))))) Gauss linear;
}

laplacianSchemes { default Gauss linear limited corrected 0.5; }
interpolationSchemes { default linear; }
snGradSchemes   { default limited corrected 0.5; }

Recommended fvSchemes for pimpleFoam (transient RANS)

ddtSchemes      { default backward; }  // 2nd-order implicit

gradSchemes
{
    default         Gauss linear;
    grad(U)         cellLimited Gauss linear 1;
}

divSchemes
{
    default         none;
    div(phi,U)      Gauss linearUpwind grad(U);
    div(phi,k)      Gauss upwind;
    div(phi,epsilon) Gauss upwind;
    div(phi,omega)  Gauss upwind;
    div((nuEff*dev2(T(grad(U))))) Gauss linear;
}

laplacianSchemes { default Gauss linear corrected; }
interpolationSchemes { default linear; }
snGradSchemes   { default corrected; }

Transient RANS with backward gives proper second-order time accuracy. If the simulation is unstable, temporarily revert to Euler for the first few time steps then restart with backward.

Recommended fvSchemes for LES (pimpleFoam)

ddtSchemes      { default backward; }

gradSchemes     { default Gauss linear; }

divSchemes
{
    default         none;
    div(phi,U)      Gauss linear;          // central differencing — LES only
    div(phi,k)      Gauss linear;
    div(phi,B)      Gauss linear;
    div(B)          Gauss linear;
    div((nuEff*dev(T(grad(U))))) Gauss linear;
}

laplacianSchemes { default Gauss linear corrected; }
interpolationSchemes { default linear; }
snGradSchemes   { default corrected; }

LES requires central differencing (Gauss linear) for momentum to avoid numerical diffusion that would dampen resolved turbulent structures. This is only valid on meshes where the LES resolution length scale is properly resolved — typically max non-orthogonality below 40°.

Interpolation and snGrad schemes

These two sections are often set to defaults and ignored, but they matter for accuracy:

// interpolationSchemes — face interpolation of cell-centred values
interpolationSchemes
{
    default     linear;   // central interpolation, 2nd order
}

// snGradSchemes — surface-normal gradient at faces
snGradSchemes
{
    default     limited corrected 0.5;  // must match laplacianSchemes correction
}

The snGradSchemes correction must be consistent with the correction in laplacianSchemes. If you use limited corrected 0.5 in laplacianSchemes, use the same in snGradSchemes. Mismatches cause subtle residual errors that are difficult to diagnose.

Scheme selection by mesh quality

The appropriate scheme choice depends on mesh quality metrics from checkMesh:

Get your fvSchemes reviewed for stability and accuracy — free

Upload your case and CFDpilot checks your divergence, gradient, and Laplacian schemes against your solver and flow regime.

Review my schemes →
Official documentation

Frequently Asked Questions

What is the difference between Gauss upwind and Gauss linearUpwind?

Gauss upwind is first-order accurate, highly diffusive, but unconditionally bounded and stable. Gauss linearUpwind is second-order accurate, less diffusive, but requires a gradient argument and can produce small overshoots. For production RANS, linearUpwind gives better accuracy. For initial convergence or difficult meshes, start with upwind and switch once the solution is established.

Can I use Gauss linear for momentum in a RANS simulation?

No. Gauss linear (central differencing) is unbounded and will diverge on realistic unstructured meshes when the mesh Peclet number is greater than 2. It is only appropriate for LES on well-resolved near-orthogonal meshes. For RANS, always use Gauss linearUpwind or Gauss upwind for div(phi,U).

What does cellLimited do in gradSchemes?

cellLimited applies a slope limiter to prevent gradient overshoots in distorted or non-orthogonal cells. The coefficient (0 to 1) controls how aggressively the gradient is limited. Use cellLimited Gauss linear 1 for grad(U) whenever your mesh has non-orthogonality above 60 degrees or checkMesh reports a high maximum non-orthogonality value.

What does 'limited corrected 0.333' mean in laplacianSchemes?

The corrected keyword applies a non-orthogonality correction to the diffusion term at cell faces. The coefficient (0 to 1) limits how much correction is applied — 0 means no correction, 1 means full correction. A value of 0.333 is a safe compromise for meshes with moderate non-orthogonality (40–70 degrees), preventing correction amplification on distorted cells.

What time scheme should I use for pimpleFoam?

Start with Euler (first-order implicit) for stability during initial flow development, then switch to backward (second-order implicit) or CrankNicolson 0.9 for production runs. CrankNicolson 0.9 blends 10% Euler and 90% Crank-Nicolson to reduce the oscillatory behaviour of pure second-order CN on coarser time steps while retaining near-second-order accuracy.

Why does my simulation diverge immediately after switching to linearUpwind?

linearUpwind requires a valid gradient field. If div(phi,U) uses Gauss linearUpwind grad(U) but gradSchemes does not define grad(U) or uses a conflicting scheme, OpenFOAM can produce NaN values. Ensure gradSchemes includes a grad(U) entry (e.g., cellLimited Gauss linear 1). Also verify that your mesh non-orthogonality is below 70 degrees — very distorted meshes cannot support linearUpwind.