Performing parameter variations can be stressful, especially if some parameters have to be altered in various places in an OpenFOAM case. This is where the python modules of PyFoam come in quite handy. I assume, that you have a basic knowledge on how an OpenFOAM case is structured and that you are familiar with programming in python. If you need a brief refreshment of your python skills, you can pick one of these tutorials.
1. Preparation
The first step is to choose a case to do a parameter variation on. For the sake of simplicity, I chose the lid driven cavity as an example. This can be found in the OpenFOAM tutorial folder located at $FOAM_TUTORIALS/incompressible/icoFoam/cavity
.
To keep the original tutorial unaltered, we need to create a parent folder and copy the cavity tutorial case folder to that directory. Now we need to change to that directory. I chose my OpenFOAM user folder for that
run
mkdir cavityParameterStudy
cd cavityParameterStudy
cp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity cavity-template
cd cavity-template
The current structure looks like this:
|-cavityParameterStudy
|---cavity-template
In the following, I will call the cavityParameterStudy folder “parent folder” and the cavity-template folder “template folder”.
2. Getting Started
The basic idea behind doing a parameter variation using PyFoam is to create a template case, clone that via a script and alter the respective parameters in that cloned case. We need to create the python script, that will do all the exhausting work for us. I am a vim fanboy, but please feel free to use whatever editor suites your need: vim parameterVariation.py
Before doing anything serious, I do perform organisational steps inside the python script:
from os import path
from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile
from PyFoam.Basics.DataStructures import Vector
templateCase = SolutionDirectory("cavity-template", archive=None, paraviewLink=False)
Very brief explanation of the above lines:
- Line two imports a class that can handle an OpenFOAM case folder with all it’s essential directories. It provides easy access to these folders and the particular dictionaries, as well as a method to clone that case to an other - not yet existing - OpenFOAM case.
- The PyFoam class, dealing with accessing and altering OpenFOAM dictionaries is imported in line three.
- As I am a lazy person, when it comes to tasks that can be simplified significantly by some piece of software, I import the Vector class of PyFoam as well. We will use it, when we alter the velocity boundary condition of the cavity.
- An instance of the imported SolutionDirectory is instantiated for the template folder, that contains the case we want to alter systematically.
3. Deciding which Parameters to vary
Within the scope of this tutorial, we will vary the tangential velocity at the top boundary of the cavity. To reduce the amount of typing, we let python calculate the respective velocities and to do so, an extra line must be added to the header:
from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile
from PyFoam.Basics.DataStructures import Vector
# Add the following line
from numpy import linspace
This imports the linspace function from numpy, which divides an interval into an arbitrary amount of steps. This function is used as such:
uTangential = linspace(0.1,0.8,8)
print uTangential
>>> array([ 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
4. Accessing the Boundary Conditions
What we’ve learned so far is to import some packages from PyFoam and to create a numpy array with some velocities in it. The next major step is to access the boundary condition file for the velocity and to modify it according to our specifications. Before we do any modifications, we should clone the template into a new case:
case = templateCase.cloneCase("testCase")
What does this do?
- Create a new folder on your disk, that is named
testCase
. - Copy all basic files and folders from
cavity-template
over totestCase
. This does not include log files, time-step folder and so on. Just0/
,constant/
andsystem/
. Any other files and folders must be specified explicitly. - Return a new SolutionDirectory object, that points to
testCase
and store it in case.
The reason why to handle the path operations by means of PyFoam and not by manually access all required paths is simply because it is much less error prone, requires less typing and is much faster.
The next step is to access the velocity boundary condition and alter the “movingWall” patch:
velBC = ParsedParameterFile(path.join(case.name,"0", "U"))
velBC["boundaryField"]["movingWall"]["value"].setUniform(uTangential[0],0,0)
velBC.writeFile()
What it does, line by line:
- Create a
ParsedParameterFile
object for the velocity boundary condition file. This can be done for any other file as well, includingconstant/
andsystem/
files, if you would like to change some other parameters. - Access the movingWall subdictionary in the boundaryField dictionary of the velocity boundary condition and set the value of the Dirichlet1 boundary condition to be uniform for all faces on the patch and use the first tangential velocity, that was defined previously using the linspace expression.
- Write all changes to the boundary condition.
5. Using a loop
As the above lines do not account for changing the velocity multiple times, we employ a for loop around the ParsedParameterFile
lines, so that the entire script should look like:
from os import path
from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile
from PyFoam.Basics.DataStructures import Vector
from numpy import linspace
uTangential = linspace(0.1,0.8,8)
templateCase = SolutionDirectory("cavity-template", archive=None, paraviewLink=False)
for uI in uTangential:
case = templateCase.cloneCase("cavity-u%.1f" %uI)
velBC = ParsedParameterFile(path.join(case.name,"0", "U"))
velBC["boundaryField"]["movingWall"]["value"].setUniform(Vector(uI,0,0))
velBC.writeFile()
This script loops over all defined velocities, clones the template case and stores the correct velocity in the velocity boundary condition. At the current state of the tutorial, the cases must be started manually, though this can be automated fairly easy with PyFoam.BasicRunner
. But this will be covered by an extra tutorial.
Contributors
- Rudolf for bugfixes