What are the syntax rules for OpenFOAM files?

OpenFOAM configuration files are discussed in depth in our OpenFOAM Training

See Training

4.2 Input/output file format

OpenFOAM needs to read a range of data structures such as strings, words, scalars, vectors, tensors, lists and fields. The input/output (I/O) format of files is extremely flexible, following a consistent set of rules that make the files easy to interpret. The OpenFOAM file format is described in the following sections.

4.2.1 General syntax rules

The format resembles C++ code, following the general principles below.

  • Files have free form, with no particular meaning assigned to any column and no need to indicate continuation across lines.

  • Lines have no particular meaning except to a // comment delimiter which makes OpenFOAM ignore any text that follows it until the end of line.

  • A comment over multiple lines is done by enclosing the text between /* and */ delimiters.

4.2.2 Dictionaries

OpenFOAM mainly uses dictionaries to specify data, in which data entries are retrieved by means of keywords. Each keyword entry follows the general format, beginning with the keyword and ending in semi-colon (;).


    <keyword>  <dataEntry1>  <dataEntryN>;
Many entries include only a single data entry as shown below.


    <keyword>  <dataEntry>;
Most data files, e.g. controlDict, are themselves dictionaries since they contain a series of keyword entries. Any dictionary can contain one or more sub-dictionaries, usually denoted by a dictionary name and its keyword entries contained within curly braces {} as follows.


    <dictionaryName>
    {
         keyword entries 
    }
(Sub-)dictionaries can be nested within others, as shown in the following example. The extract, from an fvSolution dictionary file, containing two dictionaries, solvers and PIMPLE. The solvers dictionary contains nested sub-dictionary for different matrix equations based on different solution variables, e.g. p, U and k (with some entries using regular expressions described in section 4.2.13 ).
16

17solvers

18{

19    p

20    {

21        solver           GAMG;

22        tolerance        1e-7;

23        relTol           0.01;

24

25        smoother         DICGaussSeidel;

26

27    }

28

29    pFinal

30    {

31        $p;

32        relTol          0;

33    }

34

35    "(U|k|epsilon)"

36    {

37        solver          smoothSolver;

38        smoother        symGaussSeidel;

39        tolerance       1e-05;

40        relTol          0.1;

41    }

42

43    "(U|k|epsilon)Final"

44    {

45        $U;

46        relTol          0;

47    }

48}

49

50PIMPLE

51{

52    nNonOrthogonalCorrectors 0;

53    nCorrectors         2;

54}

55

56

57// ************************************************************************* //
   

4.2.3 The data file header

All data files that are read and written by OpenFOAM begin with a dictionary named FoamFile containing a standard set of keyword entries, listed below:

  • version: I/O format version, optional, defaults to 2.0

  • format: data format, ascii or binary

  • class: class relating to the data, either dictionary or a field, e.g. volVectorField

  • object: filename, e.g. controlDict (mandatory, but not used)

  • location: path to the file (optional)

A example header for a controlDict file is shown below.


    FoamFile
    {
        format      ascii;
        class       dictionary;
        location    "system";
        object      controlDict;
    }

4.2.4 Lists

OpenFOAM applications contain lists, e.g. a list of vertex coordinates for a mesh description. Lists are commonly found in I/O and have a format of their own in which the entries are contained within round braces ( ). When a user specifies a list in an input file, e.g. the vertices list in a blockMeshDict file, it just includes the vertices keyword and the data in ( ), e.g.


    vertices
    (
         entries 
    );

When OpenFOAM writes out a list, it invariably prefixes it with the number of elements in the list. For example the points file for the mesh in the pizDailySteady case contains the following (abbreviated) list, where 25012 denotes the number of vertex points in the mesh.


    25012
    (
        (-0.0206 0 -0.0005)
        (-0.01901716308 0 -0.0005)
         entries 
    );

In some cases, when OpenFOAM writes out a list, it further prefixes it with the class name of the list. For example, the inGroups entry in a boundary file of a mesh contains a list where each group name is a word. The entry for the lowerWall patch from the pizDailySteady case is shown below, indicating the List¡word¿ class with a single (1) element.


    lowerWall
    {
        type            wall;
        inGroups        List<word> 1(wall); // Note!
        nFaces          250;
        startFace       24480;
    }

4.2.5 Scalars, vectors and tensors

A scalar is a single number represented as such in a data file. A vector contains three values, expressed using the simple List format so that the vector eqn is written:


    (1.0 1.1 1.2)
In OpenFOAM, a tensor contains nine elements, such that the identity tensor can be written:


    (1 0 0 0 1 0 0 0 1)
The values are ordered according to the following components, for an Cartesian co-ordinate system.


    (xx xy xz yx yy yz zx zy zz)
OpenFOAM also supports specifically a symmetric tensor for which the off-diagonal components are equal (xy = yx, xz = zx, yz = zy). The duplicate components are not stored, so the symmetric tensor is specified with six components only, as follows.


    (xx xy xz yy yz zz)

4.2.6 Dimensional units

In science, properties are represented in some chosen units, e.g. mass in kilograms (eqn), volume in cubic metres (eqn), pressure in Pascals (eqn). Algebraic operations must be performed on these properties using consistent units of measurement. In particular, addition, subtraction and equality are only physically meaningful for properties of the same dimensional units. As a safeguard against implementing a meaningless operation, OpenFOAM attaches dimensions to field data and physical properties and performs dimension checking on any operation.

Dimensions are described by the dimensionSet class which are represented with a unique I/O syntax using square brackets, e.g.


    [0 2 -1 0 0 0 0]
The syntax above uses the base syntax where each integer corresponds to the power of a base unit of measurement listed in sequence below, accompanied by the corresponding units for the Système International (SI) and the United States Customary System (USCS) , respectively.

  1. mass, e.g. kilogram (kg), pound-mass (lbm);

  2. length, e.g. metre (m), foot (ft);

  3. time, e.g. second (s);

  4. temperature, e.g. Kelvin (K), degree Rankine (eqnR);

  5. quantity, e.g. mole (mol);

  6. current, e.g. ampere (A);

  7. luminous intensity, e.g. candela (cd).

Dimensional units can alternatively be specified by name, starting with the base units, named mass, length, time, temperature, moles, current, and luminousIntensity. Dimensional units can be expressed using these names, rather than the array of indices, e.g. dimensions of length can be written


    [length]
instead of [0 1 0 0 0 0 0]. The example, [0 2 -1 0 0 0 0], can be written as


    [sqr(length)/time]
where sqr(length) denotes units of length*length. There are also names for “composite” dimensional units that are commonly used. For example, area represents sqr(length), so the previous example could be written


    [area/time]
In fact, these dimensions are those of kinematic viscosity, for which a named dimension is predefined by


    [kinematicViscosity]
We recommend named dimensions since they are easier to comprehend. Since v14, all example cases in OpenFOAM use named dimensional units only in input files, rather than the array of index notation. The user can list available named dimensions can be listed by running the foamUnits utility as follows (see section 4.7.7 for more details).


    foamUnits
They can then obtain further information for any given dimension by specifying the dimension as an argument to foamUnits, e.g.


    foamUnits pressure
returns, the fundamental dimensions in named and index form, and a list of corresponding units in the default unit set.


    Dimension [pressure]
    + Dimensions = [mass length^-1 time^-2]
    + Exponents = [1 -1 -2 0 0 0 0]
    + Standard Units = [kg m^-1 s^-2] [Pa]
    + Other Units = [psi] [atm] [kPa] [bar] [MPa]

Dimensions do not themselves suggest any particular set of units, e.g. SI or USCS. Instead, the set of units is specified in the $FOAM_ETC/configDict by the set keyword in the units sub-dictionary. The default set is SI, with two pre-configured alternatives of CGS (centimetre-gram-second) and USCS. The set choice can be overridden as described in section 4.3 . Indeed, an alternative custom unit set can even be specified, with any defined units, mimicking the syntax in the configDict file.

4.2.7 Units and unit conversion

Numerical parameters can be specified with accompanying units, written using their name contained within square brackets, e.g.[mm]” for millimetre units. Standard available units are listed by running the foamUnits utility as follows.


    foamUnits
The listed units are those defined in the units sub-dictionary of the $FOAM_ETC/configDict file. They begin with the fundamental units of the chosen unit set, i.e. kg, m, s, K, kmol, A and Cd, for the default SI units. A unit can be specified that combines different units with exponents, divisions and/or multipliers, e.g.[cm s^-1]” or “[cm/s]” for a speed in centimetres per second. These units could be used to specify a velocity for a fixedValue condition on an input patch as follows.


inlet
{
    type               fixedValue;
    value              uniform (1000 0 0) [cm/s];
}
The way units work is that when data is read that includes units, the numerical value is converted into the base unit by multiplying by a factor. So in the example above, the value (1000 0 0) in [cm/s] is converted into (10 0 0) in the base [m/s] units by multiplying by a conversion factor of 0.01. The conversion factor applied to a given unit can be printed using foamUnits as described in section 4.7.7 .

Units can be applied from a different base set to the only being used. For example, a parameter could be specified in [ft/min], i.e. the USCS feet per second, when the set being used in SI. The example below shows the meanVelocity being specified in this way for the flowRateInletVelocity boundary condition.


inlet
{
    type               flowRateInletVelocity;
    meanVelocity       30 [ft/s];
}
There are many units derived from more complex combinations of base units, e.g. [N] for newton, corresponding to [kg m s^-2], and [J] for joule, corresponding to [N m]. The [cal] unit for calorie applies a further conversion to [J]. With the flowRateInletVelocity boundary condition example above, a volumetricFlowRate could be specified in litres per second by [l/s] as follows.


inlet
{
    type               flowRateInletVelocity;
    volumetricFlowRate 0.2 [l/s];
}

While the volumetricFlowRate parameter above is specified by a single value, it is in fact an example of a Function1, which is described in section 6.4.4 (about time-varying boundary conditions). A Function1 is function of one variable; in this case the variable is time, allowing the user to prescribe a flow rate that varies in time. The single value syntax is a shorthand to represent a constant Function1.

There is a wide choice of available functions listed in section 6.4.4 . The volumetricFlowRate could, for example, follow a sine wave with a mean value of 0.15 l/s, amplitude 0.05 l/s and frequency 20 mHz with the following configuration


inlet
{
    type               flowRateInletVelocity;
    volumetricFlowRate
    {
        type      sine;
        level     0.15 [l/s];
        amplitude 0.05 [l/s];
        frequency 0.002 [Hz];
    }
}
This shows that the parameters used by the sine Function1 can include units. Two Function1s whose unit handling is slightly more subtle are table and polynomial. In both cases they provide a units entry in which the user can provide the units for the values and the function variable, i.e. time eqn, for example.

For the table function, the user specifies values at different values of the function variable. For example, a table entry that specifies a value of 0.2 at eqn0.1 s would be represented as (0.1 0.2) in OpenFOAM format.

Imagine we want the volumetricFlowRate to increase from 0 l/s at eqn to 0.2 l/s at eqn s. This could be achieved using a table with two entries, since the table function interpolates values linearly between successive data points, and clamps to the final value at times beyond the last point. The configuration for this example, below, shows how the optional units works.


inlet
{
    type               flowRateInletVelocity;
    volumetricFlowRate
    {
        type      table;
        units     ([ms] [l/s]);
        values
        (
            (0    0)
            (100  0.2)
        );
    }
}
Notice that the units entry sets milliseconds for the time variable, so ramps the flow rate between 0 and 0.2 l/s over 100 ms, or 0.1 s. The values themselves are specified in l/s as in the earlier example.

While units are generally associated with dimensioned quantities, the following units that are available to perform conversions between dimensionless quantities.

  • angle: fundamental unit is radian “[rad]” with scaled units of degree “[deg]” and rotations “[rot]”.

  • fractions: fundamentally without units, but can be represented by the scaled unit of percentage “[%]”.

Finally, users can define custom units of their own. To do so, they need to add entries to a configDict, discussed in section 4.3 . Essentially, they should follow the instructions to override global controls in section 4.3.1 in which the sample configDict is copied into their case system directory. They can then add custom units to the units section of that file. Below is an example which creates a niche USCS unit, acre-foot per fortnight “[aff]”, sometimes used in irrigation.


units
{
    day          24  [hr];
    fortnight    14  [day];
    chain        22  [yd];
    acre         10  [chain^2];
    aff          1   [acre*ft/fortnight];
}

4.2.8 Dimensioned types

Most physical properties are defined with their respective dimensions. They are represented by the dimensioned class which strictly includes three components: a word name; a dimensionSet and a value (scalar, vector, etc.). In the distant past, input files would include entries with all three data entries, but over time the word and dimensionSet components have become hard-coded so that the user only needs to supply the value to be read at input.

Since OpenFOAM v14, there are no example cases with the older input syntax, so the basic input syntax is shown below for parameters for density eqn and kinematic viscosity eqn.


    rho   1000;
    nu    1e-5;
The value of a dimensioned parameter can also include units. When they do so, the units are checked for consistency against the hard-coded dimensions of the dimensioned parameter. If the user wishes to be reminded of the dimensions, they can do so indirectly by including the base units (for which the resulting conversion factor is unity) as follows.


    rho   1000 [kg m^-3];
    nu    1e-5 [m^2/s];
Ultimately, the checking of supplied units differentiates a dimensioned parameter, e.g. dimensionedScalar, from its non-dimensioned counterpart, i.e. scalar. The user has the convenience of specifying a value in a non-base unit, e.g. in centistokes for eqn, with the reassurance that the chosen units are appropriate for the given parameter.


    nu    10 [cSt];

4.2.9 Fields

Field files, e.g. U and p, that are read from and written into the time directories, possess their own customised I/O with the following key entries.

  • dimensions: the dimensions of the field, e.g. [pressure] (or [1 -1 -2 0 0 0 0]).

  • internalField: values within the internal field, e.g. within each cell of a mesh.

  • boundaryField: condition (type) and data for each patch of the mesh boundary.

  • sources, optional (usually not used): field values required when a fluid mass (or volume) source is introduced internally, rather than at a boundary, see section 6.2 .

cThis section provides an overview of the basic syntax of the internalField and boundaryField entries in field files. The details of field initialisation, sources and boundary conditions are then presented in Chapter 6 .

For the internalField, there are three way it can be specified, listed below.

  • uniform: typically for initialisation, the field is uniform so can be described by a single value.

  • nonuniform: typically for results that are written out, a list of values in specified, one value for each element of the field.

  • field function: used to initialise a nonuniform field, as described in section 6.1 .

An example of a uniform field, e.g. to initialise kinematic pressure (p), is shown below. The uniform value is 100000 and follows the uniform keyword.


    internalField uniform 100000;
A uniform field can also be initialised using a set of units, e.g. for a pressure of 1 bar:


    internalField uniform 1 [bar];

When results are written out, the field is non-uniform with different values for each element (i.e. cells or faces). The output then uses the nonuniform keyword, followed by a suitable list of values. The abbreviated example below is from an output p file for a mesh of 12225 cells.


    internalField  nonuniform  List<scalar>
    12225
    (
    -4.92806
    -5.42676
    ...
    );

The boundaryField is a dictionary containing a set of entries corresponding to each patch listed in the mesh boundary, described in section 5.3 . Each entry in boundaryField is itself a sub-dictionary containing a list of keyword entries, beginning with the mandatory type entry which specifies the patch field condition for the field. Additional keyword entries are then generally required, corresponding to the specified type. They can include field data to specify a property on each patch face, that require the uniform/nonuniform syntax described above.

A selection of patch field conditions in OpenFOAM are described in sections 6.3 , 6.4 , 6.5 and 6.6 . The selection is by no means exhaustive; rather a complete list of boundary conditions can be printed using the foamToC utility (see section 4.7.6 ) with the -scalarBCs and -vectorBCs options, i.e.


    foamToC -scalarBCs
    foamToC -vectorBCs

A example field file for velocity U, containing simple entries for dimensions, internalField and boundaryField, is shown below:

16

17dimensions      [velocity];

18

19internalField   uniform (0 0 0);

20

21boundaryField

22{

23    inlet

24    {

25        type            fixedValue;

26        value           uniform (10 0 0);

27    }

28

29    outlet

30    {

31        type            zeroGradient;

32    }

33

34    upperWall

35    {

36        type            noSlip;

37    }

38

39    lowerWall

40    {

41        type            noSlip;

42    }

43

44    frontAndBack

45    {

46        type            empty;

47    }

48}

49

50// ************************************************************************* //
   

4.2.10 Macro expansion

The configuration of case files can benefit from a macro syntax which uses the dollar ($) symbol in front of a keyword to expand the data associated with the keyword. The keyword can relate to any type of entry, e.g. in the following example, grad(U) expands to “cellLimited Gauss linear 1”.


     limited   cellLimited Gauss linear 1;
     grad(U)   $limited;
Variables can be accessed within different levels of sub-dictionaries, or scope. Scoping is performed using a ‘/’ (slash) syntax, illustrated by the following example, where the timeScheme entry expands to “Euler”, the entry for default in a sub-dictionary named ddtSchemes.


    ddtSchemes
    {
        default     Euler;
    }
    timeScheme $ddtSchemes/default;
Entries can relate to scalar values. In the following example, the yMin keyword expands to the value -10.


    xMin -10;
    yMin $xMin;
    xMax #neg $xMin;
The xMax entry demonstrates the #neg directive which negates the value that follows it, such that it expands to 10 in the example above.

There are further syntax rules for macro expansions:

  • to traverse up one level of sub-dictionary, use the ‘..’ (double-dot) prefix, see below;

  • to traverse up two levels use ‘../..’ prefix, etc.;

  • to traverse to the top level dictionary use the ‘!’ (exclamation mark) prefix (most useful), see below;

  • to traverse into a separate file named otherFile, use ‘otherFile!’, see below;

  • for multiple levels of macro substitution, each specified with the ‘$’ dollar syntax, ‘{}’ brackets are required to protect the expansion, see below.

When accessing parameters from another file, the $FOAM_CASE environment variable is useful to specify the path to the file as described in section 4.2.12 and illustrated below.


    a 10;
    b a;
    c ${$b}; // returns 10, since $b returns "a", and $a returns 10

    subdictA
    {
        a 20;
    }

    subdictB
    {
        // double-dot takes scope up 1 level, then into "subdictA" => 20
        b $../subdictA/a;

        subsubdict
        {
            // exclamation mark takes scope to top level => 10
            b $!a;

            // "a" from another file named "otherFile"
            c $otherFile!a;

            // "a" from another file "otherFile" in the case directory
            d ${${FOAM_CASE}/otherFile!a};
        }
    }

4.2.11 Including files

Directives are commands that begin with the hash (#) symbol which provide further flexibility when configuring case files. There is a set of directive commands for reading a data file from within another data file. If a case requires a single value of pressure of 100 kPa, used in different input files, we could create a file, e.g. named initialConditions, which contains the following entry:


    pressure 1e+05;

In order to use this pressure for internal and initial boundary fields, the user could simply include the initialConditions file using the #include directive, then use a macro expansion on the pressure keyword, as follows.


    #include "initialConditions"
    internalField uniform $pressure;
    boundaryField
    {
        patch1
        {
            type fixedValue;
            value $internalField;
        }
    }
This example works if the included file is in the same directory as the file that includes it. Otherwise, more generally the path to the file is required, e.g. if initialConditions is in the constant directory:


    #include "$FOAM_CASE/constant/initialConditions"
Here $FOAM_CASE represents is the path of the case directory as described in the following section. The following special forms of the #include directive also exist.
  • #includeIfPresent: reads a file if it exists.

  • #includeEtc: reads a file with the $FOAM_ETC directory as the starting path.

The #include directives can be called with arguments contained in brackets "()". They can include parameters using the syntax <parameter>=<value> that are specifically used in macro expansions only. For example if a main contains the following entries,


    rhoInf    $rho;
    UInf      $U;
then the rhoInf and UInf parameters can be created by including the file using arguments for rho and U, as follows.


    #include "main"(rho=1.2,U=25)
This works if the main file does not itself contain entries for rho and U.

There are then three specialised include directives for functionObjects, fvModels and fvConstraints.

  • #includeFunc: reads a file containing a single functionObject configuration, first searching the case system directory, followed by the $FOAM_ETC directory.

  • #includeModel: reads a file containing a single fvModel configuration, first searching the case constant directory, followed by the $FOAM_ETC directory.

  • #includeConstraint: reads a file containing a single fvConstraint configuration, first searching the case system directory, followed by the $FOAM_ETC directory.

These directives can also be called with arguments contained in brackets "()". However, the arguments are treated differently from the previous directives. For the specialised directives, <parameter>=<value> arguments simply add or modify a keyword entry <parameter>, assigning the <value>.

Function arguments are used extensively with the specialised include directives. The following grep command lists several files in the example cases that demonstrate this.


    grep -rl '#includeFunc.*(' $FOAM_TUTORIALS
The use of parameters for #includeFunc is also mentioned in section 7.2.1 .

4.2.12 Environment variables

Environment variables can be used in input files. For example, the $FOAM_RUN environment variable can be used to identify the run directory, as described in the introduction to Chapter 2. This could be used to include a file, e.g. by


    #include "$FOAM_RUN/pitzDailySteady/0/U"

In addition to environment variables like $FOAM_RUN, set within the operating system, a number of “internal” environment variables are recognised, including the following.

  • $FOAM_CASE: the path and directory of the running case.

  • $FOAM_CASENAME: the directory name of the running case.

  • $FOAM_APPLICATION: the name of the running application.

4.2.13 Regular expressions

As discussed, data is looked up from files using keywords. If a particular keyword does not exist, the I/O system will try to match the keyword with any POSIX regular expression, specified inside double-quotations ("…") in the input file.

In some cases, when the I/O system searches for a keyword in a case file, a can be used to match the keyword

When running an application, data is initialised by looking up keywords from dictionaries. The user can either provide an entry with a keyword that directly matches the one being looked up, or can provide a that matches the keyword, specified inside double-quotations ("…").

Regular expressions have an extensive syntax for various matches of text patterns but in OpenFOAM input files there are only two expressions that are generally used. Firstly, ‘.’ denoting “any character”, and ‘*’ denoting “repeated any number of times, including 0 times” is often used in combination to match “any characters”. For example, to specify a noSlip boundary condition for any patch whose name ends Wall…, the user could specify in the boundaryField for U:


    ".*Wall"
    {
        type   noSlip;
    }
The other common regular expression uses () to group expressions. For example, to a noSlip boundary condition on two wall patches named upper and lower, the user could specify:


    "(upper|lower)"
    {
        type   noSlip;
    }

4.2.14 Keyword ordering

The order in which keywords are listed does not matter, except when the same keyword is specified multiple times. Where the same keyword is duplicated, the last instance is used. The most common example of a duplicate keyword occurs when a keyword is included from the file or expanded from a macro, and then overridden. The example below demonstrates this, where pFinal adopts all the keyword entries, including relTol 0.05 in the p sub-dictionary by the macro expansion $p, then overrides the relTol entry.


    p
    {
        solver          PCG;
        preconditioner  DIC;
        tolerance       1e-6;
        relTol          0.05;
    }
    pFinal
    {
        $p;
        relTol          0;
    }

Where a data lookup matches both a keyword and a regular expression, the keyword match takes precedence irrespective of the order of the entries.

4.2.15 Inline calculations

There are further directives that enable calculations from within input files:

  • #calc, for one statement calculations, described below;

  • #stream, for multi-statement calculations, also described below;

  • #codeStream, for complex multi-statement calculations, described in section 4.2.17 ;

  • #codeDict, for creating multiple dictionary entries, described in section 4.2.18 .

In the descriptions above, the term “statement” means a code statement, i.e. one piece of code ending in “;”. It can be thought of as “line”, but a single statement can be split over multiple lines, so “statement” is more precise. Examples of the use of these directives can be found in files in the test/dictionary directory in the OpenFOAM installation.

The #calc directive is used for simple one-line calculations, typically to set a parameter in an input file. An example is found at the beginning of the 0/U in the $FOAM_TUTORIALS/incompressibleFluid/drivaerFastback case.


    Uinlet          16;

    wheelRadius     0.318;
    wheelBase       2.786;
    wheelSpeed      #calc "$Uinlet / $wheelRadius";
The #calc directive is used here to calculate the rotational speed of the wheels wheelSpeed from on the rim speed Uinlet and the radius wheelRadius. It works by including the code: a) within quotations ""; or, b) within hash-bracket delimiters #{#};. The code usually includes other keyword entries expanded with macros ($), as in the case above.

Care is required with calculations involving a division because the “/” character is otherwise used to identify keywords in sub-dictionaries, "$a/b" looks for a keyword b within a sub-dictionary named a. Where a division is required, the user can put spaces around the /, e.g.


    a     3.0;
    b     2.0;
    c     #calc "$a / $b";
or they can apply brackets around the first variable, e.g.


    c     #calc "${a}/$b";
The example above involves scalar values only. However, the #calc and other inline code frameworks can use variables that represent other OpenFOAM classes, or types, such as vector, tensor, List, Field, string etc.. To create a typed variable, the type is specified inside angled brackets <>, immediately after the $ symbol, e.g. $<vector>var or $<vector>{var} substitutes a variable named var as a vector.

For example, the following code calculates eqn using #calc.


    a       (1 2 3);
    b       (1 1 0);
    c       #calc "$<vector>a & $<vector>b";
Even in the previous example, care is required over the type of the entries. For example, if a file includes,


    a     3;
    b     2;
    c     #calc "$a / $b";  // c = 1, since a and b are integers
the resulting value of c is 1, since a and b are interpreted as integers. As well as adding expressing the values as decimals in the original example, the correct value of c can be obtained by using a scalar typed variable. All the following examples would work as required.


    a     3;
    b     2;
    c     #calc "$a / $<scalar>b";
    c     #calc "$<scalar>a / $b";
    c     #calc "$<scalar>a / $<scalar>b";
A critical feature of using typed macros in the calculation environments is that they support unit conversions, whereas standard macros do not.


    nu    10 [cSt];
    rho   1000 [kg/m^3];

    mu    #calc "$<scalar>nu*$<scalar>rho";
//  mu    #calc "$nu*$rho"; // will not compile
In the example shown, the nu and rho parameters include units. The standard macro will expand as a string with both a scalar and string, when the multiplication operation will be meaningless. With the typed macro, however, the expanded variable is specifically a scalar including the conversion from the units. The multiplication will be performed with converted scalars yielding the correct result in the base units.

The hash-bracket delimiters, #{#};, (instead of "") have the advantages that: a) they support code written across multiple lines; and, b) they avoid problems with string typed variables that may contain quotation marks as in the example below.


    s "field";
    fieldName #calc
    #{
        $<string>s + "Name"
    #};
If the inline calculation involves more than one code statement, the #stream directive can be used instead of #calc. The #stream environment provides a reference to an Ostream named os. Therefore a calculated value is “set” to the relevant keyword by streaming them to os, by the following code (where value is the variable for the calculated value).


    os << value;
The #stream environment also provides dict, a const reference to the current dictionary itself. Taking the earlier example with the inner product of two vectors, we can demonstrate #stream by adding a second code statement to print an Info message to the terminal.


    a       (1 2 3);
    b       (1 1 0);
    c #stream
    #{
        Info<< "Calculating c = " << dict.lookup<vector>("a")
            << " & " << dict.lookup<vector>("b") << endl;
        os  << ($<vector>a & $<vector>b);
    #};
where the Info to the terminal:


    Calculating c = (1 2 3) & (1 1 0)
It shows within the code block multiple (two) code statements (ending “;”). In the final statement the calculated result is streamed to os to set the c entry.

4.2.16 Multiple inline code entries with #codeBlock

When code is included in an input file, using directives such as #calc and #stream, the code is compiled dynamically into a shared-object library at run-time, which is linked to the running application. The code is written and compiled in a local directory named dynamicCode containing sub-directories for each compiled library. The sub-directory / library names for #calc and #stream code are created using a SHA (secure hash algorithm) string of hexadecimal characters prefixed with an underscore, e.g. _11de73e6b530658843ed2ff917b9c23fc98ab0e7.

The complied code can be found within those directories. The code begins as template files, copied from $FOAM_ETC/codeTemplates. The code written in code blocks, e.g. inside #{#};, in the input file is then injected into relevant locations within the copied template files. Once copied, the files are compiled.

Normally, an individual library is created by each directive. In some cases, there may be a large number of directives, so generating an individual library for each one can be cumbersome. Instead, to generate a single library, the entries can be placed between a #codeBlock and #endCodeBlock directive. The $FOAM_TUTORIALS/incompressibleFluid/venturiTube example case uses #codeBlock in the system/blockMeshDict file to wrap 8 #calc directives into a single library, as shown below. When generating the mesh for this case, it is clear that only one library is generated.


    #codeBlock

    radIn      #calc "$diameter / 2.0";
    boxIn      #calc "3.0*$<scalar>radIn / 10.0";

    radVen     #calc "$<scalar>radIn / 2.0";
    boxVen     #calc "$<scalar>boxIn / 2.0";

    xInSt      0;
    xInEnd     $diameter;
    xVenSt     #calc "$<scalar>xInEnd  + 1.25*$diameter";
    xVenEnd    #calc "$<scalar>xVenSt  + 0.5*$diameter";
    xOutSt     #calc "$<scalar>xVenEnd + 2.5*$diameter";
    xOutEnd    #calc "$<scalar>xOutSt  + $diameter";

    #endCodeBlock
Calculations in written code can include standard C++ functions such as trigonometric functions, e.g. sin. They can also include functions and classed from OpenFOAM. By default, the dynamic code framework includes, as standard, some header files relating to dictionaries, fields, IO and units.

If the code requires a header file that is not included as standard, it can be included by the #codeInclude directive, which is available withing #codeBlock#endCodeBlock. Multiple #codeInclude entries are permitted when more than one addition header file is required.

The $FOAM_TUTORIALS/fluid/aerofoilNACA0012Steady example demonstrates this, where the inlet velocity is calculated using an angle of attack using the code below. It uses the transform function from the transform.H header file, to rotate unit vectors by the angle of attack to set the lift and drag directions.


    speed           250;
    angleOfAttack   5 [deg];

    #codeBlock
    #codeInclude    "transform.H"
    angle           #calc "$<scalar>angleOfAttack" // unit converted
    liftDir         #calc "transform(Ry(-$angle), vector(0, 0, 1))";
    dragDir         #calc "transform(Ry( $angle), vector(1, 0, 0))";

    Uinlet          #calc "$speed*$<vector>dragDir";
    #endCodeBlock

4.2.17 Inline code with #codeStream

The #codeStream directive is like #stream in that: a) it is designed to perform calculations involving more than one code statement; and, b) the resulting calculation is streamed to os, the reference to the Ostream.

But in addition to this, the code in #codeStream is written into a special code block that uses the hash-bracket delimiters, as shown below.


    #codeStream
    {
        code
        #{
            // ... code here ...
            os << value;
        #};
    }
In addition to this, #codeStream supports three other optional code blocks for the inclusion of multiple header files and compilations flags for locating header files and linking additional libraries associated with those header files.
  • codeInclude: specifies additional C++ #include statements to include code files.

  • codeOptions: specifies any extra compilation flags to be added to EXE_INC in Make/options.

  • codeLibs: specifies any extra compilation flags to be added to LIB_LIBS in Make/options.

All the code blocks are written with hash-bracket delimiters. It is possible to create an empty set of optional code blocks, as shown below, when writing to write a #codeStream entry.


    codeInclude
    #{
    #};

    codeOptions
    #{
    #};

    codeLibs
    #{
    #};
If a particular header file then needs including, e.g. IOobject.H, the relevant entries can be added to these blocks using the -coded option to the foamFind script, described in section 4.7.8 . For example, if the #codeStream entry was in a system/blockMeshDict file, the user could run the following command.


    foamFind IOobject.H -coded system/blockMeshDict
The resulting entries in #codeStream would be modified to include the following entries.


    codeInclude
    #{
        #include "IOobject.H"
    #};

    codeOptions
    #{
        -I$(LIB_SRC)/finiteVolume/lnInclude
    #};

    codeLibs
    #{
        -lfiniteVolume
    #};

Code, like any string, can be written across multiple lines by enclosing it within hash-bracket delimiters, i.e. #{…#}. Anything in between these two delimiters becomes a string with all newlines, quotes, etc. preserved.

An example of #codeStream is given below, where the code calculates moment of inertia of a box shaped geometry.

momentOfInertia #codeStream

{

    codeInclude

    #{

        #include "diagTensor.H"

    #};



    code

    #{

        scalar sqrLx = sqr($Lx);

        scalar sqrLy = sqr($Ly);

        scalar sqrLz = sqr($Lz);

        os  <<

            $mass

           *diagTensor(sqrLy + sqrLz, sqrLx + sqrLz, sqrLx + sqrLy)/12.0;

    #};

};
   

4.2.18 Inline code with #codeDict

The last of the code-related conditionals is #codeDict. It is broadly similar to #codeStream, with a main code block and optional codeInclude, codeOptions and codeLibs blocks. However, there is one main difference. Instead of streaming values to os (the Ostream), #codeDict is able to add and modify dictionary entries through a non-const reference dict to the dictionary.

Below is an example with #codeDict.


    nu    10 [cSt];
    rho   1000 [kg/m^3];
    #codeDict
    {
        code
        #{
            dict.add
            (
                "mu",
                dict.lookup<scalar>("nu")*dict.lookup<scalar>("rho")
            );
        #};
    }
#endCodeBlock

4.2.19 Conditionals

Input files support two conditional directives:

  • #if ()#elif ()#else#endif

  • #ifeq ()#else#endif

Note: the syntax for the #if conditional changes in OpenFOAM v14, using () to delimit the condition. The condition is something which returns a Boolean (true/false). This is generally provided by some code executed by a #calc directive.

Conditionals are particularly useful in the control files for schemes and solvers/algorithms, fvSchemes and fvSolution respectively, since they can provide different settings based on some condition. A purely hypothetical example for the #if directive is below, where the default Laplacian scheme is different based on the non-orthogonality angle of a face.


    laplacianSchemes
    {
        #if (#calc "$angle < 75")
             default Gauss linear corrected;
        #else
             default Gauss linear limited corrected 0.5;
        #endif
    }
The #ifEq compares a word or string, and executes based on a match. This is commonly used in fvSchemes and fvSolution files to set different schemes and other controls based on whether the case is steady-state or transient. To do so, it tests whether the default time scheme is set to steadyState and provides settings accordingly.


    divSchemes
    {
        #ifeq ($!ddtSchemes/default steadyState)
            div(phi,U)   bounded Gauss linearUpwind limited;
            turbulence   bounded Gauss limitedLinear 1;
        #else
            div(phi,U)   Gauss linearUpwind limited;
            turbulence   bounded Gauss limitedLinear 1;
        #endif
        ...
    }

4.2.20 Checking input parameters

The following three directives help a user check their input parameters.

  • #dump: prints the dictionary (file) contents preceding the directive.

  • #print: prints the dictionary (entire file or sub-dictionary) including all default entries.

  • #exit: causes the application to stop at the point it is read.

A example of the use of #dump and #exit is as follows. Imagine a user is creating a mesh with blockMesh with a parameterised blockMeshDict file, like the one in the $FOAM_TUTORIALS/incompressibleFluid/venturiTube example case. The file (in the system directory) contains a lot of calculated parameters like those beginning


    diameter   0.1;

    #codeBlock
    radIn      #calc "$diameter / 2.0";
    boxIn      #calc "3.0*$<scalar>radIn / 10.0";
    ...
In order to check those entries are calculating as expected, the user could add #dump and #exit at the end of these parameters (just before vertices).


    ...
    boxInN     #neg $boxIn;
    radVenN    #neg $radVen;
    boxVenN    #neg $boxVen;

    #dump
    #exit

    vertices
    (
        ...
Then when running the blockMesh, the application will dump the parameters preceding #dump to the terminal and then terminate immediately. The terminal output will include the contents of the blockMeshDict dictionary with all the calculated entries visible.


    blockMeshDict
    {
        diameter        0.1;
        radIn           0.05;
        boxIn           0.015;
        ...
        ...
        boxInN          -0.015;
        radVenN         -0.025;
        boxVenN         -0.0075;
    }

The #print directive is extremely useful for revealing default parameters used in a dictionary. A common use of this is revealing the coefficients of a turbulence model. For example, in a case, e.g. $FOAM_TUTORIALS/incompressibleFluid/pitzDaily, the user could add #print to the entire file or within the RAS sub-dictionary. In the latter case, e.g.


    simulationType RAS;

    RAS
    {
        model           kEpsilon;
        turbulence      on;
        viscosityModel  Newtonian;

        #print
    }
the code prints the following when foamRun is executed.


    Selecting RAS turbulence model kEpsilon
        Selecting generalised Newtonian model Newtonian
        constant/momentumTransport!RAS
        {
            model           kEpsilon;
            turbulence      on;
            viscosityModel  Newtonian;
            /* defaults */
            Cmu             0.09;
            C1              1.44;
            C2              1.92;
            C3              0;
            sigmak          1;
            sigmaEps        1.3;
        }
Another useful example is printing default values in the fvSolution file. Specifically, there are a number of controls in the PIMPLE sub-dictionary that have default values that are worth revealing. For example in the $FOAM_TUTORIALS/fluid/aerofoilNACA0012Steady, #print could be added to the PIMPLE sub-dictionary which otherwise includes only a small number of controls, as follows.


    PIMPLE
    {
        residualControl
        {
            p               1e-6;
            U               1e-5;
            "(k|omega|h)"   1e-5;
        }

        nNonOrthogonalCorrectors 0;

        #print
    }
When the simulation is run with foamRun, the full extent of the default entries in the PIMPLE sub-dictionary is revealed.


    system/fvSolution!PIMPLE
    {
        residualControl
        {
            p               1e-06;
            U               1e-05;
            "(k|omega|h)"   1e-05;
        }
        nNonOrthogonalCorrectors 0;
        /* defaults */
        models          true;
        thermophysics   true;
        flow            true;
        momentumPredictor true;
        transonic       false;
        consistent      false;
        nCorrectors     1;
        moveMeshOuterCorrectors false;
        simpleRho       true;
        transportPredictionFirst true;
        transportCorrectionFinal true;
    }
OpenFOAM v14 User Guide - 4.2 Input/output file format
OpenFOAM User Guide