OpenFOAM Quick Reference Guide
OpenFOAM Quick Reference Guide
Reference Guide
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.
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
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
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
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - 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 >;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
boundaryField
{
BC1
{
// ...
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
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:
• 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
• zeroGradient: A shorthand notation for a fixed gradient with zero flux (Neumann-type
boundary condition).
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;
}
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:
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
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
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.
• movingWallVelocity: A wall boundary condition for velocity where the wall is moving
in the wall’s tangential direction.
• 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).
• 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.
• 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).
• nutkWallFunction: A wall function model for the turbulent viscosity ν. This formula-
tion should be used for the log-law region (y+ > 30).
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.:
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):
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
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
k1.5
ε = Cµ0.75
l
• Turbulent viscosity νt
calculated
1.3.5.2 Outlet
• Turbulent kinetic energy k
∂k
=0
∂n
∂ ν̃
=0
∂n
• Turbulent viscosity νt
calculated
1.3.5.3 Symmetry
• Turbulent kinetic energy k
∂k
=0
∂n
• Turbulent viscosity νt
calculated
kLowReWallFunction
• Turbulent viscosity νt
nutLowReWallFunction
∂ ν̃
=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
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 () ) ;
forAll (U , cellI )
{
auto x = mesh . C () [ cellI ]. x () ;
auto y = mesh . C () [ cellI ]. y () ;
auto z = mesh . C () [ cellI ]. z () ;
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) ;
forAll (p , cellI )
{
auto x = mesh . C () [ cellI ]. x () ;
auto y = mesh . C () [ cellI ]. y () ;
auto z = mesh . C () [ cellI ]. z () ;
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 ;
// 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
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.
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - 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 ;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
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.
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
dynamicKEqnCoeffs
{
filter < filterType >;
}
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.
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.
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;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - 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
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 >;
}
transport
{
As < scalar >;
Ts < scalar >;
}
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>
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:
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:
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:
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 >;
}
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 >;
}
e qu at i on Of S ta t e
{
rho0 < scalar >;
T0 < scalar >;
beta < scalar >;
}
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.
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - 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
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
• 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:
• 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:
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.
– 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
{
// ...
}
}
MachNumber
{
type MachNo ;
libs ( fieldFunctionObjects );
writeControl writeTime ;
}
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:
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.
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) ;
}
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.
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)
);
point_probes
{
type probes ;
libs ( fieldFunctionObjects );
writeInterval 1;
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
);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - 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
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
ddtSchemes { }
gradSchemes { }
divSchemes { }
laplacianSchemes { }
interpolationSchemes { }
snGradSchemes { }
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
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
ddtSchemes
{
default bounded backward ;
}
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 >;
}
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
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 >;
}
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
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.
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 >;
}
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 >;
}
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.
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - 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 { }
relaxationFactors { }
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
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.
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.
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.
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
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).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - 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 ;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
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:
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).
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.
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.