Difference between revisions of "Contrib/multiSolver"

From OpenFOAMWiki
Line 48: Line 48:
 
== How do you program applications with it? ==
 
== How do you program applications with it? ==
  
 +
=== Overview ===
 
Here's a quick overview of how to program applications with multiSolver.
 
Here's a quick overview of how to program applications with multiSolver.
  
Line 68: Line 69:
 
  return 0;
 
  return 0;
  
<!--=== dummyControlDict ===
+
=== More details ===
In order to allow ''runTimeModification'' of '''multiSolver''''s [[TestMarupio/glossary#multiDict|multiDicts]], '''multiSolver''' required its own [[objectRegistry]].  Therefore <tt>multiDictRegistry_</tt> is a <tt>Time</tt> object.  <tt>Time</tt> was never intended to be a member variable, therefore its constructors do not allow initialization without a <tt>controlDict</tt>.  The object <tt>dummyControlDict</tt> was introduced as a self-initializing, minimal <tt>controlDict</tt>.  Ultimately it was necessary for global ''runTimeModification''.
+
The example above is a simple use of '''multiSolver''', but it is capable of more.
-->
+
 
 +
==== Order of execution ====
 +
In this example, all the [[TestMarupio/glossary#solverDomain|solverDomains]] execute in sequence, once per [[TestMarupio/glossary#superLoop|superLoop]].  This is not necessary.  You can enclose them in conditionals, they can execute in any order; they can miss entire [[TestMarupio/glossary#superLoop|superLoops]]; however, they cannot execute more than once per [[TestMarupio/glossary#superLoop|superLoop]].  Use: <tt>multiRun++</tt> between solvers to increment the [[TestMarupio/glossary#superLoop|superLoop]] number if necessary.
 +
 
 +
==== runTime must go out of scope ====
 +
Looking at the include files specified in the simple example above, you will notice that the entire solver loop is enclosed in its own set of braces { }, starting at <tt>#include "createTime.H"</tt>.  This is necessary because all the solver variables, including '''runTime''' itself must go out of scope before '''multiSolver''' initializes another [[TestMarupio/glossary#solverDomain|solverDomain]].
 +
 
 +
==== Support classes ====
 +
There are a few additional classes that were written to support '''multiSolver''', and they can be useful elsewhere.  These include:
 +
 
 +
* <tt>tuple2List</tt> class;
 +
* <tt>timeCluster</tt> class;
 +
* <tt>timeClusterList</tt> class; and
 +
* <tt>dummyControlDict</tt> class.
 +
 
 +
'''tuple2List'''<BR>
 +
To assist with the ''timeFunctions'', a sortable list of paired values was created.  It is sortable by first or second value, and currently can be any combination of <tt>scalar</tt> or <tt>label</tt>.
 +
 
 +
'''timeCluster'''<BR>
 +
<tt>timeCluster<tt> is to '''multiSolver''' what <tt>instant</tt> is to '''runTime'''.  Basically an object that can catalogue all the time data within a '''multiSolver'''-enabled application.
 +
 
 +
'''timeClusterList'''<BR>
 +
Again, similar to <tt>instantList</tt>, for '''multiSolver'''.
 +
 
 +
'''dummyControlDict'''<BR>
 +
In order to allow ''runTimeModification'' of '''multiSolver''''s [[TestMarupio/glossary#multiDict|multiDicts]], '''multiSolver''' required an [[snip objectRegistry|objectRegistry]] that doesn't dissappear between [[TestMarupio/glossary#solverDomain|solverDomains]], when '''runTime''' goes out of scope.  Therefore it needed its own [[objectRegistry]].  Hence, <tt>multiDictRegistry_</tt> is a <tt>Time</tt> object.  <tt>Time</tt> was never intended to be a member variable, therefore its constructors do not allow initialization without a <tt>controlDict</tt>.  The object <tt>dummyControlDict</tt> was introduced as a self-initializing, minimal <tt>controlDict</tt>.  Ultimately it was necessary for global ''runTimeModification''.
 +
 
 
== How do you run simulations with it? ==
 
== How do you run simulations with it? ==
 
Here is a brief overview of how to work with a '''multiSolver'''-enabled application.
 
Here is a brief overview of how to work with a '''multiSolver'''-enabled application.

Revision as of 20:42, 25 June 2010

Subpages
> glossary
> multiControlDict
multiSolver allows you to create a superSolver composed of multiple solvers within a superLoop. All solvers operate on the same dataset in sequence. For example:

  1. icoFoam - runs to completion;
  2. data is handed over to scalarTransportFoam;
  3. scalarTransportFoam - runs to completion;
  4. data is handed back to icoFoam, and the superLoop repeats.

The settings for each solver are all stored within a single case directory using a multiDict format. All the data output is sorted into subdirectories corresponding to the solver, and can be retrieved for easy data management. This kind of behaviour is very useful for:

  • multi-step processes to be modelled within a single application, e.g. fluid injection, followed by settling;
  • modelling of a flow problem characterized by two different timescales, e.g. stirring with biochemical reactions; and
  • changing boundary conditions mid-run.

It turns out this type of behaviour is difficult, if not impossible, to implement in OpenFOAM without multiSolver.

1 Why is it needed?

A fundamental assumption in the design of OpenFOAM is the existence of a universal time. Therefore the time object is the top-level object registry, which means it controls the database for every solver. This design works for nearly all simulations imaginable, except for multi-step simulations.

Multi-step simulation
Here, a multi-step simulation is a simulation where:
  1. one solver is run to completion;
  2. the dataset is handed over to another solver;
  3. this solver is run to completion; and
  4. repeat from step #2 as many times as necessary for as many different solvers as necessary.

Trying to accomplish this within the existing runTime framework is problematic. multiSolver achieves this behaviour neatly.

2 When do you need it?

The runTime framework can handle nearly every simulation imaginable, but there are situations where multiSolver will make things much easier. For example:

  • If you are modelling a multi-step process, with clearly defined changes in boundary-conditions over time; or
  • If you are modelling a single problem with internal processes characterized by vastly different timescales.

Basically, if you find yourself:

  • frequently copying data between case directories; or
  • frequently stopping and changing the simulation details, then restarting,

then multiSolver might help you.

2.1 Parallel runs and mesh motion not fully supported yet

NOTE: At this time, multiSolver does not support parallel runs at all; and mesh motion is allowed, provided the mesh returns to its original position between solvers. This functionality is planned for the future.

3 How do you program applications with it?

3.1 Overview

Here's a quick overview of how to program applications with multiSolver.

  1. Download it;
  2. Compile it;
  3. Write (or choose from existing) solvers that your superSolver will use;
  4. Set up the createFields for each solver;
  5. Add #include "multiSolver.H" to the top;
  6. Between #include "setRootCase.H" and #include "createTime.H" add:
#   include "createMultiRun.H"
    solverDomain = "nameOfFirstSolver";
#   include "setSolverDomain.H"

7. Between each solver, add the lines:

    solverDomain = "nameOfNextSolver";
#   include "setSolverDomain.H"
where nameOfNextSovler is a solverDomainName

8. From each solver, include everything from #include "createTime.H" to just before return 0;
9. At the end, add:

#   include "endMultiSolver.H"
return 0;

3.2 More details

The example above is a simple use of multiSolver, but it is capable of more.

3.2.1 Order of execution

In this example, 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 increment the superLoop number if necessary.

3.2.2 runTime must go out of scope

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

3.2.3 Support classes

There are a few additional classes that were written to support multiSolver, and they can be useful elsewhere. These include:

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

tuple2List
To assist with the timeFunctions, 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.

timeCluster
timeCluster<tt> is to multiSolver what <tt>instant is to runTime. Basically an object that can catalogue all the time data within a multiSolver-enabled application.

timeClusterList
Again, similar to instantList, for multiSolver.

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. Ultimately it was necessary for global runTimeModification.

4 How do you run simulations with it?

Here is a brief overview of how to work with a multiSolver-enabled application.

4.1 MultiControlDict

The multiControlDict(glossary) is the multiSolver analogue of the controlDict. The controlDict is auto-generated based on the content of this file. The multiControlDict is the main control dictionary and therefore it does not conform to the format of a regular multiDict(glossary). For the full break-down of all settings and options available in the multiControlDict, see multiControlDict.

4.2 MultiDicts

A regular solver reads information from various dictionary files, and these affect its behaviour. When using multiSolver, there will be dictionaries whose values need to be different for each solverDomain. To specify this behaviour, a multiDict is used.

A multiDict has the following structure:

dictionaryName   fvSchemes;

multiSolver
{
    sovlerDomainName1 // this is the solverDomain name
    {
         // settings for the first solver go here
    }
    solverDomainName2 // another solverDomain name
    {
        // settings for the second solver go here
    }
    default // optional
    {
        // default settings go here
        // these are loaded first, then overwritten by solverName (above)
        // solvers whose names are not listed above inherit only these settings, or none at all if default is absent
    }
}

4.3 Boundary conditions and initial values

The boundary conditions and initial values are located in:

case/multiSolver/[solverDomainName]/0/0

This is the analogue of the case/0 directory, except there is one for every solverDomain. Unlike in a regular simulation, multiSolver will always refer to the boundary conditions located in 0/0.

multiSolver allows the boundary conditions to change between solverDomains. To accomplish this, multiSolver will use the boundaryField specified in [solverDomainName]/0/0 and copy over the latest internalField (from the previous solverDomain).

4.4 Output Data

The data is sorted into superLoop subdirectories within subdirectories named after the solverDomain:

case/multiSolver/[solverDomainName]/[superLoopIndex]/[timeValue]

The standard location of case/[timeValue] is used as a temporary loading area, mostly for post-processing.

4.5 Post-processing

OpenFOAM is hard-coded to look for data in the case/[time] directories. In order to post-process (including sampling, and data conversion) the data needs to be there. To accomplish this,

4.6 Runtime Modification

There are two levels of runtime modification: within a solverDomain, and globally.

  • Editting a standard dictionary (e.g. controlDict, or transportProperties applies to a solverDomain. Its effect depends on whether that solver has runTimeModifiable enabled. This happens at the end of a solver iteration. However, these changes will be lost in the next superLoop when the same solverDomain is initialized.
  • Editting a multiDict dictionary applies globally. This is goverend by multiDictsRunTimeModifiable setting in the multiControlDict, but these modifications do not take place until the next solverDomain is initialized. However, these changes are permanent.

5 How does it work?

OpenFOAM is incredibly flexible, and easily extensible, but implementing a change of this kind challenged its founding assumptions. Therefore, the flexibility was not there on level it needed to be, leaving little option but to use an interface-wrapper implementation.

An interface-wrapper is using an interface for purposes that it was not intended. For example, I read an old ad for a home security system that called the monitoring company by using a robotic arm to lift the phone receiver and physically dial the numbers. Why they didn't simply plug the phone line directly into the security system, I have no idea... but this is good example of an interface-wrapper.

multiSolver works by mutating the case directory into what each solver requires. A transient solver will see the correct ddtSchemes setting in fvSchemes; likewise a steady state solver will see steadyState for ddtSchemes. This is the purpose of the multiDict(glossary) dictionary format.

The data output and input are hard-coded to the case/[timeValue] directory. Therefore, when multiSolver initializes the next solverDomain, it archives the existing output into the correct directory at case/multiSolver/[solverDomainName]/[superLoopIndex]/[timeValue]