4 Tutorials to Rule Them All!!! (Part 1)
Published:
Introducing the complexities of setting up advanced simulations in OpenFOAM.

Setting up a tutorial case in OpenFOAM might seem like a breeze, but when it comes to tackling more intricate simulations for research purposes, the waters can quickly get murky. In this article, I will dive into the intricacies of setting up complex cases in OpenFOAM, shedding light on the challenges that arise not from geometry, but from the setup itself. Throughout this article, I’ll focus on two specific cases: the PitzDaily case and the flow around a circular cylinder. These cases serve as prime examples to illustrate the nuances of setting up various types of simulations, from steady-state to unsteady RANS-based and even Laminar or Direct Numerical Simulation (DNS). For those eager to follow along, both cases are available for download here. Additionally, I’ll be leveraging the PETSc setup introduced in a previous article, alongside the latest version of OpenFOAM, OpenFOAM-v2312, to ensure compatibility and efficiency throughout the process.
To showcase how I set up a steady-state simulation, I’ll be using the PitzDaily case labeled pitzDaily_RANS
. Let’s walk through the boundary conditions necessary for this case: at the Inlet, I apply a fixed velocity of $u = (10, 0, 0) m/s$, while at the Outlet, a fixed pressure of $p = 0$ Pa is set. Additionally, I enforce No-slip conditions at the Walls. I am considering air as the fluid, with a kinematic viscosity of $1\times 10^{-5} m^2/s$. For turbulence modeling, I’ll opt for the Standard k-$\varepsilon$ model and utilize the simpleFoam solver integrated with the SIMPLE algorithm.
Now, let’s delve into key considerations when setting up a steady-state case:
0 directory
In the directory 0, it’s crucial to have the following files: “U,” “p,” “k,” “epsilon,” “nut,” and “nuTilda.”
When initializing “k” and “epsilon” (and even Omega), you can estimate their initial values using this equation:
\[k = \frac{3}{2}(T_I U_{ref})^2, \qquad \varepsilon = C_\mu \frac{k^{1.5}}{L}, \qquad \omega = \frac{k^{0.5}}{L}\]Here, $T_I$ stands for turbulence intensity, $U_{\text{ref}}$ represents the reference velocity (essentially akin to the inlet velocity), and $L$ signifies the characteristic length scale. The constant $C_\mu$ is set to 0.09. Turbulence intensity is dependent on the Reynolds number, following the relationship $T_I = 0.16\times Re^{-1/8}$. In most cases with $Re<100000$, $T_I\approx5%$, indicating a low-turbulence scenario.
Ensure that wall functions are implemented at the wall boundary. Here’s how wall functions are incorporated:
For “k”
Any_wall_or_Body_Surface
{
type kqRWallFunction;
value uniform 0.375;
}
For “epsilon”
Any_wall_or_Body_Surface
{
type epsilonWallFunction;
value uniform 14.855;
}
For “omega”
Any_wall_or_Body_Surface
{
type omegaWallFunction;
value uniform 440.15;
}
constant directory
In the constant directory, it’s essential to incorporate turbulence properties and configure the Standard k-$\varepsilon$ turbulence model as follows:
simulationType RAS;
RAS
{
// Tested with kEpsilon, realizableKE, kOmega, kOmegaSST,
// ShihQuadraticKE, LienCubicKE.
RASModel kEpsilon;
turbulence on;
printCoeffs on;
}
system directory
First setup the fvSchemes
file to include the a steady-state time scheme (ddtSchemes
), as well as the required divSchemes
. The setup of this case is as follows.
ddtSchemes
{
default steadyState;
}
gradSchemes
{
default Gauss linear;
}
divSchemes
{
default none;
div(phi,U) bounded Gauss linearUpwind grad(U);
turbulence bounded Gauss limitedLinear 1;
div(phi,k) $turbulence;
div(phi,epsilon) $turbulence;
div(phi,omega) $turbulence;
div(nonlinearStress) Gauss linear;
div((nuEff*dev2(T(grad(U))))) Gauss linear;
}
laplacianSchemes
{
default Gauss linear corrected;
}
interpolationSchemes
{
default linear;
}
snGradSchemes
{
default corrected;
}
wallDist
{
method meshWave;
}
Moving forward, I’ll configure the fvSolution
file with the necessary solvers for this case. In my example, I’ll be utilizing the PETSc solvers. Then, I’ll set up the SIMPLE algorithm to use SIMPLE-C and establish relaxation factors as follows:
SIMPLE
{
nNonOrthogonalCorrectors 0;
consistent yes;
residualControl
{
p 1e-4;
U 1e-6;
"(k|epsilon|omega|f|v2)" 1e-4;
}
}
relaxationFactors
{
equations
{
U 0.9; // 0.9 is more stable but 0.95 more convergent
".*" 0.9; // 0.9 is more stable but 0.95 more convergent
}
}
NOTE: In this context, the parameter nNonOrthogonalCorrectors
denotes the number of non-orthogonal correctors corresponding to the non-orthogonality of the mesh. Mesh non-orthogonality is defined as the angle formed by the vector joining two adjacent cell centers across their common face and the face normal. While a square-shaped mesh element is orthogonal, a rhombus-shaped element is non-orthogonal. In real-life simulations involving complex models, numerical meshes are rarely orthogonal. Therefore, non-orthogonality correction is crucial for stability and accuracy.
A general rule-of-thumb for setting nNonOrthogonalCorrectors
is as follows:
- if non-orthogonality < 70 : 0;
- if non-orthogonality > 70 : 1;
- if non-orthogonality > 80 : 2;
- if orthogonality > 85, convergence becomes challenging. (In this case, it’s advisable to improve the mesh quality.)
Now, you might wonder how to check the non-orthogonality of the mesh. You can achieve this using the checkMesh
utility. When running checkMesh
for your case, pay attention to the following output:
Mesh non-orthogonality Max: 5.95045 average: 1.63034
Non-orthogonality check OK.
In this example, the maximum non-orthogonality is 5.95045, suggesting that nNonOrthogonalCorrectors
can be set to 0.
Lastly, let’s configure the controlDict
file. For a steady-state case, the time-step or deltaT
effectively serves as an iteration counter, thus set it to 1. The endTime
can be a large number since a residualControl
is implemented in the fvSolution. This option monitors residuals and terminates the simulation once they reach specified levels.
Running the Simulation
Based on all this information, Running the simulation is a straightforward process:
- Navigate to the directory where you downloaded the
pitzDaily_RANS
case file. Open a terminal at this location. Hint: When you typels
, you should see0 constant system
. - Activate the OpenFOAM environment. This means, source the
etc/bashrc
file or if you made the alias use that. For me, I will typeOF2312
to source the OpenFOAM environment. - Generate the mesh. Type in
blockMesh
. Remember, OpenFOAM is case sensitive!!! - Run the case. Type in
simpleFoam
As I wrap up Part 1 of my exploration into advanced simulation setup in OpenFOAM, I’ve covered the essentials of preparing steady-state cases. In Part 2, I’ll delve deeper into the intricacies of setting up unsteady cases, exploring the nuances of time-stepping, turbulence modeling, and iterative solver algorithms. Stay tuned for more insights and practical tips!