mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
- consistent with use of <cstdlib> and makes for a stronger distinct from Foam::abort etc.
1683 lines
43 KiB
C
1683 lines
43 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd.
|
|
\\/ M anipulation |
|
|
-------------------------------------------------------------------------------
|
|
| Copyright (C) 2011-2017 OpenFOAM Foundation
|
|
-------------------------------------------------------------------------------
|
|
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 "sigFpe.H"
|
|
#include "sigInt.H"
|
|
#include "sigQuit.H"
|
|
#include "sigSegv.H"
|
|
#include "foamVersion.H"
|
|
#include "stringOps.H"
|
|
#include "CStringList.H"
|
|
#include "stringListOps.H"
|
|
#include "fileOperation.H"
|
|
#include "fileOperationInitialise.H"
|
|
|
|
#include <cctype>
|
|
|
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
|
|
|
bool Foam::argList::argsMandatory_ = true;
|
|
bool Foam::argList::checkProcessorDirectories_ = true;
|
|
|
|
Foam::SLList<Foam::string> Foam::argList::validArgs;
|
|
Foam::HashSet<Foam::string> Foam::argList::advancedOptions;
|
|
Foam::HashTable<Foam::string> Foam::argList::validOptions;
|
|
Foam::HashTable<Foam::string> Foam::argList::validParOptions;
|
|
Foam::HashTable<Foam::string, Foam::label, Foam::Hash<Foam::label>>
|
|
Foam::argList::argUsage;
|
|
Foam::HashTable<Foam::string> Foam::argList::optionUsage;
|
|
Foam::HashTable<std::pair<Foam::word,int>> Foam::argList::validOptionsCompat;
|
|
Foam::HashTable<std::pair<bool,int>> Foam::argList::ignoreOptionsCompat;
|
|
Foam::SLList<Foam::string> Foam::argList::notes;
|
|
Foam::word Foam::argList::postProcessOptionName("postProcess");
|
|
|
|
std::string::size_type Foam::argList::usageMin = 20;
|
|
std::string::size_type Foam::argList::usageMax = 80;
|
|
|
|
Foam::argList::initValidTables::initValidTables()
|
|
{
|
|
argList::addOption
|
|
(
|
|
"case",
|
|
"dir",
|
|
"Specify case directory to use (instead of the cwd)"
|
|
);
|
|
argList::addBoolOption("parallel", "Run in parallel");
|
|
validParOptions.set("parallel", "");
|
|
argList::addOption
|
|
(
|
|
"roots",
|
|
"(dir1 .. dirN)",
|
|
"Slave root directories for distributed running",
|
|
true // advanced option
|
|
);
|
|
validParOptions.set
|
|
(
|
|
"roots",
|
|
"(dir1 .. dirN)"
|
|
);
|
|
|
|
argList::addOption
|
|
(
|
|
"decomposeParDict",
|
|
"file",
|
|
"Use specified file for decomposePar dictionary"
|
|
);
|
|
argList::addOption
|
|
(
|
|
"hostRoots",
|
|
"((host1 dir1) .. (hostN dirN))",
|
|
"Per-host slave root directories for distributed running."
|
|
" The host specification can be a regex.",
|
|
true // advanced option
|
|
);
|
|
validParOptions.set
|
|
(
|
|
"hostRoots",
|
|
"((host1 dir1) .. (hostN dirN))"
|
|
);
|
|
|
|
argList::addBoolOption
|
|
(
|
|
"noFunctionObjects",
|
|
"Do not execute function objects"
|
|
);
|
|
|
|
argList::addOption
|
|
(
|
|
"fileHandler",
|
|
"handler",
|
|
"Override the file handler type",
|
|
true // advanced option
|
|
);
|
|
|
|
// Some standard option aliases (with or without version warnings)
|
|
// argList::addOptionCompat
|
|
// (
|
|
// "noFunctionObjects", {"no-function-objects", 0}
|
|
// );
|
|
|
|
Pstream::addValidParOptions(validParOptions);
|
|
}
|
|
|
|
Foam::argList::initValidTables dummyInitValidTables;
|
|
|
|
|
|
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
|
|
|
namespace
|
|
{
|
|
|
|
// Should issue warning if there is +ve versioning (+ve version number)
|
|
// and the this version number is older than the current OpenFOAM version
|
|
// as conveyed by the OPENFOAM compiler define.
|
|
|
|
static inline constexpr bool shouldWarnVersion(const int version)
|
|
{
|
|
return (version > 0 && version < OPENFOAM);
|
|
}
|
|
|
|
} // End anonymous namespace
|
|
|
|
|
|
namespace Foam
|
|
{
|
|
|
|
// Counted per machine name
|
|
// Does not include any sorting since we wish to know the ordering according to
|
|
// mpi rank.
|
|
//
|
|
// Always include the master too.
|
|
// This provides a better overview of the subscription
|
|
static void printHostsSubscription(const UList<string>& slaveProcs)
|
|
{
|
|
Info<< "Hosts :" << nl << "(" << nl;
|
|
|
|
std::string prev = hostName();
|
|
int count = 1;
|
|
|
|
for (const auto& str : slaveProcs)
|
|
{
|
|
std::string curr(str.substr(0, str.rfind('.')));
|
|
|
|
if (prev != curr)
|
|
{
|
|
if (count)
|
|
{
|
|
// Finish previous
|
|
Info<<" (" << prev.c_str() << " " << count << ")" << nl;
|
|
count = 0;
|
|
}
|
|
|
|
prev = std::move(curr);
|
|
}
|
|
++count;
|
|
}
|
|
|
|
if (count)
|
|
{
|
|
// Finished last one
|
|
Info<<" (" << prev.c_str() << " " << count << ")" << nl;
|
|
}
|
|
|
|
Info<< ")" << nl;
|
|
}
|
|
|
|
} // End namespace Foam
|
|
|
|
|
|
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
|
|
|
|
void Foam::argList::checkITstream(const ITstream& is, const label index)
|
|
{
|
|
const label remaining = is.nRemainingTokens();
|
|
|
|
if (remaining)
|
|
{
|
|
std::cerr
|
|
<< nl
|
|
<< "--> FOAM WARNING:" << nl
|
|
<< "Argument " << index << " has "
|
|
<< remaining << " excess tokens" << nl << nl;
|
|
}
|
|
else if (!is.size())
|
|
{
|
|
std::cerr
|
|
<< nl
|
|
<< "--> FOAM WARNING:" << nl
|
|
<< "Argument " << index << " had no tokens" << nl << nl;
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::argList::checkITstream(const ITstream& is, const word& optName)
|
|
{
|
|
const label remaining = is.nRemainingTokens();
|
|
|
|
if (remaining)
|
|
{
|
|
std::cerr
|
|
<< nl
|
|
<< "--> FOAM WARNING:" << nl
|
|
<< "Option -" << optName << " has "
|
|
<< remaining << " excess tokens" << nl << nl;
|
|
}
|
|
else if (!is.size())
|
|
{
|
|
std::cerr
|
|
<< nl
|
|
<< "--> FOAM WARNING:" << nl
|
|
<< "Option -" << optName << " had no tokens" << nl << nl;
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::argList::addArgument
|
|
(
|
|
const string& argName,
|
|
const string& usage
|
|
)
|
|
{
|
|
validArgs.append(argName);
|
|
|
|
// The first program argument starts at 1 - obtain index after the append
|
|
|
|
const label index = validArgs.size();
|
|
|
|
if (usage.empty())
|
|
{
|
|
argUsage.erase(index);
|
|
}
|
|
else
|
|
{
|
|
argUsage.set(index, usage);
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::argList::addBoolOption
|
|
(
|
|
const word& optName,
|
|
const string& usage,
|
|
bool advanced
|
|
)
|
|
{
|
|
addOption(optName, "", usage, advanced);
|
|
}
|
|
|
|
|
|
void Foam::argList::addOption
|
|
(
|
|
const word& optName,
|
|
const string& param,
|
|
const string& usage,
|
|
bool advanced
|
|
)
|
|
{
|
|
validOptions.set(optName, param);
|
|
if (!usage.empty())
|
|
{
|
|
optionUsage.set(optName, usage);
|
|
}
|
|
if (advanced)
|
|
{
|
|
advancedOptions.set(optName);
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::argList::setAdvanced(const word& optName, bool advanced)
|
|
{
|
|
if (advanced && validOptions.found(optName))
|
|
{
|
|
advancedOptions.set(optName);
|
|
}
|
|
else
|
|
{
|
|
advancedOptions.erase(optName);
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::argList::addOptionCompat
|
|
(
|
|
const word& optName,
|
|
std::pair<const char*,int> compat
|
|
)
|
|
{
|
|
validOptionsCompat.insert
|
|
(
|
|
compat.first,
|
|
std::pair<word,int>(optName, compat.second)
|
|
);
|
|
}
|
|
|
|
|
|
void Foam::argList::ignoreOptionCompat
|
|
(
|
|
std::pair<const char*,int> compat,
|
|
bool expectArg
|
|
)
|
|
{
|
|
ignoreOptionsCompat.insert
|
|
(
|
|
compat.first,
|
|
std::pair<bool,int>(expectArg, compat.second)
|
|
);
|
|
}
|
|
|
|
|
|
void Foam::argList::addUsage
|
|
(
|
|
const word& optName,
|
|
const string& usage
|
|
)
|
|
{
|
|
if (usage.empty())
|
|
{
|
|
optionUsage.erase(optName);
|
|
}
|
|
else
|
|
{
|
|
optionUsage.set(optName, usage);
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::argList::addNote(const string& note)
|
|
{
|
|
if (!note.empty())
|
|
{
|
|
notes.append(note);
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::argList::removeOption(const word& optName)
|
|
{
|
|
validOptions.erase(optName);
|
|
optionUsage.erase(optName);
|
|
advancedOptions.erase(optName);
|
|
}
|
|
|
|
|
|
void Foam::argList::noMandatoryArgs()
|
|
{
|
|
argsMandatory_ = false;
|
|
}
|
|
|
|
|
|
bool Foam::argList::argsMandatory()
|
|
{
|
|
return argsMandatory_;
|
|
}
|
|
|
|
|
|
void Foam::argList::noBanner()
|
|
{
|
|
::Foam::infoDetailLevel = 0;
|
|
}
|
|
|
|
|
|
bool Foam::argList::bannerEnabled()
|
|
{
|
|
return (::Foam::infoDetailLevel > 0);
|
|
}
|
|
|
|
|
|
void Foam::argList::noFunctionObjects(bool addWithOption)
|
|
{
|
|
removeOption("noFunctionObjects");
|
|
|
|
// Ignore this bool option without warning
|
|
// - cannot tie to any particular version anyhow
|
|
ignoreOptionCompat({"noFunctionObjects", 0}, false);
|
|
|
|
if (addWithOption)
|
|
{
|
|
addBoolOption
|
|
(
|
|
"withFunctionObjects",
|
|
"Execute functionObjects"
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::argList::noJobInfo()
|
|
{
|
|
JobInfo::writeJobInfo = false;
|
|
}
|
|
|
|
|
|
void Foam::argList::noLibs()
|
|
{
|
|
addBoolOption
|
|
(
|
|
"no-libs",
|
|
"Disable use of the controlDict libs entry",
|
|
true // advanced
|
|
);
|
|
}
|
|
|
|
|
|
void Foam::argList::noParallel()
|
|
{
|
|
removeOption("parallel");
|
|
removeOption("roots");
|
|
removeOption("decomposeParDict");
|
|
removeOption("hostRoots");
|
|
validParOptions.clear();
|
|
}
|
|
|
|
|
|
void Foam::argList::noCheckProcessorDirectories()
|
|
{
|
|
checkProcessorDirectories_ = false;
|
|
}
|
|
|
|
|
|
bool Foam::argList::postProcess(int argc, char *argv[])
|
|
{
|
|
for (int i=1; i<argc; ++i)
|
|
{
|
|
if (argv[i] == '-' + postProcessOptionName)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
|
|
|
|
Foam::fileName Foam::argList::envGlobalPath()
|
|
{
|
|
return Foam::getEnv("FOAM_CASE");
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
|
|
|
Foam::word Foam::argList::optionCompat(const word& optName)
|
|
{
|
|
// NB: optName includes the leading '-' so that the return value
|
|
// can be used directly
|
|
|
|
if (!validOptionsCompat.empty())
|
|
{
|
|
const auto fnd = validOptionsCompat.cfind(optName.substr(1));
|
|
|
|
if (fnd.found())
|
|
{
|
|
const auto& iter = *fnd;
|
|
|
|
if (shouldWarnVersion(iter.second))
|
|
{
|
|
std::cerr
|
|
<< "--> FOAM IOWarning :" << nl
|
|
<< " Found [v" << iter.second << "] '"
|
|
<< optName << "' instead of '-"
|
|
<< iter.first << "' option"
|
|
<< nl
|
|
<< std::endl;
|
|
|
|
error::warnAboutAge("option", iter.second);
|
|
}
|
|
|
|
return "-" + iter.first;
|
|
}
|
|
}
|
|
|
|
// Nothing found - pass through the original input
|
|
return optName;
|
|
}
|
|
|
|
|
|
int Foam::argList::optionIgnore(const word& optName)
|
|
{
|
|
// NB: optName is without the leading '-'
|
|
|
|
if (!ignoreOptionsCompat.empty())
|
|
{
|
|
const auto fnd = ignoreOptionsCompat.cfind(optName);
|
|
|
|
if (fnd.found())
|
|
{
|
|
const auto& iter = *fnd;
|
|
|
|
// Number to skip (including the option itself)
|
|
// '-option ARG' or '-option'
|
|
const int nskip = (iter.first ? 2 : 1);
|
|
|
|
if (shouldWarnVersion(iter.second))
|
|
{
|
|
std::cerr
|
|
<< "--> FOAM IOWarning :" << nl
|
|
<< " Ignoring [v" << iter.second << "] '-"
|
|
<< optName << (nskip > 1 ? " ARG" : "")
|
|
<< "' option"
|
|
<< nl
|
|
<< std::endl;
|
|
|
|
error::warnAboutAge("option", iter.second);
|
|
}
|
|
|
|
return nskip;
|
|
}
|
|
}
|
|
|
|
return 0; // Do not skip
|
|
}
|
|
|
|
|
|
bool Foam::argList::regroupArgv(int& argc, char**& argv)
|
|
{
|
|
int nArgs = 1;
|
|
int ignore = 0;
|
|
unsigned depth = 0;
|
|
string group; // For grouping ( ... ) arguments
|
|
|
|
// Note: we rewrite directly into args_
|
|
// and use a second pass to sort out args/options
|
|
|
|
args_[0] = fileName(argv[0]);
|
|
for (int argi = 1; argi < argc; ++argi)
|
|
{
|
|
if (strcmp(argv[argi], "(") == 0)
|
|
{
|
|
++depth;
|
|
group += '(';
|
|
}
|
|
else if (strcmp(argv[argi], ")") == 0)
|
|
{
|
|
if (depth)
|
|
{
|
|
--depth;
|
|
group += ')';
|
|
if (!depth)
|
|
{
|
|
args_[nArgs++] = group;
|
|
group.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
args_[nArgs++] = argv[argi];
|
|
}
|
|
}
|
|
else if (depth)
|
|
{
|
|
// Quote each string element
|
|
group += '"';
|
|
group += argv[argi];
|
|
group += '"';
|
|
}
|
|
else if (argv[argi][0] == '-')
|
|
{
|
|
// Appears to be an option
|
|
const char *optName = &argv[argi][1];
|
|
|
|
if (validOptions.found(optName))
|
|
{
|
|
// Known option name
|
|
args_[nArgs++] = argv[argi];
|
|
}
|
|
else if ((ignore = optionIgnore(optName)) > 0)
|
|
{
|
|
// Option to be ignored (with/without an argument)
|
|
if (ignore > 1)
|
|
{
|
|
++argi;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Try alias for the option name
|
|
args_[nArgs++] = optionCompat(argv[argi]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
args_[nArgs++] = argv[argi];
|
|
}
|
|
}
|
|
|
|
if (group.size())
|
|
{
|
|
// Group(s) not closed, but flush anything still pending
|
|
args_[nArgs++] = group;
|
|
}
|
|
|
|
args_.resize(nArgs);
|
|
|
|
std::string::size_type len = (nArgs-1); // Spaces between args
|
|
for (const auto& s : args_)
|
|
{
|
|
len += s.length();
|
|
}
|
|
|
|
// Length needed for regrouped command-line
|
|
commandLine_.reserve(len);
|
|
|
|
return nArgs < argc;
|
|
}
|
|
|
|
|
|
void Foam::argList::setCasePaths()
|
|
{
|
|
fileName caseDir;
|
|
|
|
const auto optIter = options_.cfind("case"); // [-case dir] specified?
|
|
|
|
if (optIter.found())
|
|
{
|
|
caseDir = fileName::validate(optIter.val());
|
|
caseDir.clean();
|
|
|
|
if (caseDir.empty() || caseDir == ".")
|
|
{
|
|
// Treat "", "." and "./" as if -case was not specified
|
|
caseDir = cwd();
|
|
options_.erase("case");
|
|
}
|
|
else
|
|
{
|
|
caseDir.toAbsolute();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Nothing specified, use the current dir
|
|
caseDir = cwd();
|
|
}
|
|
|
|
// The caseDir is a cleaned, absolute path
|
|
|
|
rootPath_ = caseDir.path();
|
|
globalCase_ = caseDir.name();
|
|
case_ = globalCase_; // The (processor) local case name
|
|
|
|
// OPENFOAM API
|
|
setEnv("FOAM_API", std::to_string(foamVersion::api), true);
|
|
|
|
// Global case (directory) and case-name as environment variables
|
|
setEnv("FOAM_CASE", caseDir, true);
|
|
setEnv("FOAM_CASENAME", globalCase_, true);
|
|
|
|
// Executable name, unless already present in the environment
|
|
setEnv("FOAM_EXECUTABLE", executable_, false);
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
|
|
|
Foam::argList::argList
|
|
(
|
|
int& argc,
|
|
char**& argv,
|
|
bool checkArgs,
|
|
bool checkOpts,
|
|
bool initialise
|
|
)
|
|
:
|
|
args_(argc),
|
|
options_(argc)
|
|
{
|
|
// Check for -fileHandler, which requires an argument.
|
|
word handlerType(getEnv("FOAM_FILEHANDLER"));
|
|
for (int argi = argc-2; argi > 0; --argi)
|
|
{
|
|
if (argv[argi][0] == '-')
|
|
{
|
|
const char *optName = &argv[argi][1];
|
|
|
|
if (strcmp(optName, "fileHandler") == 0)
|
|
{
|
|
handlerType = argv[argi+1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (handlerType.empty())
|
|
{
|
|
handlerType = fileOperation::defaultFileHandler;
|
|
}
|
|
|
|
// Detect any parallel options
|
|
bool needsThread = fileOperations::fileOperationInitialise::New
|
|
(
|
|
handlerType,
|
|
argc,
|
|
argv
|
|
)().needsThreading();
|
|
|
|
|
|
// 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 = 1; argi < argc; ++argi)
|
|
{
|
|
if (argv[argi][0] == '-')
|
|
{
|
|
const char *optName = &argv[argi][1];
|
|
|
|
if (validParOptions.found(optName))
|
|
{
|
|
parRunControl_.runPar(argc, argv, needsThread);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert argv -> args_ and capture ( ... ) lists
|
|
regroupArgv(argc, argv);
|
|
commandLine_ += args_[0];
|
|
|
|
// Set executable name immediately - useful when emitting errors.
|
|
executable_ = fileName(args_[0]).name();
|
|
|
|
// Check arguments and options, argv[0] was already handled
|
|
int nArgs = 1;
|
|
for (int argi = 1; argi < args_.size(); ++argi)
|
|
{
|
|
commandLine_ += ' ';
|
|
commandLine_ += args_[argi];
|
|
|
|
if (args_[argi][0] == '-')
|
|
{
|
|
const char *optName = &args_[argi][1];
|
|
|
|
if (!*optName)
|
|
{
|
|
Warning
|
|
<<"Ignoring lone '-' on the command-line" << endl;
|
|
}
|
|
else if
|
|
(
|
|
validOptions.lookup(optName, "").size()
|
|
|| validParOptions.lookup(optName, "").size()
|
|
)
|
|
{
|
|
// If the option is known to require an argument,
|
|
// get it or emit a FatalError.
|
|
|
|
++argi;
|
|
if (argi >= args_.size())
|
|
{
|
|
foamVersion::printBuildInfo(false);
|
|
|
|
Info<<nl
|
|
<<"Error: option '-" << optName
|
|
<< "' requires an argument" << nl << nl
|
|
<< "See '" << executable_ << " -help' for usage"
|
|
<< nl << nl;
|
|
|
|
Pstream::exit(1); // works for serial and parallel
|
|
}
|
|
|
|
commandLine_ += ' ';
|
|
commandLine_ += args_[argi];
|
|
// Handle duplicates by taking the last -option specified
|
|
options_.set(optName, args_[argi]);
|
|
}
|
|
else
|
|
{
|
|
// All other options (including unknown ones) are simply
|
|
// registered as existing.
|
|
|
|
options_.insert(optName, "");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (nArgs != argi)
|
|
{
|
|
args_[nArgs] = args_[argi];
|
|
}
|
|
++nArgs;
|
|
}
|
|
}
|
|
|
|
args_.resize(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_),
|
|
commandLine_(args.commandLine_)
|
|
{
|
|
parse(checkArgs, checkOpts, initialise);
|
|
}
|
|
|
|
|
|
void Foam::argList::parse
|
|
(
|
|
bool checkArgs,
|
|
bool checkOpts,
|
|
bool initialise
|
|
)
|
|
{
|
|
// Help/documentation options:
|
|
// -doc Display documentation in browser
|
|
// -doc-source Display source code in browser
|
|
// -help Display short help and exit
|
|
// -help-compat Display compatibility options
|
|
// -help-full Display full help and exit
|
|
{
|
|
bool quickExit = false;
|
|
|
|
// Display either application or source documentation, not both
|
|
if (options_.found("doc"))
|
|
{
|
|
displayDoc(false);
|
|
quickExit = true;
|
|
}
|
|
else if
|
|
(
|
|
options_.found("doc-source")
|
|
|| options_.found("srcDoc") // Compat 1706
|
|
)
|
|
{
|
|
displayDoc(true);
|
|
quickExit = true;
|
|
}
|
|
|
|
// Display either short or full help, not both
|
|
if (options_.found("help-full"))
|
|
{
|
|
printUsage(true);
|
|
quickExit = true;
|
|
}
|
|
else if (options_.found("help-notes"))
|
|
{
|
|
printNotes();
|
|
Info<< nl;
|
|
quickExit = true;
|
|
}
|
|
else if (options_.found("help"))
|
|
{
|
|
printUsage(false);
|
|
quickExit = true;
|
|
}
|
|
else if (options_.found("help-man"))
|
|
{
|
|
printMan();
|
|
quickExit = true;
|
|
}
|
|
|
|
// Allow independent display of compatibility information
|
|
if (options_.found("help-compat"))
|
|
{
|
|
printCompat();
|
|
quickExit = true;
|
|
}
|
|
|
|
if (quickExit)
|
|
{
|
|
std::exit(0);
|
|
}
|
|
}
|
|
|
|
// Print the collected error messages and exit if check fails
|
|
if (!check(checkArgs, checkOpts))
|
|
{
|
|
foamVersion::printBuildInfo(false);
|
|
FatalError.write(Info, false);
|
|
|
|
Pstream::exit(1); // works for serial and parallel
|
|
}
|
|
|
|
if (initialise)
|
|
{
|
|
const string dateString = clock::date();
|
|
const string timeString = clock::clockTime();
|
|
|
|
// Print the banner once only for parallel runs
|
|
if (Pstream::master() && bannerEnabled())
|
|
{
|
|
IOobject::writeBanner(Info, true)
|
|
<< "Build : ";
|
|
|
|
if (foamVersion::build.size())
|
|
{
|
|
Info<< foamVersion::build.c_str() << ' ';
|
|
}
|
|
|
|
Info<< "OPENFOAM=" << foamVersion::api;
|
|
|
|
if (foamVersion::patched())
|
|
{
|
|
// Patch-level, when defined
|
|
Info<< " patch=" << foamVersion::patch.c_str();
|
|
}
|
|
|
|
Info<< nl
|
|
<< "Arch : " << foamVersion::buildArch << nl
|
|
<< "Exec : " << commandLine_.c_str() << nl
|
|
<< "Date : " << dateString.c_str() << nl
|
|
<< "Time : " << timeString.c_str() << nl
|
|
<< "Host : " << hostName().c_str() << nl
|
|
<< "PID : " << pid() << endl;
|
|
}
|
|
|
|
jobInfo.add("startDate", dateString);
|
|
jobInfo.add("startTime", timeString);
|
|
jobInfo.add("userName", userName());
|
|
jobInfo.add("foamVersion", word(foamVersion::version));
|
|
jobInfo.add("code", executable_);
|
|
jobInfo.add("argList", commandLine_);
|
|
jobInfo.add("currentDir", cwd());
|
|
jobInfo.add("PPID", ppid());
|
|
jobInfo.add("PGID", pgid());
|
|
|
|
// Add build information - only use the first word
|
|
{
|
|
std::string build(foamVersion::build);
|
|
const auto space = build.find(' ');
|
|
if (space != std::string::npos)
|
|
{
|
|
build.resize(space);
|
|
}
|
|
jobInfo.add("foamBuild", build);
|
|
}
|
|
}
|
|
|
|
|
|
// Set fileHandler. In increasing order of priority:
|
|
// 1. default = uncollated
|
|
// 2. environment var FOAM_FILEHANDLER
|
|
// 3. etc/controlDict optimisationSwitches 'fileHandler'
|
|
// 4. system/controlDict 'fileHandler' (not handled here; done in TimeIO.C)
|
|
// 5. '-fileHandler' commmand-line option
|
|
|
|
{
|
|
word fileHandlerName =
|
|
options_.lookup("fileHandler", getEnv("FOAM_FILEHANDLER"));
|
|
|
|
if (fileHandlerName.empty())
|
|
{
|
|
fileHandlerName = fileOperation::defaultFileHandler;
|
|
}
|
|
|
|
auto handler = fileOperation::New(fileHandlerName, bannerEnabled());
|
|
Foam::fileHandler(handler);
|
|
}
|
|
|
|
|
|
stringList slaveProcs;
|
|
stringList slaveMachine;
|
|
const int writeHostsSwitch = debug::infoSwitch("writeHosts", 1);
|
|
|
|
// Collect slave machine/pid, and check that the build is identical
|
|
if (parRunControl_.parRun())
|
|
{
|
|
if (Pstream::master())
|
|
{
|
|
slaveProcs.resize(Pstream::nProcs()-1);
|
|
slaveMachine.resize(Pstream::nProcs()-1);
|
|
label proci = 0;
|
|
for
|
|
(
|
|
int slave = Pstream::firstSlave();
|
|
slave <= Pstream::lastSlave();
|
|
slave++
|
|
)
|
|
{
|
|
IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
|
|
|
|
string slaveBuild;
|
|
label slavePid;
|
|
fromSlave >> slaveBuild >> slaveMachine[proci] >> slavePid;
|
|
|
|
slaveProcs[proci] = slaveMachine[proci] + "." + name(slavePid);
|
|
proci++;
|
|
|
|
// Verify that all processors are running the same build
|
|
if (slaveBuild != foamVersion::build)
|
|
{
|
|
FatalErrorIn(executable())
|
|
<< "Master is running version " << foamVersion::build
|
|
<< "; slave " << proci << " is running version "
|
|
<< slaveBuild
|
|
<< exit(FatalError);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OPstream toMaster
|
|
(
|
|
Pstream::commsTypes::scheduled,
|
|
Pstream::masterNo()
|
|
);
|
|
toMaster << foamVersion::build << hostName() << pid();
|
|
}
|
|
}
|
|
|
|
|
|
// 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
|
|
setCasePaths();
|
|
|
|
// Establish location of decomposeParDict, allow override with
|
|
// the -decomposeParDict option.
|
|
fileName source = rootPath_/globalCase_/"system"/"decomposeParDict";
|
|
if (options_.found("decomposeParDict"))
|
|
{
|
|
bool adjustOpt = false;
|
|
|
|
source = options_["decomposeParDict"];
|
|
if (isDir(source))
|
|
{
|
|
source /= "decomposeParDict";
|
|
adjustOpt = true;
|
|
}
|
|
|
|
// Case-relative if not absolute and not "./" etc
|
|
if (!source.isAbsolute() && !source.startsWith("."))
|
|
{
|
|
source = rootPath_/globalCase_/source;
|
|
adjustOpt = true;
|
|
}
|
|
|
|
// Could also check for absolute path, but shouldn't be needed
|
|
if (adjustOpt)
|
|
{
|
|
source.clean();
|
|
options_.set("decomposeParDict", source);
|
|
}
|
|
}
|
|
|
|
// If running distributed (different roots for different procs)
|
|
label dictNProcs = -1;
|
|
if (this->readListIfPresent("roots", roots))
|
|
{
|
|
parRunControl_.distributed(true);
|
|
source = "-roots";
|
|
if (roots.size() != 1)
|
|
{
|
|
dictNProcs = roots.size()+1;
|
|
}
|
|
}
|
|
else if (options_.found("hostRoots"))
|
|
{
|
|
roots.resize(Pstream::nProcs()-1, fileName::null);
|
|
|
|
source = "-hostRoots";
|
|
ITstream is(source, options_["hostRoots"]);
|
|
|
|
List<Tuple2<wordRe, fileName>> hostRoots(is);
|
|
checkITstream(is, "hostRoots");
|
|
|
|
for (const auto& hostRoot : hostRoots)
|
|
{
|
|
labelList matched
|
|
(
|
|
findStrings(hostRoot.first(), slaveMachine)
|
|
);
|
|
for (const label slavei : matched)
|
|
{
|
|
if (!roots[slavei].empty())
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Slave " << slaveMachine[slavei]
|
|
<< " has multiple matching roots in "
|
|
<< hostRoots << exit(FatalError);
|
|
}
|
|
|
|
roots[slavei] = hostRoot.second();
|
|
}
|
|
}
|
|
|
|
// Check
|
|
forAll(roots, slavei)
|
|
{
|
|
if (roots[slavei].empty())
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Slave " << slaveMachine[slavei]
|
|
<< " has no matching roots in "
|
|
<< hostRoots << exit(FatalError);
|
|
}
|
|
}
|
|
|
|
if (roots.size() != 1)
|
|
{
|
|
dictNProcs = roots.size()+1;
|
|
}
|
|
}
|
|
else if (checkProcessorDirectories_)
|
|
{
|
|
// Use values from decomposeParDict, the location was already
|
|
// established above.
|
|
|
|
IFstream decompDictStream(source);
|
|
|
|
if (!decompDictStream.good())
|
|
{
|
|
FatalError
|
|
<< "Cannot read decomposeParDict from "
|
|
<< decompDictStream.name()
|
|
<< exit(FatalError);
|
|
}
|
|
|
|
dictionary decompDict(decompDictStream);
|
|
|
|
decompDict.readEntry("numberOfSubdomains", dictNProcs);
|
|
|
|
if (decompDict.lookupOrDefault("distributed", false))
|
|
{
|
|
parRunControl_.distributed(true);
|
|
decompDict.readEntry("roots", roots);
|
|
}
|
|
}
|
|
|
|
// Convenience:
|
|
// when a single root is specified, use it for all processes
|
|
if (roots.size() == 1)
|
|
{
|
|
const fileName rootName(roots[0]);
|
|
roots.resize(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 (checkProcessorDirectories_ && 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);
|
|
}
|
|
|
|
for (fileName& dir : roots)
|
|
{
|
|
dir.expand();
|
|
}
|
|
|
|
// Distribute the master's argument list (with new root)
|
|
const bool hadCaseOpt = options_.found("case");
|
|
for
|
|
(
|
|
int slave = Pstream::firstSlave();
|
|
slave <= Pstream::lastSlave();
|
|
slave++
|
|
)
|
|
{
|
|
options_.set("case", roots[slave-1]/globalCase_);
|
|
|
|
OPstream toSlave(Pstream::commsTypes::scheduled, slave);
|
|
toSlave << args_ << options_ << roots.size();
|
|
}
|
|
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
|
|
(
|
|
checkProcessorDirectories_
|
|
&& dictNProcs < Pstream::nProcs()
|
|
)
|
|
{
|
|
label nProcDirs = 0;
|
|
while
|
|
(
|
|
isDir
|
|
(
|
|
rootPath_/globalCase_
|
|
/ ("processor" + Foam::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::commsTypes::scheduled, slave);
|
|
toSlave << args_ << options_ << roots.size();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Collect the master's argument list
|
|
label nroots;
|
|
|
|
IPstream fromMaster
|
|
(
|
|
Pstream::commsTypes::scheduled,
|
|
Pstream::masterNo()
|
|
);
|
|
fromMaster >> args_ >> options_ >> nroots;
|
|
|
|
parRunControl_.distributed(nroots);
|
|
|
|
// Establish rootPath_/globalCase_/case_ for slave
|
|
setCasePaths();
|
|
}
|
|
|
|
nProcs = Pstream::nProcs();
|
|
case_ = globalCase_/("processor" + Foam::name(Pstream::myProcNo()));
|
|
}
|
|
else
|
|
{
|
|
// Establish rootPath_/globalCase_/case_
|
|
setCasePaths();
|
|
case_ = globalCase_; // Redundant, but extra safety?
|
|
}
|
|
|
|
|
|
// Keep or discard slave and root information for reporting:
|
|
if (Pstream::master() && parRunControl_.parRun())
|
|
{
|
|
if (!writeHostsSwitch)
|
|
{
|
|
// Clear here to ensures it doesn't show in the jobInfo
|
|
slaveProcs.clear();
|
|
}
|
|
if (!debug::infoSwitch("writeRoots", 1))
|
|
{
|
|
roots.clear();
|
|
}
|
|
}
|
|
|
|
if (Pstream::master() && bannerEnabled())
|
|
{
|
|
Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
|
|
<< "nProcs : " << nProcs << endl;
|
|
|
|
if (parRunControl_.parRun())
|
|
{
|
|
if (slaveProcs.size())
|
|
{
|
|
if (writeHostsSwitch == 1)
|
|
{
|
|
// Compact output (see etc/controlDict)
|
|
printHostsSubscription(slaveProcs);
|
|
}
|
|
else
|
|
{
|
|
// Full output of "slave.pid"
|
|
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 (Pstream::master() && bannerEnabled())
|
|
{
|
|
Info<< "fileModificationChecking : "
|
|
<< "Monitoring run-time modified files using "
|
|
<< regIOobject::fileCheckTypesNames
|
|
[
|
|
regIOobject::fileModificationChecking
|
|
];
|
|
if
|
|
(
|
|
(
|
|
regIOobject::fileModificationChecking
|
|
== regIOobject::timeStamp
|
|
)
|
|
|| (
|
|
regIOobject::fileModificationChecking
|
|
== regIOobject::timeStampMaster
|
|
)
|
|
)
|
|
{
|
|
Info<< " (fileModificationSkew "
|
|
<< regIOobject::fileModificationSkew << ")";
|
|
}
|
|
Info<< nl;
|
|
|
|
Info<< "allowSystemOperations : ";
|
|
if (dynamicCode::allowSystemOperations)
|
|
{
|
|
Info<< "Allowing";
|
|
}
|
|
else
|
|
{
|
|
Info<< "Disallowing";
|
|
}
|
|
Info<< " user-supplied system call operations" << nl
|
|
<< endl;
|
|
IOobject::writeDivider(Info);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
|
|
|
Foam::argList::~argList()
|
|
{
|
|
jobInfo.end();
|
|
|
|
// Delete file handler to flush any remaining IO
|
|
autoPtr<fileOperation> dummy(nullptr);
|
|
fileHandler(dummy);
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
|
|
|
Foam::label Foam::argList::count(const UList<word>& optionNames) const
|
|
{
|
|
label n = 0;
|
|
for (const word& optName : optionNames)
|
|
{
|
|
if (options_.found(optName))
|
|
{
|
|
++n;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
Foam::label Foam::argList::count
|
|
(
|
|
std::initializer_list<word> optionNames
|
|
) const
|
|
{
|
|
label n = 0;
|
|
for (const word& optName : optionNames)
|
|
{
|
|
if (options_.found(optName))
|
|
{
|
|
++n;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
bool Foam::argList::setOption(const word& optName, const string& param)
|
|
{
|
|
// Some options are always protected
|
|
if
|
|
(
|
|
optName == "case"
|
|
|| optName == "parallel"
|
|
|| optName == "roots"
|
|
)
|
|
{
|
|
FatalErrorInFunction
|
|
<<"Option: '" << optName << "' is protected" << nl
|
|
<< exit(FatalError);
|
|
return false;
|
|
}
|
|
|
|
if (options_.found(optName) ? (options_[optName] != param) : true)
|
|
{
|
|
options_.set(optName, param);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool Foam::argList::unsetOption(const word& optName)
|
|
{
|
|
// Some options are always protected
|
|
if
|
|
(
|
|
optName == "case"
|
|
|| optName == "parallel"
|
|
|| optName == "roots"
|
|
|| optName == "hostRoots"
|
|
)
|
|
{
|
|
FatalErrorInFunction
|
|
<<"Option: '" << optName << "' is protected" << nl
|
|
<< exit(FatalError);
|
|
return false;
|
|
}
|
|
|
|
// Remove the option, return true if state changed
|
|
return options_.erase(optName);
|
|
}
|
|
|
|
|
|
void Foam::argList::displayDoc(bool source) const
|
|
{
|
|
const dictionary& docDict = debug::controlDict().subDict("Documentation");
|
|
fileNameList docDirs(docDict.get<fileNameList>("doxyDocDirs"));
|
|
fileName docExt(docDict.get<fileName>("doxySourceFileExt"));
|
|
|
|
// For source code: change xxx_8C.html to xxx_8C_source.html
|
|
if (source)
|
|
{
|
|
docExt.replace(".", "_source.");
|
|
}
|
|
|
|
fileName url;
|
|
|
|
for (const fileName& dir : docDirs)
|
|
{
|
|
// http protocols are last in the list
|
|
if (dir.startsWith("http:") || dir.startsWith("https:"))
|
|
{
|
|
url = dir/executable_ + docExt;
|
|
break;
|
|
}
|
|
|
|
fileName docFile = stringOps::expand(dir/executable_ + docExt);
|
|
|
|
if
|
|
(
|
|
docFile.startsWith("file://")
|
|
? isFile(docFile.substr(7)) // check part after "file://"
|
|
: isFile(docFile)
|
|
)
|
|
{
|
|
url = std::move(docFile);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (url.empty())
|
|
{
|
|
Info<< nl
|
|
<< "No documentation found for " << executable_
|
|
<< ", but you can use -help to display the usage\n" << endl;
|
|
|
|
return;
|
|
}
|
|
|
|
string docBrowser = getEnv("FOAM_DOC_BROWSER");
|
|
if (docBrowser.empty())
|
|
{
|
|
docDict.readEntry("docBrowser", docBrowser);
|
|
}
|
|
|
|
// Can use FOAM_DOC_BROWSER='application file://%f' if required
|
|
if (docBrowser.find("%f") != std::string::npos)
|
|
{
|
|
docBrowser.replace("%f", url);
|
|
}
|
|
else
|
|
{
|
|
docBrowser += " " + url;
|
|
}
|
|
|
|
// Split on whitespace to use safer version of Foam::system()
|
|
|
|
CStringList command(stringOps::splitSpace(docBrowser));
|
|
|
|
Info
|
|
<< "OpenFOAM " << foamVersion::api << " documentation:" << nl
|
|
<< " " << command << nl << endl;
|
|
|
|
Foam::system(command, true);
|
|
}
|
|
|
|
|
|
bool Foam::argList::check(bool checkArgs, bool checkOpts) const
|
|
{
|
|
bool ok = true;
|
|
|
|
if (Pstream::master())
|
|
{
|
|
const label nargs = args_.size()-1;
|
|
if (checkArgs && nargs != validArgs.size())
|
|
{
|
|
FatalError
|
|
<< "Expected " << validArgs.size()
|
|
<< " arguments but found " << nargs << endl;
|
|
ok = false;
|
|
}
|
|
|
|
if (checkOpts)
|
|
{
|
|
forAllConstIters(options_, iter)
|
|
{
|
|
const word& optName = iter.key();
|
|
if
|
|
(
|
|
!validOptions.found(optName)
|
|
&& !validParOptions.found(optName)
|
|
)
|
|
{
|
|
FatalError
|
|
<< "Invalid option: -" << optName << endl;
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ok)
|
|
{
|
|
FatalError
|
|
<< nl
|
|
<< "See '" << executable_ << " -help' for usage"
|
|
<< nl << nl;
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
bool Foam::argList::checkRootCase() const
|
|
{
|
|
if (!fileHandler().isDir(rootPath()))
|
|
{
|
|
FatalError
|
|
<< executable_
|
|
<< ": cannot open root directory " << rootPath()
|
|
<< endl;
|
|
|
|
return false;
|
|
}
|
|
|
|
const fileName pathDir(fileHandler().filePath(path()));
|
|
|
|
if (checkProcessorDirectories_ && pathDir.empty() && Pstream::master())
|
|
{
|
|
// Allow slaves on non-existing processor directories, created later
|
|
// (e.g. redistributePar)
|
|
FatalError
|
|
<< executable_
|
|
<< ": cannot open case directory " << path()
|
|
<< endl;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|