1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM; if not, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
28 #include "OSspecific.H"
31 #include "dictionary.H"
35 #include "labelList.H"
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
40 Foam::SLList<Foam::string> Foam::argList::validArgs;
41 Foam::HashTable<Foam::string> Foam::argList::validOptions;
42 Foam::HashTable<Foam::string> Foam::argList::validParOptions;
43 bool Foam::argList::bannerEnabled(true);
46 Foam::argList::initValidTables::initValidTables()
48 validOptions.set("case", "dir");
49 validOptions.set("parallel", "");
50 validParOptions.set("parallel", "");
52 Pstream::addValidParOptions(validParOptions);
56 Foam::argList::initValidTables dummyInitValidTables;
59 // convert argv -> args_
60 // transform sequences with "(" ... ")" into string lists in the process
61 bool Foam::argList::regroupArgv(int& argc, char**& argv)
67 // note: we also re-write directly into args_
68 // and use a second pass to sort out args/options
69 for (int argI = 0; argI < argc; argI++)
71 if (strcmp(argv[argI], "(") == 0)
76 else if (strcmp(argv[argI], ")") == 0)
84 args_[nArgs++] = tmpString;
90 args_[nArgs++] = argv[argI];
95 // quote each string element
97 tmpString += argv[argI];
102 args_[nArgs++] = argv[argI];
106 if (tmpString.size())
108 args_[nArgs++] = tmpString;
111 args_.setSize(nArgs);
117 // get rootPath_ / globalCase_ from one of the following forms
121 // Also export FOAM_CASE and FOAM_CASENAME environment variables
122 // so they can be used immediately (eg, in decomposeParDict)
124 void Foam::argList::getRootCase()
128 // [-case dir] specified
129 HashTable<string>::iterator iter = options_.find("case");
131 if (iter != options_.end())
136 if (casePath.empty() || casePath == ".")
138 // handle degenerate form and '-case .' like no -case specified
140 options_.erase("case");
142 else if (casePath[0] != '/' && casePath.name() == "..")
144 // avoid relative cases ending in '..' - makes for very ugly names
145 casePath = cwd()/casePath;
151 // nothing specified, use the current dir
155 rootPath_ = casePath.path();
156 globalCase_ = casePath.name();
160 // Set the case and case-name as an environment variable
161 if (rootPath_[0] == '/')
163 // absolute path - use as-is
164 setEnv("FOAM_CASE", rootPath_/globalCase_, true);
165 setEnv("FOAM_CASENAME", globalCase_, true);
169 // qualify relative path
170 fileName casePath = cwd()/rootPath_/globalCase_;
173 setEnv("FOAM_CASE", casePath, true);
174 setEnv("FOAM_CASENAME", casePath.name(), true);
181 Foam::stringList::subList Foam::argList::additionalArgs() const
183 return stringList::subList(args_, args_.size() - 1, 1);
187 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
189 Foam::argList::argList
200 // Check if this run is a parallel run by searching for any parallel option
201 // If found call runPar (might filter argv)
202 for (int argI = 0; argI < argc; argI++)
204 if (argv[argI][0] == '-')
206 const char *optionName = &argv[argI][1];
208 if (validParOptions.found(optionName))
210 parRunControl_.runPar(argc, argv);
216 // convert argv -> args_ and capture ( ... ) lists
217 // for normal arguments and for options
218 regroupArgv(argc, argv);
220 // Get executable name
221 args_[0] = fileName(argv[0]);
222 executable_ = fileName(argv[0]).name();
224 // Check arguments and options, we already have argv[0]
226 string argListString = args_[0];
228 for (int argI = 1; argI < args_.size(); argI++)
230 argListString += ' ';
231 argListString += args_[argI];
233 if (args_[argI][0] == '-')
235 const char *optionName = &args_[argI][1];
240 validOptions.found(optionName)
241 && validOptions[optionName] != ""
244 validParOptions.found(optionName)
245 && validParOptions[optionName] != ""
250 if (argI >= args_.size())
253 << "option " << "'-" << optionName << '\''
254 << " requires an argument"
258 argListString += ' ';
259 argListString += args_[argI];
260 options_.insert(optionName, args_[argI]);
264 options_.insert(optionName, "");
271 args_[nArgs] = args_[argI];
277 args_.setSize(nArgs);
279 // Help/documentation options:
280 // -help print the usage
281 // -doc display application documentation in browser
282 // -srcDoc display source code in browser
285 options_.found("help")
286 || options_.found("doc")
287 || options_.found("srcDoc")
290 if (options_.found("help"))
295 // only display one or the other
296 if (options_.found("srcDoc"))
300 else if (options_.found("doc"))
308 // Print the usage message and exit if the number of arguments is incorrect
309 if (!check(checkArgs, checkOpts))
315 string dateString = clock::date();
316 string timeString = clock::clockTime();
318 // Print the banner once only for parallel runs
319 if (Pstream::master() && bannerEnabled)
321 IOobject::writeBanner(Info, true)
322 << "Build : " << Foam::FOAMbuild << nl
323 << "Exec : " << argListString.c_str() << nl
324 << "Date : " << dateString.c_str() << nl
325 << "Time : " << timeString.c_str() << nl
326 << "Host : " << hostName() << nl
327 << "PID : " << pid() << endl;
330 jobInfo.add("startDate", dateString);
331 jobInfo.add("startTime", timeString);
332 jobInfo.add("userName", userName());
333 jobInfo.add("foamVersion", word(FOAMversion));
334 jobInfo.add("foamBuild", Foam::FOAMbuild);
335 jobInfo.add("code", executable_);
336 jobInfo.add("argList", argListString);
337 jobInfo.add("currentDir", cwd());
338 jobInfo.add("PPID", ppid());
339 jobInfo.add("PGID", pgid());
342 // Case is a single processor run unless it is running parallel
345 // If this actually is a parallel run
346 if (parRunControl_.parRun())
349 if (Pstream::master())
351 // establish rootPath_/globalCase_/case_ for master
354 IFstream decompDictStream
356 rootPath_/globalCase_/"system/decomposeParDict"
359 if (!decompDictStream.good())
363 << decompDictStream.name()
367 dictionary decompDict(decompDictStream);
373 decompDict.lookup("numberOfSubdomains")
377 // Check number of processors.
378 // nProcs => number of actual procs
379 // dictNProcs => number of procs specified in decompositionDict
380 // nProcDirs => number of processor directories
381 // (n/a when running distributed)
383 // - normal running : nProcs = dictNProcs = nProcDirs
384 // - decomposition to more processors : nProcs = dictNProcs
385 // - decomposition to fewer processors : nProcs = nProcDirs
386 if (dictNProcs > Pstream::nProcs())
389 << decompDictStream.name()
390 << " specifies " << dictNProcs
391 << " processors but job was started with "
392 << Pstream::nProcs() << " processors."
397 if (decompDict.lookupOrDefault<Switch>("distributed", false))
400 decompDict.lookup("roots") >> roots;
402 if (roots.size() != Pstream::nProcs()-1)
405 << "number of entries in decompositionDict::roots"
406 << " is not equal to the number of slaves "
407 << Pstream::nProcs()-1
411 // Distribute the master's argument list (with new root)
412 bool hadCaseOpt = options_.found("case");
415 int slave=Pstream::firstSlave();
416 slave<=Pstream::lastSlave();
423 fileName(roots[slave-1])/globalCase_
426 OPstream toSlave(Pstream::scheduled, slave);
427 toSlave << args_ << options_;
429 options_.erase("case");
431 // restore [-case dir]
434 options_.set("case", rootPath_/globalCase_);
439 // Possibly going to fewer processors.
440 // Check if all procDirs are there.
441 if (dictNProcs < Pstream::nProcs())
448 rootPath_/globalCase_/"processor"
454 if (nProcDirs != Pstream::nProcs())
457 << "number of processor directories = "
459 << " is not equal to the number of processors = "
465 // Distribute the master's argument list (unaltered)
468 int slave=Pstream::firstSlave();
469 slave<=Pstream::lastSlave();
473 OPstream toSlave(Pstream::scheduled, slave);
474 toSlave << args_ << options_;
480 // Collect the master's argument list
481 IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
482 fromMaster >> args_ >> options_;
484 // establish rootPath_/globalCase_/case_ for slave
488 nProcs = Pstream::nProcs();
489 case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
493 // establish rootPath_/globalCase_/case_
501 // collect slave machine/pid
502 if (parRunControl_.parRun())
504 if (Pstream::master())
506 slaveProcs.setSize(Pstream::nProcs() - 1);
513 int slave=Pstream::firstSlave();
514 slave<=Pstream::lastSlave();
518 IPstream fromSlave(Pstream::scheduled, slave);
519 fromSlave >> slaveMachine >> slavePid;
521 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
526 OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
527 toMaster << hostName() << pid();
532 if (Pstream::master() && bannerEnabled)
534 Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
535 << "nProcs : " << nProcs << endl;
537 if (parRunControl_.parRun())
539 Info<< "Slaves : " << slaveProcs << nl
540 << "Pstream initialized with:" << nl
541 << " floatTransfer : " << Pstream::floatTransfer << nl
542 << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
544 << Pstream::commsTypeNames[Pstream::defaultCommsType]
549 jobInfo.add("root", rootPath_);
550 jobInfo.add("case", globalCase_);
551 jobInfo.add("nProcs", nProcs);
552 if (slaveProcs.size())
554 jobInfo.add("slaves", slaveProcs);
558 // Switch on signal trapping. We have to wait until after Pstream::init
559 // since this sets up its own ones.
560 sigFpe_.set(bannerEnabled);
561 sigInt_.set(bannerEnabled);
562 sigQuit_.set(bannerEnabled);
563 sigSegv_.set(bannerEnabled);
565 if (Pstream::master() && bannerEnabled)
568 IOobject::writeDivider(Info);
573 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
575 Foam::argList::~argList()
581 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
583 void Foam::argList::noBanner()
585 bannerEnabled = false;
589 void Foam::argList::noParallel()
591 validOptions.erase("parallel");
595 void Foam::argList::printUsage() const
598 << "Usage: " << executable_;
602 SLList<string>::iterator iter = validArgs.begin();
603 iter != validArgs.end();
607 Info<< " <" << iter().c_str() << '>';
612 HashTable<string>::iterator iter = validOptions.begin();
613 iter != validOptions.end();
617 Info<< " [-" << iter.key();
621 Info<< ' ' << iter().c_str();
627 // place help/doc/srcDoc options of the way at the end,
628 // but with an extra space to separate it a little
629 Info<< " [-help] [-doc] [-srcDoc]\n" << endl;
633 void Foam::argList::displayDoc(bool source) const
635 const dictionary& docDict = debug::controlDict().subDict("Documentation");
636 List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
637 List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
639 // for source code: change foo_8C.html to foo_8C_source.html
642 forAll(docExts, extI)
644 docExts[extI].replace(".", "_source.");
651 forAll(docDirs, dirI)
653 forAll(docExts, extI)
655 docFile = docDirs[dirI]/executable_ + docExts[extI];
672 string docBrowser(docDict.lookup("docBrowser"));
673 docBrowser.replaceAll("%f", docFile);
675 Info<< "Show documentation: " << docBrowser.c_str() << endl;
682 << "No documentation found for " << executable_
683 << ", but you can use -help to display the usage\n" << endl;
688 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
692 if (Pstream::master())
694 if (checkArgs && args_.size() - 1 != validArgs.size())
697 << "Wrong number of arguments, expected " << validArgs.size()
698 << " found " << args_.size() - 1 << endl;
704 forAllConstIter(HashTable<string>, options_, iter)
708 !validOptions.found(iter.key())
709 && !validParOptions.found(iter.key())
713 << "Invalid option: -" << iter.key() << endl;
729 bool Foam::argList::checkRootCase() const
731 if (!isDir(rootPath()))
735 << ": cannot open root directory " << rootPath()
741 if (!isDir(path()) && Pstream::master())
743 // Allow slaves on non-existing processor directories, created later
746 << ": cannot open case directory " << path()
756 // ************************************************************************* //