Files
openfoam/src/OpenFOAM/global/argList/argList.C
Henry Weller cc455173ff simpleFoam: Added experimental "-postProcess" option
Executes application functionObjects to post-process existing results.

    If the "dict" argument is specified the functionObjectList is constructed
    from that dictionary otherwise the functionObjectList is constructed from
    the "functions" sub-dictionary of "system/controlDict"

    Multiple time-steps may be processed and the standard utility time
    controls are provided.

This functionality is equivalent to execFlowFunctionObjects but in a
more efficient and general manner and will be included in all the
OpenFOAM solvers if it proves effective and maintainable.

The command-line options available with the "-postProcess" option may be
obtained by

simpleFoam -help -postProcess

Usage: simpleFoam [OPTIONS]
options:
  -case <dir>       specify alternate case directory, default is the cwd
  -constant         include the 'constant/' dir in the times list
  -dict <file>      read control dictionary from specified location
  -latestTime       select the latest time
  -newTimes         select the new times
  -noFunctionObjects
                    do not execute functionObjects
  -noZero           exclude the '0/' dir from the times list, has precedence
                    over the -withZero option
  -parallel         run in parallel
  -postProcess      Execute functionObjects only
  -region <name>    specify alternative mesh region
  -roots <(dir1 .. dirN)>
                    slave root directories for distributed running
  -time <ranges>    comma-separated time ranges - eg, ':10,20,40:70,1000:'
  -srcDoc           display source code in browser
  -doc              display application documentation in browser
  -help             print the usage

Henry G. Weller
CFD Direct Ltd.
2016-05-08 09:33:46 +01:00

1227 lines
31 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "OSspecific.H"
#include "clock.H"
#include "IFstream.H"
#include "dictionary.H"
#include "IOobject.H"
#include "JobInfo.H"
#include "labelList.H"
#include "regIOobject.H"
#include "dynamicCode.H"
#include <cctype>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
bool Foam::argList::bannerEnabled = true;
Foam::SLList<Foam::string> Foam::argList::validArgs;
Foam::HashTable<Foam::string> Foam::argList::validOptions;
Foam::HashTable<Foam::string> Foam::argList::validParOptions;
Foam::HashTable<Foam::string> Foam::argList::optionUsage;
Foam::SLList<Foam::string> Foam::argList::notes;
Foam::string::size_type Foam::argList::usageMin = 20;
Foam::string::size_type Foam::argList::usageMax = 80;
Foam::word Foam::argList::postProcessOptionName("postProcess");
Foam::argList::initValidTables::initValidTables()
{
argList::addOption
(
"case", "dir",
"specify alternate case directory, default is the cwd"
);
argList::addBoolOption("parallel", "run in parallel");
validParOptions.set("parallel", "");
argList::addOption
(
"roots", "(dir1 .. dirN)",
"slave root directories for distributed running"
);
validParOptions.set("roots", "(dir1 .. dirN)");
argList::addBoolOption
(
"noFunctionObjects",
"do not execute functionObjects"
);
Pstream::addValidParOptions(validParOptions);
}
Foam::argList::initValidTables dummyInitValidTables;
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
void Foam::argList::addBoolOption
(
const word& opt,
const string& usage
)
{
addOption(opt, "", usage);
}
void Foam::argList::addOption
(
const word& opt,
const string& param,
const string& usage
)
{
validOptions.set(opt, param);
if (!usage.empty())
{
optionUsage.set(opt, usage);
}
}
void Foam::argList::addUsage
(
const word& opt,
const string& usage
)
{
if (usage.empty())
{
optionUsage.erase(opt);
}
else
{
optionUsage.set(opt, usage);
}
}
void Foam::argList::addNote(const string& note)
{
if (!note.empty())
{
notes.append(note);
}
}
void Foam::argList::removeOption(const word& opt)
{
validOptions.erase(opt);
optionUsage.erase(opt);
}
void Foam::argList::noBanner()
{
bannerEnabled = false;
}
void Foam::argList::noParallel()
{
removeOption("parallel");
removeOption("roots");
validParOptions.clear();
}
void Foam::argList::printOptionUsage
(
const label location,
const string& str
)
{
const string::size_type textWidth = usageMax - usageMin;
const string::size_type strLen = str.size();
if (strLen)
{
// Minimum of 2 spaces between option and usage:
if (string::size_type(location) + 2 <= usageMin)
{
for (string::size_type i = location; i < usageMin; ++i)
{
Info<<' ';
}
}
else
{
// or start a new line
Info<< nl;
for (string::size_type i = 0; i < usageMin; ++i)
{
Info<<' ';
}
}
// Text wrap
string::size_type pos = 0;
while (pos != string::npos && pos + textWidth < strLen)
{
// Potential end point and next point
string::size_type curr = pos + textWidth - 1;
string::size_type next = string::npos;
if (isspace(str[curr]))
{
// We were lucky: ended on a space
next = str.find_first_not_of(" \t\n", curr);
}
else if (isspace(str[curr+1]))
{
// The next one is a space - so we are okay
curr++; // otherwise the length is wrong
next = str.find_first_not_of(" \t\n", curr);
}
else
{
// Search for end of a previous word break
string::size_type prev = str.find_last_of(" \t\n", curr);
// Reposition to the end of previous word if possible
if (prev != string::npos && prev > pos)
{
curr = prev;
}
}
if (next == string::npos)
{
next = curr + 1;
}
// Indent following lines (not the first one)
if (pos)
{
for (string::size_type i = 0; i < usageMin; ++i)
{
Info<<' ';
}
}
Info<< str.substr(pos, (curr - pos)).c_str() << nl;
pos = next;
}
// Output the remainder of the string
if (pos != string::npos)
{
// Indent following lines (not the first one)
if (pos)
{
for (string::size_type i = 0; i < usageMin; ++i)
{
Info<<' ';
}
}
Info<< str.substr(pos).c_str() << nl;
}
}
else
{
Info<< nl;
}
}
bool Foam::argList::postProcess(int argc, char *argv[])
{
bool postProcessOption = false;
for (int i=1; i<argc; i++)
{
postProcessOption = argv[i] == '-' + postProcessOptionName;
if (postProcessOption) break;
}
return postProcessOption;
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
// Convert argv -> args_
// Transform sequences with "(" ... ")" into string lists in the process
bool Foam::argList::regroupArgv(int& argc, char**& argv)
{
int nArgs = 0;
int listDepth = 0;
string tmpString;
// Note: we also re-write directly into args_
// and use a second pass to sort out args/options
for (int argI = 0; argI < argc; ++argI)
{
if (strcmp(argv[argI], "(") == 0)
{
++listDepth;
tmpString += "(";
}
else if (strcmp(argv[argI], ")") == 0)
{
if (listDepth)
{
--listDepth;
tmpString += ")";
if (listDepth == 0)
{
args_[nArgs++] = tmpString;
tmpString.clear();
}
}
else
{
args_[nArgs++] = argv[argI];
}
}
else if (listDepth)
{
// Quote each string element
tmpString += "\"";
tmpString += argv[argI];
tmpString += "\"";
}
else
{
args_[nArgs++] = argv[argI];
}
}
if (tmpString.size())
{
args_[nArgs++] = tmpString;
}
args_.setSize(nArgs);
return nArgs < argc;
}
void Foam::argList::getRootCase()
{
fileName casePath;
// [-case dir] specified
HashTable<string>::const_iterator iter = options_.find("case");
if (iter != options_.end())
{
casePath = iter();
casePath.clean();
if (casePath.empty() || casePath == ".")
{
// Handle degenerate form and '-case .' like no -case specified
casePath = cwd();
options_.erase("case");
}
else if (!casePath.isAbsolute() && casePath.name() == "..")
{
// Avoid relative cases ending in '..' - makes for very ugly names
casePath = cwd()/casePath;
casePath.clean();
}
}
else
{
// Nothing specified, use the current dir
casePath = cwd();
}
rootPath_ = casePath.path();
globalCase_ = casePath.name();
case_ = globalCase_;
// Set the case and case-name as an environment variable
if (rootPath_.isAbsolute())
{
// Absolute path - use as-is
setEnv("FOAM_CASE", rootPath_/globalCase_, true);
setEnv("FOAM_CASENAME", globalCase_, true);
}
else
{
// Qualify relative path
casePath = cwd()/rootPath_/globalCase_;
casePath.clean();
setEnv("FOAM_CASE", casePath, true);
setEnv("FOAM_CASENAME", casePath.name(), true);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::argList::argList
(
int& argc,
char**& argv,
bool checkArgs,
bool checkOpts,
const bool initialise
)
:
args_(argc),
options_(argc)
{
// Check if this run is a parallel run by searching for any parallel option
// If found call runPar which might filter argv
for (int argI = 0; argI < argc; ++argI)
{
if (argv[argI][0] == '-')
{
const char *optionName = &argv[argI][1];
if (validParOptions.found(optionName))
{
parRunControl_.runPar(argc, argv);
break;
}
}
}
// Convert argv -> args_ and capture ( ... ) lists
// for normal arguments and for options
regroupArgv(argc, argv);
// Get executable name
args_[0] = fileName(argv[0]);
executable_ = fileName(argv[0]).name();
// Check arguments and options, we already have argv[0]
int nArgs = 1;
argListStr_ = args_[0];
for (int argI = 1; argI < args_.size(); ++argI)
{
argListStr_ += ' ';
argListStr_ += args_[argI];
if (args_[argI][0] == '-')
{
const char *optionName = &args_[argI][1];
if
(
(
validOptions.found(optionName)
&& !validOptions[optionName].empty()
)
|| (
validParOptions.found(optionName)
&& !validParOptions[optionName].empty()
)
)
{
++argI;
if (argI >= args_.size())
{
FatalError
<<"Option '-" << optionName
<< "' requires an argument" << endl;
printUsage();
FatalError.exit();
}
argListStr_ += ' ';
argListStr_ += args_[argI];
options_.insert(optionName, args_[argI]);
}
else
{
options_.insert(optionName, "");
}
}
else
{
if (nArgs != argI)
{
args_[nArgs] = args_[argI];
}
++nArgs;
}
}
args_.setSize(nArgs);
parse(checkArgs, checkOpts, initialise);
}
Foam::argList::argList
(
const argList& args,
const HashTable<string>& options,
bool checkArgs,
bool checkOpts,
bool initialise
)
:
parRunControl_(args.parRunControl_),
args_(args.args_),
options_(options),
executable_(args.executable_),
rootPath_(args.rootPath_),
globalCase_(args.globalCase_),
case_(args.case_),
argListStr_(args.argListStr_)
{
parse(checkArgs, checkOpts, initialise);
}
void Foam::argList::parse
(
bool checkArgs,
bool checkOpts,
bool initialise
)
{
// Help/documentation options:
// -help print the usage
// -doc display application documentation in browser
// -srcDoc display source code in browser
if
(
options_.found("help")
|| options_.found("doc")
|| options_.found("srcDoc")
)
{
if (options_.found("help"))
{
printUsage();
}
// Only display one or the other
if (options_.found("srcDoc"))
{
displayDoc(true);
}
else if (options_.found("doc"))
{
displayDoc(false);
}
::exit(0);
}
// Print the usage message and exit if the number of arguments is incorrect
if (!check(checkArgs, checkOpts))
{
FatalError.exit();
}
if (initialise)
{
string dateString = clock::date();
string timeString = clock::clockTime();
// Print the banner once only for parallel runs
if (Pstream::master() && bannerEnabled)
{
IOobject::writeBanner(Info, true)
<< "Build : " << Foam::FOAMbuild << nl
<< "Exec : " << argListStr_.c_str() << nl
<< "Date : " << dateString.c_str() << nl
<< "Time : " << timeString.c_str() << nl
<< "Host : " << hostName() << nl
<< "PID : " << pid() << endl;
}
jobInfo.add("startDate", dateString);
jobInfo.add("startTime", timeString);
jobInfo.add("userName", userName());
jobInfo.add("foamVersion", word(FOAMversion));
jobInfo.add("code", executable_);
jobInfo.add("argList", argListStr_);
jobInfo.add("currentDir", cwd());
jobInfo.add("PPID", ppid());
jobInfo.add("PGID", pgid());
// Add build information - only use the first word
{
std::string build(Foam::FOAMbuild);
std::string::size_type found = build.find(' ');
if (found != std::string::npos)
{
build.resize(found);
}
jobInfo.add("foamBuild", build);
}
}
// Case is a single processor run unless it is running parallel
int nProcs = 1;
// Roots if running distributed
fileNameList roots;
// If this actually is a parallel run
if (parRunControl_.parRun())
{
// For the master
if (Pstream::master())
{
// Establish rootPath_/globalCase_/case_ for master
getRootCase();
// See if running distributed (different roots for different procs)
label dictNProcs = -1;
fileName source;
if (options_.found("roots"))
{
source = "-roots";
IStringStream is(options_["roots"]);
roots = readList<fileName>(is);
if (roots.size() != 1)
{
dictNProcs = roots.size()+1;
}
}
else
{
source = rootPath_/globalCase_/"system/decomposeParDict";
IFstream decompDictStream(source);
if (!decompDictStream.good())
{
FatalError
<< "Cannot read "
<< decompDictStream.name()
<< exit(FatalError);
}
dictionary decompDict(decompDictStream);
dictNProcs = readLabel
(
decompDict.lookup("numberOfSubdomains")
);
if (decompDict.lookupOrDefault("distributed", false))
{
decompDict.lookup("roots") >> roots;
}
}
// Convenience:
// when a single root is specified, use it for all processes
if (roots.size() == 1)
{
const fileName rootName(roots[0]);
roots.setSize(Pstream::nProcs()-1, rootName);
// adjust dictNProcs for command-line '-roots' option
if (dictNProcs < 0)
{
dictNProcs = roots.size()+1;
}
}
// Check number of processors.
// nProcs => number of actual procs
// dictNProcs => number of procs specified in decompositionDict
// nProcDirs => number of processor directories
// (n/a when running distributed)
//
// - normal running : nProcs = dictNProcs = nProcDirs
// - decomposition to more processors : nProcs = dictNProcs
// - decomposition to fewer processors : nProcs = nProcDirs
if (dictNProcs > Pstream::nProcs())
{
FatalError
<< source
<< " specifies " << dictNProcs
<< " processors but job was started with "
<< Pstream::nProcs() << " processors."
<< exit(FatalError);
}
// Distributed data
if (roots.size())
{
if (roots.size() != Pstream::nProcs()-1)
{
FatalError
<< "number of entries in roots "
<< roots.size()
<< " is not equal to the number of slaves "
<< Pstream::nProcs()-1
<< exit(FatalError);
}
forAll(roots, i)
{
roots[i].expand();
}
// Distribute the master's argument list (with new root)
bool hadCaseOpt = options_.found("case");
for
(
int slave = Pstream::firstSlave();
slave <= Pstream::lastSlave();
slave++
)
{
options_.set("case", roots[slave-1]/globalCase_);
OPstream toSlave(Pstream::scheduled, slave);
toSlave << args_ << options_;
}
options_.erase("case");
// Restore [-case dir]
if (hadCaseOpt)
{
options_.set("case", rootPath_/globalCase_);
}
}
else
{
// Possibly going to fewer processors.
// Check if all procDirs are there.
if (dictNProcs < Pstream::nProcs())
{
label nProcDirs = 0;
while
(
isDir
(
rootPath_/globalCase_/"processor"
+ name(++nProcDirs)
)
)
{}
if (nProcDirs != Pstream::nProcs())
{
FatalError
<< "number of processor directories = "
<< nProcDirs
<< " is not equal to the number of processors = "
<< Pstream::nProcs()
<< exit(FatalError);
}
}
// Distribute the master's argument list (unaltered)
for
(
int slave = Pstream::firstSlave();
slave <= Pstream::lastSlave();
slave++
)
{
OPstream toSlave(Pstream::scheduled, slave);
toSlave << args_ << options_;
}
}
}
else
{
// Collect the master's argument list
IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
fromMaster >> args_ >> options_;
// Establish rootPath_/globalCase_/case_ for slave
getRootCase();
}
nProcs = Pstream::nProcs();
case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
}
else
{
// Establish rootPath_/globalCase_/case_
getRootCase();
case_ = globalCase_;
}
stringList slaveProcs;
// Collect slave machine/pid
if (parRunControl_.parRun())
{
if (Pstream::master())
{
slaveProcs.setSize(Pstream::nProcs() - 1);
string slaveMachine;
label slavePid;
label proci = 0;
for
(
int slave = Pstream::firstSlave();
slave <= Pstream::lastSlave();
slave++
)
{
IPstream fromSlave(Pstream::scheduled, slave);
fromSlave >> slaveMachine >> slavePid;
slaveProcs[proci++] = slaveMachine + "." + name(slavePid);
}
}
else
{
OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
toMaster << hostName() << pid();
}
}
if (Pstream::master() && bannerEnabled)
{
Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
<< "nProcs : " << nProcs << endl;
if (parRunControl_.parRun())
{
Info<< "Slaves : " << slaveProcs << nl;
if (roots.size())
{
Info<< "Roots : " << roots << nl;
}
Info<< "Pstream initialized with:" << nl
<< " floatTransfer : " << Pstream::floatTransfer << nl
<< " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
<< " commsType : "
<< Pstream::commsTypeNames[Pstream::defaultCommsType] << nl
<< " polling iterations : " << Pstream::nPollProcInterfaces
<< endl;
}
}
if (initialise)
{
jobInfo.add("root", rootPath_);
jobInfo.add("case", globalCase_);
jobInfo.add("nProcs", nProcs);
if (slaveProcs.size())
{
jobInfo.add("slaves", slaveProcs);
}
if (roots.size())
{
jobInfo.add("roots", roots);
}
jobInfo.write();
// Switch on signal trapping. We have to wait until after Pstream::init
// since this sets up its own ones.
sigFpe_.set(bannerEnabled);
sigInt_.set(bannerEnabled);
sigQuit_.set(bannerEnabled);
sigSegv_.set(bannerEnabled);
if (bannerEnabled)
{
Info<< "fileModificationChecking : "
<< "Monitoring run-time modified files using "
<< regIOobject::fileCheckTypesNames
[
regIOobject::fileModificationChecking
]
<< endl;
Info<< "allowSystemOperations : ";
if (dynamicCode::allowSystemOperations)
{
Info<< "Allowing user-supplied system call operations" << endl;
}
else
{
Info<< "Disallowing user-supplied system call operations"
<< endl;
}
}
if (Pstream::master() && bannerEnabled)
{
Info<< endl;
IOobject::writeDivider(Info);
}
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::argList::~argList()
{
jobInfo.end();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::argList::setOption(const word& opt, const string& param)
{
bool changed = false;
// Only allow valid options
if (validOptions.found(opt))
{
// Some options are to be protected
if
(
opt == "case"
|| opt == "parallel"
|| opt == "roots"
)
{
FatalError
<<"used argList::setOption on a protected option: '"
<< opt << "'" << endl;
FatalError.exit();
}
if (validOptions[opt].empty())
{
// Bool option
if (!param.empty())
{
// Disallow change of type
FatalError
<<"used argList::setOption to change bool to non-bool: '"
<< opt << "'" << endl;
FatalError.exit();
}
else
{
// Did not previously exist
changed = !options_.found(opt);
}
}
else
{
// Non-bool option
if (param.empty())
{
// Disallow change of type
FatalError
<<"used argList::setOption to change non-bool to bool: '"
<< opt << "'" << endl;
FatalError.exit();
}
else
{
// Existing value needs changing, or did not previously exist
changed = options_.found(opt) ? options_[opt] != param : true;
}
}
}
else
{
FatalError
<<"used argList::setOption on an invalid option: '"
<< opt << "'" << nl << "allowed are the following:"
<< validOptions << endl;
FatalError.exit();
}
// Set/change the option as required
if (changed)
{
options_.set(opt, param);
}
return changed;
}
bool Foam::argList::unsetOption(const word& opt)
{
// Only allow valid options
if (validOptions.found(opt))
{
// Some options are to be protected
if
(
opt == "case"
|| opt == "parallel"
|| opt == "roots"
)
{
FatalError
<<"used argList::unsetOption on a protected option: '"
<< opt << "'" << endl;
FatalError.exit();
}
// Remove the option, return true if state changed
return options_.erase(opt);
}
else
{
FatalError
<<"used argList::unsetOption on an invalid option: '"
<< opt << "'" << nl << "allowed are the following:"
<< validOptions << endl;
FatalError.exit();
}
return false;
}
void Foam::argList::printNotes() const
{
// Output notes directly - no automatic text wrapping
if (!notes.empty())
{
Info<< nl;
forAllConstIter(SLList<string>, notes, iter)
{
Info<< iter().c_str() << nl;
}
}
}
void Foam::argList::printUsage() const
{
Info<< "\nUsage: " << executable_ << " [OPTIONS]";
forAllConstIter(SLList<string>, validArgs, iter)
{
Info<< " <" << iter().c_str() << '>';
}
Info<< "\noptions:\n";
wordList opts = validOptions.sortedToc();
forAll(opts, optI)
{
const word& optionName = opts[optI];
HashTable<string>::const_iterator iter = validOptions.find(optionName);
Info<< " -" << optionName;
label len = optionName.size() + 3; // Length includes leading ' -'
if (iter().size())
{
// Length includes space and between option/param and '<>'
len += iter().size() + 3;
Info<< " <" << iter().c_str() << '>';
}
HashTable<string>::const_iterator usageIter =
optionUsage.find(optionName);
if (usageIter != optionUsage.end())
{
printOptionUsage
(
len,
usageIter()
);
}
else
{
Info<< nl;
}
}
// Place srcDoc/doc/help options at the end
Info<< " -srcDoc";
printOptionUsage
(
9,
"display source code in browser"
);
Info<< " -doc";
printOptionUsage
(
6,
"display application documentation in browser"
);
Info<< " -help";
printOptionUsage
(
7,
"print the usage"
);
printNotes();
Info<< nl
<<"Using: OpenFOAM-" << Foam::FOAMversion
<< " (see www.OpenFOAM.org)" << nl
<<"Build: " << Foam::FOAMbuild << nl
<< endl;
}
void Foam::argList::displayDoc(bool source) const
{
const dictionary& docDict = debug::controlDict().subDict("Documentation");
List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
// For source code: change foo_8C.html to foo_8C_source.html
if (source)
{
forAll(docExts, extI)
{
docExts[extI].replace(".", "_source.");
}
}
fileName docFile;
bool found = false;
forAll(docDirs, dirI)
{
forAll(docExts, extI)
{
docFile = docDirs[dirI]/executable_ + docExts[extI];
docFile.expand();
if (isFile(docFile))
{
found = true;
break;
}
}
if (found)
{
break;
}
}
if (found)
{
string docBrowser = getEnv("FOAM_DOC_BROWSER");
if (docBrowser.empty())
{
docDict.lookup("docBrowser") >> docBrowser;
}
// Can use FOAM_DOC_BROWSER='application file://%f' if required
docBrowser.replaceAll("%f", docFile);
Info<< "Show documentation: " << docBrowser.c_str() << endl;
system(docBrowser);
}
else
{
Info<< nl
<< "No documentation found for " << executable_
<< ", but you can use -help to display the usage\n" << endl;
}
}
bool Foam::argList::check(bool checkArgs, bool checkOpts) const
{
bool ok = true;
if (Pstream::master())
{
if (checkArgs && args_.size() - 1 != validArgs.size())
{
FatalError
<< "Wrong number of arguments, expected " << validArgs.size()
<< " found " << args_.size() - 1 << endl;
ok = false;
}
if (checkOpts)
{
forAllConstIter(HashTable<string>, options_, iter)
{
if
(
!validOptions.found(iter.key())
&& !validParOptions.found(iter.key())
)
{
FatalError
<< "Invalid option: -" << iter.key() << endl;
ok = false;
}
}
}
if (!ok)
{
printUsage();
}
}
return ok;
}
bool Foam::argList::checkRootCase() const
{
if (!isDir(rootPath()))
{
FatalError
<< executable_
<< ": cannot open root directory " << rootPath()
<< endl;
return false;
}
if (!isDir(path()) && Pstream::master())
{
// Allow slaves on non-existing processor directories, created later
FatalError
<< executable_
<< ": cannot open case directory " << path()
<< endl;
return false;
}
return true;
}
// ************************************************************************* //