1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
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
120 void Foam::argList::getRootCase()
124 // [-case dir] specified
125 HashTable<string>::iterator iter = options_.find("case");
127 if (iter != options_.end())
132 // handle degenerate form and '-case .' like no -case specified
133 if (casePath.empty() || casePath == ".")
136 options_.erase("case");
141 // nothing specified, use the current dir
145 rootPath_ = casePath.path();
146 globalCase_ = casePath.name();
151 Foam::stringList::subList Foam::argList::additionalArgs() const
153 return stringList::subList(args_, args_.size() - 1, 1);
157 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
159 Foam::argList::argList
170 // Check if this run is a parallel run by searching for any parallel option
171 // If found call runPar (might filter argv)
172 for (int argi=0; argi<argc; argi++)
174 if (argv[argi][0] == '-')
176 const char *optionName = &argv[argi][1];
178 if (validParOptions.found(optionName))
180 parRunControl_.runPar(argc, argv);
186 // convert argv -> args_ and capture ( ... ) lists
187 // for normal arguments and for options
188 regroupArgv(argc, argv);
190 // Get executable name
191 args_[0] = fileName(argv[0]);
192 executable_ = fileName(argv[0]).name();
194 // Check arguments and options, we already have argv[0]
196 string argListString = args_[0];
198 for (int argi = 1; argi < args_.size(); argi++)
200 argListString += ' ';
201 argListString += args_[argi];
203 if (args_[argi][0] == '-')
205 const char *optionName = &args_[argi][1];
210 validOptions.found(optionName)
211 && validOptions[optionName] != ""
214 validParOptions.found(optionName)
215 && validParOptions[optionName] != ""
220 if (argi >= args_.size())
223 << "option " << "'-" << optionName << '\''
224 << " requires an argument"
228 argListString += ' ';
229 argListString += args_[argi];
230 options_.insert(optionName, args_[argi]);
234 options_.insert(optionName, "");
241 args_[nArgs] = args_[argi];
247 args_.setSize(nArgs);
249 // Help/documentation options:
250 // -help print the usage
251 // -doc display application documentation in browser
252 // -srcDoc display source code in browser
255 options_.found("help")
256 || options_.found("doc")
257 || options_.found("srcDoc")
260 if (options_.found("help"))
265 // only display one or the other
266 if (options_.found("srcDoc"))
270 else if (options_.found("doc"))
278 // Print the usage message and exit if the number of arguments is incorrect
279 if (!check(checkArgs, checkOpts))
285 string dateString = clock::date();
286 string timeString = clock::clockTime();
288 // Print the banner once only for parallel runs
289 if (Pstream::master() && bannerEnabled)
291 IOobject::writeBanner(Info, true)
292 << "Build : " << Foam::FOAMbuild << nl
293 << "Exec : " << argListString.c_str() << nl
294 << "Date : " << dateString.c_str() << nl
295 << "Time : " << timeString.c_str() << nl
296 << "Host : " << hostName() << nl
297 << "PID : " << pid() << endl;
300 jobInfo.add("startDate", dateString);
301 jobInfo.add("startTime", timeString);
302 jobInfo.add("userName", userName());
303 jobInfo.add("foamVersion", word(FOAMversion));
304 jobInfo.add("foamBuild", Foam::FOAMbuild);
305 jobInfo.add("code", executable_);
306 jobInfo.add("argList", argListString);
307 jobInfo.add("currentDir", cwd());
308 jobInfo.add("PPID", ppid());
309 jobInfo.add("PGID", pgid());
312 // Case is a single processor run unless it is running parallel
315 // If this actually is a parallel run
316 if (parRunControl_.parRun())
319 if (Pstream::master())
321 // establish rootPath_/globalCase_/case_ for master
324 IFstream decompDictStream
326 rootPath_/globalCase_/"system/decomposeParDict"
329 if (!decompDictStream.good())
333 << decompDictStream.name()
337 dictionary decompDict(decompDictStream);
343 decompDict.lookup("numberOfSubdomains")
347 // Check number of processors.
348 // nProcs => number of actual procs
349 // dictNProcs => number of procs specified in decompositionDict
350 // nProcDirs => number of processor directories
351 // (n/a when running distributed)
353 // - normal running : nProcs = dictNProcs = nProcDirs
354 // - decomposition to more processors : nProcs = dictNProcs
355 // - decomposition to fewer processors : nProcs = nProcDirs
356 if (dictNProcs > Pstream::nProcs())
359 << decompDictStream.name()
360 << " specifies " << dictNProcs
361 << " processors but job was started with "
362 << Pstream::nProcs() << " processors."
367 if (decompDict.lookupOrDefault<Switch>("distributed", false))
370 decompDict.lookup("roots") >> roots;
372 if (roots.size() != Pstream::nProcs()-1)
375 << "number of entries in decompositionDict::roots"
376 << " is not equal to the number of slaves "
377 << Pstream::nProcs()-1
381 // Distribute the master's argument list (with new root)
382 bool hadCaseOpt = options_.found("case");
385 int slave=Pstream::firstSlave();
386 slave<=Pstream::lastSlave();
393 fileName(roots[slave-1])/globalCase_
396 OPstream toSlave(Pstream::scheduled, slave);
397 toSlave << args_ << options_;
399 options_.erase("case");
401 // restore [-case dir]
404 options_.set("case", rootPath_/globalCase_);
409 // Possibly going to fewer processors.
410 // Check if all procDirs are there.
411 if (dictNProcs < Pstream::nProcs())
418 rootPath_/globalCase_/"processor"
424 if (nProcDirs != Pstream::nProcs())
427 << "number of processor directories = "
429 << " is not equal to the number of processors = "
435 // Distribute the master's argument list (unaltered)
438 int slave=Pstream::firstSlave();
439 slave<=Pstream::lastSlave();
443 OPstream toSlave(Pstream::scheduled, slave);
444 toSlave << args_ << options_;
450 // Collect the master's argument list
451 IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
452 fromMaster >> args_ >> options_;
454 // establish rootPath_/globalCase_/case_ for slave
458 nProcs = Pstream::nProcs();
459 case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
463 // establish rootPath_/globalCase_/case_
471 // collect slave machine/pid
472 if (parRunControl_.parRun())
474 if (Pstream::master())
476 slaveProcs.setSize(Pstream::nProcs() - 1);
483 int slave=Pstream::firstSlave();
484 slave<=Pstream::lastSlave();
488 IPstream fromSlave(Pstream::scheduled, slave);
489 fromSlave >> slaveMachine >> slavePid;
491 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
496 OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
497 toMaster << hostName() << pid();
502 if (Pstream::master() && bannerEnabled)
504 Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
505 << "nProcs : " << nProcs << endl;
507 if (parRunControl_.parRun())
509 Info<< "Slaves : " << slaveProcs << nl
510 << "Pstream initialized with:" << nl
511 << " floatTransfer : " << Pstream::floatTransfer << nl
512 << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
514 << Pstream::commsTypeNames[Pstream::defaultCommsType]
519 jobInfo.add("root", rootPath_);
520 jobInfo.add("case", globalCase_);
521 jobInfo.add("nProcs", nProcs);
522 if (slaveProcs.size())
524 jobInfo.add("slaves", slaveProcs);
529 // Set the case and case-name as an environment variable
530 if (rootPath_[0] == '/')
533 setEnv("FOAM_CASE", rootPath_/globalCase_, true);
535 else if (rootPath_ == ".")
537 // relative to the current working directory
538 setEnv("FOAM_CASE", cwd()/globalCase_, true);
542 // qualify relative path
543 setEnv("FOAM_CASE", cwd()/rootPath_/globalCase_, true);
545 setEnv("FOAM_CASENAME", globalCase_, true);
548 // Switch on signal trapping. We have to wait until after Pstream::init
549 // since this sets up its own ones.
550 sigFpe_.set(bannerEnabled);
551 sigInt_.set(bannerEnabled);
552 sigQuit_.set(bannerEnabled);
553 sigSegv_.set(bannerEnabled);
555 if (Pstream::master() && bannerEnabled)
558 IOobject::writeDivider(Info);
563 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
565 Foam::argList::~argList()
571 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
573 void Foam::argList::noBanner()
575 bannerEnabled = false;
579 void Foam::argList::noParallel()
581 validOptions.erase("parallel");
585 void Foam::argList::printUsage() const
588 << "Usage: " << executable_;
592 SLList<string>::iterator iter = validArgs.begin();
593 iter != validArgs.end();
597 Info<< " <" << iter().c_str() << '>';
602 HashTable<string>::iterator iter = validOptions.begin();
603 iter != validOptions.end();
607 Info<< " [-" << iter.key();
611 Info<< ' ' << iter().c_str();
617 // place help/doc/srcDoc options of the way at the end,
618 // but with an extra space to separate it a little
619 Info<< " [-help] [-doc] [-srcDoc]\n" << endl;
623 void Foam::argList::displayDoc(bool source) const
625 const dictionary& docDict = debug::controlDict().subDict("Documentation");
626 List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
627 List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
629 // for source code: change foo_8C.html to foo_8C-source.html
632 forAll(docExts, extI)
634 docExts[extI].replace(".", "-source.");
641 forAll(docDirs, dirI)
643 forAll(docExts, extI)
645 docFile = docDirs[dirI]/executable_ + docExts[extI];
662 string docBrowser(docDict.lookup("docBrowser"));
663 docBrowser.replaceAll("%f", docFile);
665 Info<< "Show documentation: " << docBrowser.c_str() << endl;
672 << "No documentation found for " << executable_
673 << ", but you can use -help to display the usage\n" << endl;
678 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
682 if (Pstream::master())
684 if (checkArgs && args_.size() - 1 != validArgs.size())
687 << "Wrong number of arguments, expected " << validArgs.size()
688 << " found " << args_.size() - 1 << endl;
694 forAllConstIter(HashTable<string>, options_, iter)
698 !validOptions.found(iter.key())
699 && !validParOptions.found(iter.key())
703 << "Invalid option: -" << iter.key() << endl;
719 bool Foam::argList::checkRootCase() const
721 if (!isDir(rootPath()))
725 << ": cannot open root directory " << rootPath()
731 if (!isDir(path()) && Pstream::master())
733 // Allow slaves on non-existing processor directories, created later
736 << ": cannot open case directory " << path()
746 // ************************************************************************* //