Parameter Variation in OpenFOAM Using PyFoam

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 to testCase. This does not include log files, time-step folder and so on. Just 0/, constant/ and system/. 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:

  1. Create a ParsedParameterFile object for the velocity boundary condition file. This can be done for any other file as well, including constant/ and system/ files, if you would like to change some other parameters.
  2. 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.
  3. 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

  1. Defines the value for a boundary (see wiki)