Supplement Programming Openfoam
Supplement Programming Openfoam
1
Roadmap
2
Programming in OpenFOAM®. Building blocks
3
Programming in OpenFOAM®. Building blocks
• During this session we will study the building blocks to write basic programs in 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).
4
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.
0 Scalar scalar
5
Programming in OpenFOAM®. Building blocks
Basic tensor classes in OpenFOAM®
• In OpenFOAM®, the second rank tensor (or matrix)
6
Programming in OpenFOAM®. Building blocks
Basic tensor classes in OpenFOAM®
• For instance, the following statement,
$> Txz = 3
• Notice that to output information to the screen in OpenFOAM®, we use the function Info instead
of the function cout (used in standard C++).
• The function cout will work fine, but it will give you problems when running in parallel.
7
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
You can find a complete list of all operators in the programmer’s guide 8
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.
9
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 );
10
Programming in OpenFOAM®. Building blocks
Units correspondence in dimensionSet
• The units of the class dimensionSet are defined as follows
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 );
11
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;
...
...
...
• 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().
13
Programming in OpenFOAM®. Building blocks
OpenFOAM® lists and fields
• OpenFOAM® frequently needs to store sets of data and perform mathematical operations.
• OpenFOAM® 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 field classes in the directory $FOAM_SRC/OpenFOAM/fields/Fields.
• Algebraic operations can be performed between fields, 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.
14
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 tensors are initialized to
one.
• We can access components on the list using the access operator [ ].
15
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 $HOME/OpenFOAM/OpenFOAM-
8/applications/test. 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
16
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
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?
18
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
19
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
20
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.
21
Programming in OpenFOAM®. Building blocks
Data stored in the fvMesh class
Access
Class Description Symbol
function
volScalarField Cell volumes V()
22
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.
23
Programming in OpenFOAM®. Building blocks
Accessing fields using for loops
• To access fields using for loops, we can use OpenFOAM® macro forAll, as follows,
• In the previous statement mesh.boundaryMesh() is the size of the loop, and patchI is the
iterator. The iterator always starts from zero.
• The forAll loop is equivalent to the standard for loop in C++.
• Notice that we used as iterator i instead of patchI, this does not make any difference. 24
Programming in OpenFOAM®. Building blocks
Equation discretization in OpenFOAM®
• At this stage, 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.
25
Programming in OpenFOAM®. Building blocks
Discretization of the basic PDE terms in OpenFOAM®
The list is not complete
Mathematical fvm::
Term description
expression fvc::
laplacian(phi)
Laplacian , laplacian(Gamma, phi)
, ddt(phi)
Time derivative
ddt(rho,phi)
div(psi,scheme)
Convection ,
div(psi,phi)
Sp(rho,phi)
Source
SuSp(rho,phi)
• Remember, you will need to first create the mesh, and initialize the variables and constants.
That is, all the previous steps.
• Finally, everything we have done so far inherits all parallel directives. There is no need for
specific parallel programming.
27
Programming in OpenFOAM®. Building blocks
Discretization of the basic PDE terms in OpenFOAM®
• The previous discretization is equivalent to,
• Here, fvScalarMatrix contains the matrix derived from the discretization of the model equation.
• fvScalarMatrix is used for scalar fields and fvVectorMatrix is used for vector fields.
• This syntax is more general, since it allows the easy addition of terms to the model equations.
28
Programming in OpenFOAM®. Building blocks
Discretization of the basic PDE terms in OpenFOAM®
• At this point, OpenFOAM® assembles and solves the following linear system,
Unknow quantity 29
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.
30
Programming in OpenFOAM®. Building blocks
A few OpenFOAM® programming references
• You can access the API documentation in the following link, https://cpp.openfoam.org/v5/
• 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® Programmer’s guide in the following link (it seems that this guide is not
supported anymore), http://foam.sourceforge.net/docs/Guides-a4/ProgrammersGuide.pdf
32
Implementing boundary conditions using 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 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
33
Implementing boundary conditions using high level programming
• Let us create the basic structure to write the new boundary condition, type in the terminal,
• The utility foamNewBC, will create the directory structure and all the files needed to write your
own boundary conditions.
• We are setting the structure for a fixed (the option –f) velocity (the option –v), boundary
condition, and we name our boundary condition ParabolicVelocity .
• If you want to get more information on how to use foamNewBC, type in the terminal,
34
Implementing boundary conditions using high level programming
./myParabolicVelocity
├── Make
│ ├── files
│ └── options
├── myParabolicVelocityFvPatchVectorField.C
└── myParabolicVelocityFvPatchVectorField.H
• We just declared the variables that we will use. You can now save and close the file.
37
Implementing boundary conditions using high level programming
• Let us start to modify the source file. Open the source file 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.
• Let us compile the library to see what errors we get. Type in the terminal,
1. $> wmake
38
Implementing boundary conditions using high level programming
• 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, 63-67, 90-94, 102-106, 115-119, 172-175.
• Erase the following lines (they contain the word fieldData), 126, 140, 151-153.
• Replace all the occurrences of the word scalarData with maxValue (11 occurrences).
39
Implementing boundary conditions using high level programming
• 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
40
Implementing boundary conditions using high level programming
• We are almost done; we just defined all the datatypes. Now we need to implement the actual
boundary condition.
• Look for line 147 ( updateCoeffs() member function), and add the following statements,
The actual
147 void Foam::myParabolicVelocityFvPatchVectorField::updateCoeffs() implementation of
148 { the BC is always
149 if (updated()) done in this class
150 {
151 return;
152 }
153 Find patch bounds (minimum
154 boundBox bb(patch().patch().localPoints(), true); and maximum points)
Add these lines
155
156 vector ctr = 0.5*(bb.max() + bb.min()); Coordinates of patch midpoint
157
158 const vectorField& c = patch().Cf(); Access patch face centers
159
160 scalarField coord = 2*((c - ctr) & y_)/((bb.max() - bb.min()) & y_);
x
x
Computes scalar field to be used for defining the parabolic profile
41
Implementing boundary conditions using high level programming
• The last step before compiling the new BC is to erase a few commas.
• Look for lines 48, 64, 92, 105, 119, and erase the comma at the end of each line.
• At this point we have a valid library where we implemented a new BC.
• Finally, you can go back to the header file (*.H) and document your boundary condition
implementation.
• You can add the comments in the header of the file (lines 1-73).
42
Implementing boundary conditions using high level programming
• At this point we have a valid library where we have 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 if you can not fix the compilation errors, you will find the source code in
the directory,
• $PTOFC/101programming/src/myParabolicVelocity
43
Implementing boundary conditions using high level programming
• 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 did 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.
44
Implementing boundary conditions using high level programming
• 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)
45
Implementing boundary conditions using high level programming
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 }
46
Implementing boundary conditions using high level programming
• Since we do not want the vectors n and y to be zero vectors, we add the following sanity check
starting form line 67.
• These statements check if the given n and y vectors in the input dictionary is zero or not.
• If any of the vectors are 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
47
Implementing boundary conditions using high level programming
1. $> wmake
48
Implementing boundary conditions using high level programming
• Before using the new BC, let us take a look at the logic behind the implementation.
ser defined
49
Implementing boundary conditions using high level programming
1. $> cd $PTOFC/101programming/src/case_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
50
Implementing boundary conditions using high level programming
15 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
16
17 libs ("libmyParabolicVelocity.so"); Name of the library
18 You can add as many libraries as you like
19 application icoFoam;
51
Implementing boundary conditions using high level programming
1. $> foamCleanTutorials
2. $> fluentMeshToFoam ../../../meshes_and_geometries/fluent_elbow2d_1/ascii.msh
• At this point, you can compare the three implementations (codeStream, codedFixedValue and
high-level programming).
• All of them will give the same outcome.
52
Implementing boundary conditions using high level programming
177 fixedValueFvPatchVectorField::updateCoeffs();
178
179 Info << endl << "Face centers (c):" << endl;
180 Info << c << endl;
181 Info << endl << "Patch center (ctr):" << endl;
182 Info << ctr << endl;
183 Info << endl << "Patch (c - ctr):" << endl;
184 Info << c - ctr << endl;
185 Info << endl << "Patch max bound (bb.max):" << endl;
186 Info << bb.max() << endl;
187 Info << endl << "Patch min bound (bb.max):" << endl;
188 Info << bb.min() << endl;
189 Info << endl << "Patch coord ( 2*((c - ctr) & y_)/((bb.max() - bb.min()) & y_) ):" << endl;
190 Info << coord << endl;
191 Info << endl << "Patch ( 1.0 - sqr(coord)) :" << endl;
192 Info << n_*maxValue_*(1.0 - sqr(coord))<< endl;
193 Info << endl << "Loop for c, BC assigment << endl;
194 forAll(c, faceI)
195 {
196 Info << c[faceI] << " " << n_*maxValue_*(1.0 - sqr(coord[faceI])) << endl;
197 }
198
199
• Recompile, rerun the simulation, look at the output, and do the math.
53
Implementing boundary conditions using high level programming
54
Roadmap
55
Modifying applications – 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.
56
Roadmap
57
Implementing an application from scratch
• Let us do a little bit of high-level programming, this is the hard part of working with
OpenFOAM®.
• At this point, you can work in any directory. But we recommend you to work in your
OpenFOAM® user directory, type in the terminal,
1. $> cd $WM_PROJECT_USER_DIR/run
• The utility foamNewApp, will create the directory structure and all the files needed to create the
new application from scratch. The name of the application is scratchFoam.
• If you want to get more information on how to use foamNewApp, type in the terminal,
60
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 "fv D.H“
44 #include "ini on inui yErrs.H“ Declare and initialize the cumulative continuity error
Assign PISO controls to object mesh. Creates object piso.
49 pisoControl piso(mesh);
Alternatively, you can use the header file createControl.H
51 Info<< "\nStarting time loop\n" << endl; Output some information
61
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.
63
Implementing an application from scratch
• Let us create the file createFields.H, type in the terminal,
• Now open the file with your favorite editor, and start to add the following information,
64
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.
65
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.
66
Implementing an application from scratch
• At this point, we are ready to compile. Type in the terminal,
1. $> wmake
• If everything went fine, you should have a working solver named scratchFoam.
• If you are feeling lazy or you can not fix the compilation errors, you will find the source code in
the directory,
• $PTOFC/101programming/applications/solvers/scratchFoam
$PTOFC/101programming/applications/solvers/scratchFoam/test_case
• At this point, we are all familiar with the convection-diffusion equation and OpenFOAM®, so you
know how to run the case. Do your magic.
67
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
68
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 an 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
71
Adding the scalar transport equation to icoFoam
• Let us modify a solver, we will work with icoFoam.
• We will add a passive scalar (convection-diffusion equation).
• At this point, you can work in any directory. But we recommend you to work in your
OpenFOAM® user directory, type in the terminal,
1. $> cd $WM_PROJECT_USER_DIR/run
2. $> cd my_icoFoam
72
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,
• As the passive scalar equation depends on the vector field U, we need to add this equation after
solving U.
73
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
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 ); 74
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 your favorite editor, open the file Make/files,
Original file
1 icoFoam.C
2
3 EXE = $(FOAM_APPBIN)/icoFoam
Modified file
1. $> wmake
• If everything went fine, you should have a working solver named my_icoFoam.
• If you are feeling lazy or you can not fix the compilation errors, you will find the source code in
the directory,
• $PTOFC/101programming/applications/solvers/my_icoFoam
$PTOFC/101programming/applications/solvers/my_icoFoam/test_case
76
Adding the scalar transport equation to icoFoam
Running the case
• This case is ready to run, the input files are located in the directory
$PTOFC/101programming/applications/solvers/my_icoFoam/test_case
• To run the case, type in the terminal,
1. $> foamCleanTutorials
2. $> fluentMeshToFoam ../../../../../meshes_and_geometries/fluent_elbow2d_1/ascii.msh
• Remember, you will need to create the file 0/S1 (boundary conditions and initial conditions for
the new scalar).
• You will also need to create the input dictionary constant/diffusionProperties, from this
dictionary we will read the diffusion coefficient value.
• Finally, remember to update the files system/fvSchemes and
system/fvSolution to take into account the new equation.
77
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
78