Difference between revisions of "Contrib multiSolver/programming"

From OpenFOAMWiki
(Migrated to github)
 
Line 1: Line 1:
== Programming basics ==
+
This project has migrated over to '''github'''.
Programming a '''multiSolver'''-enabled application is almost as simple as pasting two solvers together.
+
  
=== Simple example ===
+
'''''[http://github.com/Marupio/multiSolver/wiki Click here for the new website.]'''''
Often a simple example is enough to get started.  Here's a simple '''multiSolver'''-enabled application, or "[[Contrib_multiSolver/glossary#superSolver|superSolver]]":
+
  
<cpp>/*---------------------------------------------------------------------------*\
+
http://github.com/Marupio/multiSolver/wiki
                            ... STANDARD HEADER ...
+
\*---------------------------------------------------------------------------*/
+
 
+
#include "fvCFD.H"
+
#include "multiSolver.H"
+
 
+
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
 
+
int main(int argc, char *argv[])
+
{
+
 
+
#  include "setRootCase.H"
+
#  include "createMultiSolver.H"
+
 
+
// * * * * * * * * * * * * * * * * icoFoam  * * * * * * * * * * * * * * * * //
+
 
+
    Info << "*** Switching to icoFoam ***\n" << endl;
+
    solverDomain = "icoFoam";
+
#  include "setSolverDomain.H"
+
 
+
// Paste everything from icoFoam.C, starting with #include "createTime.H",
+
// and ending just before (but not including) return 0;
+
 
+
// * * * * * * * * * * * * * scalarTransportFoam * * * * * * * * * * * * * * //
+
 
+
    Info << "*** Switching to scalarTransportFoam ***\n" << endl;
+
    solverDomain = "scalarTransportFoam";
+
#  include "setSolverDomain.H"
+
 
+
// Paste everything from scalarTransportFoam.C, again, starting with
+
// #include "createTime.H", and ending just before (but not including)
+
// return 0;
+
 
+
#  include "endMultiSolver.H"
+
    return(0);
+
}
+
 
+
// ************************************************************************* //
+
</cpp>
+
 
+
=== Basic strategy ===
+
* Write (or choose existing) solvers that you intend to use with your [[Contrib_multiSolver/glossary#superSolver|superSolver]];
+
* The <tt>createFields.H</tt> files (and associate <tt>#include</tt> statements) have to be renamed if they differ between solvers;
+
* Add <tt>#include "multiSolver.H"</tt> to the top of the solver;
+
* Just after <tt>#include "setRootCase.H"</tt>, add: <tt>#include "createMultiSolver.H"</tt>
+
* Between solvers use:
+
<cpp>    solverDomain = "nextSolverDomain";
+
#  include "setSolverDomain.H"</cpp>
+
* End with:
+
<cpp>#include "endMultiSolver.H"
+
return (0);</cpp>
+
 
+
== Advanced concepts ==
+
If the basic framework described above doesn't suit your needs, read on.  This section also covers some semantics that may be useful to know.
+
 
+
=== More on solverDomains ===
+
A [[Contrib_multiSolver/glossary#solverDomain|solverDomain]] is an individual solver loop.  It is assigned a name, and the list of names is static.  A [[Contrib_multiSolver/glossary#solverDomainName|solverDomainName]] cannot be:
+
 
+
* <tt>all</tt>;
+
* <tt>constant</tt>;
+
* <tt>default</tt>; or
+
* <tt>root</tt>.
+
 
+
All [[Contrib_multiSolver/glossary#solverDomain|solverDomains]] must appear in the <tt>case/system/multiControlDict</tt> file, although declaring additional names is not a problem.
+
 
+
=== Order of execution ===
+
In the [[#simple example|simple example]] above, all the [[Contrib_multiSolver/glossary#solverDomain|solverDomains]] execute in sequence, once per [[Contrib_multiSolver/glossary#superLoop|superLoop]].  This is not necessary: you can enclose them in conditionals; they can execute in any order; they can miss entire [[Contrib_multiSolver/glossary#superLoop|superLoops]]; however, they cannot execute more than once per [[Contrib_multiSolver/glossary#superLoop|superLoop]].  Use: <tt>multiRun++</tt> between solvers to force the [[Contrib_multiSolver/glossary#superLoop|superLoop]] number to increment if necessary.
+
 
+
'''Note:''' Using a <tt>multiRun++</tt> statement may lead to user-confusion with the <tt>endSuperLoop</tt> condition for <tt>finalStopAt</tt>.
+
 
+
=== runTime must go out of scope ===
+
Looking at the include files specified in the [[Contrib_multiSolver#simpleExample|simple example]] on the main page, you will notice that the entire solver loop is enclosed in its own set of braces { }, starting before <tt>#include "createTime.H"</tt>.  This is necessary because '''runTime''', the mesh, and all fields must go out of scope before '''multiSolver''' initializes another [[Contrib_multiSolver/glossary#solverDomain|solverDomain]].
+
 
+
=== End condition ===
+
'''multiSolver''' will detect the end condition automatically during the <tt>setSolver</tt> function.  It will archive the last <tt>case/time</tt> directory, and exit the superLoop.  This is achieved using the <tt>#include</tt> framework described in the [[Contrib_multiSolver#simpleExample|simple example]] on the main page.  If you are deviating from this framework, the requirements for correctly ending the '''multiSolver''' are:
+
 
+
* <tt>setSolver</tt> will automatically detect an end condition;
+
* to force an end condition, use <tt>setFinished()</tt>;
+
* once the end condition is met, the function <tt>setSolverDomain</tt> 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 <tt>multiRun()</tt> returns true if another <tt>setSolverDomain</tt> still ''must'' be encountered; false means the run has finished, ''and'' <tt>setSolverDomain</tt> 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:
+
<cpp>if (multiRun.run())
+
{
+
    // solverDomain loop
+
}</cpp>
+
 
+
<!-- This has not been included
+
=== Solver signal end condition ===
+
There is a new end condition called <tt>solverSignal</tt>.  This allows users to tell the '''multiSolver''' that the solver ''must'' determine the end condition.  A superSolver not designed to handle this will enter a near-infinite loop.
+
-->
+
=== <tt>#undef</tt> directives ===
+
Sometimes there will be conflicts with <tt>#define</tt> directives across solverDomains.  For instance, if you have more than one solver using <tt>#include "createPhi.H"</tt>, only the first solver will recognize it.  This is caused by the fact that the <tt>createPhi.H</tt> file has this structure:
+
 
+
<cpp>#ifndef createPhi_H
+
#define createPhi_H
+
 
+
// createPhi code
+
 
+
#endif</cpp>
+
 
+
These preprocessor directives ensure the code for createPhi is read only once, regardless of how many times it is included.  This prevents the compiler from complaining that something is being redefined.  The problem is, when we switch solver domains, <tt>phi</tt> goes out of scope, and it is not recreated.  To overcome this, use an <tt>#undef</tt> directive between solver domains: <tt>#undef createPhi.H</tt>
+
 
+
This, and a few other <tt>#undef</tt> directives are already included by default in the <tt>setSolverDomain.H</tt> file.  You may encounter others that need to be undefined.  Please let me know if you do, and I will add it to the <tt>setSolverDomain.H</tt> file.  In the mean time, to add an <tt>#undef</tt> directive:
+
 
+
* using <tt>#include "setSolverDomain.H"</tt>:
+
 
+
<cpp>    Info << "*** Switching to icoFoam ***\n" << endl;
+
#  undef [conflicting definition]
+
    solverDomain = "icoFoam";
+
#  include "setSolverDomain.H"</cpp>
+
 
+
* without using <tt>#include "setSolverDomain.H"</tt>:
+
 
+
<cpp>} // previous solver domain goes out of scope
+
multiRun.setSolverDomain(solverDomain);
+
 
+
#undef [conflicting definition]
+
 
+
if (multiRun.run())
+
{ // next solver domain comes into scope</cpp>
+
 
+
where <tt>[conflicting definition]</tt> is the definition that needs to be removed.
+
 
+
== 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 a ''top-level wrapper'' implementation.
+
 
+
:A ''wrapper'' encloses the targeted object in a class that gives it the environment it expects to operate, while simultaneously presenting a different environment to other objects interfacing with it.  At the ''top-level'', the "other objects" are users.  (Strictly speaking, at the code-level, '''multiSolver''' is not a true wrapper since it doesn't include an "OpenFOAM solver" as a ''member variable'', but it is in principle.)
+
 
+
'''multiSolver''' works by ''mutating'' the case directory into what each solver requires.  A transient solver will see the correct <tt>ddtSchemes</tt> setting in <tt>fvSchemes</tt>; likewise a steady state solver will see <tt>steadyState</tt> for <tt>ddtSchemes</tt>.  This is the purpose of the [[#multiDicts|multiDict]][[Contrib_multiSolver/glossary#multiDict|(glossary)]] dictionary format.
+
 
+
The data output and input are hard-coded to the <tt>case/[timeValue]</tt> directory.  Therefore, when '''multiSolver''' initializes the next [[Contrib_multiSolver/glossary#solverDomain|solverDomain]], it archives the existing output into the correct directory at <tt>case/multiSolver/<nowiki>[</nowiki>[[Contrib_multiSolver/glossary#solverDomainName|solverDomainName]]<nowiki>]</nowiki>/<nowiki>[</nowiki>[[Contrib_multiSolver/glossary#superLoopIndex|superLoopIndex]]<nowiki>]</nowiki>/[timeValue]</tt>, and copies the latest field values to the initial time the next solver expects.
+
 
+
== Reference ==
+
This reference section gives an overview of the functions available to you.  You don't need to know any of this.  It might be better just to look at the source.
+
 
+
=== Solver interface functions ===
+
Functions designed for use within a solver.
+
 
+
==== setSolverDomain ====
+
This function ''mutates'' the case directory into what the next solverDomain expects.  It:
+
 
+
* rereads any modified multiDicts;
+
* archives the existing data to <tt>case/multiSolver/solverDomain/superLoop/time</tt>;
+
* copies the current field data to the <tt>case/time/</tt> directory, swapping the boundary conditions if necessary;
+
* creates and writes the new <tt>controlDict</tt>;
+
* swaps all multiDicts to the next solverDomain; and
+
* checks for the end condition.
+
 
+
==== 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 <tt>setSolverDomain()</tt>, then the full superSolver run is finished.)
+
 
+
==== 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 <tt>setSolverDomain()</tt> functions.
+
 
+
==== run and end ====
+
The <tt>run()</tt> and <tt>end()</tt> functions are analogous to those of the same name in the <tt>Time</tt> class.  The first is <tt>true</tt> when the run should continue; the latter is <tt>true</tt> when the run should end.
+
 
+
=== Post processing functions ===
+
There are several functions designed for post-processing.  Many of them depend on the use of the <tt>[[#timeCluster|timeCluster]]</tt> and <tt>[[#timeClusterList|timeClusterList]]</tt> classes.
+
 
+
==== setSolverDomainPostProcessing ====
+
This mutates the <tt>case</tt> directory to that expected by the specified solverDomain.  This is necessary for post-processors that read the controlDict.
+
 
+
==== timeFunctions ====
+
There are several searching / cataloging functions available for post-processing.  Their function is described in the <tt>multiSolver.H</tt> header file.  These include:
+
 
+
* <tt>findSuperLoops</tt> - list all the superLoop directories in a given path;
+
* <tt>findClosestGlobalTime</tt> - find the closest globalTime to a given value in a timeClusterList;
+
* <tt>findClosestLocalTime</tt> - 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;
+
* <tt>findInstancePath</tt> - return the path to a given timeCluster and index;
+
* <tt>findMaxSuperLoopValue</tt> - maximum superLoop by value;
+
* <tt>findMaxSuperLoopIndices</tt> - return a labelList of the timeClusters with the maximum superLoop value;
+
* <tt>nonOverlapping</tt> - if the timeClusters overlap in time, return false.  timeClusters that share starting points, or share ending points are non-overlapping;
+
* <tt>readSuperLoopTimes</tt> - catalog the <tt>time</tt> directories in <tt>case/multiSolver/givenSolverDomain/givenSuperLoop</tt>;
+
* <tt>readSolverDomainTimes</tt> - catalog the <tt>time</tt> directories in <tt>case/multiSolver/givenSolverDomain/allSuperLoops</tt>;
+
* <tt>readAllTimes</tt> - catalog the <tt>time</tt> directories in <tt>case/multiSolver/allSolverDomains/allSuperLoops</tt>;
+
* <tt>loadTimeClusterList</tt> - copy / move <tt>time</tt> directories in a timeclusterList to <tt>case/time</tt>;
+
* <tt>archiveTimeDirectories</tt> - copy / move <tt>time</tt> directories from <tt>sourcePath</tt> to <tt>destinatinoPath</tt>; and
+
* <tt>purgeTimeDirectories</tt> - delete all <tt>time</tt> directories in a given path.
+
 
+
=== Support classes ===
+
There are a few additional classes that were written to support '''multiSolver'''.  These include:
+
 
+
* <tt>tuple2List</tt> class;
+
* <tt>timeCluster</tt> class;
+
* <tt>timeClusterList</tt> class; and
+
* <tt>dummyControlDict</tt> class.
+
 
+
==== 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 <tt>scalar</tt> or <tt>label</tt>.
+
 
+
==== timeCluster ====
+
<tt>timeCluster</tt> is to '''multiSolver''' what <tt>instant</tt> is to '''runTime'''.  This object holds all the information necessary to catalog the data within a single <tt>solverDomain/superLoop</tt> directory.  It holds:
+
 
+
* the solverDomain name;
+
* the superLoop number;
+
* the globalTimeOffset; and
+
* an instantList, cataloging all the time directories within the directory.
+
 
+
Sometimes, a <tt>timeCluster</tt> is used to identify a single time directory within a solverDomain/superLoop.  This is useful for functions such as: <tt>findClosestGlobalTime</tt>, which needs to identify a single time directory.  To assist in this operation, <tt>operator()</tt> has been defined.  It creates just such a <tt>timeCluster</tt>, given the index of the <tt>instant</tt>.
+
 
+
(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.)
+
 
+
==== timeClusterList ====
+
Again, similar to <tt>instantList</tt>, for '''multiSolver'''.  A <tt>timeClusterList</tt> can catalog all the data in the case directory.  Unlike <tt>instantList</tt>, this class has some functions of its own:
+
 
+
* <tt>append</tt> - add another <tt>timeCluster</tt> or <tt>timeClusterList</tt> to its collective;
+
* <tt>globalSort</tt> - sort its constituent <tt>timeCluster</tt>s by their minimum globalTime;
+
* <tt>purgeEmpties</tt> - remove any <tt>timeCluster</tt>s that have an empty <tt>instantList</tt>.  Returns false if none remain.  Many functions depend on a non-empty <tt>instantList</tt>.
+
* <tt>selectiveSubList</tt> - returns a <tt>timeClusterList</tt> that is composed of a subList of the original <tt>timeClusterList</tt>.  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 <tt>timeClusterList</tt>.
+
 
+
'''Note:''' Use <tt>purgeEmpties</tt> to ensure there are no empty timeClusters.  Many of the [[#timeFunctions|timeFunctions]] will throw a fatal error if passed an empty timeClusterList.
+
 
+
==== dummyControlDict ====
+
In order to allow ''runTimeModification'' of '''multiSolver''''s [[Contrib_multiSolver/glossary#multiDict|multiDicts]], '''multiSolver''' required an [[snip objectRegistry|objectRegistry]] that doesn't dissappear between [[Contrib_multiSolver/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>.
+
 
+
<tt>dummycontrolDict</tt> also has constructors that take a <tt>multiControlDict</tt>; or its name.  These constructors look for the <tt>timeFormat</tt> and <tt>timePrecision</tt> keywords.  If found, it includes these in its settings.  These settings are static variables owned by <tt>Time</tt>; and to simplify implementation, it was made universal - i.e. they can only be set once (at initialization) in '''multiSolver'''.
+
 
+
Ultimately, the <tt>dummyControlDict</tt> was necessary for global ''runTimeModification''.
+

Latest revision as of 21:46, 6 September 2013

This project has migrated over to github.

Click here for the new website.

http://github.com/Marupio/multiSolver/wiki