Files
openfoam/src/OpenFOAM/global/argList/argList.C
Mark Olesen 5e4d7386ec ENH: use foamVersion::api internally in etcFiles searching (#1010)
- prefer this to using the OPENFOAM define since this improves the
  internal consistency with the build information.

  The API information could change between builds without the
  etcFiles.C being recompiled whereas the value of
  Foam::foamVersion::api is force updated during the build (triggers
  recompilation of globals.Cver)
2018-12-08 17:42:31 +01:00

1630 lines
42 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2015-2018 OpenCFD Ltd.
-------------------------------------------------------------------------------
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::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))",
"slave root directories (per host) for distributed running. "
"The host specification can use 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)
{
validArgs.append(argName);
}
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::nonMandatoryArgs()
{
argsMandatory_ = false;
}
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;
}
// * * * * * * * * * * * * * 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 = optIter.object();
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
// 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"))
{
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)
{
::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"
#if OPENFOAM
<< ' ' << OPENFOAM
#endif
<< " 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;
}
// ************************************************************************* //