This repository has been archived on 2023-11-20. You can view files and clone it, but cannot push or open issues or pull requests.
foam-extend4.1-coherent-io/applications/utilities/postProcessing/multiSolver/multiSolver.C
Henrik Rusche 67ab0b5abd Vanilla backport
- in FOAM library
updated containers
backported PackedBoolList, hashedWordList, nullObject, wordRe,
backported functions to
backported int32 support
backported tableReaders
backported Function1, TimeFunction1
backported dynamicCode (for codedBCs, ...) -- needs to be mapped out
advanced error macros (FatalIOErrorInFunction, ...) -- needs to be mapped out
backported IOobject::MUST_READ_IF_MODIFIED and added IOobject::READ_IF_PRESENT_IF_MODIFIED (only in FO)

- in postProcessing
backported IO FOs (partialWrite, removeRegisteredObject, writeDictionary, writeRegisteredObject)
backported field FOs (fieldCoordinateSystemTransform, fieldValues, nearWallFields, processorField, readFields, regionSizeDistribution, streamLine, wallBoundedStreamLine)
backported fvTools FOs (calcFvcDiv, calcFvcGrad, calcMag)
backported jobControl FOs (abortCalculation)
backported utilities FOs (ourantNo, Lambda2, Peclet, Q, codedFunctionObject, pressureTools, residuals, scalarTransport, setTimeStep, timeActivatedFileUpdate, turbulenceFields, vorticity, wallShearStress)
2018-02-16 15:07:55 +01:00

792 lines
24 KiB
C++

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | foam-extend: Open Source CFD
\\ / O peration | Version: 4.0
\\ / A nd | Web: http://www.foam-extend.org
\\/ M anipulation | For copyright notice see file Copyright
-------------------------------------------------------------------------------
License
This file is part of foam-extend.
foam-extend is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
foam-extend is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
Application
multiSolver
Description
Post-processor utility required for working with multiSolver-enabled
applications. These applications store data output in a different
location than usual. This utility loads and unloads data from that
location to where post-processors expect it.
Author
David L. F. Gaden
\*---------------------------------------------------------------------------*/
#include "fvCFD.H"
#include "multiSolver.H"
void parseOptions
(
wordList * ptrSolverDomains,
labelList * ptrSuperLoops,
string options
)
{
IStringStream optionsStream(options);
label nSolverDomains(0);
label nSuperLoops(0);
// Get solverDomainNames, if any
while (not optionsStream.eof())
{
token nextOption(optionsStream);
// Bug workaround
if (nextOption.type() == token::ERROR)
{
break;
}
if (nextOption.isLabel())
{
ptrSuperLoops->setSize(++nSuperLoops);
ptrSuperLoops->operator[](nSuperLoops - 1)
= nextOption.labelToken();
break;
}
if (nextOption.isWord())
{
ptrSolverDomains->setSize(++nSolverDomains);
ptrSolverDomains->operator[](nSolverDomains - 1)
= nextOption.wordToken();
}
else
{
// not word, not label, fail
FatalErrorIn("multiSolver::parseOptions")
<< "Expecting word or label. Neither found at position "
<< nSolverDomains - 1 << " in " << options
<< abort(FatalError);
}
}
// Get superLoopList
while (not optionsStream.eof())
{
token nextOption(optionsStream);
// Bug workaround
if (nextOption.type() == token::ERROR)
{
break;
}
if (nextOption.isLabel())
{
ptrSuperLoops->setSize(++nSuperLoops);
ptrSuperLoops->operator[](nSuperLoops - 1)
= nextOption.labelToken();
}
else if (nSuperLoops > 0)
{
// might be a range -> label : label
if (nextOption.isPunctuation())
{
token::punctuationToken p(nextOption.pToken());
if (p == token::COLON)
{
token nextNextOption(optionsStream);
if (nextNextOption.isLabel())
{
label toValue(nextNextOption.labelToken());
label fromValue
(
ptrSuperLoops->operator[](nSuperLoops - 1)
);
if (toValue > fromValue)
{
// correct range format
for (label i = fromValue + 1; i <= toValue; i++)
{
ptrSuperLoops->setSize(++nSuperLoops);
ptrSuperLoops->operator[](nSuperLoops - 1) = i;
}
}
else
{
// greater than / less than, range
FatalErrorIn("multiSolver::parseOptions")
<< "superLoop range incorrect order. 'from : "
<< "to' where 'from' should be less than "
<< "'to'. Values read are '" << fromValue
<< " : " << toValue
<< abort(FatalError);
}
}
else
{
// nextNext not label
FatalErrorIn("multiSolver::parseOptions")
<< "Incorrect syntax. Expecting label after ':' "
<< "in " << options
<< abort(FatalError);
}
}
else
{
// non : punctuation
FatalErrorIn("multiSolver::parseOptions")
<< "Incorrect syntax. Expecting label, word, or ':' "
<< "in " << options
<< abort(FatalError);
}
}
else
{
// not punctuation
FatalErrorIn("multiSolver::parseOptions")
<< "Incorrect syntax. Expecting label, word, or ':' "
<< "in " << options
<< abort(FatalError);
}
}
else
{
// not label, not word
FatalErrorIn("multiSolver::parseOptions")
<< "Incorrect syntax. Expecting label, word, or ':' "
<< "in " << options
<< abort(FatalError);
}
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::validOptions.insert("list","");
argList::validOptions.insert
(
"load","<[solverDomainName] [superLoopNumber(s)]>"
);
argList::validOptions.insert
(
"purge","<[solverDomainName] [superLoopNumber(s)]>"
);
argList::validOptions.insert("set","<solverDomainName>");
argList::validOptions.insert("preDecompose", "");
argList::validOptions.insert("postDecompose", "");
argList::validOptions.insert("preReconstruct", "");
argList::validOptions.insert("postReconstruct", "");
argList::validOptions.insert("global","");
argList::validOptions.insert("local","");
// default behaviour is purge the case/[time] directory before '-load'
// command. '-noPurge' prevents this. Allows for more complicated
// load data selections by executing multiSolver several times
argList::validOptions.insert("noPurge","");
// default behaviour is: if there is only one solverDomain specified, use
// setSolverDomain() on it. Same as multiSolver -set solverDomain.
// '-noSet' prevents this.
argList::validOptions.insert("noSet","");
// default behaviour is: if there are storeFields defined, when loading, it
// will copy the store fields into every time instance where they are
// absent. '-noStore' will prevent this.
argList::validOptions.insert("noStore","");
# include "setRootCase.H"
enum commandType
{
list,
load,
purge,
set,
preDecompose,
postDecompose,
preReconstruct,
postReconstruct
};
commandType command;
string options;
bool global = false;
bool local = false;
bool all = false;
bool root = false;
bool noPurge = false;
bool noSet = false;
bool noStore = false;
label nCommands(0);
// Read arguments
if (args.optionFound("list"))
{
nCommands++;
command = list;
}
if (args.optionFound("load"))
{
nCommands++;
command = load;
options = args.options()["load"];
}
if (args.optionFound("purge"))
{
nCommands++;
command = purge;
options = args.options()["purge"];
}
if (args.optionFound("set"))
{
nCommands++;
command = set;
options = args.options()["set"];
}
if (args.optionFound("preDecompose"))
{
nCommands++;
command = preDecompose;
}
if (args.optionFound("postDecompose"))
{
nCommands++;
command = postDecompose;
}
if (args.optionFound("preReconstruct"))
{
nCommands++;
command = preReconstruct;
}
if (args.optionFound("postReconstruct"))
{
nCommands++;
command = postReconstruct;
}
if (args.optionFound("global"))
{
global = true;
}
if (args.optionFound("local"))
{
local = true;
}
if (args.optionFound("noPurge"))
{
noPurge = true;
}
if (args.optionFound("noSet"))
{
noSet = true;
}
if (args.optionFound("noStore"))
{
noStore = true;
}
// Error checking
if (nCommands == 0)
{
FatalErrorIn("multiSolver::main")
<< "multiSolver - nothing to do. Use 'multiSolver -help' for assistance."
<< abort(FatalError);
}
else if (nCommands > 1)
{
FatalErrorIn("multiSolver::main")
<< "More than one command found. Use only one of:"
<< "\n\t-list"
<< "\n\t-purge"
<< "\n\t-set"
<< "\n\t-preDecompose"
<< "\n\t-postDecompose"
<< "\n\t-preReconstruct"
<< "\n\t-postReconstruct\n"
<< abort(FatalError);
}
if (global && local)
{
FatalErrorIn("multiSolver::main")
<< "Options global and local both specified. Use only one or "
<< "none."
<< abort(FatalError);
}
if ((command != load) && (noPurge || noSet || noStore))
{
FatalErrorIn("multiSolver::main")
<< "'noPurge', 'noSet' and 'noStore' can only be used with the "
<< "'-load' command."
<< abort(FatalError);
}
multiSolver multiRun
(
Foam::multiSolver::multiControlDictName,
args.rootPath(),
args.caseName()
);
const IOdictionary& mcd(multiRun.multiControlDict());
wordList solverDomains(0);
labelList superLoops(0);
if
(
(command != list)
&& (command != preDecompose)
&& (command != postDecompose)
&& (command != preReconstruct)
&& (command != postReconstruct)
)
{
parseOptions(&solverDomains, &superLoops, options);
}
// Special words - all, root
if (solverDomains.size() == 1)
{
if (solverDomains[0] == "all")
{
all = true;
}
else if (solverDomains[0] == "root")
{
root = true;
}
}
// More error checking
if (root && ((command == load) || (command == set)))
{
FatalErrorIn("multiSolver::main")
<< "'root' is not a valid option with '-load' or '-set'"
<< abort(FatalError);
}
if (all && (command == set))
{
FatalErrorIn("multiSolver::main")
<< "'all' is not a valid option with '-set'"
<< abort(FatalError);
}
if ((command == set) && ((solverDomains.size() > 1) || superLoops.size()))
{
FatalErrorIn("multiSolver::main")
<< "'-set' can only have a single solverDomain name as an option."
<< abort(FatalError);
}
if (all && superLoops.size())
{
FatalErrorIn("multiSolver::main")
<< "'all' cannot be followed by superLoop numbers. To specify "
<< "a superLoop range for all solverDomains, omit the solverDomain"
<< " name entirely. e.g. multiSolver -load '0:4 6'"
<< abort(FatalError);
}
if (root && superLoops.size())
{
FatalErrorIn("multiSolver::main")
<< "'root' cannot be followed by superLoop numbers. 'root' refers"
<< " to case/[time] directories. There are no superLoops here."
<< abort(FatalError);
}
// Check for correct solverDomain names
if
(
!all
&& !root
&& (command != list)
&& (command != preDecompose)
&& (command != postDecompose)
&& (command != preReconstruct)
&& (command != postReconstruct)
)
{
forAll(solverDomains, i)
{
if (solverDomains[i] == "default")
{
// default not permitted
FatalErrorIn("multiSolver::main")
<< "'default' is not a permitted solverDomain name."
<< abort(FatalError);
}
if (!mcd.subDict("solverDomains").found(solverDomains[i]))
{
// Incorrect solver domain name
FatalErrorIn("multiSolver::main")
<< "solverDomainName " << solverDomains[i] << "is not "
<< "found."
<< abort(FatalError);
}
}
}
// Load specified timeClusterLists
timeClusterList tclSource(0);
if (all)
{
// read all
tclSource = multiRun.readAllTimes();
forAll(tclSource, i)
{
if (tclSource[i].superLoop() == -1)
{
tclSource[i].times().clear();
}
}
tclSource.purgeEmpties();
}
else if
(
!superLoops.size()
&& (command != set)
&& (command != list)
&& (command != preDecompose)
&& (command != postDecompose)
&& (command != preReconstruct)
&& (command != postReconstruct)
)
{
// no superLoops specified - read entire solverDomains
forAll (solverDomains, sd)
{
tclSource.append
(
multiRun.readSolverDomainTimes(solverDomains[sd])
);
}
}
else if
(
!root
&& (command != set)
&& (command != list)
&& (command != preDecompose)
&& (command != postDecompose)
&& (command != preReconstruct)
&& (command != postReconstruct)
)
{
// read individual superLoops
if (!solverDomains.size())
{
solverDomains = mcd.subDict("solverDomains").toc();
}
forAll(superLoops, sl)
{
forAll(solverDomains, sd)
{
if (solverDomains[sd] == "default") continue;
tclSource.append
(
multiRun.readSuperLoopTimes
(
solverDomains[sd],
superLoops[sl]
)
);
}
}
}
if (tclSource.size())
{
if (!tclSource.purgeEmpties())
{
FatalErrorIn("multiSolver::main")
<< "No data found with specified parameters."
<< abort(FatalError);
}
}
switch (command)
{
case list:
{
Info << "Listing available data:\n" << endl;
Info << "superLoops by solverDomain:" << endl;
solverDomains = mcd.subDict("solverDomains").toc();
fileName listPath
(
multiRun.multiDictRegistry().path()/"multiSolver"
);
forAll(solverDomains, i)
{
if (solverDomains[i] == "default") continue;
Info << solverDomains[i] << ":" << endl;
Info << multiRun.findSuperLoops(listPath/solverDomains[i])
<< endl;
}
Info << endl;
break;
}
case load:
{
// Default behaviour - use local time unless overlapping, then use
// global time; if global overlaps, fail. -local and -global force
// the behaviour
bool localOverlap(!multiRun.nonOverlapping(tclSource, false));
bool globalOverlap(!multiRun.nonOverlapping(tclSource, true));
if (local && localOverlap)
{
FatalErrorIn("multiSolver::main")
<< "'-local' option used for data with overlapping local "
<< "values. Try using a single solverDomain / superLoop, "
<< "or leave '-local' off."
<< abort(FatalError);
}
if (globalOverlap)
{
FatalErrorIn("multiSolver::main")
<< "globalTime values are overlapping. This should not "
<< "happen. Ensure you have not specified the same "
<< "solverDomain and/or superLoop more than once. If "
<< "that fails, try using 'multiSolver -purge all' and "
<< "rerunning the simulation. If the problem persists, "
<< "it is a bug."
<< abort(FatalError);
}
if (!noPurge)
{
Info << "Purging existing time directories in case root"
<< endl;
multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
}
Info << "Loading data from multiSolver directories to case root"
<< endl;
if
(
!multiRun.loadTimeClusterList
(
tclSource,
global || localOverlap,
!noStore
)
)
{
FatalErrorIn("multiRun::main")
<< "loadTimeClusterList failed. timeClusterList contents: "
<< tclSource
<< abort(FatalError);
}
break;
}
case purge:
if (root)
{
Info << "Purging time directories from case root" << endl;
multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
}
else
{
Info << "Purging time directories from multiSolver directories"
<< endl;
forAll(tclSource, i)
{
// do not purge 'initial' directory, even if specified
if (tclSource[i].superLoop() < 0) continue;
fileName purgePath
(
multiRun.findInstancePath(tclSource[i], 0).path()
);
rmDir(purgePath);
}
}
break;
case set:
// do nothing here
break;
case preDecompose:
{
Info << "Performing preDecompose" << endl;
multiRun.preCondition();
break;
}
case postDecompose:
{
Info << "Performing postDecompose" << endl;
fileNameList dirEntries
(
readDir
(
multiRun.multiDictRegistry().path(), fileName::DIRECTORY
)
);
forAll(dirEntries, de)
{
if (dirEntries[de](9) == "processor")
{
Info << "Reading " << dirEntries[de] << endl;
multiRun.postCondition
(
dirEntries[de]
);
// Copy system to processorN
cp
(
multiRun.multiDictRegistry().path()
/multiRun.multiDictRegistry().system(),
multiRun.multiDictRegistry().path()/dirEntries[de]
);
// Copy constant/files to processorN/constant
fileNameList constantContents
(
readDir
(
multiRun.multiDictRegistry().path()
/multiRun.multiDictRegistry().constant(),
fileName::FILE
)
);
forAll(constantContents, cc)
{
cp
(
multiRun.multiDictRegistry().path()
/multiRun.multiDictRegistry().constant()
/constantContents[cc],
multiRun.multiDictRegistry().path()/dirEntries[de]
/multiRun.multiDictRegistry().constant()
);
}
// Copy constant/directories to processorN/constant
constantContents = readDir
(
multiRun.multiDictRegistry().path()
/multiRun.multiDictRegistry().constant(),
fileName::DIRECTORY
);
forAll(constantContents, cc)
{
// Ingore mesh directory
if (constantContents[cc] == "polyMesh")
{
continue;
}
cp
(
multiRun.multiDictRegistry().path()
/multiRun.multiDictRegistry().constant()
/constantContents[cc],
multiRun.multiDictRegistry().path()/dirEntries[de]
/multiRun.multiDictRegistry().constant()
);
}
}
}
multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
break;
}
case preReconstruct:
{
Info << "Performing preReconstruct" << endl;
fileNameList dirEntries
(
readDir
(
multiRun.multiDictRegistry().path(), fileName::DIRECTORY
)
);
forAll(dirEntries, de)
{
if (dirEntries[de](9) == "processor")
{
Info << "Reading " << dirEntries[de] << endl;
multiRun.preCondition
(
dirEntries[de]
);
// Fix missing 0.00000e+00 directory if it exists
mkDir
(
multiRun.multiDictRegistry().path()/dirEntries[de]/"0"
);
}
}
break;
}
case postReconstruct:
{
Info << "Performing postReconstruct" << endl;
Info << "Reading preconditioned time directories" << endl;
multiRun.postCondition();
Info << "Purging preconditioned time directories"
<< endl;
// Clean up extra time directories
fileNameList dirEntries
(
readDir
(
multiRun.multiDictRegistry().path(), fileName::DIRECTORY
)
);
forAll(dirEntries, de)
{
if (dirEntries[de](9) == "processor")
{
multiRun.purgeTimeDirs
(
multiRun.multiDictRegistry().path()/dirEntries[de]
);
}
}
break;
}
}
// Execute set command - either from an explicit '-set' or from a '-load'
// with only one solverDomain as an option
if
(
(command == set)
|| (
(command == load)
&& (solverDomains.size() == 1)
&& (!all)
)
)
{
Info << "Changing to " << solverDomains[0] << " settings." << endl;
multiRun.setSolverDomainPostProcessing(solverDomains[0]);
}
Info << "\nCommand completed successfully.\n" << endl;
return(0);
}
// ************************************************************************* //