Edit me

Generate FMU from an OpenFOAM case

    pip install fmu4foam

creating an FMU from OpenFOAM is simple and similar to the libary PythonFMU which is achieved with just one line:

    fmu4foam build -f TempControl.py -of OfCase/ --no-external-tool

OpenFOAM modifications

As in the previous method, we extend OpenFOAM with the additional libraries and add the following lines to the system/controlDict:

// loads addtional input and output function/class
// e.g. new boundary conditions.
libs(externalComm); 

// includes the file system/simulationParameters in the controlDict
// and enables the definition of Variables marked with a $ 
// only look in the current directory and is short for:
// #include "<case>/system/simulationParameters"
#include "simulationParameters" 

functions
{
    FMUController
    {
        type            FMUController; // execute functionObjects FMUController
        libs            (FMUController); // load "libFMUController.so"
        host            $host; // connect to ip specified in simulationParameters
        port            $port; // connect to port specified in simulationParameters
   
    }
}

the controlDict specifies two variables: host and port that need to be specified in the system/simulationParameters:

/*--------------------------------*- C++ -*----------------------------------*\
| =========                 |                                                 |
| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
|  \\    /   O peration     | Version:  v2012                                 |
|   \\  /    A nd           | Website:  www.openfoam.com                      |
|    \\/     M anipulation  |                                                 |
\*---------------------------------------------------------------------------*/
FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    location    "system";
    object      simulationParameters;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

host            "127.0.0.1"; // defines host 
port            8000; // defines port
STDGRAD         Gauss linear;
STDLAP          Gauss linear uncorrected;
writeInterval   0.1;


// ************************************************************************* //

The host and port variable are specified in the FMU generation base class.

  • host -> connect to other computer
  • port -> port on that computer
  • outputPath -> extract dir of the OpenFOAM Case
  • oscmd -> name of the system command (enables exectuion on docker or wsl for windows users in the future)
  • arguments -> additional parameters for the Allrun script e.g. source environment in script

NOTE: The host variable needs to be quoted. To achieve this, we define it with two qoutes ‘“Name of the Variable”’. The other variables require no specific highlighting.

class OF2Fmu(Fmi2Slave):


    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        #local host aka this computer
        self.host = '"127.0.0.1"' # Note the double quotes
        self.port = 8000 # port see above
        self.outputPath = "OFCase" # extract path relative to FMU
        self.oscmd = "bash -i" # system command to execute the Allrun 
        # additional parameters for the Allrun script
        # of2012 to source the enviroment in the Allrun script
        self.arguments = "" 
        
        self.register_variable(String("host",
            causality=Fmi2Causality.parameter, variability=Fmi2Variability.tunable))
        self.register_variable(Integer("port",
             causality=Fmi2Causality.parameter, variability=Fmi2Variability.tunable))
        self.register_variable(String("outputPath", 
            causality=Fmi2Causality.parameter, variability=Fmi2Variability.tunable))
        self.register_variable(String("oscmd", 
            causality=Fmi2Causality.parameter, variability=Fmi2Variability.tunable))
        self.register_variable(String("arguments", 
            causality=Fmi2Causality.parameter, variability=Fmi2Variability.tunable))

FMU generation input file

The last part in the generation of the FMU is a python file specified after -f TempControl.py:

from pythonfmu.variables import Boolean
from FMU4FOAM import Fmi2Causality, Fmi2Variability, Real, Boolean , Integer, String
from FMU4FOAM import OF2Fmu


class TempControl(OF2Fmu):

    author = "John Doe"
    description = "A simple description"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        # Input Output variable
        self.Qin = 0.0
        self.dTout = 0.0
        self.Tout = 298.0
        self.host = '"192.168.1.200"' # overwrite the ip
        self.outputPath = "Testing" # will extract the OfCase files in Testing

        self.register_variable(Real("Qin", causality=Fmi2Causality.input))
        self.register_variable(Real("dTout", causality=Fmi2Causality.output))
        self.register_variable(Real("Tout", causality=Fmi2Causality.output))

        # Parameters
        self.writeInterval = 1.0 # changes outputInterval of the solver
        self.STDLAP = "Gauss linear corrected" # possiblity to change discreization
        self.STDGRAD = "pointCellsLeastSquares" # possiblity to change discreization
        

        self.register_variable(String("STDGRAD", 
            causality=Fmi2Causality.parameter, variability=Fmi2Variability.tunable))
        self.register_variable(String("STDLAP", 
            causality=Fmi2Causality.parameter, variability=Fmi2Variability.tunable))
        self.register_variable(Real("writeInterval", 
            causality=Fmi2Causality.parameter, variability=Fmi2Variability.tunable))

The FMU standard defines three relevant causalities:

  • input -> input parameters that change in time (assume to be REAL aka floats)
  • output -> output parameters that change in time (assume to be REAL aka floats)
  • parameter -> set at the start of the simulation and does not change (strings, float, interges, boolean)

NOTE: FMI standard 2.0 only allows for scalar data transfer but does not support data fields so an OpenFOAM vector would be 3 input variables (This will change in FMI 3.0)

In the example above we specify following causalities:

  • input:
    • Qin
  • input:
    • Tout
    • dTout
  • parameter
    • writeInterval
    • STDLAP
    • STDGRAD
    • host (from base class)
    • port (from base class)
    • outputPath (from base class)
    • oscmd (from base class)
    • arguments (from base class)

These parameters can be modified before running the FMU for example with the fmpy by calling

python -m fmpy.gui TempControl.fmu
fmpy-gui
Display in Fmpy GUI