OpenFOAM Programming Tutorial
OpenFOAM Programming Tutorial
CHALMERS
POLITECNICO DI MILANO
Outline
CHALMERS
Overview of the OpenFOAM structure A look at icoFoam Customizing an application Implementing a transport equation in a new application Customizing a boundary condition General information
POLITECNICO DI MILANO
Structure of OpenFOAM
CHALMERS
The OpenFOAM code is structures as follows (type foam and then ls).
applications: source les of all the executables:
Doxygen Guides-a4
lib: compiled libraries. src: source library les. test: library test source les. tutorials: tutorial cases. wmake: compiler settings.
POLITECNICO DI MILANO
Structure of OpenFOAM
Navigating the source code
CHALMERS
Environment variables:
= = = =
Efcient and customised top-level solver for class of physics. Ready to run in a manner of commercial CFD/CCM software Example of OpenFOAM classes and library functionality in use
POLITECNICO DI MILANO
Walk through a simple solver
Solver walk-through: icoFoam
Types of les
CHALMERS
Header les Located before the entry line of the executable int main(int argc, char* argv[]) Contain various class denitions Grouped together for easier use
Include les Often repeated code snippets, e.g. mesh creation, Courant number calculation and similar Held centrally for easier maintenance Enforce consistent naming between executables, e.g. mesh, runTime
Local implementation les Main code, named consistently with the executable createFields.H
POLITECNICO DI MILANO
Walk through icoFoam
File organization sol cd incompressible cd icoFoam
The icoFoam directory consists of what follows (type ls):
CHALMERS
createFields.H
FoamX/
icoFoam.C
icoFoam.dep
Make/
The FoamX directory is for pre-processing. The Make directory contains instructions for the wmake compilation command. icoFoam.C is the main le, while createFields.H is included by icoFoam.C. The le fvCFD.H,included by icoFoam.C, contains all the class denitions
which are needed by icoFoam. See the le Make/options to understand where fvCFD.H is included from:
where in PATH a le name containing LETTERSFILENAME in its le name is located. Example: find $WM_PROJECT_DIR -iname "*fvCFD.H*"
POLITECNICO DI MILANO
Walk through icoFoam
A look into fvCFD.H
CHALMERS
#ifndef fvCFD_H #define fvCFD_H #include "parRun.H" #include #include #include #include #include #include #include #include #include #include #include "Time.H" "fvMesh.H" "fvc.H" "fvMatrices.H" "fvm.H" "linear.H" "calculatedFvPatchFields.H" "fixedValueFvPatchFields.H" "adjustPhi.H" "findRefCell.H" "mathematicalConstants.H"
#include "OSspecific.H" #include "argList.H" #ifndef namespaceFoam #define namespaceFoam using namespace Foam; #endif #endif The inclusion les before main are all the class denitions required by icoFoam. Have a look into the source les to understand what these classes do.
POLITECNICO DI MILANO
Walk through icoFoam
CHALMERS
int main(int argc, char *argv[]) where int argc and char *argv[] are the number of parameters and the actual parameters used when running icoFoam.
The case is initialized by:
# # # # #
include "setRootCase.H" include include include include "createTime.H" "createMesh.H" "createFields.H" "initContinuityErrs.H"
variables used in icoFoam. Have a look inside it and see how variables are created.
POLITECNICO DI MILANO
Walk through icoFoam
A look into icoFoam.C, time-loop code
The time-loop starts by:
CHALMERS
for (runTime++; !runTime.end(); runTime++) and the rest is done at each time-step
The fvSolution subdictionary PISO is read, and the Courant Number is
calculated and written to the screen by (use the find command): # include "readPISOControls.H" # include "CourantNo.H"
The momentum equations are dened and a velocity predictor is solved by:
fvVectorMatrix UEqn ( fvm::ddt(U) + fvm::div(phi, U) - fvm::laplacian(nu, U) ); solve(UEqn == programming -fvc::grad(p)); Tommaso Lucchini/ OpenFOAM tutorial
POLITECNICO DI MILANO
Walk through icoFoam
A look into icoFoam.C, the PISO loop
CHALMERS
A() returns the central coefcients of an fvVectorMatrix H() returns the H operation source of an fvVectorMatrix Sf() returns cell face area vector of an fvMesh flux() returns the face ux eld from an fvScalarMatrix correctBoundaryConditions() corrects the boundary elds of a volVectorField
Identify the object types (classes) and use the OpenFOAM Doxygen
POLITECNICO DI MILANO
Walk through icoFoam
A look into icoFoam.C, write statements
CHALMERS
runTime.write(); Info<< "ExecutionTime = " << runTime.elapsedCpuTime() << " s" << " ClockTime = " << runTime.elapsedClockTime() << " s" << nl << endl;
write() makes sure that all the variables that were dened as an IOobject
with IOobject::AUTO_WRITE are written to the time directory according to the settings in the $FOAM_CASE/system/controlDict le.
elapsedCPUTime() is the elapsed CPU time. elapsedClockTime() is the elapsed wall clock time.
POLITECNICO DI MILANO
OpenFOAM work space
General information
CHALMERS
OpenFOAM is a library of tools, not a monolithic single-executable Most changes do not require surgery on the library level: code is developed in
functionality is unaffected
Local workspace:
Run directory: $FOAM_RUN. Ready-to-run cases and results, test loop etc. May contain case-specic setup tools, solvers and utilities. Local work space: /home/tommaso/OpenFOAM/tommaso-1.4.1-dev/. Contains applications, libraries and personal library and executable space.
POLITECNICO DI MILANO
Creating your OpenFOAM applications
CHALMERS
1. Find appropriate code in OpenFOAM which is closest to the new use or provides a starting point 2. Copy into local work space and rename 3. Change le name and location of library/executable: Make/les 4. Environment variables point to local work space applications and libraries: $FOAM_PROJECT_USER_DIR, $FOAM_USER_APPBIN and $FOAM_USER_LIBBIN 5. Change the code to t your needs
POLITECNICO DI MILANO
myIcoFoam
CHALMERS
cd $WM_PROJECT_DIR/applications/solvers/incompressible
$WM_PROJECT_USER_DIR/applications directory cp -r icoFoam $WM_PROJECT_DIR/applications Rename the directory and the source le name, clean all the dependancies and
necessary.
POLITECNICO DI MILANO
Creating your OpenFOAM applications
CHALMERS
Example:
Creating the application icoScalarTransportFoam. It is an incompressible
POLITECNICO DI MILANO
icoScalarTransportFoam
CHALMERS
cd $WM_PROJECT_DIR/applications/solvers/incompressible
POLITECNICO DI MILANO
icoScalarTransportFoam
Physical/numerical model modeling
CHALMERS
We want to solve the following transport equation for the scalar eld T It is an unsteady, convection-diffusion transport equation. is the kinematic
viscosity. T + ( U T ) ( T ) = 0 t
What to do:
(1)
Create the geometric eld T in the createFields.H le Solve the transport equation for T in the icoScalarTransportFoam.C le.
POLITECNICO DI MILANO
icoScalarTransportFoam
Creating the eld T
CHALMERS
#include
"createPhi.H":
Info<< "Reading field T\n" << endl; volScalarField T ( IOobject ( "T", runTime.timeName(), mesh, IOobject::MUST_READ, IOobject::AUTO_WRITE ), mesh );
POLITECNICO DI MILANO
icoScalarTransportFoam
Creating the eld T
We have created a volScalarField object called T.
CHALMERS
runTime.timeName() directory. At the beginning of the simulation, runTime.timename() is the startTime value specied in the controlDict le.
T will be automatically written (IOobject::AUTO_WRITE) in the
It has as many internal values (internalField) as the number of mesh cells It needs as many boundary conditions (boundaryField) as the mesh boundaries specied in the constant/polyMesh/boundary le of the case.
POLITECNICO DI MILANO
icoScalarTransportFoam
Solving the transport equation for T
CHALMERS
for (int corr=0; corr<nCorr; corr++) { # include "TEqn.H" volScalarField rUA = 1.0/UEqn.A();
icoScalarTransportFoam...
POLITECNICO DI MILANO
icoScalarTransportFoam
Solving the transport equation for T
This the transport equation:
CHALMERS
T + ( U T ) ( T ) = 0 t
This is how we implement and solve it in TEqn.H
POLITECNICO DI MILANO
icoScalarTransportFoam
icoScalarTransportFoam: setting up the case
CHALMERS
Copy the cavity tutorial case in your $FOAM_RUN directory and rename it
POLITECNICO DI MILANO
icoScalarTransportFoam
Running the application - case setup - startTime
Modify T as follows: dimensions [0 0 0 0 0 0 0]; internalField uniform 0; boundaryField { movingWall { type fixedValue; value uniform 1; } fixedWalls { type fixedValue; value uniform 0; } frontAndBack { type empty; } }
CHALMERS
POLITECNICO DI MILANO
icoScalarTransportFoam
CHALMERS
scheme for laplacian(nu,T) laplacianSchemes { default none; laplacian(nu,U) Gauss linear corrected; laplacian((1|A(U)),p) Gauss linear corrected; laplacian(nu,T) Gauss linear corrected; }
POLITECNICO DI MILANO
icoScalarTransportFoam
CHALMERS
DILU;
0; 500; 1e-05; 0;
POLITECNICO DI MILANO
icoScalarTransportFoam
icoScalarTransportFoam: post-processing
Run the case
CHALMERS
icoScalarTransportFoam . cavityScalarTranport
Nice picture:
POLITECNICO DI MILANO
Implementing a new boundary condition
General information
CHALMERS
Run-Time Selection Table Functionality In many cases, OpenFOAM provides functionality selectable at run-time which needs to be changed for the purpose. Example: viscosity model; ramped xed value boundary conditions New functionality should be run-time selectable (like implemented models) . . . but should not interfere with existing code! There is no need to change existing library functionality unless we have found bugs For the new choice to become available, it needs to be instantiated and linked with the executable. Boundary Condition: Ramped Fixed Value Find closest similar boundary condition: oscillatingFixedValue Copy, rename, change input/output and functionality. Follow existing code patterns Compile and link executable; consider relocating into a library Beware of the defaultFvPatchField problem: verify code with print statements
POLITECNICO DI MILANO
Implementing a new boundary condition
What rampedFixedValue should do
CHALMERS
10 high ref. value 8 End ramp 6 4 low ref. value 2 Start ramp 0 0 3 6 Time 9 12
data
POLITECNICO DI MILANO
Implementing a new boundary condition
In a new application icoFoamRamped
CHALMERS
cp $FOAM_SOLVERS/compressible/icoFoam \
$FOAM_USER_DIR/applications/icoFoamRamped
Copy the content of
$FOAM_SRC/fields/fvPatchFields/derived/oscillatingFixedValue/ to $WM_PROJECT_USER_DIR/applications/icoFoamRamped/
Change the le names
mv mv mv mv oscillatingFixedValueFvPatchField.C oscillatingFixedValueFvPatchField.H oscillatingFixedValueFvPatchFields.C oscillatingFixedValueFvPatchFields.H rampedFixedValueFvPatchField.C rampedFixedValueFvPatchField.H rampedFixedValueFvPatchFields.C rampedFixedValueFvPatchFields.H
wclean
POLITECNICO DI MILANO
Implementing a new boundary condition
rampedFixedValueFvPatchField.H
CHALMERS
Template class, contains the class denition for the generic objects. Replace the string oscillating with the string ramped (use the replace
function of any text editor with the case sensitive option. This has the following effects:
The new class begins with #ifndef rampedFixedValueFvPatchField_H #define rampedFixedValueFvPatchField_H Class declaration template<class Type> class rampedFixedValueFvPatchField Objects we need: Reference value low bound Field<Type> refValueLow_; Reference value high bound Field<Type> refValueHigh_; Ramp start time scalar startRamp_; Ramp end time scalar endRamp_; Current time index label curTimeIndex_;
POLITECNICO DI MILANO
Implementing a new boundary condition
rampedFixedValueFvPatchField.H
All the constructors
CHALMERS
//- Construct from patch and internal field rampedFixedValueFvPatchField ( const fvPatch&, const DimensionedField<Type, volMesh>& ); // other constructors //- Construct from patch, internal field and dictionary //- Construct by mapping given rampedFixedValueFvPatchField // onto a new patch //- Construct as copy //- Construct and return a clone //- Construct as copy setting internal field reference //- Construct and return a clone setting internal field reference
Private member function to evaluate the boundary condition: currentScale() Provide member functions to access them (const/non const)
//- Return the ref value Field<Type>& refValueHigh() { return refValueHigh_; }
POLITECNICO DI MILANO
Implementing a new boundary condition
rampedFixedValueFvPatchField.H
Other member functions:
CHALMERS
Mapping
//- Map (and resize as needed) from self given a mapping object virtual void autoMap ( const fvPatchFieldMapper& ); //- Reverse map the given fvPatchField onto this fvPatchField virtual void rmap ( const fvPatchField<Type>&, const labelList& );
Write to le:
virtual void write(Ostream&) const;
POLITECNICO DI MILANO
Implementing a new boundary condition
rampedFixedValueFvPatchField.C
CHALMERS
Constructors Private member functions: Access (if not dened in the .H le) Map Evaluation Write
POLITECNICO DI MILANO
Implementing a new boundary condition
rampedFixedValueFvPatchField.C - Constructors
template<class Type> rampedFixedValueFvPatchField<Type>::rampedFixedValueFvPatchField ( const fvPatch& p, const Field<Type>& iF, const dictionary& dict ) : fixedValueFvPatchField<Type>(p, iF), refValueLow_("refValueLow", dict, p.size()), refValueHigh_("refValueHigh", dict, p.size()), startRamp_(readScalar(dict.lookup("startRamp"))), endRamp_(readScalar(dict.lookup("endRamp"))), curTimeIndex_(-1) { Info << "Hello from ramp! startRamp: " << startRamp_ << " endRamp: " << endRamp_ << endl; if (dict.found("value")) { fixedValueFvPatchField<Type>::operator== ( Field<Type>("value", dict, p.size()) ); } else { fixedValueFvPatchField<Type>::operator== ( refValueLow_ + (refValueHigh_ - refValueLow_)*currentScale() ); } }
CHALMERS
POLITECNICO DI MILANO
Implementing a new boundary condition
CHALMERS
at time t:
template<class Type> scalar rampedFixedValueFvPatchField<Type>::currentScale() const { return min ( 1.0, max ( (this->db().time().value() - startRamp_)/ (endRamp_ - startRamp_), 0.0 ) ); }
POLITECNICO DI MILANO
Implementing a new boundary condition
rampedFixedValueFvPatchField.C - updateCoeffs()
updateCoeffs(): evaluates the boundary conditions
CHALMERS
// Update the coefficients associated with the patch field template<class Type> void rampedFixedValueFvPatchField<Type>::updateCoeffs() { if (this->updated()) { return; } if (curTimeIndex_ != this->db().time().timeIndex()) { Field<Type>& patchField = *this; patchField = refValueLow_ + (refValueHigh_ - refValueLow_)*currentScale(); curTimeIndex_ = this->db().time().timeIndex(); } fixedValueFvPatchField<Type>::updateCoeffs(); }
POLITECNICO DI MILANO
Implementing a new boundary condition
CHALMERS
This function writes to a le os the boundary condition values. Useful when the
POLITECNICO DI MILANO
Implementing a new boundary condition
rampedFixedValueFvPatchFields.H
CHALMERS
POLITECNICO DI MILANO
Implementing a new boundary condition
rampedFixedValueFvPatchFields.C
CHALMERS
POLITECNICO DI MILANO
Implementing a new boundary condition
In the solver, modication of Make/files
CHALMERS
icoFoamRamped application.
POLITECNICO DI MILANO
Implementing a new boundary condition
In a dynamic library
CHALMERS
If all the user-dened boundary conditions were put in a library, they will be
Compile the library in the $WM_PROJECT_USER_DIR/myBCs with the command wmake libso
POLITECNICO DI MILANO
Implementing a new boundary condition
In a dynamic library, to be used by the solvers
CHALMERS
The boundary condition will not be recognized by any of the original OpenFOAM
solvers unless we tell OpenFOAM that the library exists. In OpenFOAM-1.4.1 this is done by adding a line in the system/controlDict le: libs ("libMyBCs.so"); i.e. the library must be added for each case that will use it, but no re-compilation is needed for any solver. libMyBCs.so is found using the LD_LIBRARY_PATH environment variable, and if you followed the instructions on how to set up OpenFOAM and compile the boundary condition this should work automatically.
You can now set up the case as we did earlier and run it using the original
icoFoam solver. icoFoam does not need to be recompiled, since libMyBCs.so is linked at run-time using dlopen.
Example. Solve the cavity tutorial with the user dened library of boundary
conditions.
POLITECNICO DI MILANO
Some programming guidelines
CHALMERS
OpenFOAM And Object-Orientation OpenFOAM library tools are strictly object-oriented: trying hard to weed out the hacks, tricks and work-arounds
Adhering to standard is critical for quality software development in C++: ISO/IEC 14882-2003 incorporating the latest Addendum notes
Writing C in C++ C++ compiler supports the complete C syntax: writing procedural programming in C is very tempting for beginners
Object Orientation represents a paradigm shift: the way the problem is approached needs to be changed, not just the programming language. This is not easy Some benets of C++ (like data protection and avoiding code duplication) may seem a bit esoteric, but they represent a real qualitative advantage 1. Work to understand why C++ forces you to do things 2. Adhere to the style even if not completely obvious: ask questions, discuss 3. Play games: minimum amount of code to check for debugging :-) 4. Analyse and rewrite your own work: more understanding leads to better code 5. Try porting or regularly use multiple compilers 6. Do not tolerate warning messages: they are really errors!
POLITECNICO DI MILANO
Enforcing consistent style
Writing Software In OpenFOAM Style
CHALMERS
OpenFOAM library tools are strictly object-oriented; top-level codes are more in functional style, unless implementation is wrapped into model libraries OpenFOAM uses ALL features of C++ to the maximum benet: you will need to learn it. Also, the code is an example of good C++: study and understand it
POLITECNICO DI MILANO
Debugging OpenFOAM
CHALMERS
Build and Debug Libraries Release build optimised for speed of execution; Debug build provides additional
run-time checking and detailed trace-back capability Using trace-back on failure gdb icoFoam: start debugger on icoFoam executable r <root> <case>: perform the run from the debugger where provides full trace-back with function names, le and line numbers Similar tricks for debugging parallel runs: attach gdb to a running process
Debug switches
Each set of classes or class hierarchy provides own debug stream . . . but complete ow of messages would be overwhelming! Choosing debug message source: $HOME/.OpenFOAM-1.4/controlDict
POLITECNICO DI MILANO
OpenFOAM environment
Environment Variables and Porting
CHALMERS
Software was developed on multiple platforms and ported regularly: better quality and adherence to standard Switching environment must be made easy: source single dot-le All tools, compiler versions and paths can be controlled with environment variables Environment variables Environment setting support one installation on multiple machines User environment: $HOME/.OpenFOAM-1.4/cshrc. Copied from OpenFOAM installation for user adjustment OpenFOAM tools: OpenFOAM-1.4/.cshrc Standard layout, e.g. FOAM_SRC, FOAM_RUN Compiler and library settings, communications library etc. Additional setting FOAM_ABORT: behaviour on abort FOAM_SIGFPE: handling oating point exceptions FOAM_SETNAN: set all memory to invalid on initialisation
POLITECNICO DI MILANO
OpenFOAM environment
CHALMERS
OpenFOAM Programming
OpenFOAM is a good and complete example of use of object orientation and C++ Code layout designed for multiple users sharing a central installation and developing tools in local workspace Consistent style and some programming guidelines available through le stubs: foamNew script for new code layout Most (good) development starts from existing code and extends its capabilities Porting and multiple platform support handled through environment variables