Professional Documents
Culture Documents
Basic Programming in OpenFOAM
Basic Programming in OpenFOAM
Basic Programming in OpenFOAM
Module 7 - Lecture 1
Roadmap
1. Programming in OpenFOAM.
Building blocks.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
During this session we will study the building blocks to write basic
programs OpenFOAM:
First, we will start by taking a look at the algebra of tensors in
OpenFOAM.
Then, we will take a look at how to generate tensor fields from tensors.
Next, we will learn how to access mesh information.
Finally we will see how to discretize a model equation and solve the
linear system of equations using OpenFOAM classes and templates.
And of course, we are going to program a little bit in C++. But do not be
afraid, after all this is not a C++ course.
0 Scalar scalar
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Basic tensor classes in OpenFOAM
In OpenFOAM, the second rank tensor (or matrix)
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Basic tensor classes in OpenFOAM
For instance, the following statement,
$> Txz = 3
Notice that to output information to the screen in OpenFOAM, we use the function
Info instead of the function cout (used in standard C++).
The function cout will work fine, but it will give you problems when running in parallel.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Algebraic tensor operations in OpenFOAM
Tensor operations operate on the entire tensor entity.
OpenFOAM syntax closely mimics the syntax used in written mathematics, using descriptive
functions (e.g. mag) or symbolic operators (e.g. +).
OpenFOAM also follow the standard rules of linear algebra when working with tensors.
Some of the algebraic tensor operations are listed in the following table (where a and b are
vectors, s is a scalar, and T is a tensor).
Mathematical OpenFOAM
Operation Remarks
description description
Addition a+b a + b
Scalar multiplication sa s * a
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Dimensional units in OpenFOAM
Units are defined using the dimensionSet class tensor, with its units defined using
the dimensioned<Type> template class, the <Type> being scalar, vector, tensor,
etc. The dimensioned<Type> stores the variable name, the dimensions and the
tensor values.
For example, a tensor with dimensions is declare in the following way:
1 dimensionedTensor sigma
2 (
3 sigma,
4 dimensionSet(1, -1, -2, 0, 0, 0, 0),
5 tensor(10e6,0,0,0,10e6,0,0,0,10e6)
6 );
1 dimensionedTensor sigma
2 (
3 sigma,
4 dimensionSet(1, -1, -2, 0, 0, 0, 0),
5 tensor(10e6,0,0,0,10e6,0,0,0,10e6)
6 );
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Units correspondence in dimensionSet
1 Mass kilogram kg
2 Length meters m
3 Time second s
4 Temperature Kelvin K
6 Current ampere A
Luminuous
7 candela cd
intensity
#include dimensionedTensor.H
...
...
...
dimensionedTensor sigma
(
"sigma",
dimensionSet(1, -1, -2, 0, 0, 0, 0),
tensor(1e6,0,0,0,1e6,0,0,0,1e6)
);
Info<< "Sigma: " << sigma << endl;
...
...
...
As for base tensors, you can access the information of dimensioned tensors.
For example, to access the name, dimensions, and values of a dimensioned tensor,
you can proceed as follows:
Note that the value() member function first converts the expression to a tensor, which
has a yy() member function.
The dimensionedTensor class does not have a yy() member function, so it is not
possible to directly get its value by using sigma.yy().
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
OpenFOAM lists and fields
OpenFOAM frequently needs to store sets of data and perform mathematical
operations.
OpenFOAM therefore provides an array template class List<Type>, making it
possible to create a list of any object of class Type that inherits the functions of the
Type. For example a List of vector is List<vector>.
Lists of the tensor classes are defined in OpenFOAM by the template class
Field<Type>.
For better code legibility, all instances of Field<Type>, e.g. Field<vector>, are
renamed using typedef declarations as scalarField, vectorField, tensorField,
symmTensorField, tensorThirdField and symmTensorThirdField.
You can find the flied classes in the directory
$FOAM_SRC/OpenFOAM/fields/Fields.
Algebraic operations can be performed between Field subject to obvious restrictions
such as the fields having the same number of elements.
OpenFOAM also supports operations between a field and a zero rank tensor, e.g.
all values of a Field U can be multiplied by the scalar 2 by simple coding the
following line, U = 2.0 * U.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Construction of a tensor field in OpenFOAM
To create fields, you need to access the tensor class.
To do so, just add the header file tensorField.H to your program. This class
inherit all the tensor algebra.
#include "tensorField.H"
...
...
...
tensorField tf1(2, tensor::one);
Info<< "tf1: " << tf1 << endl;
tf1[0] = tensor(1, 2, 3, 4, 5, 6, 7, 8, 9);
Info<< "tf1: " << tf1 << endl;
Info<< "2.0*tf1: " << 2.0*tf1 << endl;
...
...
...
In this example, we created a list of two tensor fields (tf1), and both tensor are
initialized to one.
We can access components on the list using the access operator [ ].
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Example of use of tensor and field classes
In the directory $PTOFC/programming_playground/my_tensor you will find a
tensor class example.
The original example is located in the directory
$PTOFC/programming_playground/test/tensor. Feel free to compare the
files to spot the differences.
Before compiling the file, let us recall how applications are structure,
working_directory/
applicationName.C
header-files.H
Make
files
options
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Example of use of tensor and field classes
Before compiling the file, let us recall how applications are structure.
working_directory/
applicationName.C
header-files.H
Make
files
options
The Make directory contains compilation instructions.
files: names all the source files (.C), it specifies the name of the new application and
the location of the output file.
options: specifies directories to search for include files and libraries to link the solver
against.
At the end of the file files, you will find the following line of code,
EXE = $(FOAM_USER_APPBIN)/my_Test-tensor
This is telling the compiler to name your application my_Test-tensor and to copy the executable
in the directory $FOAM_USER_APPBIN.
To avoid conflicts between applications, always remember to give a proper name and a location
to your programs and libraries.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Example of use of tensor and field classes
Let us now compile the tensor class example. Type in the terminal:
1. $> cd $PTOFC/programming_playground/my_tensor
2. $> wmake
3. $> my_Test-tensor
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
At this point, we are a little bit familiar with tensor, fields, and lists in
OpenFOAM.
They are the base to building applications in OpenFOAM.
Let us now take a look at the whole solution process:
Creation of the tensors.
Mesh assembly.
Fields creation.
Equation discretization.
All by using OpenFOAM classes and template classes
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Discretization of a tensor field in OpenFOAM
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Discretization of a tensor field in OpenFOAM
The template class geometricField relates a tensor
field to a fvMesh.
Using typedef declarations geometricField is renamed
to volField (cell center), surfaceField (cell faces), and
pointField (cell vertices).
You can find the geometricField classes in the directory
$FOAM_SRC/OpenFOAM/fields/GeometricFields.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Data stored in the fvMesh class
Access
Class Description Symbol
function
volScalarField Cell volumes V()
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Accessing fields defined in a mesh
To access fields defined at cell centers of the mesh you need to use the class
volField.
The class volField can be accessed by adding the header volFields.H to your
program.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Accessing fields using for loops
To access fields using for loops, we can use OpenFOAM macro forAll, as follows,
Outputs name of patch
forAll(mesh.boundaryMesh(), patchI)
Info << "Patch " << patchI << ": " << mesh.boundary()[patchI].name() << " with "
<< mesh.boundary()[patchI].Cf().size() << " faces. Starts at total face "
<< mesh.boundary()[patchI].start() << endl;
Outputs size of patch (number of faces)
In the previous statement mesh.boundaryMesh() is the size of the loop, and patchI
is the iterator. The iterator always starts from zero.
The forAll loop is equivalent to the standard for loop in C++.
Notice that we used as iterator i instead of patchI, this does not make any
difference. Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Equation discretization in OpenFOAM
In this phase, OpenFOAM converts the PDEs into a set of linear algebraic
equations, A x = b, where x and b are volFields (geometricField).
A is a fvMatrix, which is created by the discretization of a geometricField and
inherits the algebra of its corresponding field, and it supports many of the standard
algebraic matrix operations.
The fvm (finiteVolumeMethod) and fvc (finiteVolumeCalculus) classes contain
static functions for the differential operators, and discretize any geometricField.
fvm returns a fvMatrix, and fvc returns a geometricField.
In the directories $FOAM_SRC/finiteVolume/finiteVolume/fvc and
$FOAM_SRC/finiteVolume/finiteVolume/fvm you will find the respective
classes.
Remember, the PDEs or ODEs we want to solve involve derivatives of tensor fields
with respect to time and space. What we re doing at this point, is applying the finite
volume classes to the fields, and assembling a linear system.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Discretization of the basic PDE terms in OpenFOAM
The list is not complete
Mathematical fvm::
Term description
expression fvc::
laplacian(phi)
Laplacian , laplacian(Gamma, phi)
, ddt(phi)
Time derivative
ddt(rho,phi)
div(psi,scheme)
Convection ,
div(psi,phi)
Sp(rho,phi)
Source
SuSp(rho,phi)
Remember, you will need to first create the mesh, and initialize the variables and
constants. That is, all the previous steps.
Finally, everything we have done so far inherits all parallel directives. There is no
need for specific parallel programming.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Discretization of the basic PDE terms in OpenFOAM
The previous discretization is equivalent to,
Here, fvScalarMatrix contains the matrix derived from the discretization of the model
equation.
fvScalarMatrix is used for scalar fields and fvVectorMatrix is used for vector fields.
This syntax is more general, since it allows the easy addition of terms to the model
equations.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Discretization of the basic PDE terms in OpenFOAM
At this point, OpenFOAM assembles and solves the following linear system,
Module 7 - Lecture 1
Unknow quantity
Programming in OpenFOAM. Building blocks
Example of use of tensor and field classes
Let us study a fvMesh example. First let us compile the program my_Test-
mesh. Type in the terminal,
1. $> cd $PTOFC/programming_playground/my_mesh/
2. $> wmake
At this point, take a look at the output and study the file Test-mesh.C. Try to
understand what we have done.
FYI, the original example is located in the directory
$PTOFC/programming_playground/test/mesh.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
A few OpenFOAM programming references
You can access the API documentation in the following link,
https://cpp.openfoam.org/v4/
You can access the coding style guide in the following link,
https://openfoam.org/dev/coding-style-guide/
You can report programming issues in the following link,
https://bugs.openfoam.org/rules.php
You can access openfoamwiki coding guide in the following link,
http://openfoamwiki.net/index.php/OpenFOAM_guide
You can access the user guide in the following link,
https://cfd.direct/openfoam/user-guide/
You can read the OpenFOAM Programmers guide in the following link (it seems
that this guide is not supported anymore)
http://foam.sourceforge.net/docs/Guides-a4/ProgrammersGuide.pdf
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
A few good C++ references
The C++ Programming Language.
B. Stroustrup. 2013, Addison-Wesley.
The C++ Standard Library.
N. Josuttis. 2012, Addison-Wesley.
C++ for Engineers and Scientists.
G. J. Bronson. 2012, Cengage Learning.
Sams Teach Yourself C++ in One Hour a Day.
J. Liberty, B. Jones. 2004, Sams Publishing.
C++ Primer.
S. Lippman, J. Lajoie, B. Moo. 2012, Addison-Wesley.
http://www.cplusplus.com/
http://www.learncpp.com/
http://www.cprogramming.com/
http://www.tutorialspoint.com/cplusplus/
http://stackoverflow.com/
Module 7 - Lecture 1
Module 7
Basic programming in OpenFOAM
codeStream
Lecture 2
Module 7 - Lecture 2
Roadmap
1. Highlights
2. Implementing boundary conditions using
codeStream
3. Solution initialization using codeStream
4. High level programming
Module 7 - Lecture 2
Highlights
There are many boundary conditions available in OpenFOAM.
But from time to time, it may happen that you do not find what you are looking for.
It is possible to implement your own boundary conditions, so in theory you can do whatever you
want.
Remember, we have the source code.
To implement your own boundary conditions, you have three options:
Use codeStream.
Use high level programing.
Use an external library (swak4foam).
codeStream is the simplest way to implement boundary conditions, and most of the times you
will able to code boundary conditions with no problem.
If you can not implement your boundary conditions using codeStream, you can use high level
programming. However, this requires some knowledge on C++ and OpenFOAM API library.
Hereafter, we are going to work with codeStream and basic high level programming.
We are not going to work with swak4Foam because it is an external library that is not officially
supported by the OpenFOAM foundation. However, we have to say that it works very well.
Module 7 - Lecture 2
Highlights
When it comes to initial conditions, you can use the utility setFields.
This utility is very flexible, you can even read STL files and use them to initialize fields.
But again, it may happen that you can not get the desired results.
As for boundary conditions, to implement your own initials conditions you have three options:
Use codeStream.
Use high level programing.
Use an external library (swak4foam).
codeStream is the simplest way to implement initial conditions, and most of the times you will
able to code initial conditions with no problem.
If you can not implement your initial conditions using codeStream, you can use high level
programming. However, this requires some knowledge on C++ and OpenFOAM API library.
Hereafter, we are going to work only with codeStream.
Using high level programming is a little bit more trickier, and we guarantee you that 99.9 % of
the time codeStream will work.
We are not going to work with swak4Foam because it is an external library that is not officially
supported by the OpenFOAM foundation. However, we have to say that it works very well.
Module 7 - Lecture 2
Highlights
You can access the API documentation in the following link,
https://cpp.openfoam.org/v4/
API documentation
Module 7 - Lecture 2
Roadmap
1. Highlights
2. Implementing boundary conditions using
codeStream
3. Solution initialization using codeStream
4. High level programming
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
OpenFOAM includes the capability to compile, load and execute C++ code
at run-time.
This capability is supported via the directive #codeStream, that can be used
in any input file for run-time compilation.
This directive reads the entries code (compulsory), codeInclude (optional),
codeOptions (optional), and codeLibs (optional), and uses them to
generate the dynamic code.
The source code and binaries are automatically generated and copied in the
directory dynamicCode of the current case.
The source code is compiled automatically at run-time.
The use of codeStream is a very good alternative to avoid high level
programming of boundary conditions or the use of external libraries.
Hereafter we will use codeStream to implement new boundary conditions,
but have in mind that codeStream can be used in any dictionary.
For example, you can use codeStream in the controlDict dictionary to
control the write interval.
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Body of the codeStream directive for boundary conditions
codeOptions
#{
-I$(LIB_SRC)/finiteVolume/lnInclude \ Compilation options
-I$(LIB_SRC)/meshTools/lnInclude
#};
code
#{ Insert your code here.
At this point, you need to know
#}; how to access mesh information
};
}
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a parabolic inlet profile using codeStream
Let us implement a parabolic inlet profile.
The firs step is identifying the patch, its location and the dimensions.
You can use paraview to get all visual references.
Outlet
pressure-outlet-7
Inlet
velocity-inlet-5
For this specific case c is the patch midpoint in the y direction (8), r is the patch
semi-height or radius (8) and Umax is the maximum velocity.
We should get a parabolic profile similar to this one,
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
The codeStream BC in the body of the file U is as follows,
code
#{ Insert your code here.
At this point, you need to know
#}; how to access mesh information
};
}
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
The code section of the codeStream BC in the body of the file U is as follows,
Lines 3-11, are always standard, they are used to access boundary mesh information.
In lines 3-6 we access the current dictionary.
In line 8 we access the mesh database.
In line 9 we get the label id (an integer) of the patch velocity-inlet-5 (notice that you need to give the name of
the patch).
In line 10 using the label id of the patch, we access the boundary mesh information.
In line 12 we initialize the vector field. The statement patch.size() gets the number of faces in the patch, and
the statement vector(0, 0, 0) initializes a zero Module
vector7 field in 2the patch.
- Lecture
Implementing boundary conditions using codeStream
The code section of the codeStream BC in the body of the file U is as follows,
1 code Index used to access the
2 #{ y coordinate
3 ... 0x
4 ...
5 ...
1y
6 const scalar pi = constant::mathematical::pi; 2z
7 const scalar U_0 = 2.; //maximum velocity
8 const scalar p_ctr = 8.; //patch center
9 const scalar p_r = 8.; //patch radius
10
11 forAll(U, i) //equivalent to for (int i=0; patch.size()<i; i++)
12 {
13 const scalar y = patch.Cf()[i][1];
14 U[i] = vector(U_0*(1-(pow(y - p_ctr,2))/(p_r*p_r)), 0., 0.);
15 }
16 Assign input profile to vector field U (component x)
17 U.writeEntry("", os);
18 #};
1. $> cd $PTOFC/101programming/codeStream_BC/2Delbow_UparabolicInlet
2. $> foamCleanTutorials
3. $> fluentMeshToFoam ../../../meshes_and_geometries/fluent_elbow2d_1/ascii.msh
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a parabolic inlet profile using codeStream
If everything went fine, you should get something like this
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
codeStream works with scalar and vector fields
We just implemented the input parabolic profile using a vector field.
You can do the same using a scalar field, just proceed in a similar way.
Remember, now we need to use scalars instead of vectors.
And you will also use an input dictionary holding a scalar field.
1 code
2 #{
3 ...
4 ...
5 ...
6 scalarField S(patch.size(), scalar(0) ); Initialize scalar field
7
8 forAll(S, i) Loop using scalar field size
9 {
10 const scalar y = patch.Cf()[i][1];
11 S[i] = scalar( 2.0*sin(3.14159*y/8.) );
Write profile values
12 } in scalar field
13
14 S.writeEntry("", os);
Write output to input
15 #}; dictionary
Notice that the name of the field does not need to be the same as the name of the input dictionary
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a paraboloid inlet profile using codeStream
Let us work in a case a little bit more complicated, a paraboloid input profile.
As usual, the first step is to get all the spatial references.
Inlet
auto3
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
The codeStream BC in the body of the file U is as follows,
codeLibs
#{
-lmeshTools \
-lfiniteVolume
#};
Insert your code here.
code
#{
We will implement the following
equation
#};
};
}
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Hereafter, we only show the actual implementation of the codeStream boundary
condition.
The rest of the body is a template that you can always reuse. Including the section of
how to access the dictionary and mesh information.
Remember, is you are working with a vector, you need to use vector fields. Whereas,
if you are working with scalars, you need to use scalars fields.
1 code
2 #{
3 ...
4 ...
5 ...
6 vectorField U(patch.size(), vector(0, 0, 0) ); Initialize vector field
7
8 const scalar s = 0.5; Initialize scalar
9
10 forAll(U, i)
11 {
12 const scalar x = patch.Cf()[i][0]; Access faces center
13 const scalar y = patch.Cf()[i][1]; coordinates (x, y, and z)
14 const scalar z = patch.Cf()[i][2];
15
16 U[i] = vector(-1.*(pow(z/s, 2) + pow((y-s)/s,2) - 1.0), 0, 0);
17 }
18
19 U.writeEntry("", os);
20 #};
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a paraboloid inlet profile using codeStream
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/codeStream_BC/3Delbow_Uparaboloid/
To run the case, type in the terminal,
1. $> cd $PTOFC/101programming/codeStream_BC/3Delbow_Uparaboloid/
2. $> foamCleanTutorials
3. $> gmshToFoam ../../../meshes_and_geometries/gmsh_elbow3d/geo.msh
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a paraboloid inlet profile using codeStream
If everything went fine, you should get something like this
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
codedFixedValue and codedMixed boundary conditions
OpenFOAM also includes the boundary conditions codedFixedValue and
codedMixed.
These boundary conditions are derived from codeStream and work in a
similar way.
They use a friendlier notation and let you access more information of the
simulation database (e.g. time).
The source code and binaries are automatically generated and copied in the
directory dynamicCode of the current case.
Another feature of these boundary conditions, is that the code section can be
read from an external dictionary (system/codeDict), which is run-time
modifiable.
The boundary condition codedMixed works in similar way. This boundary
condition gives you access to fixed values (Dirichlet BC) and gradients
(Neumann BC).
Let us implement the parabolic profile using codedFixedValue.
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Body of the codedFixedValue boundary conditions
codeInclude
#{
#include "fvCFD.H"
#include <cmath> Files needed for compilation
#include <iostream>
#};
In this section we do the actual
code implementation of the boundary
#{ condition.
This is the only part of the body
#}; that you will need to change. The
} rest of the body is a template that
Module 7 - Lecture 2
you can always reuse.
Implementing boundary conditions using codeStream
The code section of the codeStream BC in the body of the file U is as follows,
1 code
2 #{
3 const fvPatch& boundaryPatch = patch();
4 const vectorField& Cf = boundaryPatch.Cf();
5 vectorField& field = *this;
6
7 scalar U_0 = 2, p_ctr = 8, p_r = 8;
8
9 forAll(Cf, faceI)
10 {
11 field[faceI] = vector(U_0*(1-(pow(Cf[faceI].y()-p_ctr,2))/(p_r*p_r)),0,0);
12 }
13 #};
Lines 3-5, are always standard, they give us access to mesh and field information in the patch.
The coordinates of the faces center are stored in the vector field Cf (line 4).
In this case, as we are going to implement a vector profile, we initialize a vector field where we are going to
assign the profile (line 5).
In line 7 we initialize a few constants that will be used in our implementation.
In lines 9-12 we use a forAll loop to access the boundary patch face centers and to assign the velocity profile
values.
In line 11 we do the actual implementation of the boundary profile (similar to the codeStream case). The
vector field was initialize in line 5.
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
codedFixedValue and codedMixed boundary conditions
As you can see, the syntax and use of the codedFixedValue and codedMixed
boundary conditions is much simpler than codeStream.
You can use these instructions as a template. At the end of the day, you only need to
modify the code section.
Depending of what you want to do, you might need to add new headers and
compilation options.
Remember, is you are working with a vector, you need to use vector fields. Whereas,
if you are working with scalars, you need to use scalars fields.
One disadvantage of these boundary conditions, is that you can not visualize the
fields at time zero. You will need to run the simulation for at least one iteration.
On the positive side, accessing time and other values from the simulation database is
straightforward.
Time can be accessed by adding the following statement,
this->db().time().value()
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Let us add time dependency to the parabolic profile.
1 code
2 #{
3 const fvPatch& boundaryPatch = patch();
4 const vectorField& Cf = boundaryPatch.Cf();
5 vectorField& field = *this;
6
7 scalar U_0 = 2, p_ctr = 8, p_r = 8;
8
9 scalar t = this->db().time().value(); Time
10
11 forAll(Cf, faceI)
12 {
13 field[faceI] = vector(sin(t)*U_0*(1-(pow(Cf[faceI].y()-p_ctr,2))/(p_r*p_r))),0,0);
14 }
15 #};
Time dependency
This implementation is similar to the previous one, we will only address how to deal with time.
In line 8 we access simulation time.
In line 13 we do the actual implementation of the boundary profile (similar to the codeStream
case). The vector field was initialize in line 5 and time is accessed in line 9.
In this case, we added time dependency by simple multiplying the parabolic profile by the
function sin(t).
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a parabolic inlet profile using codedFixedValue
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/codeStream_BC/2Delbow_UparabolicInlet_timeDep
To run the case, type in the terminal,
1. $> cd $PTOFC/101programming/codeStream_BC/2Delbow_UparabolicInlet_timeDep
2. $> foamCleanTutorials
3. $> fluentMeshToFoam ../../../meshes_and_geometries/fluent_elbow2d_1/ascii.msh
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a parabolic inlet profile using codedFixedValue
If everything went fine, you should get something like this
www.wolfdynamics.com/wiki/BCIC/elbow_unsBC1.gif
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Filling a tank using codedFixedValue
Let us do a final example.
We will deal with scalar and vector fields at the same time.
We will use codedFixedValue.
For simplicity, we will only show the code section of the input files.
Remember, the rest of the body can be used as a template.
And depending of what you want to do, you might need to add new headers, libraries, and
compilation options. Water enters here
This is a face selection in single boundary patch
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Definition of the vector field boundary condition (dictionary file U),
7 code Code section. The actual implementation of the BC is done in this section
8 #{
...
... Loop using size of boundary patch (Cf) and iterator
... faceI.
19 This is equivalent to:
20 forAll(Cf, faceI) for (int faceI=0; Cf.size()<faceI; faceI++)
21 {
22
23 if (
24 (Cf[faceI].z() > minz) &&
Use conditional structure to
25 (Cf[faceI].z() < maxz) &&
26 (Cf[faceI].y() > miny) && select faces according to the
27 (Cf[faceI].y() < maxy) variables defined in lines 13-16
28 )
29 {
30 if ( t < 1.)
31 { Use conditional structure to
32 field[faceI] = vector(1,0,0); add time dependency and
33 } assign values to the
34 else
selected faces.
35 {
36 field[faceI] = vector(0,0,0); The variable field was
37 } initialize in line 11.
38 }
39 }
40 #};
41 }
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Definition of the scalar field boundary condition (dictionary file alpha.water),
13 field = patchInternalField(); Assign value from the internal field to the patch
14
15 scalar minz = 0.4;
16 scalar maxz = 0.6;
17 scalar miny = 0.5; Initialize variables
18 scalar maxy = 0.7;
20
21 scalar t = this->db().time().value(); Access time
22
...
...
...
42 #};
43 }
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Definition of the scalar field boundary condition (dictionary file alpha.water),
7 code Code section. The actual implementation of the BC is done in this section
8 #{
...
Loop using size of boundary patch (Cf) and iterator
...
... faceI.
22 This is equivalent to:
23 forAll(Cf, faceI) for (int faceI=0; Cf.size()<faceI; faceI++)
24 {
25 if (
26 (Cf[faceI].z() > minz) && Use conditional structure to
27 (Cf[faceI].z() < maxz) && select faces according to the
28 (Cf[faceI].y() > miny) &&
variables defined in lines 13-16
29 (Cf[faceI].y() < maxy)
30 )
31 {
32 if ( t < 1.)
33 { Use conditional structure to add
34 field[faceI] = 1.; time dependency and assign
35 }
36 else
values to the selected faces.
37 { The variable field was initialize in
38 field[faceI] = 0.; line 11.
39 }
40 }
41 }
42 #};
43 }
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a parabolic inlet profile using codedFixedValue
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/codeStream_BC/fillBox_BC/
To run the case, type in the terminal,
1. $> cd $PTOFC/101programming/codeStream_BC/fillBox_BC/
2. $> foamCleanTutorials
3. $> blockMesh
4. $> decomposePar
5. $> mpirun -np 4 interFoam -parallel | tee log
6. $> reconstructPar
7. $> paraFoam
00
00
00
0 02
0 01
0
0 02 0 0 0 1 12 1 1 1 2
1. Highlights
2. Implementing boundary conditions using
codeStream
3. Solution initialization using codeStream
4. High level programming
Module 7 - Lecture 2
Solution initialization using codeStream
When it comes to initial conditions, you can use the utility setFields.
This utility is very flexible, you can even read STL files and use them to
initialize your fields.
But in case that you can not get the desired results using setFields, you
can implement your own initial conditions using codeStream.
To implement initial conditions using codeStream, we proceed in a similar
way as for boundary conditions.
The source code and binaries are automatically generated and copied in the
directory dynamicCode of the current case.
The source code is compiled automatically at run-time.
The use of codeStream is a very good alternative to avoid high level
programming of initial conditions or the use of external libraries.
Hereafter we will use codeStream to implement new initial conditions.
Module 7 - Lecture 2
Solution initialization using codeStream
Body of the codeStream directive for initial conditions
#};
codeOptions
#{
-I$(LIB_SRC)/finiteVolume/lnInclude \ Compilation options
-I$(LIB_SRC)/meshTools/lnInclude
#};
codeLibs
Libraries needed for compilation.
#{
-lmeshTools \ Needed if you want to visualize the
-lfiniteVolume output of the initial conditions at
#}; time zero
code
#{
Insert your code here.
#}; At this point, you need to know
}; how to access internal mesh
} information
Module 7 - Lecture 2
Solution initialization using codeStream
Implementation of an elliptic initialization using codeStream
Let us implement an elliptic initialization using codeStream.
The firs step is to know your domain and identify the region that you want to initialize.
Then you will need to do a little bit of math to get the expression for the initialization.
In this example, we are also going to show you how to do the same initialization by
reading a STL file with the utility setFields.
Phase 1
Module 7 - Lecture 2
Solution initialization using codeStream
The codeStream IC in the body of the file alpha.phase1 is as follows,
code
#{
Insert your code here.
#}; At this point, you need to know
}; how to access internal mesh
} information
Module 7 - Lecture 2
Solution initialization using codeStream
The code section of the codeStream IC in the body of the file alpha.phase1 is as follows,
following statement
}
alpha.writeEntry("", os);
#};
Module 7 - Lecture 2
Solution initialization using codeStream
Implementation of an elliptic initialization using codeStream
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/codeStream_INIT/elliptical_IC
To run the case, type in the terminal,
1. $> cd $PTOFC/101programming/codeStream_INIT/elliptical_IC
2. $> foamCleanTutorials
3. $> blockMesh
4. $> rm rf 0
5. $> cp r 0_org 0
6. $> paraFoam
7. $> interFoam | tee log
8. $> paraFoam
Surface tension driven flow - Bubble in a zero gravity flow using interFoam
Module 7 - Lecture 2
Solution initialization using codeStream
Elliptic initialization using setFields
Let us do the same initialization using a STL file with setFields.
First, you will need to create the solid model that encloses the region you want to
initialize. For this, you can use your favorite CAD/solid modeling software.
Remember to save the geometry is STL format.
Then you will need to read in the STL file using setFields. You will need to modify
the setFieldsDict dictionary.
Region defined by
the STL file
Computational domain
Module 7 - Lecture 2
Solution initialization using codeStream
The setFieldsDict dictionary
defaultFieldValues
(
volScalarFieldValue alpha.phase1 0 Initialize the whole domain to zero
);
regions
setFields method to read STL files.
( If you want to know all the options
available use a word that does not exist
surfaceToCell in the enumerator list (e.g. banana)
{
file "./geo/ellipse.stl"; Location of the STL file to read
outsidePoints ((0.5 0.85 0)); A point located outside the STL
1. $> cd $PTOFC/101programming/codeStream_INIT/elliptical_IC
2. $> foamCleanTutorials
3. $> blockMesh
4. $> rm rf 0
5. $> cp r 0_org 0
6. $> setFields
7. $> paraFoam
Module 7 - Lecture 2
Solution initialization using codeStream
Rayleigh-Taylor instability initialization
Module 7 - Lecture 2
Solution initialization using codeStream
The code section of the codeStream IC in the body of the file alpha.phase1 is as follows,
{
const scalar x = mesh.C()[i][0];
const scalar y = mesh.C()[i][1];
Access cell centers coordinates
if (y >= -0.05*cos(2*constant::mathematical::pi*x))
{
alpha[i] = 1.;
}
}
alpha.writeEntry("", os);
#}; Write output to input dictionary
Module 7 - Lecture 2
Solution initialization using codeStream
Rayleigh-Taylor instability initialization
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/codeStream_INIT/rayleigh_taylor
To run the case, type in the terminal,
1. $> cd $PTOFC/101programming/codeStream_INIT/rayleigh_taylor
2. $> foamCleanTutorials
3. $> blockMesh
4. $> interFoam | tee log
5. $> paraFoam
Module 7 - Lecture 2
Solution initialization using codeStream
Rayleigh-Taylor instability initialization
If everything went fine, you should get something like this
Module 7 - Lecture 2
Solution initialization using codeStream
Filling a tank using codeStream and codedFixedValue
Let us do a final example.
We will implement BCs and ICs at the same.
For simplicity, we will only show the code section of the input files.
This setup is similar to the last example of the previous section (filling a tank using
codedFixedValue).
Module 7 - Lecture 2
Solution initialization using codeStream
The code section of the codeStream IC in the body of the file alpha.water is as follows,
internalField #codeStream
Use codeStream to set the
{ value of the initial conditions
if (y <= 0.2)
{ Assign value to alpha according to
alpha[i] = 1.; conditional structure
}
}
Module 7 - Lecture 2
Solution initialization using codeStream
The code section of the codeFixedValue BC in the body of the file U is as follows,
Module 7 - Lecture 2
Solution initialization using codeStream
The code section of the codeFixedValue BC in the body of the file U is as follows,
if (
(Cf[faceI].z() > min) &&
(Cf[faceI].z() < max) && Use conditional structure to
(Cf[faceI].y() > min) && select faces.
(Cf[faceI].y() < max)
)
{
if ( t < 2.)
{
field[faceI] = vector(1,0,0); Use conditional structure to
} add time dependency and
else
assign values to the
{
field[faceI] = vector(0,0,0); selected faces.
}
}
}
#};
Module 7 - Lecture 2
Solution initialization using codeStream
The code section of the codeFixedValue BC in the body of the file alpha.water is as follows,
field = patchInternalField(); Assign value from the internal field to the patch
Module 7 - Lecture 2
Solution initialization using codeStream
Filling a tank using codeStream and codedFixedValue
If everything went fine, you should get something like this
02
02
02
0 22
02
01
0 0 1 1 2 2
Module 7 - Lecture 2
Roadmap
1. Highlights
2. Implementing boundary conditions using
codeStream
3. Solution initialization using codeStream
4. High level programming
Module 7 - Lecture 2
High level programming
Hereafter we will work with high level programming, this is the hard part of
programming in OpenFOAM.
High level programming requires some knowledge on C++ and OpenFOAM
API library.
Before doing high level programming, we highly recommend you to try with
codeStream, most of the time it will work.
We will implement the parabolic profile, so you can compare this
implementation with codeStream ad codedFixedValue BCs.
When we program boundary conditions, we are actually building a new
library that can be linked with any solver. To compile the library, we use the
command wmake (distributed with OpenFOAM).
At this point, you can work in any directory. But we recommend you to work
in your OpenFOAM user directory, type in the terminal,
1. $> cd $WM_PROJECT_USER_DIR/run
Module 7 - Lecture 2
High level programming
Let us create the basic structure to write the new boundary condition, type in
the terminal,
The utility foamNewBC, will create the directory structure and all the files needed to
write your own boundary conditions.
We are setting the structure for a fixed (the option f) velocity (the option v),
boundary condition, and we name our boundary condition ParabolicVelocity .
If you want to get more information on how to use foamNewBC, type in the terminal,
Module 7 - Lecture 2
High level programming
Directory structure of the new boundary condition
./myParabolicVelocity
Make
files
options
myParabolicVelocityFvPatchVectorField.C
myParabolicVelocityFvPatchVectorField.H
Let us compile the library to see what errors we get. Type in the terminal,
1. $> wmake
38 Foam::myParabolicVelocityFvPatchVectorField::
...
...
...
Using this line as your reference location in the source code, follow these steps,
Erase the following lines in incremental order (be sure to erase only the lines that
contain the words fieldData, timeVsData, wordData, labelData and boolData):
48-52, 64-68, 92-96, 105-109, 119-123, 177-180.
Erase the following lines (they contain the word fieldData), 131, 146, 159-161.
Replace all the occurrences of the word scalarData with maxValue (11
occurrences).
Module 7 - Lecture 2
High level programming
The source file (.C)
Duplicate all the lines where the word data appears (6 lines), change the word data
to n in the first line, and to y in the second line, erase the comma in the last line. For
example,
Original statements
45 fixedValueFvPatchVectorField(p, iF),
46 maxValue_(0.0),
47 data_(Zero),
48 data_(Zero),
Modified statements
45 fixedValueFvPatchVectorField(p, iF),
46 maxValue_(0.0),
47 n_(Zero),
48 y_(Zero) Remember to erase the comma
Module 7 - Lecture 2
High level programming
The source file (.C)
We are almost done, we just defined all the datatypes. Now we need to implement
the actual boundary condition.
Starting in line 156, add the following statements,
157
158 vector ctr = 0.5*(bb.max() + bb.min()); Coordinates of patch midpoint
159
160 const vectorField& c = patch().Cf(); Access patch face centers
161
162 scalarField coord = 2*((c - ctr) & y_)/((bb.max() - bb.min()) & y_);
163
x
x
Computes scalar field to be used for defining the parabolic profile
Module 7 - Lecture 2
High level programming
The source file (.C)
Add the following statement in line 166,
1. $> wmake
If you are feeling lazy or you can not fix the compilation errors, you will find the source
code in the directory,
$PTOFC/101programming/src/myParabolicVelocity
Module 7 - Lecture 2
High level programming
The source file (.C)
Before moving forward, let us comment a little bit the source file.
First at all, there are five classes constructors and each of them have a specific task.
In our implementation we do not use all the classes, we only use the first two classes.
The first class is related to the initialization of the variables.
The second class is related to reading the input dictionaries.
We will not comment on the other classes as it is out of the scope of this example
(they deal with input tables, mapping, and things like that).
The implementation of the boundary condition is always done using the
updateCoeffs() member function.
When we compile the source code, it will compile a library with the name specified in
the file Make/file. In this case, the name of the library is libmyParabolicVelocity.
The library will be located in the directory $(FOAM_USER_LIBBIN), as specified in
the file Make/file.
Module 7 - Lecture 2
High level programming
The source file (.C)
The first class is related to the initialization of the variables declared in the header file.
In line 47 we initialize maxValue with the value of zero. The vectors n and y are
initialized as a zero vector by default or (0, 0, 0).
It is not a good idea to initialize these vectors as zero vectors by default. Let us use
as default initialization (1, 0, 0) for vector n and (0,1,0) for vector y.
38 Foam::myParabolicVelocityFvPatchVectorField::
39 myParabolicVelocityFvPatchVectorField
40 (
41 const fvPatch& p,
42 const DimensionedField<vector, volMesh>& iF
43 )
44 :
45 fixedValueFvPatchVectorField(p, iF),
46 maxValue_(0.0),
47 n_(Zero), Change to n_(1,0,0)
48 y_(Zero)
49 {
50 } Change to y_(0,1,0)
Module 7 - Lecture 2
High level programming
The source file (.C)
The second class is used to read the input dictionary.
Here we are reading the values defined by the user in the dictionary U.
The function lookup will search the specific keyword in the input file.
53 Foam::myParabolicVelocityFvPatchVectorField::
54 myParabolicVelocityFvPatchVectorField
55 (
56 const fvPatch& p,
57 const DimensionedField<vector, volMesh>& iF,
58 const dictionary& dict
59 )
60 :
61 fixedValueFvPatchVectorField(p, iF),
dict.lookup will look for
62 maxValue_(readScalar(dict.lookup("maxValue"))),
63 n_(pTraits<vector>(dict.lookup("n"))), these keywords in the
64 y_(pTraits<vector>(dict.lookup("y"))) input dictionary
65 {
66
67
68 fixedValueFvPatchVectorField::evaluate();
77 }
Module 7 - Lecture 2
High level programming
The source file (.C)
Since we do not want the vectors n and y to be zero vectors, we add the following
sanity check from lines 67-75.
These statements check if the given n and y vectors in the input dictionary is zero or
not.
If any of the vectors is zero it gives the fatal error and terminate the program.
On the other hand if everything is ok, it will normalize n and y, since in our
implementation they are direction vectors.
66
Module 7 - Lecture 2
High level programming
The source file (.C)
At this point, we are ready to go.
Save files and recompile. Type in the terminal,
1. $> wmake
Module 7 - Lecture 2
High level programming
The source file (.C)
Before using the new BC, let us take a look at the logic behind the implementation.
a a a e
e e a e a
a a e a
a a e
Module 7 - Lecture 2
High level programming
Running the case
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/src/case_elbow2d
Go to the case directory,
1. $> cd $PTOFC/101programming/cases/elbow2d
Open the file 0/U, and look for the definition of the new BC velocity-inlet-5,
velocity-inlet-5
{
type myParabolicVelocity; Name of the boundary condition
maxValue 2.0;
n (1 0 0); User defined values
y (0 1 0); max value, n, y
} If you set n or y to (0 0 0), the solver will
abort execution
Module 7 - Lecture 2
High level programming
Running the case
We also need to tell the application that we want to use the library we just compiled.
To do so, we need to add the new library in the dictionary file controlDict,
15 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
16
17 libs ("libmyParabolicVelocity.so"); Name of the library
18 You can add as many libraries as you like
19 application icoFoam;
Module 7 - Lecture 2
High level programming
Running the case
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/src/case_elbow2d
To run the case, type in the terminal,
1. $> foamCleanTutorials
2. $> fluentMeshToFoam ../../../meshes_and_geometries/fluent_elbow2d_1/ascii.msh
Module 7 - Lecture 2
High level programming
Adding some verbosity to the BC implementation
Let us add some outputs to the BC.
Starting at line 181 (after the function updateCoeffs), add the following lines,
179 fixedValueFvPatchVectorField::updateCoeffs();
180
181 Info << endl << "Face centers (c):" << endl;
182 Info << c << endl;
183 Info << endl << "Patch center (ctr):" << endl;
184 Info << ctr << endl;
185 Info << endl << "Patch (c - ctr):" << endl;
186 Info << c - ctr << endl;
187 Info << endl << "Patch max bound (bb.max):" << endl;
188 Info << bb.max() << endl;
189 Info << endl << "Patch min bound (bb.max):" << endl;
190 Info << bb.min() << endl;
191 Info << endl << "Patch coord ( 2*((c - ctr) & y_)/((bb.max() - bb.min()) & y_) ):" << endl;
192 Info << coord << endl;
193 Info << endl << "Patch ( 1.0 - sqr(coord)) :" << endl;
194 Info << n_*maxValue_*(1.0 - sqr(coord))<< endl;
195 Info << endl << "Loop for c, BC assigment << endl;
196 forAll(c, faceI)
197 {
198 Info << c[faceI] << " " << n_*maxValue_*(1.0 - sqr(coord[faceI])) << endl;
199 }
200
201
Recompile, rerun the simulation, look at the output, and do the math.
Module 7 - Lecture 2
High level programming
Do you take the challenge?
Starting from this boundary condition, try to implement a paraboloid BC.
If you are feeling lazy or at any point do you get lost, in the directory
$PTOFC/101programming/src/myParaboloid you will find a working
implementation of the paraboloid profile.
Open the source code and try to understand what we did (pretty much similar to the
previous case).
In the directory $PTOFC/101programming/src/case_elbow3d you will find a
case ready to use.
Module 7 - Lecture 2
Module 7
Basic programming in OpenFOAM
Modifying applications
Lecture 3
Module 7 - Lecture 2
Roadmap
1. Highlights
2. Implementing an application from scratch
3. Adding the scalar transport equation to
icoFoam
Module 7 - Lecture 2
Highlights
Implementing a new application from scratch in OpenFOAM (or any other
high level programming library), can be an incredible daunting task.
OpenFOAM comes with many solvers, and as it is today, you do not need
to implement new solvers from scratch.
Of course, if your goal is to write a new solver, you will need to deal with
programming. What you usually do, is take an existing solver and modify it.
But in case that you would like to take the road of implementing new
applications from scratch, we are going to give you the basic building blocks.
We are also going to show how to add basic modifications to existing solvers.
We want to remind you that this requires some knowledge on C++ and
OpenFOAM API library.
Also, you need to understand the FVM, and be familiar with the basic algebra
of tensors.
Some common sense is also helpful.
Module 7 - Lecture 2
Roadmap
1. Highlights
2. Implementing an application from scratch
3. Adding the scalar transport equation to
icoFoam
Module 7 - Lecture 2
Implementing an application from scratch
Let us do a little bit of high level programming, this is the hard part of working
with OpenFOAM.
At this point, you can work in any directory. But we recommend you to work
in your OpenFOAM user directory, type in the terminal,
1. $> cd $WM_PROJECT_USER_DIR/run
The utility foamNewApp, will create the directory structure and all the files needed to
create the new application from scratch. The name of the application is
scratchFoam.
If you want to get more information on how to use foamNewApp, type in the terminal,
Module 7 - Lecture 2
Implementing an application from scratch
Stating from line 31, add the following statements.
We are going to use the PISO control options, even if we do not have to deal with
velocity-pressure coupling.
30
31 #include "fvCFD.H
Module 7 - Lecture 2
Implementing an application from scratch
Let us create the file createFields.H, type in the terminal,
Now open the file with your favorite editor, and start to add the following information,
Module 7 - Lecture 2
Implementing an application from scratch
Remember, in the file createFields.H, we declare all the variables (or fields) that
we will use (U and T in this case).
The dimensions of the fields are defined in the input dictionaries, you also have the
option to define the dimensions in the source code.
You can also define the fields directly in the source file scratchFoam.C, but it is
good practice to do it in the header. This improves code readability.
Module 7 - Lecture 2
Implementing an application from scratch
We also need to declare the constant DT, that is read from the dictionary
transportProperties.
The dimensions are defined in the input dictionary.
Module 7 - Lecture 2
Implementing an application from scratch
At this point, we are ready to compile. Type in the terminal,
1. $> wmake
If everything went fine, you should have a working solver named scratchFoam.
If you are feeling lazy or you can not fix the compilation errors, you will find the source
code in the directory,
$PTOFC/101programming/applications/solvers/scratchFoam
$PTOFC/101programming/applications/solvers/scratchFoam/test_case
At this point, we are all familiar with the convection-diffusion equation and
OpenFOAM, so you know how to run the case. Do your magic.
Module 7 - Lecture 2
Implementing an application from scratch
Let us now add a little bit more complexity, a non-uniform initialization of the scalar
field T.
Remember codeStream? Well, we just need to proceed in a similar way.
As you will see, initializing directly in the source code of the solver is more intrusive
than using codeStream in the input dicitionaries.
It also requires recompiling the application.
Add the following statements to the createFields.H file, recompile and run again
the test case.
16
17 forAll(T, i) We add the initialization of T after the its declaration
18 {
19 const scalar x = mesh.C()[i][0];
20 const scalar y = mesh.C()[i][1]; Access cell center coordinates.
21 const scalar z = mesh.C()[i][2]; In this case y and z coordinates are not used.
22
23 if ( 0.3 < x && x < 0.7) Conditional structure
24 {
25 T[i] = 1.;
26 }
27 }
28 T.write(); Write field T. As the file createFields.H is outside the time loop
the value is saved in the time directory 0
Module 7 - Lecture 2
Implementing an application from scratch
Let us compute a few extra fields. We are going to compute the gradient, divergence,
and Laplacian of T.
We are going to compute these fields in a explicit way, that is, after finding the
solution of T.
Therefore we are going to use the operator fvc.
Add the following statements to the source code of the solver (scratchFoam.C),
68 }
69
70 #include "continuityErrs.H"
71 #include "write.H" Add this header file
72 runTime.write();
73 The file is located in the directory
74 } $PTOFC/101programming/applications/solvers/scratchFoam
In this file we declare and define the new variables, take a look at it
1. Highlights
2. Implementing an application from scratch
3. Adding the scalar transport equation to
icoFoam
Module 7 - Lecture 2
Adding the scalar transport equation to icoFoam
Let us modify a solver, we will work with icoFoam.
We will add a passive scalar (convection-diffusion equation).
At this point, you can work in any directory. But we recommend you to work
in your OpenFOAM user directory, type in the terminal,
1. $> cd $WM_PROJECT_USER_DIR/run
2. $> cd my_icoFoam
Module 7 - Lecture 2
Adding the scalar transport equation to icoFoam
Open the file icoFoam.C using your favorite editor and add the new
equation in lines 115-120,
Module 7 - Lecture 2
Adding the scalar transport equation to icoFoam
Open the file createFields.H using your favorite editor and add the following lines
at the beginning of the file,
1 Info<< "Reading field S1 (passive scalar 1)\n" << endl;
2 volScalarField S1
3 (
4 IOobject
5 ( Declaration of scalar field S1.
6 "S1", The solver will read the input file S1
7 runTime.timeName(), (BC and IC).
8 mesh,
9 IOobject::MUST_READ, You will need to create the file S1 in
10 IOobject::AUTO_WRITE the time directory 0.
11 ),
12 mesh
13 );
14
15 Info<< "Reading diffusionProperties\n" << endl;
16
17 IOdictionary diffusionProperties
18 (
19 IOobject Declaration of input/output dictionary
20 ( file.
21 "diffusionProperties", The name of the dictionary is
22 runTime.constant(), diffusionProperties and is located in
23 mesh,
24 IOobject::MUST_READ_IF_MODIFIED,
the directory constant.
25 IOobject::NO_WRITE
26 )
27 );
28
29 Info<< "Reading diffusivity DT\n" << endl;
30 dimensionedScalar DT
31 (
Read DT value from the dictionary
32 diffusionProperties.lookup("DT") diffusionProperties.
33 ); Module 7 - Lecture 2
Adding the scalar transport equation to icoFoam
Those are all the modifications we need to do.
But before compiling the new solver, we need to modify the compilation
instructions.
Using you favorite editor, open the file Make/files,
Original file
1 icoFoam.C
2
3 EXE = $(FOAM_APPBIN)/icoFoam
Modified file
1. $> wmake
If everything went fine, you should have a working solver named my_icoFoam.
If you are feeling lazy or you can not fix the compilation errors, you will find the source
code in the directory,
$PTOFC/101programming/applications/solvers/my_icoFoam
$PTOFC/101programming/applications/solvers/my_icoFoam/test_case
Module 7 - Lecture 2
Adding the scalar transport equation to icoFoam
Running the case
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/applications/solvers/my_icoFoam/test_case
To run the case, type in the terminal,
1. $> foamCleanTutorials
2. $> fluentMeshToFoam ../../../../../meshes_and_geometries/fluent_elbow2d_1/ascii.msh
Remember, you will need to create the file 0/S1 (boundary conditions and initial
conditions for the new scalar).
You will also need to create the input dictionary constant/diffusionProperties,
from this dictionary we will read the diffusion coefficient value.
Finally, remember to update the files system/fvSchemes and
system/fvSolution to take into account the new equation.
Module 7 - Lecture 2
Adding the scalar transport equation to icoFoam
Running the case
If everything went fine, you should get something like this
S1 = 300
S1 = 350
S1 = 400
Module 7 - Lecture 2
Module 7
Programming in OpenFOAM Building
blocks
Wrap up
Module 7 - Wrap up
Main takeaways
After finishing this module, you should be able to do the following:
Basic understanding of high level programming in OpenFOAM.
Implement the convection-diffusion equation from scratch.
Program your own boundary conditions using codeStream and high
level programming.
Field initialization using codeStream.
Modify existing solvers.
Writing a model solver from scratch.
Understand the main structure of solvers and utilities.
Explore the source code.
Find information in the source code and the doxygen documentation.
Module 7 - Wrap up