Crash Course

As a quick introduction we start with analyzing the Python version of such a well known OpenFOAM solver as icoFoam. In pythonFlu it comes as icoFlux, the full version of each can be seen at corresponding page. Let's start. The first six lines of icoFlux :

from Foam.OpenFOAM.include import setRootCase
args = setRootCase( argc, argv )
 
from Foam.OpenFOAM.include import createTime
runTime = createTime( args )
 
from Foam.OpenFOAM.include import createMesh
mesh = createMesh( runTime )

do the same as the following C++ ones
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"

namely,
  • parsing and analyzing command-line arguments
  • creating Time and fvMesh objects

Very simple, very easy and … almost the same, almost.

The difference between pythonFlu and OpenFOAM versions consist in that in the first case you precisely know which "createMesh" algorithm was used and where it comes from ("Foam.OpenFOAM.include" Python module in our case). This difference highlights one of the important pythonFlu features - "there is no separation between the code, its structure and how it should be interpreted". So, whatever is written in Python precisely reflects the code dependencies. Therefore, if something happened at "createMesh" step, you precisely know where to look for the answer.

The next line in icoFlux highlights another important clarification in solver definition introduced by pythonFlu :

transportProperties, nu, p, U, phi, pRefCell, pRefValue = createFields( runTime, mesh )

in comparision with the referenced OpenFOAM definition :
#include "createFields.H"

pythonFlu version clearly states that "createFileds" functionality
  • defines the following variables - transportProperties, nu, p, U, phi, pRefCell, pRefValue
  • in order to do its job it requires Time and fvMesh objects as its arguments

As the result, you can clearly see that these magic transportProperties, nu, p, U, phi, pRefCell, pRefValue variables come exactly from "createFileds" function (not from nowhere) and that "createTime" and "createMesh" should be strictly called before it, to provide necessary input arguments - runTime and mesh.

Let's write a target solver equation - $\frac{\partial \rho \mathbf{U}}{\partial t} + \nabla \cdot\phi\mathbf{U} - \nabla \cdot\mu\nabla\mathbf{U} = - \nabla p$ in pythonFlu

from Foam import fvm
UEqn = ( fvm.ddt( U ) + fvm.div( phi, U ) - fvm.laplacian( nu, U ) )
 
from Foam import fvc
from Foam.finiteVolume import solve
solve( UEqn == -fvc.grad( p ) )

and compare it with the referenced OpenFOAM version
fvVectorMatrix UEqn( fvm::ddt( U ) + fvm::div( phi, U ) - fvm::laplacian( nu, U ) );
solve( UEqn == -fvc::grad( p ) );

There are other two important points :
  • C++ namespaces (like fmv and fvc) in pythonFlu are reflected into the corresponding Python modules (Foam.fvm and Foam.fvc)
  • If you need to use a function you need to load the corresponding library first - to call solve, which is defined in fintiteVolume library, you need to load it first by executing "from Foam.finiteVolume import solve"

These points show that in pythonFlu there is no distinction between the library and namespace notions.

As we saw, pythonFlu repeats almost every line, every symbol from the corresponding OpenFOAM definition. However, there are of course some differences. The first is a Python reflection of "=" operator. Here Python and C++ use different approaches. In C++ = operator means "assignment", when one object is copied to another. In Python the same "=" operator means "creating a new reference" to an object. pythonFlu highlights this by introduction of z special member function - "ext_assign". Compare pythonFlu code

phi.ext_assign( ( fvc.interpolate( U ) & mesh.Sf() ) + fvc.ddtPhiCorr( rUA, U, phi ) )

and the corresponding OpenFOAM one
phi = ( fvc::interpolate( U ) & mesh.Sf() ) + fvc::ddtPhiCorr( rUA, U, phi );

Of course, this is not a big issue, but be careful with C++ "=" operators. If, you need to change the value of an existing object use its "ext_assign" member function.

"ext_" is a special prefix, by its usage pythonFlu highlights that this function is completely new and was invented to substitute the principal difference between Python and C++ (such as operator "=") or SWIG (C++ to Python wrapper) limitations that prevent from using the initial C++ notation in Python. For example, the following pythonFlu code

from Foam.OpenFOAM import ext_Info, nl
ext_Info() << "\nStarting time loop\n" << nl

is different with the referenced OpenFOAM one
Info << "\nStarting time loop\n" << endl;

because of SWIG special handling of extern defined variables (it just omits them)
extern messageStream Info;

As you can see pythonFlu coding almost coincides with the referenced OpenFOAM one. Some pythonFlu C++ differences (like usage of namespaces, includes and libraries) lead to a better structured and clearer solver code definition. The differences which come from Python, C++ and SWIG wrapping natures are explicitly highlighted by "ext_" prefix. The complete list of such differences can be found here.

So, once you are familiar with OpenFOAM it would be nothing for you to start using pythonFlu (from the outside they are managed in the same way). To be able to program your own solver in pythonFlu terms you need to know OpenFOAM API (the referenced documentation could be found here).