OpenFOAM® v3.0+: New Solution Control Functionality

OpenFOAM® v3.0+: New Solution Control Functionality

13/01/2016

Co-simulation

OpenFOAM includes a set of boundary conditions for communicating between OpenFOAM and external codes using file-based data transfer. In previous versions the control between OpenFOAM and the external code was performed inside the boundary conditions itself.

In this version, the control logic has been moved to a special function object, externalCoupled, which controls the coupling of all regions at every time step. The control between OpenFOAM and the external code employs a lock file whereby the existence of the lock file instructs the external code to wait, and either read or write the boundary values.

The default file format is currently hard-coded to be a text based format with one line per boundary face, but this can be overridden inside the boundary condition.

The special settings in this functionObject are the definition of the directory to use for the file exchange, and the fields to read/write per region, per patch. For the patch, either a single patch name can be used or a patch group. The patch group enables multiple patches to be collated into one coupling interface.

readFields
{
    // Where to load it from (if not already in solver)
    functionObjectLibs ("libjobControl.so");

    type externalCoupled;

    // Directory to use for communication
    commsDir "$FOAM_CASE/comms";

    // Does external process start first
    initByExternal true;

    // Additional output
    log true;

    regions
    {
        // Region name
        region0
        {
            // Patch or patchGroup
            coupleGroup
            {
                // Fields to output in commsDir
                writeFields     (T);
                // Fields to read from commsDir
                readFields      (T);
            }
        }
    }
}

Examples
multi-region case: $FOAM_TUTORIALS/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater

This example shows the setup for a multi-region case, with reading and writing fixedValue boundary conditions for data transfer.

Source code
$FOAM_SRC/postProcessing/functionObjects/jobControl/externalCoupled

Case Termination Controls

Simulation duration controls are defined using the main run-time controls in the case controlDict dictionary, or, for steady cases, the residualControl section in the fvSolution dictionary. However, for transient cases, it is often more appropriate to end the calculation when relevant statistics have converged to within a given tolerance.

The new runTimeControl function object offers several termination options, including:

  • average: when the average of a function object value does not vary outside a given tolerance over a given time
  • equationInitialResidualDivergence: when an equation initial residual exceeds a given value
  • equationMaxIter: when an equation number of solve iterations exceeds a given value
  • minMax: when a function object value exceeds the bounds defined by minimum or maximum values
  • minTimeStep: when the time step falls below a given value

Conditions can be grouped by setting a groupID for each condition. In order for the condition to be marked as satisfied, all members of that group must be satisfied. A write step is performed each time any group is determined to be satisfied.

Case1: Terminate simulation based on drag coefficient convergence
The example case shows the application of the runTimeControl function object to terminate the calculation when the change in the average drag coefficient falls below a given threshold. Firstly, the force coefficients are calculated using a forceCoeffs function object:

forceCoeffs1
{
    type                forceCoeffs;
    functionObjectLibs  ( "libforces.so" );
    outputControl       timeStep;
    timeInterval        1;
    log                 yes;

    patches             (motorBikeGroup);

    // Indicates incompressible
    rhoName             rhoInf;

    // Redundant for incompressible
    rhoIn               1.0;

    liftDir             (1 0 1);

    dragDir             (1 0 0);

    // Axle midpoint on ground
    CofR                (0.72 0 0);

    pitchAxis           (0 1 0);

    magUInf             20;

    // Wheelbase length
    lRef                1.42;

    // Estimated
    Aref                0.75;
}

The average drag coefficient is then calculated using a valueAverage function object:

valueAverage1
{
    type                valueAverage;
    functionObjectLibs  ( "libfieldFunctionObjects.so" );
    outputControl       timeStep;

    // Retrieve Cd from forceCoeffs1 object, and average using a
    // window of 50
    functionObjectName  forceCoeffs1;
    fields              (Cd);
    window              50;
}
Finally, the variation in the average drag coefficient is computed by the averageCondition within the runTimeControl function object:

runTimeControl1
{
    type                runTimeControl;
    functionObjectLibs  ("libjobControl.so");

    conditions
    {
        // Terminate when average CDMean varies by less than tolerance
        average1
        {
            type                average;
            functionObjectName  valueAverage1;
            fields              (CdMean);
            tolerance           1e-4;
            window              50;
        }
    }
}
A typical output of a simulation may take the form:

Time = 435

solver output ...
forceCoeffs forceCoeffs1 output:
Coefficients
Cm : 0.028232 (pressure: 0.028297 viscous: -6.49179e-05)
Cd : 0.424388 (pressure: 0.409931 viscous: 0.0144568)
Cl : 0.0668758 (pressure: 0.0669115 viscous: -3.57411e-05)
Cl(f) : 0.0616699
Cl(r) : 0.00520584
valueAverage: valueAverage1 averages:
CdMean: 0.421788
runTimeControl runTimeControl1 output:
average: average1 averages:
CdMeanMean: 0.421856, delta: 6.83194e-05
average: average1 condition satisfied
Stopping calculation
Writing fields - final step
End

Case2: Write on diverge
The failure mode of OpenFOAM cases can be immediate, whereby the calculation can stop on the current iteration. In other cases, the failure mode can lead to a slow death whereby the calculation proceeds, sometimes indefinitely. In order to identify the latter case, a typical set-up may be:

runTimeControl1
{
    type                runTimeControl;
    functionObjectLibs  ("libjobControl.so");
    nWriteStep          3;

    conditions
    {
        condition1
        {
            ...
        }
        condition2
        {
            ...
        }
    }
}

The nWriteStep entry sets the number of write steps to perform prior to termination. For the example above, data will be written for a maximum of 3 times before the calculation stops.

Source code
runTimeControl $FOAM_SRC/postProcessing/functionObjects/jobControl/runTimeControl