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 if (casePath.empty() || casePath == ".")
134 // handle degenerate form and '-case .' like no -case specified
136 options_.erase("case");
138 else if (casePath[0] != '/' && casePath.name() == "..")
140 // avoid relative cases ending in '..' - makes for very ugly names
141 casePath = cwd()/casePath;
147 // nothing specified, use the current dir
151 rootPath_ = casePath.path();
152 globalCase_ = casePath.name();
157 Foam::stringList::subList Foam::argList::additionalArgs() const
159 return stringList::subList(args_, args_.size() - 1, 1);
163 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
165 Foam::argList::argList
176 // Check if this run is a parallel run by searching for any parallel option
177 // If found call runPar (might filter argv)
178 for (int argI = 0; argI < argc; argI++)
180 if (argv[argI][0] == '-')
182 const char *optionName = &argv[argI][1];
184 if (validParOptions.found(optionName))
186 parRunControl_.runPar(argc, argv);
192 // convert argv -> args_ and capture ( ... ) lists
193 // for normal arguments and for options
194 regroupArgv(argc, argv);
196 // Get executable name
197 args_[0] = fileName(argv[0]);
198 executable_ = fileName(argv[0]).name();
200 // Check arguments and options, we already have argv[0]
202 string argListString = args_[0];
204 for (int argI = 1; argI < args_.size(); argI++)
206 argListString += ' ';
207 argListString += args_[argI];
209 if (args_[argI][0] == '-')
211 const char *optionName = &args_[argI][1];
216 validOptions.found(optionName)
217 && validOptions[optionName] != ""
220 validParOptions.found(optionName)
221 && validParOptions[optionName] != ""
226 if (argI >= args_.size())
229 << "option " << "'-" << optionName << '\''
230 << " requires an argument"
234 argListString += ' ';
235 argListString += args_[argI];
236 options_.insert(optionName, args_[argI]);
240 options_.insert(optionName, "");
247 args_[nArgs] = args_[argI];
253 args_.setSize(nArgs);
255 // Help/documentation options:
256 // -help print the usage
257 // -doc display application documentation in browser
258 // -srcDoc display source code in browser
261 options_.found("help")
262 || options_.found("doc")
263 || options_.found("srcDoc")
266 if (options_.found("help"))
271 // only display one or the other
272 if (options_.found("srcDoc"))
276 else if (options_.found("doc"))
284 // Print the usage message and exit if the number of arguments is incorrect
285 if (!check(checkArgs, checkOpts))
291 string dateString = clock::date();
292 string timeString = clock::clockTime();
294 // Print the banner once only for parallel runs
295 if (Pstream::master() && bannerEnabled)
297 IOobject::writeBanner(Info, true)
298 << "Build : " << Foam::FOAMbuild << nl
299 << "Exec : " << argListString.c_str() << nl
300 << "Date : " << dateString.c_str() << nl
301 << "Time : " << timeString.c_str() << nl
302 << "Host : " << hostName() << nl
303 << "PID : " << pid() << endl;
306 jobInfo.add("startDate", dateString);
307 jobInfo.add("startTime", timeString);
308 jobInfo.add("userName", userName());
309 jobInfo.add("foamVersion", word(FOAMversion));
310 jobInfo.add("foamBuild", Foam::FOAMbuild);
311 jobInfo.add("code", executable_);
312 jobInfo.add("argList", argListString);
313 jobInfo.add("currentDir", cwd());
314 jobInfo.add("PPID", ppid());
315 jobInfo.add("PGID", pgid());
318 // Case is a single processor run unless it is running parallel
321 // If this actually is a parallel run
322 if (parRunControl_.parRun())
325 if (Pstream::master())
327 // establish rootPath_/globalCase_/case_ for master
330 IFstream decompDictStream
332 rootPath_/globalCase_/"system/decomposeParDict"
335 if (!decompDictStream.good())
339 << decompDictStream.name()
343 dictionary decompDict(decompDictStream);
349 decompDict.lookup("numberOfSubdomains")
353 // Check number of processors.
354 // nProcs => number of actual procs
355 // dictNProcs => number of procs specified in decompositionDict
356 // nProcDirs => number of processor directories
357 // (n/a when running distributed)
359 // - normal running : nProcs = dictNProcs = nProcDirs
360 // - decomposition to more processors : nProcs = dictNProcs
361 // - decomposition to fewer processors : nProcs = nProcDirs
362 if (dictNProcs > Pstream::nProcs())
365 << decompDictStream.name()
366 << " specifies " << dictNProcs
367 << " processors but job was started with "
368 << Pstream::nProcs() << " processors."
373 if (decompDict.lookupOrDefault<Switch>("distributed", false))
376 decompDict.lookup("roots") >> roots;
378 if (roots.size() != Pstream::nProcs()-1)
381 << "number of entries in decompositionDict::roots"
382 << " is not equal to the number of slaves "
383 << Pstream::nProcs()-1
387 // Distribute the master's argument list (with new root)
388 bool hadCaseOpt = options_.found("case");
391 int slave=Pstream::firstSlave();
392 slave<=Pstream::lastSlave();
399 fileName(roots[slave-1])/globalCase_
402 OPstream toSlave(Pstream::scheduled, slave);
403 toSlave << args_ << options_;
405 options_.erase("case");
407 // restore [-case dir]
410 options_.set("case", rootPath_/globalCase_);
415 // Possibly going to fewer processors.
416 // Check if all procDirs are there.
417 if (dictNProcs < Pstream::nProcs())
424 rootPath_/globalCase_/"processor"
430 if (nProcDirs != Pstream::nProcs())
433 << "number of processor directories = "
435 << " is not equal to the number of processors = "
441 // Distribute the master's argument list (unaltered)
444 int slave=Pstream::firstSlave();
445 slave<=Pstream::lastSlave();
449 OPstream toSlave(Pstream::scheduled, slave);
450 toSlave << args_ << options_;
456 // Collect the master's argument list
457 IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
458 fromMaster >> args_ >> options_;
460 // establish rootPath_/globalCase_/case_ for slave
464 nProcs = Pstream::nProcs();
465 case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
469 // establish rootPath_/globalCase_/case_
477 // collect slave machine/pid
478 if (parRunControl_.parRun())
480 if (Pstream::master())
482 slaveProcs.setSize(Pstream::nProcs() - 1);
489 int slave=Pstream::firstSlave();
490 slave<=Pstream::lastSlave();
494 IPstream fromSlave(Pstream::scheduled, slave);
495 fromSlave >> slaveMachine >> slavePid;
497 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
502 OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
503 toMaster << hostName() << pid();
508 if (Pstream::master() && bannerEnabled)
510 Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
511 << "nProcs : " << nProcs << endl;
513 if (parRunControl_.parRun())
515 Info<< "Slaves : " << slaveProcs << nl
516 << "Pstream initialized with:" << nl
517 << " floatTransfer : " << Pstream::floatTransfer << nl
518 << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
520 << Pstream::commsTypeNames[Pstream::defaultCommsType]
525 jobInfo.add("root", rootPath_);
526 jobInfo.add("case", globalCase_);
527 jobInfo.add("nProcs", nProcs);
528 if (slaveProcs.size())
530 jobInfo.add("slaves", slaveProcs);
535 // Set the case and case-name as an environment variable
536 if (rootPath_[0] == '/')
538 // absolute path - use as-is
539 setEnv("FOAM_CASE", rootPath_/globalCase_, true);
540 setEnv("FOAM_CASENAME", globalCase_, true);
544 // qualify relative path
545 fileName casePath = cwd()/rootPath_/globalCase_;
548 setEnv("FOAM_CASE", casePath, true);
549 setEnv("FOAM_CASENAME", casePath.name(), true);
552 // Switch on signal trapping. We have to wait until after Pstream::init
553 // since this sets up its own ones.
554 sigFpe_.set(bannerEnabled);
555 sigInt_.set(bannerEnabled);
556 sigQuit_.set(bannerEnabled);
557 sigSegv_.set(bannerEnabled);
559 if (Pstream::master() && bannerEnabled)
562 IOobject::writeDivider(Info);
567 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
569 Foam::argList::~argList()
575 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
577 void Foam::argList::noBanner()
579 bannerEnabled = false;
583 void Foam::argList::noParallel()
585 validOptions.erase("parallel");
589 void Foam::argList::printUsage() const
592 << "Usage: " << executable_;
596 SLList<string>::iterator iter = validArgs.begin();
597 iter != validArgs.end();
601 Info<< " <" << iter().c_str() << '>';
606 HashTable<string>::iterator iter = validOptions.begin();
607 iter != validOptions.end();
611 Info<< " [-" << iter.key();
615 Info<< ' ' << iter().c_str();
621 // place help/doc/srcDoc options of the way at the end,
622 // but with an extra space to separate it a little
623 Info<< " [-help] [-doc] [-srcDoc]\n" << endl;
627 void Foam::argList::displayDoc(bool source) const
629 const dictionary& docDict = debug::controlDict().subDict("Documentation");
630 List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
631 List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
633 // for source code: change foo_8C.html to foo_8C-source.html
636 forAll(docExts, extI)
638 docExts[extI].replace(".", "-source.");
645 forAll(docDirs, dirI)
647 forAll(docExts, extI)
649 docFile = docDirs[dirI]/executable_ + docExts[extI];
666 string docBrowser(docDict.lookup("docBrowser"));
667 docBrowser.replaceAll("%f", docFile);
669 Info<< "Show documentation: " << docBrowser.c_str() << endl;
676 << "No documentation found for " << executable_
677 << ", but you can use -help to display the usage\n" << endl;
682 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
686 if (Pstream::master())
688 if (checkArgs && args_.size() - 1 != validArgs.size())
691 << "Wrong number of arguments, expected " << validArgs.size()
692 << " found " << args_.size() - 1 << endl;
698 forAllConstIter(HashTable<string>, options_, iter)
702 !validOptions.found(iter.key())
703 && !validParOptions.found(iter.key())
707 << "Invalid option: -" << iter.key() << endl;
723 bool Foam::argList::checkRootCase() const
725 if (!isDir(rootPath()))
729 << ": cannot open root directory " << rootPath()
735 if (!isDir(path()) && Pstream::master())
737 // Allow slaves on non-existing processor directories, created later
740 << ": cannot open case directory " << path()
750 // ************************************************************************* //