Usage

CaseFOAM allows the creation and analysis of parameter-studies for OpenFOAM cases.

Generation of Parameter studies

To setup two cases with a different 'internalField' , we have to define the structure of the cases. This has to be a list within a list.

>>> caseStructure = [['Ux1', 'Ux2']]

CaseFOAM’s mkCases method generates the caseStructure which is defined above.

>>> import casefoam
>>> baseCase = 'pitzDaily'
>>> casefoam.mkCases(baseCase, caseStructure, caseData, hierarchy='tree')

There are two options available how the cases are created specified by the function parameter``hierarchy`` which can be flat (pitzDaily_Ux1, pitzDaily_Ux1) or tree (pitzDaily/Ux1, pitzDaily/Ux2). The variable baseCase is the path to the case we want to modify.

caseData specifies the data for the case manipulation. It links the entries from the caseStructure to the modification dictionaries.

>>> caseData = {'Ux1': update_Ux1, 'Ux2': update_Ux2}

The values for 'Ux1' and 'Ux2' should modify e.g the internal velocity field of the case

>>> update_Ux1 = {'0/U': {'internalField': 'uniform (1 0 0)'}}
>>> update_Ux2 = {'0/U': {'internalField': 'uniform (2 0 0)'}}

There a three different options how the modification data can be specified

  • replacing a string inside the specified files
  • executing a bash script
  • specifying a dictionary

But all method have in common that the are dictionaries.

replacing strings

Manipulating a file by exchanging a string is straighforward, just specify the file in which the string should be replaced After the filename a dictionary follows starting with the keyword #!stringManipulation with another dictionary with the string and the replacement values.

update_grid1 = {
    'system/blockMeshDict': {'#!stringManipulation': {'varA': '23',
                                                      'varB': '8',
                                                      'varC': '19',
                                                      'varD': '42',
                                                      'varE': '4'}}
}

In this case we would replace the varA to varE in the system/blockMeshDict

executing a script

To execute bash commands from casefoam create a dictionary with the keyword #!bash followed by a command

update_coarse = {
    'Allrun.slurm': {'#!stringManipulation': {'JOBNAME': 'coarse'}},
    '#!bash': 'cp -rn meshes/coarse/constant koala/coarse'}

Here, we replace the string JOBNAME in the Allrun.slurm script and copy the mesh from a different location in our case

specifying a dictionary

Another variant for changing OpenFOAM files is with the help of the PyFoam. PyFoam can load openfoam files and represented them as python-dictionaries. To get the correct format you can use the getFileStructure utility from casefoam.utility.

>>> import casefoam
>>> casefoam.utility.getFileStructure('forwardStep/0/U')
{'boundaryField': {'bottom': {'type': 'symmetryPlane'},
                   'defaultFaces': {'type': 'empty'},
                   'inlet': {'type': 'fixedValue',
                             'value': 'uniform (3 0 0)'},
                   'obstacle': {'type': 'slip'},
                   'outlet': {'type': 'inletOutlet',
                              'inletValue': 'uniform (3 0 0)',
                              'value': 'uniform (3 0 0)'},
                   'top': {'type': 'symmetryPlane'}},
 'dimensions': '[ 0 1 -1 0 0 0 0 ]',
 'internalField': 'uniform (3 0 0)'}

Again, first the filename followed by a dictionary is specified. For every sub-dictionary in OpenFOAM, a new dictionary needs to be specified. Following command would manipulate the inlet velocity:

update_Ux = {
    '0/U': {'boundaryField': {'inlet': {'value': 'uniform (3 0 0)'}}}
}

Help in Python

If you are running IPython, you can get direct help for the modules.

>>> import casefoam
>>> help(casefoam.mkCases)
Make OpenFOAM cases.

Make OpenFOAM cases based on a base case. The structure and folder
hierarchy can be choosen freely. The case structure is set with an list of
items to be changed. For example:

    >>> [[parent1, parent2],
         [child1, child2, child3],
         [grandchild1, grandchild2]]

gives cases `parent1/child1/grandchild1` ... `parent2/child3/grandchild2`.

Post-Processing

All post-processing functions return a pandas dataframe in the long-format. The advantage of this format is that non Nan values are necessary which frequently happens if multiple cases with different time step size are compared. The conversion between long and wide format is straighforward and is achieved with the pandas command pd.pivot_table.

>>> pd.pivot_table(df,index=df.index, columns=['col_1','col_2'])

Various tools for visualiation of pandas dataframe exist. They have an in-built function plot() which generates a matplotlib figure. It plots every column and is helpful if the dataFrame is in the wide format. Another powerful visualiation tool is holoview which has his strength in interactive visualiation and combination with jupyter-notebook. The created figures are interactive and can be stored in a html which can passed to colleagues.

A more examples can be find in the example folder. Here is a basic one:

>>> import casefoam

>>> # directory of the base case
>>> baseCase = 'damBreak'

>>> # list of parent, child and grandchild names
>>> caseStructure = [['grid1', 'grid2', 'grid3']]

>>> # probe location
>>> probeDir = 'probes/0'

>>> # load probe data
>>> p = casefoam.time_series(probeDir, 'p', caseStructure, baseCase)

Three functions are available for the post-processing:

  • time_series
  • positional_field
  • posField_to_timeSeries

time_series is intended for plotting time series as probe data or forces. positional_field can plot fields suchs as sets and surface. posField_to_timeSeries converts a positional_field to a time series by reducing the postional field to a single value with a user specified function.

The below video shows the damBreak test case

The column height and resolution of the grid is varied with casefoam. The freesurface at 0.3 seconds can be rendered in a html file with:

import casefoam
import matplotlib.pyplot as plt
import pandas as pd
import holoviews as hv
hv.extension('bokeh')

caseStructure = [['height_02', 'height_03', 'height_04'],
                 ['grid1', 'grid2', 'grid3']]
baseCase = 'Cases'
surfaceDir = 'freeSurface'
surface = casefoam.positional_field(solutionDir=surfaceDir,
                                    file='U_freeSurface.raw',
                                    time=0.3,
                                    caseStructure=caseStructure,
                                    baseCase=baseCase)
surface.columns = ['x','y','z','Ux','Uy','Uz','col_height','res']
surface_ds = hv.Dataset(surface, [ 'col_height','res'],
                        ['x','y','z','Ux','Uy','Uz'])

holoviews is optimized for the use for the jupyter notebooks. The %%opts arguments are used to modify the layout of the plot. holoviews renders an interactive plot which can be exported as html:

%%output filename="contour" fig="html"
%%opts Scatter [width=600,
                height=600,
                title='freeSurface at 0.3s',
                tools=['hover']]
%%opts (muted_alpha=0.0)
surface_ds.to(hv.Scatter,'x','y').overlay('res')