OpenFOAM bits: the #includeIfPresent dictionary directive

As mentioned in the ReleaseNotes-1.6,

the new #includeIfPresent directive is similar to the #include directive, but does not generate an error if the file does not exist.

Initially, this may not sound particularly useful. Perhaps you haven’t really found very many uses for the normal #include directive anyway, so why would you ever want it to fail silently if the file doesn’t exist? This functionality turns out, however, to actually be quite useful.

Before examining this, we should first be aware of the OpenFOAM string expansion that is implicit in the #include and in the #includeIfPresent directives.


OpenFOAM String Expansion

In many places where a string or filename is expected within an OpenFOAM dictionary, the string is first subjected to an expansion. The two most essential aspects:

  1. Elements within the string that resemble shell variables (eg, $VAR, ${VAR}, ..) are expanded using environment variables.

  2. A leading ~OpenFOAM is expanded to a user/site/shipped OpenFOAM configuration directory. The command foamEtcFile -list can be used to report the corresponding directories for your installation. While a leading ~/ and a leading ~user are also expanded with their usual Unix shell meanings, the ~OpenFOAM pseudo-user is a very much more flexible solution.

In addition to the original environment, the following environment variables are also defined internally (since almost all of the OpenFOAM solvers and utilities use the argList class):

FOAM_CASE
set to the path of the global case (same for serial and parallel jobs). This corresponds roughly to the value provided by the -case option or the current working directory if the -case option was not specified.
FOAM_CASENAME
set to the name of the global case. This is the equivalent of the basename of FOAM_CASE (ie, stripped of any leading directory components).

Whereas the FOAM_CASENAME variable is new in the OpenFOAM-1.6 version, the FOAM_CASE variable has existed for sometime in OpenFOAM. With FOAM_CASE, we have a convenient means of addressing locations elsewhere within our calculation case. This is often useful when specifying the location of tabulated boundary conditions that don’t really fit anywhere nicely in OpenFOAM’s constant/system/timeValue directory scheme.

If you happen to encounter a dictionary that uses $FOAM_ROOT/$FOAM_CASE, this is from the days before OpenFOAM had the -case option. In current versions (OpenFOAM-1.5 and newer), $FOAM_ROOT/ is no longer defined or needed and you can simply remove it.


Example 1

Sharing constant/ and system/ directories between related calculations is an example of where #includeIfPresent comes in handy.

Suppose we have an existing mesh, boundary conditions and solver settings in the directory oldCalc and we would like to reuse as much as possible for a similar calculation newCalc. The only chage might be, for example, a change in the inlet velocity boudary condition. The brute-force method would be to use cp or rsync to copy everything and then make our changes to the boundary conditions before running the newCalc. This, however, not only wastes disk space, but is really annoying later when you try to figure out if oldCalc and newCalc did actually use exactly the same mesh.

A much more elegant solution is to simply use file links. For example,

    mkdir newCalc
    cd newCalc

    ln -s ../oldCalc/0 .
    ln -s ../oldCalc/constant .
    ln -s ../oldCalc/system .

Of course since we have linked in the entire constant/ and system/ directories, it is going to be rather difficult to modify anything in newCalc/system/ without inadvertently changing the values for oldCalc/system/. The solution is to have slightly modified versions of the important system/ dictionaries (ie, system/controlDict, system/decomposeParDict, system/fvSchemes, system/fvSolution) that have a very minor modification near the end of them:

// system/controlDict
...
#includeIfPresent "$FOAM_CASE/controlDict"

and

// system/fvSolution
...
#includeIfPresent "$FOAM_CASE/fvSolution"

etc.

This makes it possible to share most of the settings while retaining the ability to make local adjstments for a particular case without affecting the other. This type of statement can be added to all of your system/ dictionaries without any problems. The files will only be included if they are present.

Since these included files are only included by another dictionary and are never read directly by other OpenFOAM operations, we can forgo the niceties of having a FoamFile header if we wish. For example, to alter a few relaxation factors:

    echo "relaxationFactors { p 0.15; U 0.6; }" > fvSolution
    touch system/fvSolution

Which also lends itself quite nicely to scripting. Since we didn’t actually edit any values in system/fvSolution itself, but instead used the #includeIfPresent to merge in the new values, the extra touch is required to change the file modification time of system/fvSolution and get OpenFOAM to notice that something has changed.

Later, when our calculation is now over the rough bits, we may wish to revert to the normal relaxation factors. We have a variety of ways to achieve it. We’ll take the simplest:

    rm fvSolution
    touch system/fvSolution

Example 2

Now that we have the general idea, we can address what was glossed over in the previous example: if the 0/ directory is shared, how can we specify different boundary conditions?

The solution builds on the same concept that I posted on the OpenFOAM forum some time ago. The idea being to specially craft the 0/ fields to avoid directly mentioning any specific values.

As a concrete example, we could have a 0/T file that looks like this:

FoamFile
{
    version     2.0;
    format      ascii;
    class       volScalarField;
    object      T;
}
// * * * * * * * * * * * * * * * * * * //
#include        "$FOAM_CASE/boundaryConditions"
dimensions      [0 0 0 1 0 0 0];
internalField   uniform $temperature;

inletCondition
{
    type        fixedValue;
    value       $internalField;
}
wallCondition
{
    type        zeroGradient;
}
#include        "boundaryField"

The first point of interest is that we place all of our boundary conditions in a single file and place this file directly in the $FOAM_CASE directory. This not only lets us share the 0/ files between the calculations, it also places all of our essential boundary conditions in a single file where they are easy to find and simple to edit. The $FOAM_CASE/boundaryConditions file would contain something like this:

FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      boundaryConditions;
}
// * * * * * * * * * * * * * * * * * * //
flowRate        0.2;     // 720 kg/h
pressure        1.15e+5;
temperature     1273;    // 1000 C
intensity       0.1;
mixingLength    0.005;

The second point of interest is that the 0/U, 0/T, … files define an inletCondition that can be used in a generic way within the 0/boundaryField file. This file can (should) include any specific specializations near the end:

// 0/boundaryField
...
boundaryField
{
    #includeIfPresent   "$FOAM_CASE/boundaryField"
    #includeIfPresent   "$FOAM_CASE/boundaryField-$FOAM_CASENAME"
}

The contents (if any) of the “$FOAM_CASE/boundaryField” and “$FOAM_CASE/boundaryField-$FOAM_CASENAME” files can now be used to specify which particular patches are currently to have an inletCondition and which should have a wallCondition etc.


Closure

Clever use of dictionary structuring and the dictionary directives allows us to reuse most, if not all, of the dictionaries and initial condition definitions between related calculations. The #include and #includeIfPresent directives let us do so with a very large degree of flexiblity.

Caution

Although the #include looks like a pre-processor directive, it is in fact a function written without any space in between:

    #include    "..."   <- OK
    # include   "..."   <- WRONG
17 Nov 2009 | OpenFOAM