Wire pyOFTools into system/controlDict

This recipe shows how to attach a PostProcessorBase subclass as an OpenFOAM function object so it runs every write interval during the solver loop. Use this when you have a working post-processor (see Your first in-situ post-processor) and want to execute it in-situ.

Prerequisites

  • OpenFOAM is sourced and pybFoam is installed.

  • A running case directory with system/controlDict, constant/, 0.orig/.

  • libembeddingPython.so is available — this is what lets OpenFOAM load a Python function object (ships with pybFoam-based builds).

Recipe

  1. Write the post-processor. Save as postProcess.py in the case root (next to system/):

    from pyOFTools.postprocessor import PostProcessorBase
    from pyOFTools.builders import field, iso_surface, residuals
    from pyOFTools.aggregators import VolIntegrate, Sum
    
    postProcess = PostProcessorBase()
    
    @postProcess.Table("vol_alpha.csv")
    def vol_alpha(mesh):
        return field(mesh, "alpha.water") | VolIntegrate()
    
    @postProcess.Table("free_surface_area.csv")
    def free_surface_area(mesh):
        return iso_surface(mesh, "alpha.water", 0.5) | Sum()
    
    @postProcess.Table("residuals.csv")
    def solver_residuals(mesh):
        return residuals(mesh)
    

    The module-level instance must be called postProcess if you use the default pyClassName below — otherwise change the class name to match.

  2. Register the function object in system/controlDict:

    functions
    {
        pyPostProcessing
        {
            libs            ("libembeddingPython.so");
            type            pyPostProcessing;
            writeControl    timeStep;
            writeInterval   1;
            pyFileName      postProcess;   // postProcess.py
            pyClassName     postProcess;   // module-level instance name
        }
    }
    
    • pyFileName is the Python module (no .py extension).

    • pyClassName is the name of the ``PostProcessorBase`` instance in that module, not a class. The embedding loader looks up postProcess.postProcess in this example.

  3. Run the solver from the case directory:

    blockMesh
    setFields       # if the case needs it
    interFoam      # or your solver
    
  4. Read the output from postProcessing/:

    postProcessing/
    ├── vol_alpha.csv
    ├── free_surface_area.csv
    └── residuals.csv
    

    Each file has a time column and one column per aggregated value.

Parallel runs

Run as usual:

decomposePar
mpirun -np 4 interFoam -parallel

TableWriter runs the workflow on every rank (so internal Foam::reduce calls see all cells) but only writes on rank 0, so you get one CSV per output file — not one per processor.

Picking writeControl

  • timeStep + writeInterval 1 — write every step. Highest cost, best time resolution. Good for statistics.

  • writeTime — write only when the solver writes fields. Cheapest, same cadence as the fields on disk.

  • adjustableRunTime + writeInterval 0.05 — fixed wall-clock cadence, useful when adjustTimeStep yes.

See also