0% found this document useful (0 votes)
49 views

OpenFOAM Quick Reference Guide

Uploaded by

SADEES PG
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
49 views

OpenFOAM Quick Reference Guide

Uploaded by

SADEES PG
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 85

OpenFOAM Quick

Reference Guide

For ESI-based OpenFOAM


distributions
Copyright © 2023 Tom-Robin Teschner

Version 1.0.0

OpenFOAM has a step learning curve and can quickly overwhelm new users with the myriad
of inputs required to set up even the simplest of cases. This quick reference guide dives
deep into the possible options you can select to get a case setup and running, each with its
own comment, so you know when to use it.

Cover page image: Link


Table of Contents image: Link
Section 1 image: Link
Section 2 image: Link
Section 3 image: Link
Contents

1 0 Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Basic file structure 1
1.2 Variables and their units 2
1.3 Most common boundary conditions 3
1.3.1 Basic boundary conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.2 Inlet boundary conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3.3 Outlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.4 Walls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.5 Turbulent quantities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

1.4 Adding custom initialisation 18


1.5 Adding custom boundary conditions 20

2 Constant Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.1 Overview of Turbulence models 23
2.2 Overview of RANS turbulence models 24
2.3 Overview of LES (sub-grid scale) models 25
2.4 Overview of DES models 26
2.5 Overview of SAS models 27
2.6 Overview of Delta models 27
2.7 Overview of thermophysical models (compressible flows) 29
2.7.1 <thermoType> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.7.2 <mixtureModel> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.7.3 <transportModel> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.7.4 <thermoModel> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.7.5 specie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.7.6 <eosModel> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.7.7 <energyEquationType> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

2.8 Overview of transport models (Newtonian / Non-Newtonian fluids) 38

3 System Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.1 Overview of solver settings (controlDict file) 41
3.2 Function Objects 45
3.2.1 Additional fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.2.2 Turbulence quantities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.2.3 Forces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.2.4 Custom function objects (coded) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.2.5 Sampling points and surfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

3.3 Overview of numerical schemes (fvSchemes file) 54


3.3.1 ddtSchemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.3.2 gradSchemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.3.3 divSchemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.3.4 laplacianSchemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.3.5 snGradSchemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.3.6 interpolationSchemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.3.7 wallDist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

3.4 Overview of solution settings (fvSolution file) 65


3.4.1 Linear algebra solvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.4.2 Pressure-velocity coupling schemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
3.4.3 Under relaxation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
V

3.5 Overview of parallel execution (decomposePar file) 73


3.5.1 How many CPU cores should I use? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.5.2 How to decompose the domain in OpenFOAM . . . . . . . . . . . . . . . . . . . . . 74
3.5.3 How to execute code in parallel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1. 0 Directory

1.1 Basic file structure


The zero directory contains all variables required to run a simulation. Each file contains
all information for a single variable, and the filename is based on the variable name used
internally by OpenFOAM for calculations. The basic file structure for each variable is as
follows:

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - C ++ -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
| ========= | |
| \\ / F ield | OpenFOAM : The Open Source CFD Toolbox |
| \\ / O peration | Version : < OpenFOAM Version > |
| \\ / A nd | Website : www . openfoam . com |
| \\/ M anipulation | |
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
FoamFile
{
version 2.0;
format ascii ;
class < volSca larFie ld | vo lVecto rField | volSymmTensorField >;
object < variableName >;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

dimensions < variableDimension >;

internalField uniform < initialValue >


2 Chapter 1. 0 Directory

boundaryField
{
BC1
{
// ...
}
}

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

1.2 Variables and their units


Each variable has a type (either scalar, vector or symmetric tensor) and a dimension.
Dimensions are given as exponents of SI base units, and the seven units that OpenFOAM
exposes are:
[mass length time temperature quantity current luminous intensity]

Or, in SI units:
[kg m s K kgmol A cd]

The following table provides an overview of all quantities OpenFOAM provides with their
type and units:

Variable name Filename Type Units

Velocity vector U volVectorField [0 1 -1 0 0 0 0]


Velocity Flux phi volScalarField [0 3 -1 0 0 0 0]
Pressure (incompressible) p volScalarField [0 2 -2 0 0 0 0]
Pressure (compressible) p volScalarField [1 -1 -2 0 0 0 0]
Temperature T volScalarField [0 0 0 1 0 0 0]
Turbulent viscosity nut volScalarField [0 2 -1 0 0 0 0]
Turbulent kinematic viscosity nuTilda volScalarField [0 2 -1 0 0 0 0]
Turbulent kinetic energy k volScalarField [0 2 -2 0 0 0 0]
Turbulent kinetic energy kt volScalarField [0 2 -2 0 0 0 0]
Laminar kinetic energy kl volScalarField [0 2 -2 0 0 0 0]
Turbulent dissipation rate epsilon volScalarField [0 2 -3 0 0 0 0]
Specific turbulent dissipation rate omega volScalarField [0 0 -1 0 0 0 0]
Reynolds stress tensor R volSymmTensorField [0 2 -2 0 0 0 0]
1.3 Most common boundary conditions 3

Turbulent thermal diffusivity alphat volScalarField [1 -1 -1 0 0 0 0]


Transition Reynolds number ReTheta volScalarField [0 0 0 0 0 0 0]
Turbulent intermittency gammaInt volScalarField [0 0 0 0 0 0 0]
Resolved turbulent stresses FLM flm volscalarField [0 4 -4 0 0 0 0]
Resolved turbulent stresses FMM fmm volScalarField [0 4 -4 0 0 0 0]

1.3 Most common boundary conditions


Each file within the 0 directory contains the associated boundary conditions for its variable.
Where a value needs to be specified, this will be abbreviated to either <scalar>, <vector>,
or <tensor>. Sometimes either of these quantities can be prescribed, in which case they
may be given as <scalar/vector/tensor>. Using a zero-based scalar, vector, or tensor,
we can transform the following statement:
value uniform <scalar/vector/tensor>;

Into either a scalar, vector, or tensor, as follows:


• Scalar:
value uniform 0;

• Vector:
value uniform (0 0 0); \\ x, y, z

• Symmetric tensor:
value uniform (0 0 0 0 0 0); \\ xx, xy, xz, yy, yz, zz

1.3.1 Basic boundary conditions


List of all basic boundary conditions: Latest OpenFOAM guide. Important basic boundary
conditions are listed below.
• fixedValue: A fixed value boundary condition allows imposing a constant value across
a boundary (Dirichlet-type boundary condition).

< patchName >


{
type fixedValue ;
4 Chapter 1. 0 Directory

value uniform < scalar / vector / tensor >;


}

• fixedGradient: A fixed gradient boundary condition allows imposing a constant flux


across a boundary (Neumann-type boundary condition).

< patchName >


{
type fixedGradient ;
gradient uniform < scalar >;
}

• zeroGradient: A shorthand notation for a fixed gradient with zero flux (Neumann-type
boundary condition).

< patchName >


{
type zeroGradient ;
}

• symmetry: Symmetry boundary conditions are a shorthand notation for zeroGradient


boundary condition. Used for frictionless (slip) walls.

< patchName >


{
type symmetry ;
}

• symmetryPlane: Similar to a symmetry boundary condition, but with the additional


restriction that the boundary patch needs to be a plane. OpenFOAM will check for
alignment of normal vectors.

< patchName >


{
type symmetryPlane ;
}

• empty: An empty boundary condition is used to simulate 1D or 2D domains. It is


equivalent to a symmetryPlane boundary condition, with additional constraints (at
least two patches need to be of type empty).
1.3 Most common boundary conditions 5

< patchName >


{
type empty ;
}

• cyclic: A cyclic, or periodic, boundary condition allows introducing flow on one


boundary that is leaving another cyclic boundary.

< patchName >


{
type cyclic ;
}

By itself, this boundary condition does not work and requires modifying the mesh
by changing the neighbourPatch entry within the constant/polyMesh/boundary
file. This contains information about which cyclic boundary patches are connected,
example:

left
{
type cyclic ;
neig hbourP atch right ;
nFaces 4992;
startFace 9922;
}
right
{
type cyclic ;
neig hbourP atch left ;
nFaces 4992;
startFace 14914;
}

1.3.2 Inlet boundary conditions


List of all inlet boundary conditions: Latest OpenFOAM guide. Important inlet boundary
conditions are listed below.
• freestream: A freestream boundary condition is a mixed boundary condition (Robin-
type), where based on the sign of the velocity, either a constant value (inflow), or zero
gradient (outflow) is prescribed. Useful in cases where both inlet and outlet boundary
patches are combined into a single patch.
6 Chapter 1. 0 Directory

< patchName >


{
type freestream ;
f re es t re am V al u e uniform < scalar / vector / tensor >;
}

• freestreamVelocity: Similar to the freestream boundary condition, but for velocity.


The specified <value> is the velocity at the inlet (i.e. inflow velocity).

< patchName >


{
type freestreamVelocity ;
f re es t re am V al u e uniform < vector >;
}

• freestreamPressure: Similar to the freestream boundary condition, but for pressure.


This boundary conditions should be used in conjunction with freestreamVelocity, and
not mixed with other boundary conditions. The specified <value> is the pressure at
the inlet (i.e. farfield pressure).

< patchName >


{
type freestreamPressure ;
f re es t re am V al u e uniform < scalar >;
}

• inletOutlet: This boundary condition is similar to the freestream boundary condition,


where a constant (Dirichlet) value is prescribed at the inflow, and a zero gradient at
the outflow (Neumann). In addition to the freestream boundary condition, it allows
for reverse flow on outlets. If reverse flow is detected (change of sign of the fluxes), a
constant value is prescribed using the inletValue keyword.

< patchName >


{
type inletOutlet ;
inletValue uniform < scalar / vector / tensor >;
value uniform < scalar / vector / tensor >;
}

• flowRateInletVelocity: Use this boundary condition to specify a flow rate, either in


terms of mass (kg/s) or volume (m3/s). Specify a value to indicate the direction of
1.3 Most common boundary conditions 7

the flow and magnitude, which is corrected by the required mass or volumetric flow
field. For mass flow rates, density at the inlet needs to be prescribed for cases where
the density is not available/solved (e.g. incompressible flows). For mass flow rate
use:

< patchName >


{
type flowRateInletVelocity ;
massFlowRate < scalar >; // ( kg / s )
rho rho ;
rhoInlet < scalar >;
value uniform < value >;
}

For volumetric flow rate use:

< patchName >


{
type flowRateInletVelocity ;
volumetricFlowRate < scalar >; // ( m3 / s )
value uniform < value >;
}

• fixedProfile: This boundary condition allows imposing a non-uniform profile at


boundaries, which may be useful to impose a velocity profile at inlets. It reads a
*.csv file and interpolates between values found in the file.

< patchName >


{
type fixedProfile ;
profile csvFile ;

profileCoeffs
{
nHeaderLine 0; // Number of header lines
refColumn 0; // Reference column index
componentColumns (1 2 3) ; // Component column indices
separator " ," ; // Optional ( defaults to " ,")
m er ge S ep a ra to r s no ; // Merge multiple separators
file " Uprofile . csv " ; // name of csv data file
outOfBounds clamp ; // Optional out - of - bounds handling
interpolationScheme linear ; // Optional interpolation scheme
}
8 Chapter 1. 0 Directory

direction < vector >;


origin < scalar >;
}

• atmBoundaryLayer: This boundary condition allows imposing an atmospheric bound-


ary layer. It requires a flow direction vector, a vector indicating the z-direction (i.e. in
which direction to grow the atmospheric boundary layer), a reference mean stream-
wise velocity Uref, and a reference height Zref at which Uref was estimated. The
values of z0 and d represent the surface roughness (in metres) and the displacement
or offset, respectively. The displacement is used for cases where there are densely
populated objects such as trees or buildings. In this case, d should match the height
of these objects.

< patchName >


{
flowDir < vector >;
zDir < vector >;
Uref < scalar >;
Zref < scalar >;
z0 uniform < scalar >;
d uniform < scalar >;
}

1.3.3 Outlet

List of all outlet boundary conditions: Latest OpenFOAM guide. Important outlet boundary
conditions are listed below.
• freestream: See Inlet boundary condition section. This boundary condition can be
used for both inlets and outlets.
• freestreamVelocity: See Inlet boundary condition section. This boundary condition
can be used for both inlets and outlets.
• freestreamPressure: See Inlet boundary condition section. This boundary condition
can be used for both inlets and outlets.
• inletOutlet: See Inlet boundary condition section. This boundary condition can be
used for both inlets and outlets.
• advective: A boundary condition that solves the advection equation at the outlet to
allow non-reflective removal of flow (no pressure waves are bounced back into the
domain).
1.3 Most common boundary conditions 9

< patchName >


{
type advective ;
phi phi ;
rho rho ;
}

• flowRateOutletVelocity: Similar to the flowRateInletVelocity boundary condition,


now imposing a fixed mass or volumetric flow rate at the outlet. Mass (kg/s) or volume
(m3/s) flow rates can be specified. For mass flow rates, density at the outlet needs to
be prescribed for cases where the density is not available/solved (e.g. incompressible
flows). For mass flow rate use:

< patchName >


{
type flowRateOutletVelocity ;
massFlowRate < scalar >; // ( kg / s )
rhoOutlet < scalar >;
value uniform < vector >;
}

For volumetric flow rate use:

< patchName >


{
type flowRateOutletVelocity ;
volumetricFlowRate < scalar >; // ( m3 / s )
value uniform < vector >;
}

• matchedFlowRateOutletVelocity: In cases where the flow rate should be matched to


the one at the inlet, we can use this boundary condition to prescribe the same flow
rate by defining a target inletPatch. The integrated flow rate at the outlet will then
be scaled by the inlet flow rate based on the specified inletPatch.

< patchName >


{
type matchedFlowRateOutletVelocity ;
inletPatch inlet ;
value uniform < vector >;
}
10 Chapter 1. 0 Directory

1.3.4 Walls
List of all wall boundary conditions: Latest OpenFOAM guide. Important wall boundary
conditions are listed below.
• noSlip: A wall boundary condition with zero velocity at the wall. Applied to velocity
only, and typically used in conjunction with fixedGradient or zeroGradient for other
flow variables.

< patchName >


{
type noSlip ;
}

• movingWallVelocity: A wall boundary condition for velocity where the wall is moving
in the wall’s tangential direction.

< patchName >


{
type movingWallVelocity ;
value < vector >;
}

• rotatingWallVelocity: A wall boundary condition for velocity which allows for ro-
tation of the walls. Requires a rotation axis and an origin for that axis. Rotational
speeds are provided in (rad/s).

< patchName >


{
type rotatingWallVelocity ;
origin < vector >;
axis < vector >;
omega < scalar >; // ( rad / s )
}

• kqRWallFunction: A wall function model for the turbulent quantities of turbulent


kinetic energy (k), square-root of the turbulent kinetic energy (q), and Reynolds
stresses (R). This version requires y+ > 30, i.e. this model operates in the log-law
region of the boundary layer.

< patchName >


{
type kq R Wa ll F un ct i on ;
1.3 Most common boundary conditions 11

• kLowReWallFunction: A wall function model for the turbulent quantities of turbulent


kinetic energy (k), square-root of the turbulent kinetic energy (q), and Reynolds
stresses (R). This version requires y+ ≈ 1, i.e. this model operates in the viscous
sublayer of the boundary layer.

< patchName >


{
type kLowReWallFunction ;
}

• epsilonWallFunction: A wall function model for the dissipation rate (ε). Depending
on the setting of lowReCorrection, it can either operate as a low Reynolds number
version (i.e. y+ ≈ 1) or high Reynolds number version (i.e. y+ > 30). Note that the
keyword lowReCorrection is not required and defaults to false.

< patchName >


{
type epsilonWallFunction ;
l ow Re C or re c ti o n false ; // false = y + > 30 , true = y + ~ 1
}

• omegaWallFunction: A wall function model for the specific dissipation rate (ω).
Unlike the wall model for ε, it can be used for both low and high Reynolds number
applications, i.e. it works in both the log-law (y+ > 30) and viscous sublayer (y+ ≈
1).

< patchName >


{
type omegaWallFunction ;
}

• nutkWallFunction: A wall function model for the turbulent viscosity ν. This formula-
tion should be used for the log-law region (y+ > 30).

< patchName >


{
type nutkWallFunction ;
}
12 Chapter 1. 0 Directory

• nutLowReWallFunction: A wall function model for the turbulent viscosity ν. This


formulation should be used for the viscous sublayer (y+ ≈ 1).

< patchName >


{
type nutLowReWallFunction ;
}

• nutUSpaldingWallFunction: An implementation for the turbulent viscosity ν, which


can be used in both the viscous sublayer (y+ ≈ 1) and log-law region (y+ > 30). The
y+ value is calculated locally and based on that, the turbulent viscosity is obtained.
Ideally used with ω-based turbulence models (i.e. k − −ω family), which allows
seamless blending between the viscous sublayer and log-law region.

< patchName >


{
// Mandatory entries
type nutUSpaldingWallFunction ;
}

• nutURoughWallFunction: A wall function model for the turbulent viscosity ν, which


accounts for surface roughness effects. Can be used in both the viscous sublayer
(y+ ≈ 1) and log-law region (y+ > 30). The roughnessHeight correspond to the Ks
value and is specified in meters. Ks is the surface roughness height itself, obtained
from measurements. The roughnessConstant is typically between 0.5–1.0, where
0.5 corresponds to an evenly distributed surface roughness, while 1.0 represents
more stochastic variation in the surface roughness height. The roughnessFactor
scales the surface roughness by a specific value, typically left to 1 if no scaling is
required.

< patchName >


{
// Mandatory entries
type nutURoughWallFunction ;
r ou gh n es sH e ig h t < scalar >; // in meter
roughnessConstant < scalar >; // typically 0.5 , i . e . uniform surface
roughness height
r ou gh n es sF a ct o r < scalar >; // scaling factor , typically left to 1
}
1.3 Most common boundary conditions 13

1.3.5 Turbulent quantities


The turbulent quantities that are solved for by the turbulence model need to have boundary
conditions specified. For the most common choices, the boundary conditions are listed
below for each quantity. Note: All of these quantities can also be calculated conveniently
with the cfd toolbox (Google chrome extension).

Whenever a variable is calculated, such as the turbulent kinetic energy at the inlet, i.e. k =
1.5(UI)2 , then we implement that with a fixedValue boundary condition, i.e.:

< patchName >


{
type fixedValue ;
value uniform < scalar / vector / tensor >;
}

Assuming k = 0.3, then we would have value uniform 0.3;. If we want to implement a
tensor such as the Reynolds stress tensor R, e.g.

 
 
2/3k 0 0 
 
 
R= , (1.1)
 
 0 2/3k 0 
 
 
 
0 0 2/3k

then we use the same fixedValue entry. In this case, we use a symmetric tensor, whose
entries would be (Rxx Rxy Rxz Ryy Ryz Rzz ), which would now become (still with k = 0.3):

< patchName >


{
type fixedValue ;
value uniform (0.2 0 0 0.2 0 0.2) ;
}

Whenever we want to specify a Neumann condition, i.e. ∂ k/∂ n = 0, we use the zeroGradient
boundary condition, i.e.
14 Chapter 1. 0 Directory

< patchName >


{
type zeroGradient ;
}

In the following, these variables are used:


• U: Mean velocity magnitude (e.g. freestream velocity)
• I: Freestream turbulent intensity (e.g. 5% turbulent intensity I = 0.05)
• l: Turbulent length-scale (for its calculation, see below)
• Cµ : Closure coefficient, always set to Cµ = 0.09
The turbulent length scale can be computed as:
• Internal flow – Circular pipe:
l = 0.038 · dh

with dh being the pipe diameter


• Internal flow – Square pipe:
2wh
l = 0.038 · w+h

with w and h being the width and height of the square pipe
• External flows / Free wall-bounded boundary layers: First, approximate the boundary
layer height as

δx = 0.37x · Re−0.2

with x being the reference length of the geometry and Re the Reynolds number, then,
approximate the turbulent length scale with
l = 0.22 · δx

1.3.5.1 Inlet
• Turbulent kinetic energy k
3
k= (UI)2
2

• Turbulent dissipation rate ε


1.3 Most common boundary conditions 15

k1.5
ε = Cµ0.75
l

• Specific turbulent dissipation rate ω



k
ω = Cµ−0.25
l

• Modified turbulent viscosity ν̃



ν̃ = 1.5 (UIl)

• Turbulent viscosity νt
calculated

• Reynolds stress tensor R


 
 
2/3k 0 0 
 
 
R=
 

 0 2/3k 0 
 
 
 
0 0 2/3k

1.3.5.2 Outlet
• Turbulent kinetic energy k
∂k
=0
∂n

• Turbulent dissipation rate ε


∂ε
=0
∂n

• Specific turbulent dissipation rate ω


∂ω
=0
∂n

• Modified turbulent viscosity ν̃


16 Chapter 1. 0 Directory

∂ ν̃
=0
∂n

• Turbulent viscosity νt
calculated

• Reynolds stress tensor R


∂R
=0
∂n

1.3.5.3 Symmetry
• Turbulent kinetic energy k
∂k
=0
∂n

• Turbulent dissipation rate ε


∂ε
=0
∂n

• Specific turbulent dissipation rate ω


∂ω
=0
∂n

• Modified turbulent viscosity ν̃


∂ ν̃
=0
∂n

• Turbulent viscosity νt
calculated

• Reynolds stress tensor R


∂R
=0
∂n

1.3.5.4 Wall (y+ ≈ 1)


• Turbulent kinetic energy k
1.3 Most common boundary conditions 17

kLowReWallFunction

• Turbulent dissipation rate ε


epsilonWallFunction
lowReCorrection true

• Specific turbulent dissipation rate ω


omegaWallFunction

• Modified turbulent viscosity ν̃


ν̃ = ν/2

• Turbulent viscosity νt
nutLowReWallFunction

• Reynolds stress tensor R


 
 
0 0 0
 
 
R=
 

0 0 0
 
 
 
0 0 0

1.3.5.5 Wall (y+ > 30)


• Turbulent kinetic energy k
kqRWallFunction

• Turbulent dissipation rate ε


epsilonWallFunction

• Specific turbulent dissipation rate ω


omegaWallFunction

• Modified turbulent viscosity ν̃


18 Chapter 1. 0 Directory

∂ ν̃
=0
∂n

To use the Spalart–Allmaras model for y+ > 30, the negative Spalart–Allmaras model
needs to be used. One of my former students implemented that into OpenFOAM, and
you can find it on github with installation instructions.
• Turbulent viscosity νt
nutkWallFunction

• Reynolds stress tensor R


kqRWallFunction

1.4 Adding custom initialisation


Instead of setting the initial field to a constant value, it can be programmed to have a spatially
changing value. In OpenFOAM, this is achieved by injecting C++ code directly into the
internalField via the #codeStream directive. It requires a little boilerplate code, which
is followed by a code #{ ... #} section, which can be used to set the internal field at
each grid point. Below is an example for how to modify the velocity vector field for the
Taylor Green Vortex problem.

internalField # codeStream
{
codeInclude
#{
# include " fvCFD . H "
#};

codeOptions
#{
- I$ ( LIB_SRC ) / finiteVolume / lnInclude \
- I$ ( LIB_SRC ) / meshTools / lnInclude
#};

codeLibs
#{
- lmeshTools \
- lfiniteVolume
#};

code
#{
1.4 Adding custom initialisation 19

const IOdictionary & d = static_cast < const IOdictionary & >( dict ) ;
const fvMesh & mesh = refCast < const fvMesh >( d . db () ) ;
vectorField U ( mesh . nCells () ) ;

auto U_0 = 1.0;

forAll (U , cellI )
{
auto x = mesh . C () [ cellI ]. x () ;
auto y = mesh . C () [ cellI ]. y () ;
auto z = mesh . C () [ cellI ]. z () ;

U [ cellI ]. x () = U_0 * Foam :: sin ( x ) * Foam :: cos ( y ) * Foam :: cos ( z ) ;


U [ cellI ]. y () = - U_0 * Foam :: cos ( x ) * Foam :: sin ( y ) * Foam :: cos ( z ) ;
U [ cellI ]. z () = 0.0;
}
U . writeEntry ( " " , os ) ;
#};
};

Something equivalent can be achieved for scalars as well, in this case the code section
becomes (ignoring the boilerplate code before):

code
#{
const IOdictionary & d = static_cast < const IOdictionary & >( dict ) ;
const fvMesh & mesh = refCast < const fvMesh >( d . db () ) ;
scalarField p ( mesh . nCells () , 0.0) ;

auto p_0 = 100.0;


auto U_0 = 1.0;
auto r_0 = 1.0;

forAll (p , cellI )
{
auto x = mesh . C () [ cellI ]. x () ;
auto y = mesh . C () [ cellI ]. y () ;
auto z = mesh . C () [ cellI ]. z () ;

auto cos2x = Foam :: cos (2.0 * x ) ;


auto cos2y = Foam :: cos (2.0 * y ) ;
auto cos2z = Foam :: cos (2.0 * z ) ;

p [ cellI ] = p_0 + (( r_0 * Foam :: pow ( U_0 , 2) ) / 16.0) * ( cos2x + cos2y ) * (


cos2z + 2.0) ;
}
p . writeEntry ( " " , os ) ;
# };
20 Chapter 1. 0 Directory

1.5 Adding custom boundary conditions


Similar to custom initial condition, custom boundary conditions can be imposed similarly.
We use the type codedFixedValue for this, which requires an additional value property
(which is ignored) and a name, which needs to be different from the <patchName>, i.e.
the boundary condition itself. We have the same boilerplate code initially, and then a
code #{ ... #} section with the boundary condition implementation. Since it is a bound-
ary condition, we can vary a value over time and thus can access the time information as
well to steer the boundary condition as the simulation progresses.

< patchName >


{
type c od ed F ix ed V al u e ;
value uniform (0 0 0) ;
name inletBC ;

codeInclude
#{
# include " fvCFD . H "
#};

codeOptions
#{
- I$ ( LIB_SRC ) / finiteVolume / lnInclude \
- I$ ( LIB_SRC ) / meshTools / lnInclude
#};

codeLibs
#{
- lmeshTools \
- lfiniteVolume
#};

code
#{
// get access to current boundary patch and field
vectorField & field = * this ;

// get access to computational mesh and velocity vector


const fvMesh & mesh = refCast < const fvMesh >( this - > db () ) ;
const vo lVecto rField & U = this - > db () . lookupObject < volVectorField >( " U " )
;

// current ( total ) time


const scalar currentTime = this - > db () . time () . value () ;

// additional variables
auto a n g u l a r F r e q u e n c y = 1.0;
1.5 Adding custom boundary conditions 21

auto meanVelocity = 10.0;


auto amplitude = 2.5;

// loop over all boundary faces at within current patch


forAll ( field , faceI )
{
// implement pulsating boundary condition
field [ faceI ]. x () = meanVelocity + amplitude * Foam :: sin (
a n g u l a r F r e q u e n c y * currentTime ) ;
}
#};
}
2. Constant Directory

The constant directory is responsible for setting up material properties such as the viscosity
and runtime models, such as turbulence models. This section will highlight the most
important use cases.

2.1 Overview of Turbulence models


Within the constant directory, a file called turbulenceProperties exist for solvers that
use turbulence models. This file has the basic structure of:

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - C ++ -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
| ========= | |
| \\ / F ield | OpenFOAM : The Open Source CFD Toolbox |
| \\ / O peration | Version : < OpenFOAM Version > |
| \\ / A nd | Website : www . openfoam . com |
| \\/ M anipulation | |
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
FoamFile
{
version 2.0;
format ascii ;
class dictionary ;
object turbulenceProperties ;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

simu lation Type < laminar | RAS | LES >;


24 Chapter 2. Constant Directory

RAS
{
RASModel < RASModel >;
}

LES
{
LESModel < LESModel >;
delta < DeltaModel >;
}

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

The simulationType keyword steers the type of turbulence model. The following options
are available:
• laminar: Use this for no turbulence model or Direct Numerical Simulations (DNS)
• LES: Use this for scale-resolved turbulence models (LES, DES, SAS)
• RAS: Use this for RANS turbulence models
Both RAS and LES require additional dictionary entries as indicated, which are the <RASModel>,
<LESModel>, and <DeltaModel>. The option for these entries are indicated in the next
sections.

2.2 Overview of RANS turbulence models


The following RANS turbulence models are available in OpenFOAM that can be specified
for the <RASModel> variable shown above:
• kOmega: Baseline k − ω model
• kOmegaSST: Shear-stress transport version of the k − ω model. If you have no other
indication to go by, use this model as your default RANS model.
• kEpsilon: Standard k − ε model
• realizableKE: Realizable version of the k − ε model
• RNGkEpsilon: Renormalised group version of the k − ε model
• LienLeschziner: Lien–Leschziner version of the k − ε model, for incompressible
flows only.
• LamBremhorstKE: Lam–Bremhorst version of the k − ε model
• LaunderSharmaKE: Launder–Sharma version of the k − ε model
• qZeta: A k − ε model variant, where the transport properties are the square root of

the turbulent kinetic energy q = k, and the rate of destruction of q, i.e. ζ = ε/2q.
• SpalartAllmaras: Standard Spalart–Almaras model, for y+ ≈ 1 only.
2.3 Overview of LES (sub-grid scale) models 25

• kOmegaSSTLM: Transition-based turbulence model, based on the k − ω SST model.


Solves two additional transport equation for the transitional Reynolds number Reθ ,t
and turbulence intermittency γ.
• kkLOmega: Transition-based turbulence model, that solves for the laminar and
turbulent kinetic energy kl and kt . Dissipation is solved for by the standard ω
equation.
• LienCubicKE: k − ε model variant for incompressible flows only, using a non-linear
eddy viscosity approach by Lien.
• ShihQuadraticKE: k − ε model variant for incompressible flows only, using a non-
linear eddy viscosity approach by Shih.
• LRR: Reynolds-stress transport-based turbulence model by Launder, Reece and Rodi.
• SSG: Reynolds-stress transport-based turbulence model by Speziale, Sarkar and
Gatski.

2.3 Overview of LES (sub-grid scale) models


For large eddy simulations, a dictionary for LES needs to be specified and contains two
additional entries LESModel and delta, for which two models need to be specified as
shown below:

LES
{
LESModel < LESModel >;
delta < DeltaModel >;
}

This section is concerned with the <LESModel> setting, while the entry for <DeltaModel>
is discussed in a separate section (as it is not exclusively used in LES). The available
<LESModel> in OpenFOAM are:
• Smagorinsky: The Smagorinsky sub-grid scale model. It contains a tuneable param-
eter Ck (sometimes also found under the name CS in the literature), which can be
modified by an addition dictionary entry within the LES dictionary.

SmagorinskyCoeffs
{
Ck 0.094;
}
26 Chapter 2. Constant Directory

• SmagorinskyZhang: Modified Smagorinsky sub-grid scale model that accounts for


multiphase, particularly bubble-driven flows.
• WALE: The Wall-adapting local eddy-viscosity (WALE) sub-grid scale model modi-
fies the eddy viscosity in such a way that wall-bounded flows are properly captured.
• kEqn: Models a single partial differential equation for the sub-grid scale turbulent
kinetic energy, which in turn is used to scale the value of the eddy viscosity.
• NicenoKEqn: Extension of the kEqn sub-grid scale model to two-phase, bubble-
driven flows.
• dynamicKEqn: A variant of the kEqn sub-grid scale model, where the model coef-
ficients are dynamically calculated. A filter property needs to be prescribed which
specifies how the sub-grid scales are filtered:

dynamicKEqnCoeffs
{
filter < filterType >;
}

Here, the <filterType> can take three possible values:


– simple: Simple top-hat filter.
– anisotropic: Anisotropic filter, accounting for anisotropic behaviour in the
equations.
– laplace: Laplace operator-based filter, providing smoothed sub-grid scales.
• continuousGasKEqn: Another variant of the kEqn sub-grid scale model, which allows
for a two phase system with phase inversion.
• DeardorffDiffStress: Instead of modelling for a sub-grid scale model, the entire
sub-grid scale tensor is modelled instead in this model.
• dynamicLagrangian: A dynamic sub-grid scale model where averaging is introduced
through Lagrangian averaging.

2.4 Overview of DES models


In LES, sub-grid scale models impose strict grid requirements, which can be relaxed
by switching them for a RANS turbulence model. This is the domain of detached eddy
simulations, or DES. OpenFOAM offers two RANS models; the Spalart-Allmaras and
k − ω SST model. Either model is available as a detached eddy simulation (DES), delayed
detached eddy simulation (DDES), or improved delayed detached eddy simulation (IDDES).
Similar to LES simulations, we need to provide the following dictionary entry:
2.5 Overview of SAS models 27

LES
{
LESModel < DESModel >;
delta < DeltaModel >;
}

Here, instead of specifying a sub-grid scale model, we specify the DES RANS model as the
LESModel. The following choices are permissible:
• kOmegaSSTDES: DES-based version using the k − ω SST model.
• kOmegaSSTDDES: DDES-based version using the k − ω SST model.
• kOmegaSSTIDDES: IDDES-based version using the k − ω SST model.
• SpalartAllmarasDES: DES-based version using the Spalart-Allmaras model.
• SpalartAllmarasDDES: DDES-based version using the Spalart-Allmaras model.
• SpalartAllmarasIDDES: IDDES-based version using the Spalart-Allmaras model.

2.5 Overview of SAS models


Scale-adaptive simulations (SAS) are closely related to detached-eddy simulations (DES),
and differ only the prescribed filtering of sub-grid scale and when to deploy the RANS
model near the wall. The farfield is solved with LES in the same way as in DES. SAS
requires the following LES dictionary entry:

LES
{
LESModel < SASModel >;
delta < DeltaModel >;
}

Similar to DES, we specify the SAS RANS model as the LESModel. There is only one SAS
model available (as SAS is far less popular than DES):
• kOmegaSSTSAS: SAS model using the kOmegaSST RANS model as a baseline.

2.6 Overview of Delta models


For LES, DES, and SAS simulations, we need to specify a DeltaModel, which calculates
the representative grid size (∆) required by either the sub-grid scale model (LES) or RANS
model (DES and SAS). The following entries are available:
28 Chapter 2. Constant Directory

• cubeRootVol: Calculates the grid size as ∆ = 3 V , where V is the volume per of the
cell. Default choice and good for cases where the mesh size is changing (i.e. for
complex geometries).
• maxDeltaxyz: Calculates the grid size as ∆ = max|∆x, ∆y, ∆z|. More conservative
(and potentially more dissipative) than cubeRootVol as largest cell dimension is
chosen.
• maxDeltaxyzCubeRoot: This is a combination of the cubeRootVol and maxDeltaxyz
model, where both deltas are calculated with the respective model and then the
maximum value is taken.
• smooth: Calculates a grid size using any available delta model (see delta below) and
then applies a smoothing step so that neighbouring cells have a grid size ratio of no
more than a specified delta ratio (see maxDeltaRatio below, good for a continuous
representation and smoothly varying grid sizes and thus near wall turbulent quantities).
When the DeltaModel is set to smooth, the following additional entries can be
specified in a sub-dictionary:

smoothCoeffs
{
delta cubeRootVol ;
maxDeltaRatio 1.1;
}

• Prandtl: Calculates a grid size based on any available delta model (see delta en-
try below) and then compares that to a mixing-length based length scale based on
Prandtl’s mixing-length model. It is calculated as ∆ = min|∆, (κ/C∆ )y|. This model
requires an additional entry

PrandtlCoeffs
{
delta cubeRootVol ;
// optional entries ( will use default values if not specified )
Cdelta 0.158;
}

• vanDriest: Calculates a grid size based on any available delta model (see delta
entry below) and then compares that to the mixing-length corrected version of van
Driest, accounting for the presence of walls. First, the van Driest damping coefficient
is calculated as D = 1 − exp −y+ /A+ and then the grid size is calculated with ∆ =
min|∆, D(yκ/C∆ )|. This model requires an additional entry:
2.7 Overview of thermophysical models (compressible flows) 29

v an Dr i es tC o ef f s
{
delta cubeRootVol ;
// optional entries ( will use default values if not specified )
kappa 0.41;
Aplus 26;
Cdelta 0.158;
}

• IDDESDelta: Used for improved delayed detached-eddy simulations (IDDES).

2.7 Overview of thermophysical models (compressible flows)


To incorporate temperature changes that are coupled to the flow properties, we need to
specify a thermophysicalProperties file. This is useful (and required) when simulating
compressible flows, as well as temperature-dependent viscosity changes. The basic file
structure looks like the following:

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - C ++ -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
| ========= | |
| \\ / F ield | OpenFOAM : The Open Source CFD Toolbox |
| \\ / O peration | Version : < OpenFOAM Version > |
| \\ / A nd | Website : www . openfoam . com |
| \\/ M anipulation | |
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
FoamFile
{
version 2.0;
format ascii ;
class dictionary ;
location " constant " ;
object thermophysicalProperties ;
}

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

thermoType
{
type < thermoType >;
mixture < mixtureModel ;
transport < transportModel >;
thermo < thermoModel >;
e qu at i on O fS ta t e < eosModel >;
specie specie ;
energy < energyEquationType >;
}
30 Chapter 2. Constant Directory

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

The main entry is the thermoType dictionary, which allows for a few different entries. A
list of possible entries is provided below. Note that not all of these model can be combined
arbitrarily. OpenFOAM will exit with an error message if incompatible models are used, and
provide a list of possible combinations. In the following, each entry is discussed separately.
A good summary is also provided in the User guide.

2.7.1 <thermoType>
The thermoType indicates how the equations are going to be coupled, either through the
density or compressibility. The following thermoTypes are available:
• heRhoThermo: Thermophysical model that solves the flow for either a fixed composi-
tion (i.e. single phase) or reactive flow. The equations are coupled through the density
in this description.
• hePsiThermo: Thermophysical model that solves the flow for either a fixed compo-
sition (i.e. single phase) or reactive flow. The equations are coupled through the
compressibility, i.e. ψ = (RT )−1 in this description.
• heheuPsiThermo: Thermophysical model that solves the flow for combustion. The
equations are coupled through the compressibility and are based on the unburnt gas
enthalpy.
The names are constructed in such a way that the letters he at the beginning signify that this
model can be based either on the enthalpy h or internal energy e. Rho and Psi refers to the
density and compressibility, respectively. Heu is the unburnt enthalpy h or internal energy e.

2.7.2 <mixtureModel>
The mixtureModel entry specifies how the fluid is composed and its (thermodynamic) prop-
erties. Based on this entry, an additional dictionary entry of type mixture or reactingMixture
may be required to list the properties of the fluid (see below the following list). The following
mixtureModels are available:
• pureMixture: Single component mixture, i.e. non-reactive flow. This is the default if
you are not using combustion and simply need a thermodynamic model to close the
equations for compressible flows.
• homogeneousMixture: Mixture model for combustion, allowing for homogeneous
mixtures.
2.7 Overview of thermophysical models (compressible flows) 31

• inhomogeneousMixture: Mixture model for combustion, restricted to inhomogeneous


mixtures.
• veryInhomogeneousMixture: More restrictive in terms of mixing than inhomoge-
neousMixture, with additional mixing constant specified.
• multiComponentMixture: Allows for several mixtures.
• reactingMixture: Mixture for reactive flows.
• singleStepReactingMixture: Mixture for reactive flows using a single (mixture) step.
When using pureMixture, a mixture dictionary is then required and follows the thermoType
dictionary. The following example is given for air.

mixture
{
specie
{
molWeight 28.9;
}
ther modyna mics
{
Cp 1005.0;
Hf 0.0;
}
transport
{
mu 0.000625;
Pr 0.71;
}
}

2.7.3 <transportModel>
The transportModel determines how the molecular viscosity µ and thermal conductivity
κ are solved. The thermal conductivity is sometimes also expressed in terms of the non-
dimensional Prandtl number Pr = C p µ/κ. The following transportModels are available:
• const: Constant viscosity and thermal conductivity. If this is set, the then the mixture
dictionary needs to contain the following sub-dictionary entry:

transport
{
mu < scalar >;
Pr < scalar >;
}

Values for air are µ = 1.7894e − 5 and Pr = 0.71.


32 Chapter 2. Constant Directory

• sutherland: Calculate the viscosity based on Sutherland’s equation µ = As 1+TTs /T . If
sutherland is specified as the transportModel, the model coefficients As and Ts
need to be specified through the following dictionary entry:

transport
{
As < scalar >;
Ts < scalar >;
}

Values for air are As = 1.4792e − 06 and Ts = 116.


• polynomial: Allows to calculate the molecular viscosity µ and thermal conductivity κ
based on a polynomial of the temperature as µ, κ = ∑N−1 i
i=0 ai T . Requires an additional
dictionary entry of the form:

transport
{
muCoeffs <8 > ( 1000 -0.05 0.003 0 0 0 0 0 ) ;
kappaCoeffs <8 > ( 2000 -0.15 0.023 0 0 0 0 0 ) ;
}

The above dictionary now calculates the viscosity as µ = 1000 − 0.05T + 0.003T 2
and the thermal diffusivity as κ = 2000 − 0.15T + 0.023T 2 .
• WLF: Viscosity is calculated using the Williams-Landel-Ferry model, particularly
useful for polymers. This is done through µ = µ0 exp(−C1 (T − Tr )/(C2 + T − Tr )).
Its coefficients (plus the Prandtl number) are specified in the following sub-dictionary
entry:

transport
{
mu0 < scalar >;
Tr < scalar >;
C1 < scalar >;
C2 < scalar >;
Pr < scalar >;
}

• tabulated: Allows to specify viscosity and thermal diffusivity as a lookup table. For
each temperature, a value for viscosity and thermal diffusivity is provided, and linear
interpolation is applied to obtain values in-between. This requires the following
additional dictionary entry:
2.7 Overview of thermophysical models (compressible flows) 33

transport
{
mu
(
// ( T mu )
(200 1.82 e -05)
(350 2.61 e -05)
(400 3.93 e -05)
);

kappa
(
// ( T kappa )
(200 2.56 e -5)
(350 3.33 e -5)
(400 4.72 e -5)
);
}

2.7.4 <thermoModel>

The thermoModel is responsible to calculate C p (specific heat at constant pressure) based


on different models. The following thermoModels are available:
• hConst: This model assumes that both C p and the heat of fusion H f are constant
values, and thus can be specified directly. This requires an additional sub-dictionary
entry within the mixture entry of the form:

ther modyna mics


{
Cp < scalar >;
Hf < scalar >;
}

For air and a non-reactive (i.e. single component, compressible) flow, default values
are C p = 1005 and H f = 0.
• eConst: Similar to hConst, only that the specific heat at constant volume Cv and H f are
assumed constant now. Requires a similar dictionary entry of the form:

ther modyna mics


{
Cv < scalar >;
Hf < scalar >;
}
34 Chapter 2. Constant Directory

For air and a non-reactive (i.e. single component, compressible) flow, a default value
is Cv = 718.
• janaf: Calculates C p as a polynomial-based function of temperature as C p = R((((a4 T +
a3 )T + a2 )T + a1 )T + a0 ). Additional coefficients are taken from the janaf database
of thermodynamics. This model requires the specification of a low, high, and common
temperature, as well as the polynomial coefficients for the low and high temperature.
The thermodynamics sub-dictionary then becomes:

ther modyna mics


{
Tlow < scalar >;
Thigh < scalar >;
Tcommon < scalar >;
highCpCoeffs ( < scalar > < scalar > < scalar > < scalar > < scalar > < scalar > <
scalar >) ;
lowCpCoeffs ( < scalar > < scalar > < scalar > < scalar > < scalar > < scalar > <
scalar >) ;
}

For the highCpCoeffs and lowCpCoeffs, the first 5 scalar entries are the polynomial
constants a0 through a4 , with the sixth entry being the high/low temperature’s enthalpy
offset and the seventh entry the temperature’s entropy offset. A list of common fluids
is provided in this summary. For example, from this link, we can see that for air the
thermodynamics entry becomes:

ther modyna mics


{
Tlow 200;
Thigh 6000;
Tcommon 1000;
highCpCoeffs ( 3.08793 0.00124597 -4.23719 e -07 6.74775 e -11 -3.97077 e
-15 -995.263 5.95961 ) ;
lowCpCoeffs ( 3.5684 -0.000678729 1.55371 e -06 -3.29937 e -12 -4.66395 e
-13 -1062.35 3.71583 ) ;
}

• hPolynomial: Calculates C p from a polynomial representation. This requires the


specification of polynomial coefficients within the thermodynamics sub-dictionary
as follows:

ther modyna mics


{
2.7 Overview of thermophysical models (compressible flows) 35

Hf < scalar >;


Sf < scalar >;
CpCoeffs <8 > (1000 -0.05 0.003 0 0 0 0 0 ) ;
}

Here, C p is represented now by the following polynomial: CP = 1000 − 0.05T +


0.003T 2 . S f is the standard entropy in this dictionary entry.

2.7.5 specie
The specie entry only allows for a single selection (namely specie) and this requires an
additional sub-dictionary entry within the mixture dictionary called specie of the form:

specie
{
nMoles < scalar >;
molWeight < scalar >;
}

nMoles is only used for combustion modelling and homogenous mixtures, otherwise it is
set to 1. May also stand for number of Talpidaes, but that is unlikely... molWeight specifies
the molecular weight in terms of grams per mole.

2.7.6 <eosModel>
The equation of state describes how thermodynamic variables are related, specifically to the
density. The following eosModels are available:
• rhoConst: In this model, the density is assumed to be constant. Good approximation
for low Mach number flows or mediums with low compressibility (e.g. water).
• perfectGas: Uses the ideal gas law as the equation of state in the form of ρ = p/(RT ).
Not suitable for low temperature and high pressure situations.
• incompressiblePerfectGas: Similar to the perfectGas, using a reference pressure
instead to fix the density to a constant value, i.e. ρ = pre f /(RT ). Requires the
specification of the reference pressure in a sub-dictionary entry within the mixture
dictionary of the form:

e qu at i on Of S ta t e
{
pref < scalar >;
}
36 Chapter 2. Constant Directory

• perfectFluid: In this model, the ideal gas law is given as ρ = p/ (RT ) + ρ0 , where ρ0
is the density at a temperature of zero degree Celsius. It requires the additional entries
for the specific gas constant R and ρ0 as:

e qu at i on Of S ta t e
{
R < scalar >;
rho0 < scalar >;
}

• PengRobinsonGas: The Peng-Robinson equation of state is an extension of the ideal


gas law and particularly useful around critical points in the mixture. It requires
additional entries for the critical temperature, volume, and pressure of the fluid, as
well as the acentric factor ω, and the sub-dictionary entry becomes:

e qu at i on Of S ta t e
{
Tc < scalar >;
Vc < scalar >;
Pc < scalar >;
omega < scalar >;
}

• adiabaticPerfectFluid: Assumes an adiabatic ideal gas, for which the equation of state
reduces to ρ = ρ0 [(p + B)/(p0 + B)]1/γ . The reference density ρ0 , pressure p0 and
ratio of specific heats γ, as well as the constant B are specified through the following
sub-dictionary entry:

e qu at i on Of S ta t e
{
rho0 < scalar >;
p0 < scalar >;
gamma < scalar >;
B < scalar >;
}

• Boussinesq: This model uses the Boussinesq approximation of the form ρ = ρ0 [1 −


β (T − T0 )]. The reference density rho0 and temperature T0 , as well as the coefficient
of volumetric expansion β need to be provided as:
2.7 Overview of thermophysical models (compressible flows) 37

e qu at i on Of S ta t e
{
rho0 < scalar >;
T0 < scalar >;
beta < scalar >;
}

• icoPolynomial: This describes the density as a polynomial of the form ρ = ∑N−1 i


i=0 ai T .
The polynomial coefficients need to be specified in a sub-dictionary entry of the
form:

e qu at i on Of S ta t e
{
rhoCoeffs <8 > ( < scalar > < scalar > < scalar > < scalar > < scalar > < scalar > <
scalar > < scalar >) ;
}

• icoTabulated: This allows to specify a tabulated version of the density against tem-
perature. Linear interpolation is assumed between tabulated values. An example is
given below for three temperature entries for the equationOfState sub-dictionary
entry:

e qu at i on Of S ta t e
{
rho
(
// T rho
(200 1010)
(350 1000)
(400 980)
);
}

• rPolynomial: Polynomial representation of the density for liquid and solids with better
fit than a standard polynomial representation. The density is calculated as 1/ρ =
c0 + c1 T + c2 T 2 − c3 p − c4 pT . The coefficients c0 to c4 are given as a dictionary
entry as

e qu at i on Of S ta t e
{
C ( < scalar > < scalar > < scalar > < scalar > < scalar >) ;
}
38 Chapter 2. Constant Directory

2.7.7 <energyEquationType>
There are two choices available for the energyEquationType. It is either based on the
enthalpy h or the internal energy e. For both variable, the user can specify if the heat
of formation is to be included or not. Thus, the following energyEquationTypes are
available:
• sensibleEnthalpy: Energy equation is based on enthalpy and does exclude the heat of
formation.
• sensibleInternalEnergy: Energy equation is based on internal energy and does exclude
the heat of formation.
• absoluteEnthalpy: Energy equation is based on enthalpy and does include the heat of
formation.
• absoluteInternalEnergy: Energy equation is based on internal energy and does include
the heat of formation.

2.8 Overview of transport models (Newtonian / Non-Newtonian


fluids)
The transport models in OpenFOAM allow to specify how viscosity is changing in response
to strain-rate. If there is a linear relationship, fluids are said to be Newtonian, if it is
non-linear, these are called non-Newtonian fluids. The transport models are specified within
the constant directory in a file called transportProperties. The basic file structure is
as follows:

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - C ++ -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
| ========= | |
| \\ / F ield | OpenFOAM : The Open Source CFD Toolbox |
| \\ / O peration | Version : v2212 |
| \\ / A nd | Web : www . OpenFOAM . com |
| \\/ M anipulation | |
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

FoamFile
{
version 2.0;
format ascii ;
class dictionary ;
location " constant " ;
object transportProperties ;
}
2.8 Overview of transport models (Newtonian / Non-Newtonian fluids) 39

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

tran sportM odel < transportModel >;


// additional scalars for each transport model ...

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

The following transport models are available:


• Newtonian: This describes a linear relationship between viscosity and strain-rate.
It only requires an additional entry for ν for an incompressible fluid (it will be
calculated for a compressible fluid through the thermodynamic properties given in
the thermophysicalProperties file).

tran sportM odel Newtonian ;


nu < scalar >;

For air, use ν = 1.46e − 6 as a default value at room temperature.


• BirdCarreau: An incompressible, non-Newtonian model, where the viscosity changes
according to ν = ν∞ + (ν0 − ν∞ )[1 + (kγ̇)a ](n−1)/a . Its coefficients are defined as:

tran sportM odel BirdCarreau ;


nu0 < scalar >;
nuInf < scalar >;
k < scalar >;
n < scalar >;
a < scalar >; // defaults to 2 if not specified

• powerLaw: The power-law allows specifying a change in viscosity which is bound by


and upper and lower value for the viscosity, i.e. ν = kγ̇ n−1 for νmin < ν < νmax . The
model coefficients are specified as:

tran sportM odel powerLaw ;


nuMax < scalar >;
nuMin < scalar >;
k < scalar >;
n < scalar >;

• CrossPowerLaw: The cross power-law allows for a continuous change in viscosity,


without the need to specify a lower and upper limit. The viscosity is calculated as
ν = ν∞ + (ν0 − ν∞ )/[1 + (mγ̇)n ] and requires the following input:
40 Chapter 2. Constant Directory

tran sportM odel CrossPowerLaw ;


nuInf < scalar >;
nu0 < scalar >;
m < scalar >;
n < scalar >;

• HerschelBulkley: In this model, the viscosity is either treated as a constant or as a


power-law, if the shear stresses exceed a threshold value. The viscosity is calculated
as ν = min(ν0 , τ0 /γ̇ + kγ̇ n−1 ) and the following inputs are required:

tran sportM odel H er sc h el Bu l kl ey ;


tau0 < scalar >;
nu0 < scalar >;
k < scalar >;
n < scalar >;

• Casson: Developed for blood flows, the Casson model allows to specify a square-root
relationship between viscosity and strain-rate, which is capped by an upper and lower
p √
bound for viscosity. The viscosity is calculated as ν = ( τ0 /γ̇ + m)2 and requires
input as:

tran sportM odel Casson ;


nuMax < scalar >;
nuMin < scalar >;
tau0 < scalar >;
m < scalar >;

• strainRateFunction: The user may want to provide their own implementation for the
change in viscosity based on the strain-rate, which this model allows for. It uses the
Function1 syntax, which essentially allows injecting a bit of code / mathematical
expressions into OpenFOAM. As an example, using a polynomial representation of
the viscosity as ν = 2γ̇ − 3γ̇ 2 + 4γ̇ 8 would be implemented as:

tran sportM odel strainRateFunction ;


strainRateFunctionCoeffs
{
function polynomial ((2 1) ( -3 2) (4 8) ) ;
}

Here, we require a function input, we specify it to be of type polynomial that


requires an input of pairs where the first entry is the polynomial coefficient and the
second argument the polynomial’s exponent.
3. System Directory

The system directory houses the main files to control the solver settings such as number of
time-steps / iterations, as well as the numerical setup in terms of the numerical discretisation.
In the following, we will look at the most common input files and their options.

3.1 Overview of solver settings (controlDict file)


The controlDict file allows the user to set the basic simulation input parameters. The
following provides a list of common input arguments and their options and settings:
• application: This entry specifies the solver to be used by the simulation. If you
want OpenFOAM to run based on this keyword, you need to first load the runTime
functions with the following command:
source ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions
Then, you can execute the solver with the following command:
runApplication $(getApplication)
• startFrom: Specifies the starting point of the simulation. It can have the following
inputs:
– firstTime: Look at all time dictionaries and start the simulation from the earliest
one. Good for re-running simulations and overwritting the solution.
– latestTime: Same as firstTime, but instead look for the last time directory.
Good for continuing to run an existing simulation.
42 Chapter 3. System Directory

– startTime: Specify an exact time directory from which to start. Requires an


additional input named startTime, see below.
• startTime: If startFrom is set to be startTime, then this additional entry is required
to set the time directory from which to start the simulation.
• stopAt: Controls when to stop the simulation. The following inputs are available:
– endTime: Specify a time directory at which to stop once reached. Requires an
additional input endTime, see below.
– writeNow: Stop the current simulation at the end of this time step and write out
the solution. Requires runTimeModifiable yes; (see below).
– noWriteNow: Stop the current simulation at the end of this time step, but don’t
write out the solution. Requires runTimeModifiable yes; (see below).
– nextWrite: Wait until the next scheduled write and then stop the simulation.
Requires runTimeModifiable yes; (see below).
• endTime: If stopAt is set to endTime, this input is required to specify the time
directory at which to stop the simulation once reached.
• deltaT: Specifies the time step of the simulation. For constant time steps, this value
will be used throughout, for variable time steps, this is used for the first time step
and then adjusted. It is common to set deltaT 1; for steady state simulation, which
allows treating time steps as iterations.
• adjustTimeStep: Requires an input of either yes or no to indicate whether the time
step should be changed during the simulation based on the CFL number.
• maxCo: Specify the maximum permissible CFL number. OpenFOAM will steadily
ramp up the time step until it reaches maxCo for a single cell. The average CFL
number for all cells may be much lower than maxCo based on the size distribution of
the mesh.
• maxDeltaT: Regardless of the maxCo settings, if a certain deltaT value is reached,
stop increasing the CFL number if it would exceed this entry.
• writeInterval: Specifies how often the solution should be written out, i.e. the frequency
or interval of file writing. The meaning of this value is specified by the writeControl
entry, see below.
• writeControl: Specifies what meaning is associated with writeInterval. The
following options are available.
– timeStep: Specify after how many time steps the simulation should be output.
Needs to be an integer number, i.e. a value of 7 would indicate to write out the
simulation at every 7th time step.
3.1 Overview of solver settings (controlDict file) 43

– runTime: Specifies the frequency in terms of simulation time. If this value is set
to 0.3, for example, the simulation output is written to disk every 0.3 seconds,
i.e. at intervals of 0.3, 0.6, 0.9, 1.2, ... and so on.
– adjustableRunTime: Same as runTime, but will adjust the deltaT value for the
time step at which to output the simulation to achieve nicer time directory names.
Requires adjustTimeStep yes;, otherwise will work exactly as runTime.
– cpuTime: Specifies after how many elapsed seconds on the CPU to write out
the simulation. Good for large cases where a snapshot after a certain amount of
time is required. CPU time only counts time spend on solving the simulation,
but disregards time required for writing data to disk and printing residuals to
screen.
– clockTime: Similar to CPU time, but includes file writing and screen outputting.
For small simulations, a lot of information may be printed to screen quickly,
which will slow down the execution. For large simulations, file writing may take
some time, especially if done frequently. clockTime is more conservative than
cpuTime and will write more frequently.
• writeFormat: Specify how to write the output. The following two entries are available:
– ascii: Write data using ASCII files. OK for small scale simulation, not recom-
mended for simulations with millions of cells.
– binary: Preferred choice for large simulations. Will reduce the file size of
simulation data significantly.
• writePrecision: If ascii is selected for writeFormat, then writePrecission de-
termines how many digits should be used when writing out floating point num-
bers. For example, the number 123456789 would be represented as 1.23e9 with
writePrecision 3;, and as 1.234567e9 with writePrecision 7;. The default is
6 if nothing is specified.
• writeCompression: Allows for further data sizer reduction by compressing data
(predominantly for ascii data). Allows for the following input:
– uncompressed: The default value if nothing else is specified. Does not compress
the data.
– compressed: Compresses the data using the gzip format.
• timeFormat: When simulation data is written to disk, it is done so by creating a new
folder whose name is that of the current simulation time in seconds. The timeFormat
entry specifies how this time is written, and the following entries are available:
– fixed: Writes out the time directory as a floating point number, e.g. 0.00123 or
44 Chapter 3. System Directory

46.521.
– scientific: Writes out the time directory using scientific notation. The numbers
above (0.00123 and 46.521) would be written as 1.23e-3 and 4.6521e+1.
– general: The default. It is a mix of general and scientific. Scientific notation is
used if the exponent gets too small or if the number of digits are not sufficient to
represent the time accurately.

• timePrecision: This steers how many digits should be used when writing the time
directories. The default number of digits is 6. In general, it is advised to leave
the timeFormat as either general or scientific and the timePrecision to 6 or
higher to avoid inaccuracies for later processing (you may require the simulation
timestamp later for integration, at which point more precision may be required).
• runTimeModifiable: This entry can be set to either yes or no. If set to yes, changes
can be made to the simulation while it is running, for example, the endTime variable
may be changed to stop the simulation early but still write out the solution at the next
time step. Useful if simulation is running on a cluster but running out of time and the
latest progress should be changed. OpenFOAM will read all settings again if changes
are detected and update the simulation setup. Allows for drastic changes such as
changing the numerical schemes, solvers, and even turbulence model at runtime.
• purgeWrite: Specifies how many time directories to keep at any given time. When
you are running large scale simulations, you may not have sufficient storage available
to keep all data, purgeWrite can then be used to automatically delete older time
directories as newer ones are created. For example, a value of 5 here would mean
that only the last 5 time directories are kept. The oldest one is deleted as a new time
directory is created. A value of 0 turns this feature off and all time directories are kept.
Useful when running simulations on the cluster and keeping a few time directories
from which the simulation could be restarted.
• libs: When extending OpenFOAM, for example, implementing your own turbulence
model, then this needs to be loaded into OpenFOAM at runtime through a shared
object (or so in short, though it is typically known as a shared library among pro-
grammers). The libs entry allows specifying the name of the shared object as
("libName.so") and this will be loaded into OpenFOAM.
• functions: This keyword allows executing additional on the fly post-processing
functions (function object). The syntax is as follows:
3.2 Function Objects 45

functions
{
postProcessingFunction1
{
// ...
}
postProcessingFunction2
{
// ...
}
}

3.2 Function Objects


As alluded to above, function object allow injecting some additional post-processing we
want OpenFOAM to perform. Below is a collection of useful function objects that can be
used to extract commonly used additional information:

3.2.1 Additional fields


• Mach number: Allows to calculate the Mach number for each cell and write it out as
a volScalarField for later inspection during post-processing. The entry is given
as:

MachNumber
{
type MachNo ;
libs ( fieldFunctionObjects );
writeControl writeTime ;
}

• Q-criterion: Calculates the Q-criterion to visualise coherent turbulent structures. Its


entries are given as:

QCriterion
{
type Q;
libs ( fieldFunctionObjects );
writeControl writeTime ;
}

• Vorticity: Similar to the Q-criterion, this function computes the vorticity for later
post-processing.
46 Chapter 3. System Directory

Vorticity
{
type vorticity ;
libs ( fieldFunctionObjects );
writeControl writeTime ;
}

• wall shear stresses: Wall shear stresses can be included in a number of ways, though
the simplest, and probably most common application is to simply include the following
line within the functions dictionary within the controlDict file:

# includeFunc " w al lS h ea r St re s s "

This will calculate an additional volScalarField field which becomes available for
post-processing. Another approach is to include the following statement:

wallShearStress1
{
// Mandatory entries ( unmodifiable )
type wa l lS he a rS tr e ss ;
libs ( fieldFunctionObjects );
writeControl writeTime ;
}

The above code, however, will only write out the wall shear stresses for all wall
boundary patches and only their min and max value.

3.2.2 Turbulence quantities


• y+ value: Allows to calculate the y+ at a specific time (denoted by writeControl).
If set to writeTime, the y+ value is only written to disk, as the simulation data is
written to disk as well. The entry looks as follows:

yPlus
{
type yPlus ;
libs ( fieldFunctionObjects );
writeControl writeTime ;
}

• Turbulent field quantities: This function object allows computing some turbulent
fields based on the input provided.
3.2 Function Objects 47

turbulenceFields
{
type turbulenceFields ;
libs ( fieldFunctionObjects );
fields ( k epsilon L I R ) ;
writeControl writeTime ;
}

In the above example, we calculate the turbulent kinetic energy k, the dissipation rate
epsilon, the integral length-scale L, the turbulence intensity I, and the Reynolds
stress tensor R. For a list of all entries, see the turbulenceFields function object
documentation.
• field averaging: Allows to average a given quantity in time. Can either calcu-
late the mean or the root-mean-square (RMS) values (denoted by prime2Mean in
OpenFOAM). This function object requires additional sub-dictionaries for the vari-
ables that should be averaged. In the example given below, we want to calculate
both the mean and the RMS value of the velocity and pressure field. Each time
directory will now contain a new entry called UMean, UPrime2Mean, pMean, and
pPrime2Mean.

fieldAverage
{
type fieldAverage ;
libs ( fieldFunctionObjects );
writeControl writeTime ;

fields
(
U
{
mean on ;
prime2Mean on ;
base time ;
}

p
{
mean on ;
prime2Mean on ;
base time ;
}
);
}
48 Chapter 3. System Directory

3.2.3 Forces
• force coefficients: Force coefficients can be computed with the forceCoeffs function
object. It requires input for the boundary patches for which to calculate the force
coefficients, optionally the density (rhoInf for incompressible flows), the direction
for the lift and drag vector, the center of rotation (CofR) for the moment and its pitch
axis. Additional quantities for non-dimensionalisation are the velocity magnitude
magUInf, the reference length lRef, and the reference Area Aref. An example
is shown below. Notice that the writeControl here is set to timeStep and the
timeInterval to 1, indicating that for each time step we want to write out the force
coefficients.

forceCoefficients
{
type forceCoeffs ;
libs ( forces ) ;
writeControl timeStep ;
timeInterval 1;

log yes ;

patches ( airfoil ) ;
rho rhoInf ;
rhoInf 1.0;
liftDir (0 1 0) ;
dragDir (1 0 0) ;
CofR (0 0 0) ;
pitchAxis (0 0 1) ;
magUInf 6.0;
lRef 1.0;
Aref 1.0;
}

• forces: Forces are specified similarly to the force coefficients but require specifying
the origin of the coordinate system for the moment evaluation. It requires the origin
and then two direction vectors, either one of e1, e2, or e3, where 1, 2, and 3 refer to
the x, y, and z direction, respectively. An example is shown below:

force
{
type forces ;
libs ( forces ) ;
writeControl timeStep ;
timeInterval 1;
3.2 Function Objects 49

patches ( airfoil ) ;
origin (0 0 0) ;
e1 (1 0 0) ;
e3 (0 0 1) ;
rho rhoInf ;
rhoInf 1.0;
}

• pressure coefficient: Similar to the force and force coefficients entries, we can also
calculate the force coefficient as follows:

pressureCoefficient
{
type pressure ;
libs ( fieldFunctionObjects );
writeControl writeTime ;

mode staticCoeff ;
result cp ;

rho rhoInf ;
rhoInf 1.0;
pInf 0;
UInf (6.0 0.0 0.0) ;
}

For the mode, we can specify either


– static: Static pressure
– total: Total pressure
– isentropic: Isentropic pressure
– staticCoeff: Static pressure coefficient (shown above)
– totalCoeff: Total pressure coefficient
The result will indicate the name of the volScalarField that is being generated.
To non-dimensionalise the pressure, additional arguments may be required as shown
above, at a minimum the density, and the pressure and velocity at the farfield.
• force coefficient-based stopping criterion: OpenFOAM provides a mechanism to
check at runtime if certain integral quantities have converged and then stop the
simulation based on this criterion. Typically, we use that for force coefficients, but it
can be any other specified integral quantity. It is a really powerful utility that allows
to chain different conditions together and there are various conditions that can be
executed. But for the most part, checking like done in the code below is a very typical
use case:
50 Chapter 3. System Directory

runT imeCon trol


{
type ru nTimeC ontrol ;
libs ( utilityFunctionObjects );
conditions
{
condition1
{
type average ;
func tionOb ject forceCoeffs ;
fields ( Cd Cl ) ;
tolerance 1e -05;
window 20;
windowType approximate ;
}
}
}

In the function object above, we are checking if both the Cd and Cl values do not
change by more than 1e-5 (or 1e-3%), checked over an average of 20 iterations. Thus,
we need a minimum of 21 iterations before this function object can be executed,
where it will calculate the average Cd and Cl value over 1–20 iterations and then over
2–21 iterations and compare their difference. If it is less than 1e-3%, the simulation
will stop.
The example above uses average as the condition and there are 7 in total avail-
able (average, equationInitialResidual, equationMaxIter, maxDuration, minMax,
minTimeStep, none). Some more information on their usage can be found here:
Guide on runTimeControl.

3.2.4 Custom function objects (coded)


• coded function object: OpenFOAM does come with a wealth of function objects,
and the list provided in the quick reference guide is by no means exhaustive, but
there are times when you need something so specific that you need to implement that
yourself. In this case, OpenFOAM provides coded function objects, which allows you
injecting c++ code directly into your on-the-fly post-processing step. An example for
calculating the overall kinetic energy and then taking the time derivative of that field
is given below:

codedFunctionObject
{
3.2 Function Objects 51

type coded ;
libs ( utilityFunctionObjects );
name ddt_k ;
writeControl writeTime ;

codeWrite
#{
const Time & runTime = mesh () . time () ;
const vo lVecto rField & U = mesh () . lookupObject < volVectorField >( " U " ) ;
volS calarF ield k
(
IOobject
(
"k",
runTime . timeName () ,
mesh () ,
IOobject :: NO_READ ,
IOobject :: AUTO_WRITE
),
mesh () ,
d i m e n s i o n e d S c a l a r ( " k " , dimensionSet (0 , 2 , -2 , 0 , 0 , 0 , 0) , 0.0)
);

forAll ( mesh () . C () , cellI )


{
k [ cellI ] = 0.5 * (
U [ cellI ]. x () * U [ cellI ]. x () +
U [ cellI ]. y () * U [ cellI ]. y () +
U [ cellI ]. z () * U [ cellI ]. z ()
);
}

volS calarF ield ddt_k ( " ddt_k " , fvc :: ddt ( k ) ) ;


ddt_k () . write () ;
#};
}

3.2.5 Sampling points and surfaces


• 0D point sampling: Point sampling allows writing out any quantity at a specified
location within the domain. The basic syntax is as follows:

point_probes
{
type probes ;
libs ( fieldFunctionObjects );

// write solution for each timestep


writeControl timeStep ;
52 Chapter 3. System Directory

writeInterval 1;

prob eLocat ions


(
(1 0.01 0)
);

fields
(
U
p
);
}

The above probe will write out the values for U and p for each time step at the location
(1, 0.01, 0).
• 1D line probe: This utility allows writing out data across 1D lines in the domain, and
this requires a start and end point, as well as the number of samples along the line.
An example is shown below, where the velocity U and pressure p are written out on a
line between (2 1 0.5) and (2 -1 0.5) using 100 points across the line:

line_at_x =2
{
type sets ;
libs ( sampling ) ;
interpolationScheme cellPoint ;
setFormat raw ;
// write only when solution is written to disk , i . e . not every timestep
writeControl writeTime ;

sets
(
x =2
{
type uniform ;
axis xyz ;
start (2 1 0.5) ;
end (2 -1 0.5) ;
nPoints 100;
}
);

fields
(
U
p
);
}
3.2 Function Objects 53

• 2D surface cutting plane: A cutting plane, then, allows slicing the domain with a 2D
surface and write out the solution for this specific surface. Useful for large simulations
where the data can’t be stored for each time step but where data for a specific plane
should be recorded for each time step, e.g. to produce an animation of the flow field.
An example is given below:

plane_x =0
{
type surfaces ;
libs ( sampling ) ;
interpolationScheme cellPoint ;
surfaceFormat vtk ;
writeControl writeTime ;

surfaces
{
plane_x =0
{
type cuttingPlane ;
planeType pointA ndNorm al ;
pointAndNormalDict
{
point (0 0 0) ;
normal (1 0 0) ;
}
interpolate true ;
}
}

fields
(
U
p
);
}

• 3D iso-surfaces: Finally, 3D iso-surfaces are essentially also just 2D surfaces but con-
structed based on iso-values of a specific quantity. This type of surface is commonly
used to visualise flow structures, especially vortical structures in turbulent flows. An
example for constructing an iso-surface based on the Q-criterion is given below (here,
an iso-surface is constructed for values where Q = 0.1. We store an additional quantity
p on the iso-surface to colour it later in our post-processing software):
54 Chapter 3. System Directory

isoSurface_Q
{
type surfaces ;
libs ( sampling ) ;
interpolationScheme cellPoint ;
surfaceFormat vtk ;
writeControl writeTime ;

surfaces
{
isoSurface_Q
{
type isoSurface ;
isoField Q;
isoValue 0.1;
interpolate true ;
}
}

fields
(
Q
p
);
}

3.3 Overview of numerical schemes (fvSchemes file)


Within the system directory, there is a file called fvSchemes which is responsible for setting
up the numerical discretisation schemes in OpenFOAM. It contains 6 mandatory entries,
usually supplemented by a 7th entry to help with wall normal distance calculations, required
by a few RANS turbulence models. A basic fvSchemes file will look the following:

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - C ++ -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
| ========= | |
| \\ / F ield | OpenFOAM : The Open Source CFD Toolbox |
| \\ / O peration | Version : < OpenFOAM Version > |
| \\ / A nd | Web : www . OpenFOAM . com |
| \\/ M anipulation | |
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

FoamFile
{
version 2.0;
format ascii ;
class dictionary ;
3.3 Overview of numerical schemes (fvSchemes file) 55

location " system " ;


object fvSchemes ;
}

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

ddtSchemes { }

gradSchemes { }

divSchemes { }

laplacianSchemes { }

interpolationSchemes { }

snGradSchemes { }

// optional entry for RANS modelling


wallDist { }

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

Each sub-dictionary entry requires the specification of numerical schemes for each discreti-
sation operator. If the same discretisation should be applied to all operators for a specific
sub-directory, the default keyword can be used to state the default scheme. Alternatively,
default none; can be specified to tell OpenFOAM that each operator needs to be defined
explicitly in the file. A mix of both is possible as well, where a default scheme is provided
and then overwritten only for specific operators. In the following, we will be looking at
each entry in turn.

3.3.1 ddtSchemes
The ddtSchemes entry specifies how time derivatives are solved in OpenFOAM. The
sub-directory takes the form of

ddtSchemes
{
default < scheme >;
}

where the <scheme> keyword can take any of the following entries:
• steadyState: Assumes that the time derivative is zero, i.e. there is no unsteadiness
in the flow. This can be used for otherwise transient solvers (e.g. pimpleFoam,
rhoPimpleFoam, etc.) to make them operate as a steady state solver.
56 Chapter 3. System Directory

• Euler: First-order accurate in time, bounded and implicit.


• backward: Second-order accurate in time, unbounded and implicit. It can be made
bounded (stable) by using the bounded keyword in-front of the backward keyword,
as shown in the following:

ddtSchemes
{
default bounded backward ;
}

• CrankNicolson: The Crank–Nicolson scheme is a blend between a first- and second-


order scheme. How much of each scheme is used is determined by the blending factor
φ , which is given after the name, e.g.

ddtSchemes
{
default CrankNicolson 0.5;
}

Here, an equal mix of both first- and second-order is achieved. If φ = 0, pure first-
order (Euler) is achieved, for φ = 1, pure second-order is achieved. May be unstable
for higher CFL numbers and can be made bounded again using the bounded keyword
in-front of the scheme’s name. A value of 0.7 ≤ φ ≤ 0.9 is typically chosen as a good
compromise.
• localEuler: The local Euler version is also first-order accurate in time but uses a
local time step for each cell. Requires a solver that implements local time stepping
(LTS). LTS may improve convergence rate for grids with large variation in cell sizes
(e.g. grids with very small cells due to fine near-wall grid resolution and large cells in
the farfield).
• SLTS: A stabilised local time step (SLTS) version of the localEuler implementation.
This scheme adjusts the time step to retain a diagonally dominant matrix structure.

3.3.2 gradSchemes
The gradSchemes entry will determine the gradient discretisation for each variable. The
general structure is given as follows:
3.3 Overview of numerical schemes (fvSchemes file) 57

gradSchemes
{
default < limiter > < scheme > < blending - factor >;
}

For the <scheme> entry, there are two options available:


• Gauss linear: Using the Gauss-theorem to calculate the gradients.
• leastSquares: Using the least-squares approximation for the gradients.
While the theory suggests that leastSquares is to be preferred for mixed-element grids
(i.e. those where more than one cell-type is used), OpenFOAM does seem to operate best
with Gauss linear, but this needs to be established on a case by case basis. If in doubt,
use Gauss linear.

An additional limiter can be specified using the <limiter> keyword. This is optional and
may be omitted. If the <limiter> keyword is specified, however, the <blending-factor>
needs to be specified as well, which ranges from 0–1, and where a value of 0 means the
limiter is switched off, while a value of 1 indicates full limiting (may affect the convergence
rate and accuracy but may retain stability). Limiting may be required for low-quality grids
and strong variations in flow variables (compressible flows). The following limiters are
available:
• faceLimited: Limits the gradient after it has been interpolated from the cell-center
(where it is calculated) to the cell faces. This is the most dissipative approach (stable,
but less accurate).
• faceMDLimited: A multi-directional (MD)) variant of faceLimited, i.e. each direc-
tion is limited with separately, rather than applying the same limiter in all direction.
This is less diffusive and potentially more accurate than the pure faceLimited
scheme.
• cellLimited: Applies the limiting at the cell-center. The gradients that were limited
are then interpolated to the faces, and face-based gradients may not be guaranteed to
be limited as a result. Less diffusive than faceLimited schemes and more accurate,
but potentially more unstable.
• cellMDLimited: Similar to faceMDLimited, a multi-directional limiter for cell-
based limiters.
• cellLimited<Venkatakrishnan>: A cell-based limiter using the limiter of Venkatakr-
ishnan. Useful for compressible flows
58 Chapter 3. System Directory

• cellLimited<cubic>: Using a polynomial-based limiter. This has the advantage


that the limiter is differentiable and thus may affect convergence less than other,
non-differentiable limiters. This limiter was implemented incorrectly in OpenFOAM
in the past and got fixed over time, make sure you use the latest OpenFOAM version
if you plan on using this limiter.
A <blending-factor> of 0.5 is a good start if you experience problems with convergence.
More aggressive blending (i.e. close to or equal to 1) will lose accuracy due to clipping of
the gradients. Ideally you don’t want to use any gradient limiting, but for certain types of
applications that may not be feasible, especially those with strong gradient variations (e.g.
compressible flows due to pressure- and shock-waves).

Here is an example for a partially-bounded gradient calculation for the velocity and fully
unbounded gradient calculation for pressure:

gradSchemes
{
default none ;
grad ( U ) cellLimited Gauss linear 0.3;
grad ( p ) Gauss linear ;
}

3.3.3 divSchemes
The divSchemes entry determines predominantly the convective term discretisation and
has likely the largest influence on the overall solution accuracy and stability (except for low
Reynolds number flows, i.e. those of the order of 1 or less). There are 60+ divSchemes
available, but only a handful of them are used commonly. Due to the sheer number of
divSchemes, there are a few ways how to define them with additional options, as shown
below:

divSchemes
{
div ( < variable >) Gauss < scheme > < gradient >;
div ( phi , < variable >) Gauss < scheme > < gradient >;
}

In the list above, we can either have div(<variable>) or div(phi,<variable>). The


first one is related to linear operations, as for example in the incompressible continuity
equation ∇ · u = 0. If we wanted to discretise this term (note: most incompressible algorithm
3.3 Overview of numerical schemes (fvSchemes file) 59

do not solve the continuity equation this way but rather derive a new continuity equation in-
volving the pressure somehow), we would have div(U) Gauss <scheme> <gradient>;.
On the other hand, if we wanted to discretise the convective term in the momentum equation,
i.e. (U · ∇)U, we first need to linearise this term as

(Un+1 · ∇)Un+1 ≈ (Un · ∇)Un+1 = (φ · ∇)Un+1 (3.1)

In OpenFOAM, φ represents the linearised component (i.e. the velocity at the previous time
step). The way that OpenFOAM has implemented φ is as the flux through a face, i.e. it is
interpolating the velocity from the previous time step to the cell face and then this value is
multiplied by the face area and normal direction to obtain a flux. If we are dealing with this
convective term (for any transport equation, i.e. momentum, temperature, turbulence kinetic
energy, dissipation rate, etc.), we always have to specify phi first, followed by the entry
for the transport equation, e.g. div(phi, U) for momentum, div(phi,T) for temperature,
and so on.
• upwind: This is a first-order scheme and simply copies the values from the cell
center to the faces, with respect to the flow direction. This is a bounded and stable
scheme, but produces high numerical diffusion. This scheme is good for initially
running a simulation and then switching to a higher-order scheme once the solution is
converging, if issues are encountered at the beginning of the simulation. It is also a
good choice for RANS quantities like the convective term in the transport equations
such as div(phi,k) Gauss upwind;, as RANS equations are just adding diffusion
themselves (so a bit more numerical dissipation / diffusion will add stability without
affecting the accuracy too greatly).
• linearUpwind: This is a second-order version of the simple upwind scheme and
interpolates the values to the cell interfaces as U f = Uc + d · ∇Uc , i.e. it takes the
value at the cell center Uc and then adds the gradient of the velocity vector, multiplied
by the distance from the cell center to the cell face. It is an unbounded scheme but
generally stable enough and the default choice in most cases. Since we need the
gradient here, i.e. ∇Uc , we also need to specify how this gradient should be calculated.
Typically, we follow this entry with grad(U) to say that we want to calculate the
gradient based on the grad(U) entry within the gradSchemes dictionary entry (see
previous section). We can, however, specify any other entry here as well such as
grad(p), this will only signal to OpenFOAM that we want to use the same scheme
60 Chapter 3. System Directory

to calculate the gradient as the pressure gradient term, we are not actually using the
pressure gradient here. Even though this is possible, you would use grad(U) all the
time unless you have specific reasons not to.
• linearUpwindV: Same as the linearUpwind scheme above, but calculates the gra-
dient in each direction separately and applies that to each direction, rather than the
magnitude of the gradient applied in all direction equally. A bit more accurate and
stable than linearUpwind, though it is likely that you will see very little differences,
if any, between these schemes.
• linear: For linear operations that do not involve the convective term. It is a simple
linear interpolation between cell center values and is second order accurate but
unbounded.
• limitedLinear: Adds some limiting to the linear scheme. While it is nominal
second order, as is linearUpwind, it adds less diffusion to the flow and given the
small cell sizes in scale-resolving applications like LES, DES, SAS, etc., it is preferred
here due to low cell Reynolds and Peclet numbers, where upwinding becomes less of
a problem. It requires an additional blending factor after the scheme to indicate how
much limiting should be done. This value ranges between 0–1 and a value closer to
1 results in more limiting. A default value may be 0.5 which may then be adjusted
based on the convergence history.
• vanLeer: A bounded, second-order accurate, gradient-limited scheme (that follows
the principle of total variation diminishing (TVD)).
• Minmod: Similar to vanLeer, i.e. it is a TVD scheme with a different limiter applied.
Second-order accurate and bounded.
• vanAlbada: Similar to the two schemes above, bounded second-order accurate using
the limiter function of van Albada.
• LUST: This scheme blends between the linear scheme (75%) and the linearUpwind
scheme (25%). As a result, it is unbounded but with more stability than the pure
linear scheme. Good for scale-resolving turbulence simulations, i.e. LES, DES,
SAS, etc. Requires again an entry for how the gradient needs to be calculated for the
linearUpwind scheme, e.g. gradu(U).
• MUSCL: A second-order, bounded scheme. Similar to linearUpwind but using a TVD
limiter. Note that the MUSCL scheme can be made to be third-order accurate (for
Cartesian grids anyway) but this implementation is not provided in OpenFOAM, the
second-order variant is implemented here.
• MUSCLV: Same as the scheme above, but using a multi-directional approximation for
3.3 Overview of numerical schemes (fvSchemes file) 61

vector quantities.
• QUICK: Another popular second-order, unbounded interpolation scheme widely used
in CFD codes. No real advantage over linearUpwind, but may perform better in
certain situations (trial and testing required).
• QUICKV: A multi-directional version of the QUICK scheme.
• downwind: An unconditionally unstable, second-order scheme. Will not work! Will
always diverge! I love this is an option!

3.3.4 laplacianSchemes
The laplacianSchemes entry steers the discretisation of the diffusive terms. Since it is a
linear operator, the discretisation of this term is by far less important than the divSchemes
for the convective term. Though, it needs to be selected based on the underlying grid to
avoid excessive numerical diffusion, which would be difficult to tell apart from the physical
diffusion resolved by this operator. The general structure of the laplacianSchemes is:

laplacianSchemes
{
default Gauss < interpolation > < surface - normal - gradient > < weight >;
}

The <interpolation> scheme here is almost always linear, unless you have a specific
reason why not to use it. Otherwise, use linear here and be happy that you have one less
quantity to worry about. The <surface-normal-gradient>, on the other hand, has a few
entries and all of them are related to how mesh non-orthogonality is handled. The following
schemes are the most commonly used ones:
• orthogonal: Use this if you are using an orthogonal grid, i.e. Cartesian grid.
Typically, that is the case if you generate a mesh with blockMesh, though this can be
used to introduce non-orthogonality. For grids with non-orthogonality (you can check
your mesh for non-orthogonality by running checkMesh on your case and checking
the max and average non-orthogonality printed to the screen. If it is close to zero, the
orthogonal scheme is a good option). The orthogonal scheme does not apply any
non-orthogonal correction.
• uncorrected: Similar to the orthogonal approach, but the surface-normal gradient
is calculated in a slightly different way, though still without non-orthogonal correction.
Used for Cartesian grids only with low or no non-orthogonality.
• corrected: This approach is correcting for the non-orthogonality in the grid, which
62 Chapter 3. System Directory

requires additional non-orthogonal corrector loops during each time step. Required
for unstructured grids with cell types other than hexahedral elements.
• limited: Blends between corrected and uncorrected and thus requires a value
after the scheme, e.g. Gauss linear limited 0.5;. The blending factor deter-
mines how much non-orthogonality correction is applied. A value of 1 is equivalent
to using just the corrected method, a value of 0, on the other hand, is equivalent to
using just the uncorrected method. A blending factor in-between is a mix of both
and is set to optimise accuracy and stability. In general, you want to keep the mesh
max non-orthogonality below 65, where a value of 0–0.5 for the blending factor may
be sufficient. For larger values, you want to go up to 1 to limit any new extrema being
introduced as part of the low-quality mesh.
In addition to the 4 schemes given above (which you are likely going to use most of the
time), there are additional schemes available for problem specific applications:
• faceCorrected: A similar approach to the corrected approach above, just im-
plementing the non-orthogonal correction in a different manner (there are a few
approaches available which are different in mathematical terms but likely perform
very similar over a large number of cases in a numerical sense).
• skewCorrected: Similar, different way of correcting the non-orthogonality, but with
additional corrections for poorly skewed cells.
• linearFit: Applies a linear (polynomial-based) correction to the non-orthogonality.
This requires a polynomial coefficient between 0 and 3 to work and is given after the
name of the scheme, e.g. Gauss linear linearFit 0.5;
• quadraticFit: Similar to linearFit, just using a quadratic polynomial instead.
Also requires a polynomial coefficient to between 0 and 3 to work.
• relaxed: Allows to explicitly under- or over-relax the surface-gradient calculation. It
does not take a relaxation value after the scheme’s name, instead, you have to include
that in the under-relaxation parameters in the fvSolution file, e.g.

// system / fvSchemes file


laplacianSchemes
{
default Gauss linear relaxed ;
}

// system / fvSolution file


relaxationFactors
{
3.3 Overview of numerical schemes (fvSchemes file) 63

fields
{
snGrad ( U ) 0.7;
}
}

3.3.5 snGradSchemes
The snGradSchemes entry determines how surface normal gradients are calculated in gen-
eral. We had to specify this entry in the laplacianSchemes as well (where it is calculated),
and so the choices are exactly identical and will not be repeated here (see the laplacian
schemes section above). It is common practice to ensure that the snGradSchemes are set
the same way as the laplacianSchemes. The syntax for the snGradSchemes is similar
but the Gauss <interpolation> is removed when compared to the laplacianSchemes,
i.e.

snGradSchemes
{
default < surface - normal - gradient > < weight >;
}

An example of the laplacianSchemes and snGradSchemes together is shown below:

laplacianSchemes
{
default Gauss linear limited 0.5;
}

snGradSchemes
{
default limited 0.5;
}

In both cases, we use the same limited 0.5 surface normal gradient scheme.

3.3.6 interpolationSchemes
The interpolationSchemes entry is used when specific variables are interpolated from
the cell center to the cell faces. The same option as in the divSchemes can be set here and
thus will not be repeated. Though, the reason for the wealth of the interpolation schemes
given in the divSchemes section is the fact that the convective term is non-linear and
requires some sophisticated interpolation. For general interpolation from the cell center
to its faces, a standard second order central approximation is sufficient, and it is unlikely
64 Chapter 3. System Directory

that you will ever change this entry (unless you have a good reason to do so). Thus, the
interpolationScheme entry can be left as the following for all cases:

interpolationSchemes
{
default linear ;
}

If you do want to play around with different interpolation schemes, you have to specify how
the flux of a variable should be calculated, for example:

interpolationSchemes
{
default linear ;
flux ( U ) linear ;
}

3.3.7 wallDist
For calculations involving turbulent flows, we need to calculate the shortest distance from the
cell center to the wall, as this is an input required by some closure models. This is a challenge
to do (in a computationally efficient manner), and so OpenFOAM has implemented a few
ways to do this. The general syntax is

wallDist
{
method < method >;
}

The option we can specify for <method> are listed below:


• meshWave: This method is able to get accurate predictions close to the wall for grids
with low non-orthogonality. A mesh with inflation layers around solid walls typically
satisfies this condition and so meshWave is typically used as the default method in
OpenFOAM, as it is relatively quick. For meshes with non-orthogonality (in the near-
wall region), this method may introduce errors, but the mesh should be reconsidered
as this will introduce all sorts of other problems (e.g. wrong skin friction / wall shear
stresses and thus drag predictions).
• directionalMeshWave: A variant of meshWave, but requires an additional entry
n (0 0 1), where the entries for n specify the direction in which to search. This can
be used for simple problems (e.g. flat plate) where we know that the wall is planar.
3.4 Overview of solution settings (fvSolution file) 65

Faster than the meshWave algorithm but works for aforementioned special, simplified
geometries only.
• advectionDiffusion: Solves an advection equation (to be specific, the Eikonal
equation) with smoothing applied. The smoothing step adds stability and potentially
better approximate results. There are a fair bit of additional properties that needs to
be specified, which are detailed here.
• Poisson: Solves a Poisson equation to find an approximation to the nearest wall
patch. Since a linear system of equation is solved (in this case for yPsi), an additional
entry for yPsi needs to be specified in the fvSolution file (see next section for
details on how to specify linear solver settings and additional considerations here).
• exactDistance: As the name suggestions, this method calculates the distance
exactly, and does not approximate it (as the other methods above do). It is done
by constructing a search tree near the wall which is then used to find neighbour
patches in the vicinity, all of which are then checked for their distance and the
smallest one is then selected. May be slower than the approximate methods listed
above, but it will be exact.

3.4 Overview of solution settings (fvSolution file)


The final required ingredient in our simulation setup is that of linear solvers and pressure-
velocity coupling scheme. This is done in the fvSolution file, which generally has the
following structure:

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - C ++ -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
| ========= | |
| \\ / F ield | OpenFOAM : The Open Source CFD Toolbox |
| \\ / O peration | Version : < OpenFOAM Version > |
| \\ / A nd | Web : www . OpenFOAM . com |
| \\/ M anipulation | |
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

FoamFile
{
version 2.0;
format ascii ;
class dictionary ;
location " system " ;
object fvSolution ;
}
66 Chapter 3. System Directory

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

solvers { }

< pressure - velocity - coupling - scheme > { }

relaxationFactors { }

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

We will look at all of them in turn in the following.

3.4.1 Linear algebra solvers


The linear algebra solvers defined in the solvers section determine how the linear system
of equations Ax = b is solved in OpenFOAM. For every equation that is solved implicitly,
OpenFOAM construct such an equation, and we have to provide an implementation for it.
The minimum setup is shown in the following:

< variable >


{
solver < solver >;
prec onditi oner < preconditioner >;
smoother < smoother >
tolerance < scalar >;
relTol < scalar >;
}

Here, the <variable> keyword is used to indicate the variable we are solving for, e.g. U
(velocity), p (pressure), k (turbulence kinetic energy), etc. Sometimes you will need to
specify a <variable>Final entry, e.g. UFinal or pFinal; this is required by certain pres-
sure velocity coupling algorithms and OpenFOAM will ask you to provide one if required.
This keyword indicates how the last (or final) iteration within a current time step should be
solved. The idea is that you may have several iterations within a time step, so to reduce the
cost, OpenFOAM allows us to run the simulations with relaxed convergence parameters at
first, but then we can overwrite them at the final iteration and make them tighter. Thus, we
get better convergence within each time step but only pay for it at the last iteration within
each time step.

Related to that are the entries for tolerance and relTol. Both of them are related to the
stopping criterion for each linear solver. tolerance is the absolute tolerance and states
that the linear solver will stop if the residual r = Ax − b has reduced below the tolerance
3.4 Overview of solution settings (fvSolution file) 67

threshold. This may not always be achieved, and so we can provide OpenFOAM with a
second hint, the relTol, which indicates a relative tolerance. You can think of the relTol
as the residual threshold, as if the residual was always normalised to 1 at the first iteration
of the linear algebra solver. So if we are not hitting the absolute tolerance, then it is OK to
terminate the solver if we hit the relative tolerance instead.

What values should you use? This will depend on your case, but generally it is advis-
able to have values of 1e − 4 for both tolerance and relTol or lower if you want to
resolve time (unsteady simulations), otherwise you may introduce excessive numerical
diffusion in time. For steady state solution, it seems customary to have a relTol set to
between 1e − 3 to 1e − 2 and then the tolerance to the absolute convergence threshold.
For high Reynolds number flows, especially for those cases where grids were generated with
snappyHexMesh, the tolerance setting may never be reached and the residual r = Ax − b
may stagnate at rather low values, e.g. 1e − 3 to 1e − 1. In these cases, it is better to judge
convergence based on some integral quantity and its convergence history, for that see also
section 3.2.3 for force coefficient-based stopping criteria.

Sometimes it may be useful to limit the number of max (or minimum) iterations per
time step, for this we can specify the keywords minIter and maxIter. The default for min
and max are 1 and 1000, respectively. Should you hit the maximum iterations of 1000, it
is likely that either your mesh is of too low quality, or that you have an issue with your
simulation setup. At this point, it is advisable to check your setup again from start to finish.

3.4.1.1 Solver

Returning to the above dictionary entry, let’s look at the available entries. We have the
following entries for the solver:
• PCG: This is the preconditioned conjugate gradient algorithm. It is part of the Krylov
subspace methods and a rather fast approach to find the minimum to a linear algebra
system such as Ax = b. A preconditioner needs to be specified (see section 3.4.1.2).
This solver is the default solver for symmetric matrices (typically, only the pressure
has a symmetric matrix).
• FPCG: Drop-in replacement for the PCG algorithm. Available since version v2306
and driven by the community, the “Faster PCG" (or FPCG) reduces some of the
internal calculation that could reduce computational times and memory usage for
simple preconditioners. See linked merge request for additional insights. As is always
68 Chapter 3. System Directory

the case, test and see if this solver is performing better for you than the PCG if you
want to use it.
• PPCG: Preconditioned pipelined conjugate gradient (sometimes also referred to as
pipelined preconditioned conjugate gradient). This is reorganising some internal
computations to enhance parallel efficiency. You may wish to consult this article for
further information.
• PPCR: A pipelined version of the preconditioned conjugate residual method. It is
very similar to the PCG method, with minor modifications to the internal calculation
of some parameters. Generally requires more computational time and storage, thus
there is no advantage over the PPCG method. Having said that, theory and practice
can always differ, so testing is required if the differences want to be explored.
• PBiCG: An extension of the PCG solver to asymmetric matrices (as typically the
case for velocity and turbulent quantities, i.e. any equations that use an upwind-based
discretisation).
• PBiCGStab: An extension of the PBiCG method, named the preconditioned bi-
conjugate gradient stabilised version. Generally preferred over the PBiCG method,
unless you have reasons to believe otherwise, and the default for solving asymmetric
system of equations (e.g. velocity and turbulence quantities).
• GAMG: GAMG stands for Geometric agglomerated algebraic multigrid and is Open-
FOAM’s implementation for a multigrid solver. It requires a smoother (see sec-
tion 3.4.1.3) instead of a preconditioner which is used to solve the equation at each
multigrid level. The V-cycle is used by default and is hardcoded. A good choice
for elliptic equations (e.g. pressure) but less suitable for hyperbolic and parabolic
equations (e.g. velocity and turbulence quantities), especially for compressible flows
where the behaviour is globally hyperbolic (e.g. pressure waves are resolved in the
domain). May be a bad choice for large cases with parallel execution, as the coarsest
levels may have too few elements to allow for good scaling on the multigrid. A
conjugate gradient method is preferred here.
• smoothSolver: Instead of using a sophisticated solver based on the conjugate gradient
method, a matrix-free solver may be used instead, such as the Gauss Seidel or Jacobi
method (see section 3.4.1.3). For equations such as the velocity and turbulence
quantities where the number of iterations is not critical, this may yield improve
(reduced) memory usage, as the matrix does not need to be stored. It is a trade-off
between computational time and storage requirements. Typically, only used for small
test cases where performance is not critical (e.g. 2D cases).
3.4 Overview of solution settings (fvSolution file) 69

3.4.1.2 Preconditioners
Preconditioners are required to reduce the so-called condition number of a matrix. A large
condition number results in slow convergence of a linear algebra solver. If we can reduce the
condition number and bring it closer to 1, convergence will be accelerated. A preconditioner
will do this for us. We have the following preconditioners available in OpenFOAM:
• none: Switches the preconditioner off (and so the PCG algorithm, for example,
becomes the CG algorithm). Not all cases may require preconditioning but most will.
Generally, it is not advisable to use none unless you have a good reason to do so.
• diagonal: Also sometimes known as a Jacobi preconditioner, this preconditioner may
be useful for diagonally dominant matrices. In practice, though, this preconditioner
does not provide much performance gain and is generally not recommended.
• DIC: Preconditioner based on the diagonal Incomplete Cholesky factorisation. Only
applicable to symmetric matrices (i.e. pressure).
• distributedDIC: An enhanced version of the DIC algorithm for parallel execution.
• FDIC: The name says it all, “Faster DIC", so, enhanced performance over the DIC.
Only applicable to symmetric matrices.
• DILU: Diagonal-based Incomplete LU preconditioner, a DIC or FDIC precondi-
tioner for asymmetric matrices (e.g. velocity and turbulence quantities). Default
preconditioner choice for asymmetric matrices.
• distributedDILU: An enhanced version of the DILU algorithm for parallel execution.
• GAMG: We can use a multigrid as well as the preconditioner to get even more
performance. This is one of those cases where adding the multigrid can either
enhance your performance significantly or reduce it, based on your problem, so
testing is required.

3.4.1.3 Smoother
Both the entries for GAMG and smoothSolver require the additional entry smoother to be
specified, instead of a preconditioner. The following entries are available:
• DIC: Diagonal Incomplete Cholesky, see section 3.4.1.2 for more details. This
algorithm can be used both as a preconditioner and as a smooth solver.
• FDIC: Similar comments as for the above DIC solver apply. See section 3.4.1.2 for
more details on the FDIC algorithm.
• GaussSeidel: The classical Gauss Seidel algorithm. Efficient in its implementation,
but slow in its convergence. May be sufficient for multigrid applications though, as
the convergence rate is significantly enhanced through the error reduction on different
70 Chapter 3. System Directory

miltigrid levels (where the Gauss Seidel algorithm typically is very slow to converge
on its own).
• DICGaussSeidel: A combination of DIC and Gauss Seidel. DIC may introduce
numerical spikes in the solution, which the Gauss Seidel can efficiently smooth out,
thus resulting in faster convergence.
• nonBlockingGaussSeidel: A variant of the Gauss Seidel algorithm using non-blocking
MPI (Messenger Passing Interface) calls. This can, in theory, improve the parallel
efficiency, though the documentation of OpenFOAM does suggest that there is no
practical advantage of using this method over the classical Gauss Seidel algorithm.
• symGaussSeidel: A symmetric variant of the Gauss Seidel implementation.

3.4.2 Pressure-velocity coupling schemes


The pressure-velocity coupling schemes entry is required for any solver that makes use of
such a coupling algorithm. For example, the SIMPLE, PISO, and PIMPLE algorithm are all
example of pressure-velocity coupling algorithms. OpenFOAM is based on these methods,
and so you likely have to provide entries for either one of these coupling schemes in your
solution setup. A general dictionary outline with common option is provided below:

" ( SIMPLE | PISO | PIMPLE ) "


{
momentumPredictor yes ;
consistent yes ;
nCorrectors 2;
nOuterCorrectors 2;
nNonOrthogonalCorrectors 2;
pRefCell 0;
pRefValue 0;
pMax 1 e7 ;
pMin 1 e4 ;
}

Here, the entries given above are detailed below:


• momentumPredictor: Specifies whether a predictor step should be included in the
momentum equation. Generally advised (and default is yes if not specified), as the
cost is relatively low, but it adds additional stability and accuracy to the solution.
• consistent: In the SIMPLE algorithm, neighbouring cell contributions are neglected
in the momentum equation. The SIMPLEC algorithm (SIMPLE-Consistent) enables
this contribution, and we can tell OpenFOAM to use the SIMPLEC method instead
by setting consistent to yes. May provide faster convergence rates, but should not
3.4 Overview of solution settings (fvSolution file) 71

have a significant influence on the overall accuracy.


• nCorrectors: Specifies the number of times the pressure equation should be solved.
Only affects the PISO and PIMPLE algorithm (i.e. SIMPLE does not provide any
corrector steps). It will increase computational cost but also stabilise the overall
solution process. In general, 2 corrector steps are recommended for general purpose
computations (i.e. unstructured grids for industrial or engineering applications).
Increase this number if divergence becomes an issue.
• nOuterCorrectors: Only affects the PIMPLE algorithm. This number determines
how many times we solve the corrector steps. For example, if both nCorrectors
and nOuterCorrectors are set to 3, a total of 9 pressure evaluations are done. The
inclusion of an outer corrector loop around the corrector loop allows to stabilise the
solution process further. In-fact, it allows to use larger CFL numbers as a result and
thus allows for fast time stepping. If you want to use large CFL numbers, increase
the value of the nOuterCorrectors variable and use a solver based on the PIMPLE
algorithm, e.g. pimpleFOAM (incompressible) or rhoPimpleFOAM (compressible).
This type of outer corrector steps is very similar to using dual time stepping, which
equally accelerates the convergence of the solution.
• nNonOrthogonalCorrectors: This value determines how many non-orthogonal
corrector steps should be done. For each pressure evaluation, at least one non-
orthogonal corrector step is done. The value specified here are additional steps. So,
for example, a value of 2 as specified above would result in a total of 3 non-orthogonal
corrector steps. For meshes with low non-orthogonal quality metrics, you can set
this value to 0. For meshes with moderate non-orthogonality (say up to 30), a value
of 1 should be sufficient. Anything above should be well handled with a value of 2.
Adding more non-orthogonal corrector steps is unlikely to stabilise your solution or
change the overall accuracy. If you encounter still divergence or non-convergence,
it’s time to improve your mesh.
• pRefCell: Pressure-velocity coupling algorithm need to fix the pressure in one
cell, typically cell 0, as the value is arbitrary. Only simulations with a complete
Neumann-type boundary condition for the pressure will make use of this approach
(e.g. a domain consisting only of walls). If the pressure is fixed at one boundary (for
example, the outlet for incompressible flows), then the reference cell approach is not
used.
• pRefValue: This is the value specified for the pressure. As it is a reference pressure,
any value is OK here, but 0 is typically used so that the pressure we calculate is the
72 Chapter 3. System Directory

static pressure.
• pMax: Maximum pressure allowed in the domain. This is set for compressible flows,
typically, to ensure the pressure is not reaching unrealistically high values.
• pMin: Similar to pMax, except that we set the minimum pressure value.

3.4.3 Under relaxation

The relaxationFactorsentry allows us to under (or over) relax our equations. We need
under-relaxation to ensure we are not adding too much of the newly computed solution
to our field variables, as the non-linear term generally introduces some uncertainties (or
rather, numerical approximation side effects) that would lead to divergence if we added the
newly computed solution in full. We can either under-relax fields directly or the equations
that are used to solve for them. Which approach we chose depends on how the solver has
implemented under-relaxation. If in doubt, provide under-relaxation factors for both fields
and equations. this will not have any negative side effects.

The relaxationFactors entry is given below, with examples:

relaxationFactors
{
fields
{
" (.*) " 0.7;
p 0.3
}

equations
{
" (.*) " 0.7;
U 0.9;
}
}

We see that for both the fields and equation entry, we use the generic statement "(.*)"
which is a regular expression (or regex), which essentially means to capture any variable.
It is a bit like the default keyword in the fvSchemes file, i.e. we are providing a default
under-relaxation factor here. We can then provide specific fields and equations afterwards
and overwrite this default value as shown above for the pressure field and velocity (or
momentum) equation.
3.5 Overview of parallel execution (decomposePar file) 73

3.5 Overview of parallel execution (decomposePar file)


While not technically required to run a case in OpenFOAM, parallel execution is very
common, but can present a few hurdles. The following sections aim to provide enough
background information to determine when to use parallel code execution and how many
processors should be used, as well as show how to decompose the domain (required) and
then executing the meshing and solving in parallel.

3.5.1 How many CPU cores should I use?


There are a few basic considerations we have to keep in mind:
• In an ideal scenario, we add more computational resources and get an equivalent boost
in our simulation times, i.e. adding an additional N number of processors should
reduce the computational time by a factor of N as well.
• Parallel execution will add overhead to our computation. We have to exchange
information across processor boundaries, which require additional communication.
• Both of the above points combined mean that there is a natural limit after which
communication is becoming too costly, and no more speed up is recorded. Our
parallel efficiency will drop at this point significantly.
As a general rule of thumb (and I invite you to make your own statistics, but this has worked
well for me in general in the past), you should aim to have no less than 100 000 cells per
processor. Say you have 1 million cells, you may use up to 10 CPU cores. However, there
is another rule of thumb, especially when working with high-performance clusters (HPC),
which is that your number of cores should be based on the following equation

nCores = 2N , N∈N (3.2)

That is, the number of cores nCores used, should be calculated based on 2N with N only
taken from the set of natural (counting) numbers, e.g. N = 1, 2, 3, 4, .... Thus, we should
aim for processor numbers nCores = 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, .... So in the
example above, there is no N in the set of natural numbers that would result in nCores = 10,
the closest we can get is either nCores = 8 (using N = 3) or nCores = 16 (using N = 4). 8
cores would result in 125 000 cells per processor and 16 cores in 62 500 cells per processor.
Thus, in this case 8 cores would be preferred is this gives us the closest value to 100 000
cells which does not go below the value of 100 000 cells.
74 Chapter 3. System Directory

Other CFD codes may have lower values, especially commercial codes put a lot of emphasis
on providing good scalability for a reduced number of cells (ANSYS Fluent, for example,
may scale well down to 50 000 cells for specific applications). At the same time, though, it
has to be said that this value needs to be treated with a grain of caution. I have seen people
claiming good scalability where there were only 10–20 cells left per core. I have no reason
to doubt these numbers, but if you have a very inefficient code, or you are solving something
where you have substantially more computations per time step than a second order finite
volume code (such as OpenFOAM), then it is not difficult to reduce this number. Take a
discontinuous Galerkin implementation with a higher-order polynomial, or a spectral code,
and you get substantially more computations, thus lowering your number of cells per core
at which you are still operating efficiently. You always need to look at the actual time it
takes to run your code (wall clock time) in conjunction with your cells per core count (and
potentially, the parallel efficiency).

3.5.2 How to decompose the domain in OpenFOAM


Having said that, in order to run your cases in parallel, you need to provide decomposeParDict
file within the system directory. The file structure is as follows:

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - C ++ -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
| ========= | |
| \\ / F ield | OpenFOAM : The Open Source CFD Toolbox |
| \\ / O peration | Version : < OpenFOAM Version > |
| \\ / A nd | Web : www . OpenFOAM . com |
| \\/ M anipulation | |
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

FoamFile
{
version 2.0;
format ascii ;
class dictionary ;
location " system " ;
object decomposeParDict ;
}

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

numberOfSubdomains < scalar >;

method < decomposition - method >;

< method - specific - entries >


3.5 Overview of parallel execution (decomposePar file) 75

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

The numberOfSubdomains entry specifies how many cores should be used. On the other
hand, the <decomposition-method>, tells OpenFOAM how to decompose the domain.
The decomposition method plays a crucial role in minimising the number of processor
boundaries and number of cells within the processor boundaries, as that will directly
scale your communication cost. Different methods are available, some of which require
the specification of <method-specific-entries>. A list of available methods is given
below:
• simple: The simple decomposition strategy requires an input that states into how many
parts the domain should be split in each direction. We can split the geometry in each
direction into as many parts as we want, as long as the number of subdomains in each
direction multiplied together results in the same number as the numberOfSubdomains.
It requires an additional entry of the form:

numberOfSubdomains < scalar >;


method simple ;
coeffs
{
n ( < scalar -x > < scalar -y > < scalar -z >) ;
}

The values for <scalar-x>, <scalar-y>, and <scalar-z> indicate into how many
parts the domain should split in each direction. If we have a uniform mesh, this may
be a viable approach, but in general, this decomposition strategy is not recommended
for more complex cases or anything with a complex geometry.
• hierarchical: This is the same as the simple decomposition strategy, but it allows to
additionally specify in which order we split the domain, i.e. in the simple approach,
the order is always xyz, that is, the domain is first split in x, then y, and then z. With
hierarchical, we can specify an additional entry order which allows us to change
that order. If we do not specify anything here, then hierarchical and simple are
identical (the default order is xyz).

numberOfSubdomains < scalar >;


method hierarchical ;
coeffs
{
n ( < scalar -x > < scalar -y > < scalar -z >) ;
76 Chapter 3. System Directory

order xyz ;
}

• random: As the name suggests, this decomposes your domain randomly. Probably
the worst possible decomposition strategy available and not intended for production
use, but rather for stress testing parallel code implementations. Do not use!
• metis: This is a graph partitioning algorithm which provides a fast and efficient
decomposition without any further user input. It will reduce the number of processor
boundaries and minimise the number of faces per processor boundary, to reduce
communication time overall. You will need to install metis first, as it does not come
bundled with OpenFOAM.
• scotch: Very similar to metis, but does work out of the box, no additional library
required. Unless you have good reasons to chose otherwise, scotch should be your
default selection for both complex and simplified domains for best parallel workload
distribution.
• kahip: Similar to metis, a fast graph partitioner that is also not included by default in
OpenFOAM. Can be made to use by installing the library. It then offers additional
entries for config and imbalance, where the config entry allows specifying either
fast, eco, or strong (indicating the performance of the graph partitioning problem)
and imbalance allows to specify what imbalance between processors is allowed. The
default value here is 0.01, meaning that the number of cells for different processors is
allowed to vary by at most 1%. A lower value will give better parallel performance
but also increase the decomposition time.
• multiLevel: The multiLevel approach allows to specify different decomposition
strategies for different regions. This may be useful for applications where more
than one solver is used, e.g. solids and fluids for fluid-structure interactions (FSI).
Then, we can specify how many processors we want to use for each domain and what
decomposition strategy to use. An example for this type of problem is given below,
where 24 cores are to be used on the fluid region and 8 on the solid region:

numberOfSubdomains 32;
method scotch ;

regions
{
fluid
{
3.5 Overview of parallel execution (decomposePar file) 77

numberOfSubdomains 24;
method scotch ;
}

solid
{
numberOfSubdomains 8;
method scotch ;
}
}

We are able to change the decomposition strategy for each level (i.e. use any of the
above-mentioned strategies), but as also mentioned above, scotch should be your
default, unless you have a good reason to select something else otherwise. For more
details on multi-level decomposition, see this guide.
• manual: This allows to specify a file that contains a manual mapping, i.e. the file
contains a single ID per line, where the line number indicates the cell, and the ID
which processor this cell belongs to. This is typically the output of graph partitioning
algorithms such as metis and so this allows to include additional graph partitioning
algorithm if you want to use your favourite that is not yet included in OpenFOAM.

3.5.3 How to execute code in parallel


There are essentially two types of applications you may want to run in parallel; meshing and
solving a case. To run your case in parallel during meshing (in particular snappyHeMesh,
blockMesh is unlikely to take a long time to mesh your blocks), you would typically follow
the following steps (assuming here that we are meshing in parallel using 4 CPU cores):

surfaceFeatureExtract
blockMesh
decomposePar
mpirun - np 4 snappyHexMesh - parallel - overwrite
mpirun - np 4 checkMesh - parallel - latestTime
r e c o n s t r u c t P a r M e s h - constant

On the other hand, if we want to solve a case in parallel (potentially after running snappy-
HexMesh in parallel), you would use the following commands:

decomposePar
mpirun - np 4 < solver > - parallel
reco nstruc tPar < options >
78 Chapter 3. System Directory

In the code above, the <solver> is the actual solver you are going to use, e.g. simpleFoam.
For the reconstructPar entry, it is customary to provide a few options, the most common
option being -latestTime, which will only reconstruct the case for the last time step in
the solution process. This is useful if you are only interest in the solution of the last time
step, which is typically the case for steady state simulations.

You might also like