Contrib multiSolver/programming

From OpenFOAMWiki

1 Solver mechanics

1.1 solverDomains

A solverDomain is an individual solver loop. It is assigned a name, and the list of names is static. A solverDomainName cannot be:

  • all;
  • default; or
  • root.

All solverDomains must appear in the case/system/multiControlDict file, although declaring additional names is not a problem.

1.2 Order of execution

In the simple example on the main page, all the solverDomains execute in sequence, once per superLoop. This is not necessary: you can enclose them in conditionals; they can execute in any order; they can miss entire superLoops; however, they cannot execute more than once per superLoop. Use: multiRun++ between solvers to force the superLoop number to increment if necessary.

Using a multiRun++ statement may lead to user-confusion with the endSuperLoop condition for finalStopAt.

1.3 runTime must go out of scope

Looking at the include files specified in the simple example on the main page, you will notice that the entire solver loop is enclosed in its own set of braces { }, starting before #include "createTime.H". This is necessary because runTime, the mesh, and all fields must go out of scope before multiSolver initializes another solverDomain.

1.4 End condition

multiSolver will detect the end condition automatically during the setSolver function. It will archive the last case/time directory, and exit the superLoop. This is achieved using the #include framework described in the simple example on the main page. If you are deviating from this framework, the requirements for correctly ending the multiSolver are:

  • setSolver will automatically detect an end condition;
  • to force an end condition, use setFinished();
  • once the end condition is met, the function setSolverDomain must be encountered at least once more (although it may be encountered any number of times) to perform the final data clean-up;
  • the function multiRun() returns true if another setSolverDomain still must be encountered; false means the run has finished, and setSolverDomain has completed the final clean-up;
  • the inidivual solverDomain loops cannot be encountered after the final clean-up has taken place. Enclose them each in:
if (multiRun.run())
{
    // solverDomain loop
}

2 Solver interface functions

Functions designed for use within a solver.

2.1 setSolverDomain

This function mutates the case directory into what the next solverDomain expects. It:

  • rereads any modified multiDicts;
  • archives the existing data to case/multiSolver/solverDomain/superLoop/time;
  • copies the current field data to the case/time/ directory, swapping the boundary conditions if necessary;
  • creates and writes the new controlDict;
  • swaps all multiDicts to the next solverDomain; and
  • checks for the end condition.

2.2 setFinished

This function tells multiSolver that once the current solverDomain is finihsed, the full superSolver run is finished. (Technical, it tells multiSolver to save the last data and clean-up at the next setSolverDomain(), then the full superSolver run is finished.)

2.3 operator++

This function increments the superLoop number. It must be used if the same solverDomain is visited more than once in the same superLoop (otherwise it will overwrite its previous data). It can be used once between any pair of setSolverDomain() functions.

2.4 run and end

The run() and end() functions are analogous to those of the same name in the Time class. The first is true when the run should continue; the latter is true when the run should end.

3 Post processing functions

There are several functions designed for post-processing. Many of them depend on the use of the timeCluster and timeClusterList classes.

3.1 setSolverDomainPostProcessing

This mutates the case directory to that expected by the specified solverDomain. This is necessary for post-processors that read the controlDict.

3.2 timeFunctions

There are several searching / cataloging functions available for post-processing. Their function is described in the multiSolver.H header file. These include:

  • findSuperLoops - list all the superLoop directories in a given path;
  • findClosestGlobalTime - find the closest globalTime to a given value in a timeClusterList;
  • findClosestLocalTime - find the closest localTime to a given value in a timeClusterList. If timeClusters are overlapping, this function only uses the those from the latest superLoop;
  • findInstancePath - return the path to a given timeCluster and index;
  • findMaxSuperLoopValue - maximum superLoop by value;
  • findMaxSuperLoopIndices - return a labelList of the timeClusters with the maximum superLoop value;
  • nonOverlapping - if the timeClusters overlap in time, return false. timeClusters that share starting points, or share ending points are non-overlapping;
  • readSuperLoopTimes - catalog the time directories in case/multiSolver/givenSolverDomain/givenSuperLoop;
  • readSolverDomainTimes - catalog the time directories in case/multiSolver/givenSolverDomain/allSuperLoops;
  • readAllTimes - catalog the time directories in case/multiSolver/allSolverDomains/allSuperLoops;
  • loadTimeClusterList - copy / move time directories in a timeclusterList to case/time;
  • archiveTimeDirectories - copy / move time directories from sourcePath to destinatinoPath; and
  • purgeTimeDirectories - delete all time directories in a given path.

4 Support classes

There are a few additional classes that were written to support multiSolver. These include:

  • tuple2List class;
  • timeCluster class;
  • timeClusterList class; and
  • dummyControlDict class.

4.1 tuple2List

This is a sortable list of paired values was created. It is sortable by first or second value, and currently can be any combination of scalar or label.

4.2 timeCluster

timeCluster is to multiSolver what instant is to runTime. This object holds all the information necessary to catalog the data within a single solverDomain/superLoop directory. It holds:

  • the solverDomain name;
  • the superLoop number;
  • the globalTimeOffset; and
  • an instantList, cataloging all the time directories within the directory.

Sometimes, a timeCluster is used to identify a single time directory within a solverDomain/superLoop. This is useful for functions such as: findClosestGlobalTime, which needs to identify a single time directory. To assist in this operation, operator() has been defined. It creates just such a timeCluster, given the index of the instant.

(This is a bit messy, it might have been smarter to create a different class to distinguish between single time directories, and time directory lists.)

4.3 timeClusterList

Again, similar to instantList, for multiSolver. A timeClusterList can catalog all the data in the case directory. Unlike instantList, this class has some functions of its own:

  • append - add another timeCluster or timeClusterList to its collective;
  • globalSort - sort its constituent timeClusters by their minimum globalTime;
  • purgeEmpties - remove any timeClusters that have an empty instantList. Returns false if none remain. Many functions depend on a non-empty instantList.
  • selectiveSubList - returns a timeClusterList that is composed of a subList of the original timeClusterList. The sublist is seleted by index using a labelList. The sublist is not a true sublist like in other classes; rather it is simply another timeClusterList.

Note: Use purgeEmpties to ensure there are no empty timeClusters. Many of the timeFunctions will throw a fatal error if passed an empty timeClusterList.

4.4 dummyControlDict

In order to allow runTimeModification of multiSolver's multiDicts, multiSolver required an objectRegistry that doesn't dissappear between solverDomains, when runTime goes out of scope. Therefore it needed its own objectRegistry. Hence, multiDictRegistry_ is a Time object. Time was never intended to be a member variable, therefore its constructors do not allow initialization without a controlDict. The object dummyControlDict was introduced as a self-initializing, minimal controlDict.

dummycontrolDict also has constructors that take a multiControlDict; or its name. These constructors look for the timeFormat and timePrecision keywords. If found, it includes these in its settings. These settings are static variables owned by Time; and to simplify implementation, it was made universal - i.e. they can only be set once (at initialization) in multiSolver.

Ultimately, the dummyControlDict was necessary for global runTimeModification.