OpenFOAM - Airfoil Calculations
In this tutorial we will look at incompressible flow over NACA 4-digit airfoils. The goal of this document is:
- To show how simple scripting tools such like Octave can be used together with snappyHexMesh to create advanced meshed, in this case a mesh around a NACA 4-digit arifoil
- Use the new edge handling feature in snappyHexMesh to better resolve sharp edges (like the trailing edge of an airfoil)
- To use several different solvers in sequence on the same geometry to (hopefully) produce good results with less computational time
- To calculate forces, in this case drag and lift on a airfoil
Prerequisites
Before you start on this tutorial, please make sure that you have
- A decent laptop or workstation
- A working OpenFOAM 2.x.x installation
- GNU Octave (Matlab will probably also work with minor modifications, although not tested)
- Done the basic cavity and damBreak tutorials that is distributed together with OpenFOAM and described in the documentation
- Done the spillway tutorial provided here (or in any other way familiarized yourself with snappyHexMesh).
- Some basic knowledge about airfoils, drag and lift coefficients etc.
1: Setup of basic case
This time we will use the wingMotion tutorial as our basic case. This is because this tutorial uses two steps in the solution, first it calculates the stationary solution before it starts the transient solver. The advantage of this procedure is that one easily and effectively gets rid of the effects from non-physical initial conditions and quickly can calculate an averaged stationary flow field. This field is then used as the initial condition for a more computationally demanding transient solver. However, this stationary calculation is not necessarily correct, so one should always use good engineering judgements to assess the stationary solution before proceeding.
Copy the tutorial files over in a folder of your choice, rename the all folders so that their names get the correct meaning. The airfoil_simpleFoam/system/createPatchDict
and the airfoil_pimpleFoam/constant/dynamicMeshDict
can be deleted, because these functions will not be used here.
Terminal window
cp $FOAM_TUTORIALS/incompressible/pimpleDyMFoam/wingMotion ./ mv wingMotion airfoil cd airfoil mv wingMotion2D_snappyHexMesh airfoil_snappyHexMesh mv wingMotion2D_simpleFoam airfoil_simpleFoam mv wingMotion2D_pimpleDyMFoam airfoil_pimpleFoam rm airfoil_simpleFoam/system/createPatchDict rm airfoil_pimpleFoam/constant/dynamicMeshDict |
2: Geometry and mesh
Before starting with the geometry definitions and mesh generation you might want to read about NACA airfoils at Wikipedia. As the geometry of NACA airfoils is described by polynomial expressions, the coordinates of the airfoil surface can easily be calculated. It is not difficult to create a triangulated surface of this geometry:
To save you for a lot of typing, an Octave-script NACA2STL.m has been made. Download the script into a directory of your choice and make it executable.:
Terminal window
chmod +x NACA2STL.m ./NACA2STL.m |
These commands will create a STL-file of a NACA0015 airfoil with a chord length of 1 meter. You might need to edit the first line in the script (#!/usr/bin/octave -qf
) if your Octave-binary is located in another location than /usr/bin
. Put the file in the airfoil_snappyHexMesh/constant/triSurface
directory.
The next step is to prepare for the meshing with snappyHexMesh. As the airfoil has a sharp edge of significant importance, we must make sure that this is resolved perfectly. To do this we will use the new edge handling feature. The first step is to extract the edges from the STL-file. That is done with the surfaceFeatureExtract
-tool:
Terminal window
cd airfoil_snappyHexMesh surfaceFeatureExtract -includedAngle 150 -writeObj constant/triSurface/airfoil.stl airfoil |
This will automatically detect any edges and produce a file constant/triSurface/airfoil.eMesh
containing these. Then snappyHexMesh will need to be "informed" about the edges and that it should snap to them. Make sure that the features
-section of system/snappyHexMeshDict
looks like:
system/snappyHexMeshDict
features ( { file "airfoil.eMesh"; level 0; } ); |
and that under snapControls
there is a parameter
system/snappyHexMeshDict
//- Highly experimental and wip: number of feature edge snapping // iterations. Leave out altogether to disable. nFeatureSnapIter 10; |
Exercise 2a
The NACA2STL.m script will create a solid for any NACA 4-digit airfoil. All parameters are for simplicity located in the top of the script itself. Try to adjust the parameters and follow the changes. The STL file can always be inspected in ParaView.
Exercise 2b
Modify the rest of snappyHexMeshDict to suit your needs. Change refinement levels, regions, surfaces and layers to create a good mesh. Adjust the mesh resolution to the available computational resources. You will typically create a finer mesh if you have a desktop or workstation available than if you just have a laptop.
Exercise 2c
Define the boundary conditions for all the necessary patches:
- inlet
- outlet
- topAndBottom
- airfoil
Remove all references to patches not present in this analysis, and make good judgements on the turbulent quantities k and omega. You can use the CFD-online turbulence properties calculator as an aid. Pick a Reynolds number to use in the simulation and calculate the velocity based on the chord length and kinematic viscosity. The kinematic viscosity in the wingMotion
tutorial we started from is 1e-5 m^2/s (if you change this you must change it in both the airfoil_simpleFoam
and airfoil_pimpleFoam
folders).
For the rest of this tutorial the following values are used, however, you are of course free to use your own choices:
Airfoil | NACA0015 |
---|---|
Chord length | 1 m |
Span | 1 m |
Angle of attack | 5 degrees |
Reynolds number | 2.00e6 |
Freestream velocity | 2.00 m/s |
Kinematic viscosity | 1e-6 m^2/s |
Fluid density | 1000 kg/m^3 |
This parameters is chosen more or less "randomly" (based on the experience that the NACA0015 airfoil is widely used in research), but you should keep in mind that we are (for the purpose of learning) going to use a stationary solver on the problem to create an initial condition for the transient solver. If you choose a too high angle of attack, vortex shedding and other transient phenomena will occur, and the steady-state solver will never converge.
3: Calculation of forces and force coefficients
One of the goals of (nearly) all airfoil calculations are to calculate lift and drag. OpenFOAM handle this very smoothly. Add a section in the bottom of the controlDict
-files located in both the airfoil_simpleFoam/system
and airfoil_pimpleFoam/system
directories:
system/controlDict
functions { #include "forceCoeffs" } |
If you are familiar with the C/C++ preprocessor #include
-statement, you will recognize the syntax used here.
Then create a new file forceCoeffs
in both {[airfoil_simpleFoam/system}} and airfoil_pimpleFoam/system
with the contents:
system/forceCoeffs
forces { type forces; functionObjectLibs ("libforces.so"); outputControl timeStep; outputInterval 1; patches ( "airfoil.*" ); pName p; UName U; rhoName rhoInf; log true; CofR (0.25 0 0); rhoInf 1000; } forceCoeffs { type forceCoeffs; functionObjectLibs ( "libforces.so" ); outputControl timeStep; outputInterval 1; patches ( "airfoil.*" ); pName p; UName U; rhoName rhoInf; log true; liftDir (0 0 1); dragDir (-1 0 0); CofR (0.25 0 0); pitchAxis (0 1 0); magUInf 2.00; rhoInf 1000; lRef 1; Aref 1; } |
The contents should be self-explanatory. It specifies the lift and drag direction and the values used when calculating the non-dimensional lift and drag coefficient:
It also calculates a pitching-moment coefficient based on the moment Q around the axis specified by CofR
and pitchAxis
.
The forces and force coefficients will be recorded every timestep, independent on the writeInterval
-setting. The recorded values will be put in two files one foeceCoeffs/0/forceCoeffs.dat
and forces/0/forces.dat
in the two solver directories.
4: Running simpleFoam
As previously mentioned simpleFoam is a stationary solver. It's job is to find a reasonable initial condition for the transient analysis. Since it is a stationary solver the time steps does not influence the analysis. The default value is a time step of 1 and an end time of 3000. This means that the solver will iterate 3000 times and then stop.
In recent versions of OpenFOAM there is also another way of stopping the simpleFoam solver. You can specify the residuals at witch the solver shall stop. This threshold is specified in system/fvSolution
:
system/fvSolution
SIMPLE { nNonOrthogonalCorrectors 0; pRefCell 0; pRefValue 0; residualControl { p 1e-5; U 1e-5; "(k|omega)" 1e-5; } } |
The solver will now run until all the residuals at the start of a new iteration have reached 1e-5 or a maximum of 3000 iterations, whichever comes first. When you specify the residuals you might also want to increase the stop time to a lot higher value, for example 10 000. You should however not set this extremely high, as there is a chance that the simulation never will converge, for example if the problem is unsteady (vortex shedding etc).
You are now ready to start the stationary solver. We won't run this in parallel, as it is a relatively simple and quick calculation:
Terminal window
simpleFoam |
When the analysis are finished, inspect the results in ParaView. You should also check the force coefficients and make sure they are within reasonable ranges.
Remember that this is a stationary solver only, and the purpose is to find a flow field that we can use as an initial condition for the transient solver. Do not expect accurate results! When the solution is complete, you should verify that the results look reasonable. If they don't, just leave them. simpleFoam will not always converge toward the desired solution, especially if the problem in reality is transient with for example vortex shedding.
5: Running pimpleFoam
When simpleFoam has finished, you must copy the mesh and boundary conditions from the airfoil_simpleFoam
directory to the airfoil_pimpleFoam
directory:
Terminal window
cd airfoil_pimpleFoam cp -r ../airfoil_simpleFoam/constant/polyMesh/* constant/polyMesh/ cp -r ../airfoil_simpleFoam/0.org ./ cp 0.org/* 0/ |
Now here comes the crux of this tutorial: mapping the results from simpleFoam into an initial condition for pimpleFoam to start on. If the results from simpleFoam for any reason does not look physical or in any other way seems unusable, skip this step (pimpleFoam will just need a bit longer calculation time):
Terminal window
mapFields ../airfoil_simpleFoam -sourceTime latestTime -consistent |
Now you can open the case in Paraview, and inspect the initial flow field.
The last and final step is to decompose the case and run pimpleFoam:
Terminal window
decomposePar mpirun -np 4 pimpleFoam -parallel |
That's it! You can now plot the lift and drag coefficients with gnuplot or similar tools.
6: Download
You can download all necessary case files in airfoil.tar.gz. Scripts to run and clean the case is also attached.
7: Alternative meshing
If you think the snappyHexMesh and extrudeMesh procedure is too cumbersome, it is actually not very difficult to create a C-mesh around the foil just by using blockmesh:
We have again prepared a little octave script for you that will create the necessary blockMeshDict
-file for you. You should experiment with expansion rates, resolutions and other settings to get the best possible mesh. This meshing is a general procedure, and will work best with foils at low angles of attack.