Basic Programming in OpenFOAM

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 140

Module 7

Basic programming in OpenFOAM


Building blocks
Lecture 1

Module 7 - Lecture 1
Roadmap

1. Programming in OpenFOAM.
Building blocks.

Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks

In the directory $WM_PROJECT_DIR/applications/test, you will


find the source code of several test cases that show the usage of most
of the OpenFOAM classes.
We highly encourage you to take a look at these test cases, and try to
understand how to use the classes.
We will use these basic test cases to understand the following base
classes: tensors, fields, mesh, and basic discretization.
For your convenience, we already copied the directory
$WM_PROJECT_DIR/applications/test into the directory
$PTOFC/programming_playground/test

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.

Remember, all OpenFOAM components are implemented in library form for


easy re-use.
OpenFOAM encourage code re-use. So basically we are going to take
something that already exist and we are going to modify it to fix our needs.
We like to call this method CPAC (copy-paste-adapt-compile).
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Basic tensor classes in OpenFOAM
OpenFOAM represents scalars, vectors and matrices as tensor fields. A zero rank
tensor is a scalar, a first rank tensor is a vector and a second rank tensor is a matrix.
OpenFOAM contains a C++ class library named primitive
($FOAM_SRC/OpenFOAM/primitives/). In this library, you will find the classes for
the tensor mathematics.
In the following table, we show the basic tensor classes available in OpenFOAM,
with their respective access functions.

Tensor Rank Common name Basic class Access function

0 Scalar scalar

1 Vector vector x(), y(), z()

2 Tensor tensor xx(), xy(), xz()

Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Basic tensor classes in OpenFOAM
In OpenFOAM, the second rank tensor (or matrix)

can be declared in the following way

tensor T(1, 2, 3, 4, 5, 6, 7, 8, 9);

We can access the component or using the xz ( ) access function,

Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Basic tensor classes in OpenFOAM
For instance, the following statement,

Info << Txz = << T.xz ( ) << endl;

Will generate the following screen output,

$> 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

Outer product rank a, b >=1 ab a * b

Inner product rank a, b >=1 a.b a & b

Double inner product rank a, b >=2 a:b a && b

Magnitude |a| mag(a)

Determinant det T det(T)

You can find a complete listModule


of all 7operators
- Lecture 1 in the programmers guide
Programming in OpenFOAM. Building blocks
Dimensional units in OpenFOAM

As we already know, OpenFOAM is fully dimensional.


Dimensional checking is implemented as a safeguard against implementing
a meaningless operation.
OpenFOAM encourages the user to attach dimensional units to any tensor
and it will perform dimension checking of any tensor operation.
You can find the dimensional classes in the directory
$FOAM_SRC/OpenFOAM/dimensionedTypes/
The dimensions can be hardwired directly in the source code or can be
defined in the input dictionaries.
From this point on, we will be attaching dimensions to all the tensors.

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 );

In line 1 we create the object sigma.


In line 4, we use the class dimensonSet to attach units to the object sigma.
In line 5, we set the input values of the tensor sigma.
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Units correspondence in dimensionSet

The units of the class dimensionSet are defined as follows

dimensionSet (kg, m, s, K, mol, A, cd)

Therefore, the tensor sigma,

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 );

Has pressure units or

Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Units correspondence in dimensionSet

No. Property Unit Symbol

1 Mass kilogram kg

2 Length meters m

3 Time second s

4 Temperature Kelvin K

5 Quantity moles mol

6 Current ampere A
Luminuous
7 candela cd
intensity

dimensionSet (kg, m, s, K, mol, A, cd)


Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Dimensional units examples
To attach dimensions to any tensor, you need to access dimensional units class.
To do so, just add the header file dimensionedTensor.H to your program.

#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;
...
...
...

The output of the previous program should looks like this:


sigma sigma [1 -1 -2 0 0 0 0] (1e+06 0 0 0 1e+06 0 0 0 1e+06)
Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Dimensional units examples

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:

Info << Sigma name: << sigma.name ( ) << endl;


Info << Sigma dimensions: << sigma.dimensions ( ) << endl;
Info << Sigma value: << sigma.value ( ) << endl;

To extract a value of a dimensioned tensor, you can proceed as follows:

Info<< "Sigma yy (22) value: " << sigma.value().yy() << endl;

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

applicationName.C: is the actual source code of the application.


header_files.H: header files required to compile the application.

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

In step 2, we used wmake (distributed with OpenFOAM) to compile the


source code.
The name of the executable will be my_Test-tensor and it will be located in
the directory $FOAM_USER_APPBIN (as specified in the file Make/files)
At this point, take a look at the output and study the file Test-tensor.C.
Try to understand what we have done.
After all, is not that difficult. Right?

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

The discretization is done using the FVM (Finite Volume Method).


The cells are contiguous, i.e., they do not overlap and completely fill the domain.
Dependent variables and other properties are stored at the cell centroid.
No limitations on the number of faces bounding each cell.
No restriction on the alignment of each face.
The mesh class polyMesh is used to construct the polyhedral mesh using the
minimum information required.
You can find the polyMesh classes in the directory $FOAM_SRC/OpenFOAM/meshes
The fvMesh class extends the polyMesh class to include additional data needed for
the FVM discretization.
You can find the fvMesh classes in the directory
$FOAM_SRC/src/finiteVolume/fvMesh

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.

The template class geometricField stores internal


fields, boundary fields, mesh information, dimensions,
old values and previous iteration values.
A geometricField inherits all the tensor algebra of its
corresponding field, has dimension checking, and can
be subjected to specific discretization procedures.
Let us now access the mesh information of a simple
case.

Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Data stored in the fvMesh class

Access
Class Description Symbol
function
volScalarField Cell volumes V()

surfaceVectorField Face area vector Sf()

surfaceScalarField Face area magnitude magSf()

volVectorField Cell centres C()

surfaceVectorField Face centres Cf()

surfaceScalarField Face fluxes Phi()

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.

volScalarField p Create scalar volField p


(
IOobject
(
"p",
runTime.timeName(), Assign and initialization of
mesh, scalar volField to the
IOobject::MUST_READ, mesh
IOobject::AUTO_WRITE
),
mesh
);
Info<< p << endl;
Output some information
Info<< p.boundaryField()[0] << endl;

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++.

for (int i = 0; i < mesh.boundaryMesh().size(); i++)


Info << "Patch " << i << ": " << mesh.boundary()[i].name() << " with "
<< mesh.boundary()[i].Cf().size() << " faces. Starts at total face "
<< mesh.boundary()[i].start() << endl;

Outputs starting face of patch

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)

vol<type>Field scalar, volScalarField surfaceScalarField


Module 7 - Lecture 1
Programming in OpenFOAM. Building blocks
Discretization of the basic PDE terms in OpenFOAM
To discretize the fields in a valid mesh, we need to access the finite volume class.
This class can be accessed by adding the header fvCFD.H to your program.
To discretize the scalar transport equation in a mesh, we can proceed as follows,

Assemble and solve


solve linear system arising form
the discretization
(
fvm::ddt(T)
+ fvm::div(phi,T) Discretize equations
- fvm::laplacian(DT,T)
);

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,

Creates object TEqn that


fvScalarMatrix TEqn contains the coefficient matrix
arising from the discretization
(
fvm::ddt(T)
+ fvm::div(phi,T) Discretize equations
- fvm::laplacian(DT,T)
);

Teqn.solve(); Solve the linear system Teqn

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,

Coefficient Matrix (sparse, square)


The coefficients depend on geometrical quantities, Boundary conditions
fluid properties and non linear equations and source terms

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

To access the mesh information, we need to use this program in a valid


mesh.
1. $> cd $PTOFC/programming_playground/my_mesh/cavity
2. $> blockMesh
3. $> my_Test-mesh

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

Boundary conditions and


functionObjects 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

patch-name Patch name


{
type fixedValue;
value #codeStream
Use codeStream to set the value
{ of the boundary condition
codeInclude
#{
#include "fvCFD.H" Files needed for compilation
#};

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 boundary condition
#}; at time zero

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

Inlet Bounds of velocity-inlet-5 boundary patch


velocity-inlet-6
Parabolic inlet profile Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a parabolic inlet profile using codeStream
We will use the following formula to implement the parabolic inlet profile

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,

velocity-inlet-5 Patch name


{
type fixedValue;
value #codeStream
{
codeInclude
#{
#include "fvCFD.H"
#};
Depending of what are you trying
codeOptions to do, you will need to add new
#{ files, options and libraries.
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude For most of the cases, this part is
#};
always the same.
codeLibs
#{
-lmeshTools \
-lfiniteVolume
#};

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,

To access boundary mesh information


1 code
2 #{
3 const IOdictionary& d = static_cast<const IOdictionary&>
4 (
5 dict.parent().parent()
6 );
7
8 const fvMesh& mesh = refCast<const fvMesh>(d.db());
9 const label id = mesh.boundary().findPatchID("velocity-inlet-5");
10 const fvPatch& patch = mesh.boundary()[id];
11
12 vectorField U(patch.size(), vector(0, 0, 0));
13
14 ...
15 ... Remember to update this value with the
16 ...
actual name of the patch
17 #};

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 #};

In lines 6-17 we implement the new boundary condition.


In lines 6-9 we declare a few constant needed in our implementation.
In lines 11-15 we use a forAll loop to access the boundary patch face centers and to assign the velocity profile
values. Notice the U was previously initialized.
In line 13 we get the y coordinates of the patch faces center.
In line 14 we assign the velocity value to the patch faces center.
In line 17 we write the U values to the dictionary.
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a parabolic inlet profile using codeStream
This case is ready to run, the input files are located in the directory
$PTOFC/101programming/codeStream_BC/2Delbow_UparabolicInlet
To run the case, type in the terminal,

1. $> cd $PTOFC/101programming/codeStream_BC/2Delbow_UparabolicInlet

2. $> foamCleanTutorials
3. $> fluentMeshToFoam ../../../meshes_and_geometries/fluent_elbow2d_1/ascii.msh

4. $> icoFoam | tee log


5. $> paraFoam

The codeStream boundary condition is implemented in the file 0/U.

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

Bounds of auto3 boundary patch


Paraboloid inlet profile
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Implementation of a paraboloid inlet profile using codeStream
We will implement the following equation in the boundary patch auto3.

Module 7 - Lecture 2
Implementing boundary conditions using codeStream
The codeStream BC in the body of the file U is as follows,

auto3 Patch name


{
type fixedValue;
value #codeStream
{
codeInclude
#{
#include "fvCFD.H"
#};
For most of the cases, this part is
codeOptions always the same. But depending of
#{
what are you trying to do, you will
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude need to add more files, options and
#}; libraries.

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

4. $> autoPatch 75 -overwrite


5. $> createPatch -overwrite
6. $> renumberMesh -overwrite
7. $> icoFoam | tee log
8. $> paraFoam

The codeStream boundary condition is implemented in the file 0/U.

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

patch-name Patch name


{
type codedFixedValue; Use codedFixedValue and
value uniform (0 0 0); initializations
Unique name of the new boundary
redirectType name_of_BC; condition.
If you have more codedFixedValue
BC, the names must be different
codeOptions
#{
-I$(LIB_SRC)/finiteVolume/lnInclude \ Compilation options
-I$(LIB_SRC)/meshTools/lnInclude
#};

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

4. $> icoFoam | tee log


5. $> paraFoam

The codeStream boundary condition is implemented in the file 0/U.

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

Hereafter we will setup an inlet boundary condition in


a portion of an existing patch.
By using codedFixedValue BC, we do not need to
modify the actual mesh topology.
We will assign a velocity field and a scalar field to a
set of faces (dark area in the figure).
We are going to simulate filling a tank with water.
We will use the solver interFoam.
The tank is initially empty
Module 7 - Lecture 2
Implementing boundary conditions using codeStream
Definition of the vector field boundary condition (dictionary file U),

Name of the patch where we want to implement the boundary condition

1 leftWall Use codedFixedValue BC and initialize value.


2 { The initialization is only needed for paraview
3 type codedFixedValue; in order to visualize something at time zero.
4 value uniform (0 0 0);
5 redirectType inletProfile1; Unique name of the BC
6 Do not use the same name in other patches
7 code
8 #{
9 const fvPatch& boundaryPatch = patch(); Access boundary mesh
10 const vectorField& Cf = boundaryPatch.Cf(); information and initialize
11 vectorField& field = *this; vector field field
12
13 scalar minz = 0.4;
14 scalar maxz = 0.6;
15 scalar miny = 0.5; Initialize variables
16 scalar maxy = 0.7;
17
18 scalar t = this->db().time().value(); Access time
...
...
...
40 #};
41 }

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),

Name of the patch where we want to implement the boundary condition

1 leftWall Use codedFixedValue BC and initialize value.


2 { The initialization is only needed for paraview
3 type codedFixedValue;
in order to visualize something at time zero.
4 value uniform 0;
5 redirectType inletProfile2; Unique name of the BC
6 Do not use the same name in other patches
7 code
8 #{
9 const fvPatch& boundaryPatch = patch(); Access boundary mesh
Code section. The actual implementation

10 const vectorField& Cf = boundaryPatch.Cf(); information and initialize


11 scalarField& field = *this; scalar field field
12
of the BC is done in this section

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

As you can see, we can also run in parallel with no problem.


FYI, the stand alone version of Paraview does not handle codedFixedValue BC.
To visualize the results, you need to use paraFoam with no options (avoid the option
builtin). 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

00

00

00

0 02

0 01

0
0 02 0 0 0 1 12 1 1 1 2

Visualization of water phase Volume integral of water entering the


(alpha.water) domain
www.wolfdynamics.com/wiki/BCIC/filltank1.gif
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
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

internalField #codeStream Use codeStream to set the value


{ of the initial conditions
{
codeInclude
#{
#include "fvCFD.H" Files needed for compilation
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.

Initialization using STL


Phase 2

Phase 1

Initialization using codeStream Initialization using a STL with setFields

Module 7 - Lecture 2
Solution initialization using codeStream
The codeStream IC in the body of the file alpha.phase1 is as follows,

internalField #codeStream Use codeStream to set the value


{ of the initial conditions
{
codeInclude
#{
#include "fvCFD.H"
#};

codeOptions Depending of what are you trying


#{ to do, you will need to add new
-I$(LIB_SRC)/finiteVolume/lnInclude \ files, options and libraries.
-I$(LIB_SRC)/meshTools/lnInclude
#};
For most of the cases, this part is
codeLibs always the same.
#{
-lmeshTools \
-lfiniteVolume
#};

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,

Access internal mesh information


code
#{
const IOdictionary& d = static_cast<const IOdictionary&>(dict);
const fvMesh& mesh = refCast<const fvMesh>(d.db());

scalarField alpha(mesh.nCells(), 0.); Initialize scalar field to zero


scalar he = 0.5;
Initialize variables
scalar ke = 0.5;
scalar ae = 0.3;
scalar be = 0.15; forAll loop to access cell centers and to assign alpha values.
Notice the alpha was previously initialized.
forAll(alpha, i) The size of the loop is defined by alpha and the iterator is i.
{
const scalar x = mesh.C()[i][0];
const scalar y = mesh.C()[i][1]; Access cell centers coordinates
const scalar z = mesh.C()[i][2];
Assign value to alpha

if ( pow(y-ke,2) <= ((1 - pow(x-he,2)/pow(ae,2) )*pow(be,2)) )

If this condition is true, do the


{
alpha[i] = 1.;
}

following statement
}
alpha.writeEntry("", os);
#};

Write output to input dictionary

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

In step 6, we launch paraFoam to visualize the initialization.


FYI, you can run in parallel with no problem.
Module 7 - Lecture 2
Solution initialization using codeStream
Implementation of an elliptic initialization using codeStream
If everything went fine, you should get something like this

codeStream initialization setFields initialization


Visualization of volume fraction (alpha.phase1) Visualization of volume fraction (alpha.phase1)
www.wolfdynamics.com/wiki/BCIC/bubble_zeroG.gif www.wolfdynamics.com/wiki/BCIC/bubble_zeroG_SF.gif

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

includeInside true; Use what is inside the STL


includeOutside false; Use what is outside the STL
includeCut false; Include cells cut by the STL
fieldValues
(
volScalarFieldValue alpha.phase1 1 Initialize this value.
); In this case the initialization will be inside
the STL
}
);
Module 7 - Lecture 2
Solution initialization using codeStream
Elliptic initialization using setFields
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. $> setFields
7. $> paraFoam

At this point, compare this initialization with the previous one.


Also, feel free to launch the simulation using interFoam.

Module 7 - Lecture 2
Solution initialization using codeStream
Rayleigh-Taylor instability initialization

Let us study the Rayleigh-Taylor


instability.
In this case, we have two phases with
different physical properties (one phase
is heavier).
To onset this instability, we need to
perturbate somehow the interface
between the two phases.
We will use codeStream to initialize the
two phases.
For simplicity, we will only show the
code section of the input files.
The entries codeInclude, codeOptions,
and codeLibs, are the same most of the
times.

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,

code Access internal mesh information


#{
const IOdictionary& d = static_cast<const IOdictionary&>(dict);
const fvMesh& mesh = refCast<const fvMesh>(d.db());

scalarField alpha(mesh.nCells(), 0.); Initialize scalar field to zero


forAll(alpha, i)
Assign value to alpha

{
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

For simplicity, we only show the code section.


The rest of the body of the codeStream IC is a template.

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

FYI, you can run in parallel with no problem.

Module 7 - Lecture 2
Solution initialization using codeStream
Rayleigh-Taylor instability initialization
If everything went fine, you should get something like this

Initial conditions Visualization of volume fraction, static pressure and velocity


magnitude
www.wolfdynamics.com/wiki/BCIC/rayleigh_taylor_ins1.gif

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).

Water enters here


This is a single boundary patch

Initial water level

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

Access internal mesh information


...
...
...
code
#{
const IOdictionary& d = static_cast<const IOdictionary&>(dict);
const fvMesh& mesh = refCast<const fvMesh>(d.db());

scalarField alpha(mesh.nCells(), 0.); Initialize scalar field to zero


forAll(alpha, i)
{
const scalar x = mesh.C()[i][0];
const scalar y = mesh.C()[i][1]; Access cell centers
const scalar z = mesh.C()[i][2]; coordinates

if (y <= 0.2)
{ Assign value to alpha according to
alpha[i] = 1.; conditional structure
}
}

alpha.writeEntry("", os); Write output to input dictionary


#};

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,

Name of the patch where we want to implement the boundary condition

leftWall Use codedFixedValue BC and initialize value.


{ The initialization is only needed for paraview
type codedFixedValue;
in order to visualize something at time zero.
value uniform (0 0 0);
Unique name of the BC
redirectType inletProfile1;
Do not use the same name in other patches
code Code section. The actual implementation of the BC is done here
#{
const fvPatch& boundaryPatch = patch(); Access boundary mesh
const vectorField& Cf = boundaryPatch.Cf(); information and initialize
vectorField& field = *this; vector field field
scalar min = 0.5;
scalar max = 0.7; Initialize variables

scalar t = this->db().time().value(); Access time


...
...
...
#};
}

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,

code Code section. The actual implementation of the BC is done here


#{
...
... Loop using size of boundary patch (Cf) and iterator
... faceI.
This is equivalent to:
forAll(Cf, faceI) for (int faceI=0; Cf.size()<faceI; faceI++)
{

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,

Name of the patch where we want to implement the boundary condition

leftWall Use codedFixedValue BC and initialize value.


{ The initialization is only needed for paraview
type codedFixedValue;
in order to visualize something at time zero.
value uniform 0;
Unique name of the BC
redirectType inletProfile2;
Do not use the same name in other patches

code Code section. The actual implementation of the BC is done here


#{
const fvPatch& boundaryPatch = patch(); Access boundary mesh
const vectorField& Cf = boundaryPatch.Cf(); information and initialize
scalarField& field = *this; scalar field field

field = patchInternalField(); Assign value from the internal field to the patch

scalar min = 0.5;


scalar max = 0.7;
Initialize variables

scalar t = this->db().time().value(); Access time


...
...
...
#};
}
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,

code Code section. The actual implementation of the BC is done here


#{
...
Loop using size of boundary patch (Cf) and iterator
...
... faceI.
This is equivalent to:
forAll(Cf, faceI) for (int faceI=0; Cf.size()<faceI; faceI++)
{
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] = 1.;
Use conditional structure to add
}
else time dependency and assign
{ values to the selected faces.
field[faceI] = 0.;
}
}
}
#};

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

Visualization of water phase (alpha.water) Volume integral of water entering


www.wolfdynamics.com/wiki/BCIC/filltank2.gif the domain

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,

1. $> foamNewBC f v myParabolicVelocity


2. $> cd myParabolicVelocity

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,

1. $> foamNewBC help

Module 7 - Lecture 2
High level programming
Directory structure of the new boundary condition

./myParabolicVelocity
Make
files
options
myParabolicVelocityFvPatchVectorField.C
myParabolicVelocityFvPatchVectorField.H

The directory contains the source code of the boundary condition.


myParabolicVelocityFvPatchVectorField.C: is the actual source code of the
application. This file contains the definition of the classes and functions.
myParabolicVelocityFvPatchVectorField.H: header files required to compile the
application. This file contains variables, functions and classes declarations.
The Make directory contains compilation instructions.
Make/files: names all the source files (.C), it specifies the boundary condition
library name and location of the output file.
Make/options: specifies directories to search for include files and libraries to link the
solver against.
Module 7 - Lecture 2
High level programming
The header file (.H)
Let us start to do some modifications, for this, you will need to open the header file
using your favorite text editor (we use gedit).

99 //- Single valued scalar quantity, e.g. a coefficient


100 scalar scalarData_; In lines 99-126 different types of
101 private data are declared.
102 //- Single valued Type quantity, e.g. reference pressure pRefValue_
103 // Other options include vector, tensor These are the variables we will
104 vector data_;
105
use to do the implementation of
106 //- Field of Types, typically defined across patch faces the new BC.
107 // e.g. total pressure p0_. Other options include vectorField
108 vectorField fieldData_; In our implementation we need
109
110 //- Type specified as a function of time for time-varying BCs
to use vectors and scalars,
111 autoPtr<Function1<vector>> timeVsData_; therefore we can keep the lines
112 100 and 104.
113 //- Word entry, e.g. pName_ for name of the pressure field on database
114 word wordData_; We can delete lines 106-120, as
115
116 //- Label, e.g. patch index, current time index we do not need those datatypes.
117 label labelData_;
118 Also, as we will use two vectors
119 //- Boolean for true/false, e.g. specify if flow rate is volumetric_ in our implementation, we can
120 bool boolData_;
121
duplicate line 104.
122
123 // Private Member Functions You can leave the rest of the file
124 as it is.
125 //- Return current time
126 scalar t() const;
Module 7 - Lecture 2
High level programming
The header file (.H)
At this point, your header file should looks like this one,

99 //- Single valued scalar quantity, e.g. a coefficient


100 scalar scalarData_;
101
102 //- Single valued Type quantity, e.g. reference pressure pRefValue_
103 // Other options include vector, tensor
104 vector data_;
105 vector data_;

Change the name of scalarData_ to maxValue_ (line 100).


Change the names of the two vectors data (lines 104-105). Name first one n_ and the
last one y_.

99 //- Single valued scalar quantity, e.g. a coefficient


100 scalar maxValue_;
101 It is recommended to define
102 //- Single valued Type quantity, e.g. reference pressure pRefValue_
103 // Other options include vector, tensor then in the same order as you
104 vector n_; declare them in the header file
105 vector y_;

You can now save and close the file.


Module 7 - Lecture 2
High level programming
The source file (.C)
Let us start to modify the source file, open it with your favorite editor.
Lines 34-37 refers to a private function definition. This function allows us to access
simulation time. Since in our implementation we do not need to use time, we can
safely remove these lines.

34 Foam::scalar Foam::myParabolicVelocityFvPatchVectorField::t() const


35 {
36 return db().time().timeOutputValue();
37 }

Let us compile the library to see what errors we get. Type in the terminal,

1. $> wmake

You will get a lot of errors.


Since we deleted the datatypes fieldData, timeVsData, wordData, labelData and
boolData in the header file, we need to delete them as well in the C file. Otherwise
the compiler complains. Module 7 - Lecture 2
High level programming
The source file (.C)
At this point, let us erase all the occurrences of the datatypes fieldData, timeVsData,
wordData, labelData, and boolData.
Locate line 38,

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,

150 void Foam::myParabolicVelocityFvPatchVectorField::updateCoeffs()


151 {
152 if (updated())
153 {
154 return;
155 } Find patch bounds (minimum
156 boundBox bb(patch().patch().localPoints(), true); and maximum points)
Add these lines

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,

The actual implementation of the


164 fixedValueFvPatchVectorField::operator== BC is always done in this class
165 (
166 n_*maxValue_*(1.0 - sqr(coord)) Our boundary condition
167 );
168

At this point we have a valid library where we implemented a new BC.


Try to compile it, we should not get any error (maybe one warning). Type in the
terminal,

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

Add these statements


67 if (mag(n_) < SMALL || mag(y_) < SMALL)
68 {
69 FatalErrorIn("parabolicVelocityFvPatchVectorField(dict)")
70 << "n or y given with zero size not correct"
71 << abort(FatalError);
72 }
73
74 n_ /= mag(n_);
75 y_ /= mag(y_);
76
77 fixedValueFvPatchVectorField::evaluate();
78

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

We should not get any error (maybe one warning).


At this point we have a valid library that can be linked with any solver.
If you get compilation errors read the screen and try to sort it out, the compiler is
always telling you what is the problem.
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 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;

The solver will dynamically link the library.


At this point, we are ready to launch the simulation.

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

3. $> icoFoam | tee log


4. $> paraFoam

At this point, you can compare the three implementations (codeStream,


codedFixedValue and high level programming).
All of them will give the same outcome.

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

To create the basic structure of a new application, type in the terminal,

1. $> foamNewApp scratchFoam


2. $> cd scratchFoam

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,

1. $> foamNewApp help


Module 7 - Lecture 2
Implementing an application from scratch
Directory structure of the new boundary condition
scratchFoam/
createFields.H Does not exist, we will create it later
scratchFoam.C
Make
files
options

The scratchFoam directory contains the source code of the solver.


scratchFoam.C: contains the starting point to implement the new application.
createFields.H: in this file we declare all the field variables and initializes the solution.
This file does not exist at this point, we will create it later.
The Make directory contains compilation instructions.
Make/files: names all the source files (.C), it specifies the name of the solver and
location of the output file.
Make/options: specifies directories to search for include files and libraries to link the
solver against.
To compile the new application, we use the command wmake.
Module 7 - Lecture 2
Implementing an application from scratch
Open the file scratchFoam.C using your favorite text editor, we will use gedit.
At this point you should have this file, this does not do anything. We need to add the
statements to create a working applications.
This is the starting point for new applications.
This header is extremely important, it will add all the class
30 declarations needed to access mesh, fields, tensor algebra, fvm/fvc
31 #include "fvCFD.H" operators, time, parallel communication, linear algebra, and so on.
32
33 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
34
35 int main(int argc, char *argv[])
36 {
37 #include "setRootCase.H"
38 #include "createTime.H"
39
40 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
41
42 Info<< nl << "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
43 << " ClockTime = " << runTime.elapsedClockTime() << " s"
44 << nl << endl;
45
46 Info<< "End\n" << endl;
47
48 return 0;
49 }
50

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

32 #include "pisoControl.H" Solution control using PISO class


33
34 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
35
36 int main(int argc, char *argv[])
37 {
38 #include "setRootCase.H Set directory structure

39 #include "createTime.H Create time (object runtime)

40 #include "createMesh.H Create time (object mesh)

41 #include "createFields.H Initialize fields


This source file does not exist yet, we need to create it
42 #include "CourantNo.H Calculates and outputs the Courant Number

43 #include "initContinuityErrs.H Declare and initialize the cumulative continuity error


44
Assign PISO controls to object mesh. Creates object piso.
45 pisoControl piso(mesh);
Alternatively, you can use the header file createControl.H
46
47 Info<< "\nStarting time loop\n" << endl; Output some information
48
Module 7 - Lecture 2
Implementing an application from scratch
We are going to use the PISO control options, even if we do not have to deal with
velocity-pressure coupling.

49 while (runTime.loop()) Time loop


50 {
51 Info<< "Time = " << runTime.timeName() << nl << endl;
52
53 #include "CourantNo.H" Calculates and outputs the Courant Number
54
55 while (piso.correct()) PISO options (correct loop)
56 {
PISO options (non orthogonal corrections
57 while (piso.correctNonOrthogonal())
loop)
58 {
59 fvScalarMatrix Teqn Create object TEqn.
fvScalarMatrix is a scalar instance of fvMatrix
60 (
61 fvm::ddt(T) Model equation (convection-diffusion)
62 + fvm::div(phi, T) We need to create the scalar field T, vector
63 - fvm::laplacian(DT, T) field U (used in phi or face fluxes), and the
64 ); constant DT.
We will declare these variables in the
createFields.H header file.
In the dictionary fvSchemes, you will need to
define how to compute the differential
65 operators, that is,
66 TEqn.solve(); ddt(T)
67 } div(phi, T)
Solve TEqn
68 } laplacian(DT, T)
At this point the object
69
TEqn holds the solution.
Module 7 - Lecture 2
Implementing an application from scratch
We are going to use the PISO control options, even if we do not have to deal with
velocity-pressure coupling.

If you want to compute the CPU time of each iteration,


69
70 #include "continuityErrs.H" Computes continuity errors
71

add the same statement inside the time loop


Write CPU time at the end of the time loop.
72 runTime.write(); Write the solution in the runtime folder
It will write the data requested in the file createFields.H

73 } At this point we are outside of the time loop


74
75 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
76
77 Info<< nl << "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
78 << " ClockTime = " << runTime.elapsedClockTime() << " s"
79 << nl << endl;
80
81 Info<< "End\n" << endl; Output this message
82

83 return 0; End of the program (exit status).


84 } If everything went fine, the program should return 0.
85 To now the return value, type in the terminal,
86 $> echo $?

Module 7 - Lecture 2
Implementing an application from scratch
Let us create the file createFields.H, type in the terminal,

1. $> touch createFields.H

Now open the file with your favorite editor, and start to add the following information,

1 Info<< "Reading field T\n" << endl;


2
3 volScalarField T Create scalar field T
4 (
5 IOobject Create object for input/output operations
6 (
7 "T", Name of the dictionary file to read/write
8 runTime.timeName(), runtime directory
9 mesh, Object registry
10 IOobject::MUST_READ,
11 IOobject::AUTO_WRITE Read the dictionary in the runtime directory
12 ), (MUST_READ, and write the value in the runtime
13 mesh Link object to mesh directory (AUTO_WRITE).
14 ); If you do not want to write the value, use the option
NO_WRITE

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.

17 Info<< "Reading field U\n" << endl;


18
19 volVectorField U Create vector field U
20 (
21 IOobject
22 (
23 "U", Name of the dictionary file to read/write
24 runTime.timeName(),
25 mesh,
26 IOobject::MUST_READ,
27 IOobject::AUTO_WRITE
28 ),
29 mesh
30 );
31

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.

33 Info<< "Reading transportProperties\n" << endl;


34
35 IOdictionary transportProperties Create object transportProperties used to
36 ( read data
37 IOobject
38 ( Name of the input dictionary
39 "transportProperties",
40 runTime.constant(), Location of the input dictionary, in this case
41 mesh, is located in the directory constant
42 IOobject::MUST_READ_IF_MODIFIED, Re-read data if it is modified
43 IOobject::NO_WRITE
44 ) Do not write anything in the dictionary
45 );
46
47
48 Info<< "Reading diffusivity DT\n" << endl;
49
50 dimensionedScalar DT Create scalar DT (diffusion coefficient)
51 (
52 transportProperties.lookup("DT") Access value of DT in the object
53 ); transportProperties
54
55 #include "createPhi.H" Creates and initializes the relative face-
56 flux field phi.

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

You will find a case ready to run in the directory,

$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

Recompile the solver and rerun the test case.


The solver will complain, try to figure out what is the problem (you are missing some
information in the fvSchemes dictionary).
Module 7 - Lecture 2
Implementing an application from scratch
Let us talk about the file write.H,

1 volVectorField gradT(fvc::grad(T)); Compute gradient of T.


2 fvc is the explicit operator, it will compute the
3 volVectorField gradT_vector
requested value using the solution of T
4 (
5 IOobject
6 (
7 "gradT",
8 runTime.timeName(), Save vector field in output dictionary gradT
9 mesh,
10 IOobject::NO_READ,
11 IOobject::AUTO_WRITE
12 ),
13 gradT
14 );
15
...
56
57 volScalarField divGradT
58 (
59 IOobject
60 (
Compute divergence of gradT.
61 "divGradT", The output of this operation is a scalar field.
62 runTime.timeName(), In this case we compute the quantity inside the scalar field
63 mesh, declaration (line 67).
64 IOobject::NO_READ, We use the fvc operator because the solution of gradT is
65 IOobject::AUTO_WRITE already known.
66 ),
67 fvc::div(gradT)
68 ); In the dictionary fvSchemes, you will need to tell the solver how to do
69 ... the interpolation of the term div(grad(T))
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
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

Let us clone the original solver, type in the terminal,

1. $> cp -r $FOAM_APP/solvers/incompressible/icoFoam/ my_icoFoam

2. $> cd my_icoFoam

At this point, we are ready to modify the solver.

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,

111 U = HbyA - rAU*fvc::grad(p);


112 U.correctBoundaryConditions();
113 }
114
115 solve
116 ( Scalar transport equation.
117 fvm::ddt(S1) The name of the scalar is S1.
118 + fvm::div(phi, S1) We need to declare it in the
createFields.H file.
119 - fvm::laplacian(DT, S1) . We also need to read the coefficient DT.
120 );
121
122 runTime.write();
123

As the passive scalar equation depends on the vector field U, we need to


add this equation after solving U.

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

Name of the executable.


1 icoFoam.C Name of the input file To avoid conflicts with the original
installation, we give a different
2 name to the executable
3 EXE = $(FOAM_USER_APPBIN)/my_icoFoam
Location of the executable.
To avoid conflicts with the original installation, we install the
executable in the users personal directory
Module 7 - Lecture 2
Adding the scalar transport equation to icoFoam
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 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

You will find a case ready to run in the directory,

$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

3. $> my_icoFoam | tee log


4. $> paraFoam

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

S1 inlet values Visualization of velocity magnitude and passive scalar S1


www.wolfdynamics.com/wiki/BCIC/2delbow_S1

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

You might also like