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 <OpenFOAM/OSspecific.H>
29 #include <OpenFOAM/clock.H>
30 #include <OpenFOAM/IFstream.H>
31 #include <OpenFOAM/dictionary.H>
32 #include <OpenFOAM/Switch.H>
33 #include <OpenFOAM/IOobject.H>
34 #include <OpenFOAM/JobInfo.H>
35 #include <OpenFOAM/labelList.H>
36 #include <OpenFOAM/ListOps.H>
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41 Foam::SLList<Foam::string> Foam::argList::validArgs;
42 Foam::HashTable<Foam::string> Foam::argList::validOptions;
43 Foam::HashTable<Foam::string> Foam::argList::validParOptions;
44 bool Foam::argList::bannerEnabled(true);
47 Foam::argList::initValidTables::initValidTables()
49 validOptions.set("case", "dir");
50 validOptions.set("parallel", "");
51 validParOptions.set("parallel", "");
53 Pstream::addValidParOptions(validParOptions);
57 Foam::argList::initValidTables dummyInitValidTables;
60 // convert argv -> args_
61 // transform sequences with "(" ... ")" into string lists in the process
62 bool Foam::argList::regroupArgv(int& argc, char**& argv)
68 // note: we also re-write directly into args_
69 // and use a second pass to sort out args/options
70 for (int argI = 0; argI < argc; argI++)
72 if (strcmp(argv[argI], "(") == 0)
77 else if (strcmp(argv[argI], ")") == 0)
85 args_[nArgs++] = tmpString;
91 args_[nArgs++] = argv[argI];
96 // quote each string element
98 tmpString += argv[argI];
103 args_[nArgs++] = argv[argI];
107 if (tmpString.size())
109 args_[nArgs++] = tmpString;
112 args_.setSize(nArgs);
118 // get rootPath_ / globalCase_ from one of the following forms
122 // Also export FOAM_CASE and FOAM_CASENAME environment variables
123 // so they can be used immediately (eg, in decomposeParDict)
125 void Foam::argList::getRootCase()
129 // [-case dir] specified
130 HashTable<string>::iterator iter = options_.find("case");
132 if (iter != options_.end())
137 if (casePath.empty() || casePath == ".")
139 // handle degenerate form and '-case .' like no -case specified
141 options_.erase("case");
143 else if (casePath[0] != '/' && casePath.name() == "..")
145 // avoid relative cases ending in '..' - makes for very ugly names
146 casePath = cwd()/casePath;
152 // nothing specified, use the current dir
156 rootPath_ = casePath.path();
157 globalCase_ = casePath.name();
161 // Set the case and case-name as an environment variable
162 if (rootPath_[0] == '/')
164 // absolute path - use as-is
165 setEnv("FOAM_CASE", rootPath_/globalCase_, true);
166 setEnv("FOAM_CASENAME", globalCase_, true);
170 // qualify relative path
171 fileName casePath = cwd()/rootPath_/globalCase_;
174 setEnv("FOAM_CASE", casePath, true);
175 setEnv("FOAM_CASENAME", casePath.name(), true);
182 Foam::stringList::subList Foam::argList::additionalArgs() const
184 return stringList::subList(args_, args_.size() - 1, 1);
188 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
190 Foam::argList::argList
201 // Check if this run is a parallel run by searching for any parallel option
202 // If found call runPar (might filter argv)
203 for (int argI = 0; argI < argc; argI++)
205 if (argv[argI][0] == '-')
207 const char *optionName = &argv[argI][1];
209 if (validParOptions.found(optionName))
211 parRunControl_.runPar(argc, argv);
217 // convert argv -> args_ and capture ( ... ) lists
218 // for normal arguments and for options
219 regroupArgv(argc, argv);
221 // Get executable name
222 args_[0] = fileName(argv[0]);
223 executable_ = fileName(argv[0]).name();
225 // Check arguments and options, we already have argv[0]
227 string argListString = args_[0];
229 for (int argI = 1; argI < args_.size(); argI++)
231 argListString += ' ';
232 argListString += args_[argI];
234 if (args_[argI][0] == '-')
236 const char *optionName = &args_[argI][1];
241 validOptions.found(optionName)
242 && validOptions[optionName] != ""
245 validParOptions.found(optionName)
246 && validParOptions[optionName] != ""
251 if (argI >= args_.size())
254 << "option " << "'-" << optionName << '\''
255 << " requires an argument"
259 argListString += ' ';
260 argListString += args_[argI];
261 options_.insert(optionName, args_[argI]);
265 options_.insert(optionName, "");
272 args_[nArgs] = args_[argI];
278 args_.setSize(nArgs);
280 // Help/documentation options:
281 // -help print the usage
282 // -doc display application documentation in browser
283 // -srcDoc display source code in browser
286 options_.found("help")
287 || options_.found("doc")
288 || options_.found("srcDoc")
291 if (options_.found("help"))
296 // only display one or the other
297 if (options_.found("srcDoc"))
301 else if (options_.found("doc"))
309 // Print the usage message and exit if the number of arguments is incorrect
310 if (!check(checkArgs, checkOpts))
316 string dateString = clock::date();
317 string timeString = clock::clockTime();
319 // Print the banner once only for parallel runs
320 if (Pstream::master() && bannerEnabled)
322 IOobject::writeBanner(Info, true)
323 << "Build : " << Foam::FOAMbuild << nl
324 << "Exec : " << argListString.c_str() << nl
325 << "Date : " << dateString.c_str() << nl
326 << "Time : " << timeString.c_str() << nl
327 << "Host : " << hostName() << nl
328 << "PID : " << pid() << endl;
331 jobInfo.add("startDate", dateString);
332 jobInfo.add("startTime", timeString);
333 jobInfo.add("userName", userName());
334 jobInfo.add("foamVersion", word(FOAMfullVersion));
335 jobInfo.add("foamBuild", Foam::FOAMbuild);
336 jobInfo.add("code", executable_);
337 jobInfo.add("argList", argListString);
338 jobInfo.add("currentDir", cwd());
339 jobInfo.add("PPID", ppid());
340 jobInfo.add("PGID", pgid());
343 // Case is a single processor run unless it is running parallel
346 // If this actually is a parallel run
347 if (parRunControl_.parRun())
350 if (Pstream::master())
352 // establish rootPath_/globalCase_/case_ for master
355 IFstream decompDictStream
357 rootPath_/globalCase_/"system/decomposeParDict"
360 if (!decompDictStream.good())
364 << decompDictStream.name()
368 dictionary decompDict(decompDictStream);
374 decompDict.lookup("numberOfSubdomains")
378 // Check number of processors.
379 // nProcs => number of actual procs
380 // dictNProcs => number of procs specified in decompositionDict
381 // nProcDirs => number of processor directories
382 // (n/a when running distributed)
384 // - normal running : nProcs = dictNProcs = nProcDirs
385 // - decomposition to more processors : nProcs = dictNProcs
386 // - decomposition to fewer processors : nProcs = nProcDirs
387 if (dictNProcs > Pstream::nProcs())
390 << decompDictStream.name()
391 << " specifies " << dictNProcs
392 << " processors but job was started with "
393 << Pstream::nProcs() << " processors."
398 if (decompDict.lookupOrDefault<Switch>("distributed", false))
401 decompDict.lookup("roots") >> roots;
403 if (roots.size() != Pstream::nProcs()-1)
406 << "number of entries in decompositionDict::roots"
407 << " is not equal to the number of slaves "
408 << Pstream::nProcs()-1
412 // Distribute the master's argument list (with new root)
413 bool hadCaseOpt = options_.found("case");
416 int slave=Pstream::firstSlave();
417 slave<=Pstream::lastSlave();
424 fileName(roots[slave-1])/globalCase_
427 OPstream toSlave(Pstream::scheduled, slave);
428 toSlave << args_ << options_;
430 options_.erase("case");
432 // restore [-case dir]
435 options_.set("case", rootPath_/globalCase_);
440 // Possibly going to fewer processors.
441 // Check if all procDirs are there.
442 if (dictNProcs < Pstream::nProcs())
449 rootPath_/globalCase_/"processor"
455 if (nProcDirs != Pstream::nProcs())
458 << "number of processor directories = "
460 << " is not equal to the number of processors = "
466 // Distribute the master's argument list (unaltered)
469 int slave=Pstream::firstSlave();
470 slave<=Pstream::lastSlave();
474 OPstream toSlave(Pstream::scheduled, slave);
475 toSlave << args_ << options_;
481 // Collect the master's argument list
482 IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
483 fromMaster >> args_ >> options_;
485 // establish rootPath_/globalCase_/case_ for slave
489 nProcs = Pstream::nProcs();
490 case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
494 // establish rootPath_/globalCase_/case_
502 // collect slave machine/pid
503 if (parRunControl_.parRun())
505 if (Pstream::master())
507 slaveProcs.setSize(Pstream::nProcs() - 1);
514 int slave=Pstream::firstSlave();
515 slave<=Pstream::lastSlave();
519 IPstream fromSlave(Pstream::scheduled, slave);
520 fromSlave >> slaveMachine >> slavePid;
522 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
527 OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
528 toMaster << hostName() << pid();
533 if (Pstream::master() && bannerEnabled)
535 Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
536 << "nProcs : " << nProcs << endl;
538 if (parRunControl_.parRun())
540 Info<< "Slaves : " << slaveProcs << nl
541 << "Pstream initialized with:" << nl
542 << " floatTransfer : " << Pstream::floatTransfer << nl
543 << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
545 << Pstream::commsTypeNames[Pstream::defaultCommsType]
550 jobInfo.add("root", rootPath_);
551 jobInfo.add("case", globalCase_);
552 jobInfo.add("nProcs", nProcs);
553 if (slaveProcs.size())
555 jobInfo.add("slaves", slaveProcs);
559 // Switch on signal trapping. We have to wait until after Pstream::init
560 // since this sets up its own ones.
561 sigFpe_.set(bannerEnabled);
562 sigInt_.set(bannerEnabled);
563 sigQuit_.set(bannerEnabled);
564 sigSegv_.set(bannerEnabled);
566 if (Pstream::master() && bannerEnabled)
569 IOobject::writeDivider(Info);
574 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
576 Foam::argList::~argList()
582 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
584 void Foam::argList::noBanner()
586 bannerEnabled = false;
590 void Foam::argList::noParallel()
592 validOptions.erase("parallel");
596 void Foam::argList::printUsage() const
599 << "Usage: " << executable_;
603 SLList<string>::iterator iter = validArgs.begin();
604 iter != validArgs.end();
608 Info<< " <" << iter().c_str() << '>';
613 HashTable<string>::iterator iter = validOptions.begin();
614 iter != validOptions.end();
618 Info<< " [-" << iter.key();
622 Info<< ' ' << iter().c_str();
628 // place help/doc/srcDoc options of the way at the end,
629 // but with an extra space to separate it a little
630 Info<< " [-help] [-doc] [-srcDoc]\n" << endl;
634 void Foam::argList::displayDoc(bool source) const
636 const dictionary& docDict = debug::controlDict().subDict("Documentation");
637 // List of doxygen documentation file indices
638 List<fileName> docIndexFiles(docDict.lookup("doxyDocIndices"));
643 forAll(docIndexFiles,idxI)
645 IFstream indexFile(docIndexFiles[idxI]);
647 if (!indexFile.good())
649 WarningIn("Foam::argList::displayDoc(bool)")
650 << "Cannot open documentation index file " << docIndexFiles[idxI]
656 dictionary indexDict(indexFile);
657 if (indexDict.found("docDir") && indexDict.found("docFiles"))
661 // the documentation directory
662 indexDict.lookup("docDir") >> docDir;
663 // read the docFiles dictionary
664 docFiles = indexDict.subDict("docFiles");
665 // prefix docDir to all doc files
666 if (docFiles.found(executable_))
669 docFile = docDir / FixedList<fileName,2>( docFiles.lookup(executable_) )[label(source)];
674 WarningIn("Foam::argList::displayDoc(bool)")
675 << "The file " << docIndexFiles[idxI]
676 << "does not contain one or both of the entries"
677 << "\"docDir\" and \"docFiles\"."
685 string docBrowser(docDict.lookup("docBrowser"));
686 if (docBrowser != "ECHO")
688 docBrowser.replaceAll("%f", docFile);
690 Info<< "Show documentation: " << docBrowser.c_str() << endl;
696 Info<< "Documentation available at: " << docFile << endl;
702 << "No documentation found for " << executable_
703 << ", but you can use -help to display the usage\n" << endl;
708 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
712 if (Pstream::master())
714 if (checkArgs && args_.size() - 1 != validArgs.size())
717 << "Wrong number of arguments, expected " << validArgs.size()
718 << " found " << args_.size() - 1 << endl;
724 forAllConstIter(HashTable<string>, options_, iter)
728 !validOptions.found(iter.key())
729 && !validParOptions.found(iter.key())
733 << "Invalid option: -" << iter.key() << endl;
749 bool Foam::argList::checkRootCase() const
751 if (!isDir(rootPath()))
755 << ": cannot open root directory " << rootPath()
761 if (!isDir(path()) && Pstream::master())
763 // Allow slaves on non-existing processor directories, created later
766 << ": cannot open case directory " << path()
776 // ************************ vim: set sw=4 sts=4 et: ************************ //