1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2004-2011 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
13 the Free Software Foundation, either version 3 of the License, or
14 (at your 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, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
27 #include "OSspecific.H"
30 #include "dictionary.H"
33 #include "labelList.H"
34 #include "regIOobject.H"
35 #include "dynamicCode.H"
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41 bool Foam::argList::bannerEnabled = true;
42 Foam::SLList<Foam::string> Foam::argList::validArgs;
43 Foam::HashTable<Foam::string> Foam::argList::validOptions;
44 Foam::HashTable<Foam::string> Foam::argList::validParOptions;
45 Foam::HashTable<Foam::string> Foam::argList::optionUsage;
46 Foam::SLList<Foam::string> Foam::argList::notes;
47 Foam::string::size_type Foam::argList::usageMin = 20;
48 Foam::string::size_type Foam::argList::usageMax = 80;
51 Foam::argList::initValidTables::initValidTables()
56 "specify alternate case directory, default is the cwd"
58 argList::addBoolOption("parallel", "run in parallel");
59 validParOptions.set("parallel", "");
62 "roots", "(dir1 .. dirN)",
63 "slave root directories for distributed running"
65 validParOptions.set("roots", "(dir1 .. dirN)");
67 argList::addBoolOption
70 "do not execute functionObjects"
73 Pstream::addValidParOptions(validParOptions);
77 Foam::argList::initValidTables dummyInitValidTables;
80 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
82 void Foam::argList::addBoolOption
88 addOption(opt, "", usage);
92 void Foam::argList::addOption
99 validOptions.set(opt, param);
102 optionUsage.set(opt, usage);
107 void Foam::argList::addUsage
115 optionUsage.erase(opt);
119 optionUsage.set(opt, usage);
124 void Foam::argList::addNote(const string& note)
133 void Foam::argList::removeOption(const word& opt)
135 validOptions.erase(opt);
136 optionUsage.erase(opt);
140 void Foam::argList::noBanner()
142 bannerEnabled = false;
146 void Foam::argList::noParallel()
148 removeOption("parallel");
149 removeOption("roots");
150 validParOptions.clear();
154 void Foam::argList::printOptionUsage
156 const label location,
160 const string::size_type textWidth = usageMax - usageMin;
161 const string::size_type strLen = str.size();
165 // minimum of 2 spaces between option and usage:
166 if (string::size_type(location) + 2 <= usageMin)
168 for (string::size_type i = location; i < usageMin; ++i)
175 // or start a new line
177 for (string::size_type i = 0; i < usageMin; ++i)
184 string::size_type pos = 0;
185 while (pos != string::npos && pos + textWidth < strLen)
187 // potential end point and next point
188 string::size_type curr = pos + textWidth - 1;
189 string::size_type next = string::npos;
191 if (isspace(str[curr]))
193 // we were lucky: ended on a space
194 next = str.find_first_not_of(" \t\n", curr);
196 else if (isspace(str[curr+1]))
198 // the next one is a space - so we are okay
199 curr++; // otherwise the length is wrong
200 next = str.find_first_not_of(" \t\n", curr);
204 // search for end of a previous word break
205 string::size_type prev = str.find_last_of(" \t\n", curr);
207 // reposition to the end of previous word if possible
208 if (prev != string::npos && prev > pos)
214 if (next == string::npos)
219 // indent following lines (not the first one)
222 for (string::size_type i = 0; i < usageMin; ++i)
228 Info<< str.substr(pos, (curr - pos)).c_str() << nl;
232 // output the remainder of the string
233 if (pos != string::npos)
235 // indent following lines (not the first one)
238 for (string::size_type i = 0; i < usageMin; ++i)
244 Info<< str.substr(pos).c_str() << nl;
254 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
256 // convert argv -> args_
257 // transform sequences with "(" ... ")" into string lists in the process
258 bool Foam::argList::regroupArgv(int& argc, char**& argv)
264 // note: we also re-write directly into args_
265 // and use a second pass to sort out args/options
266 for (int argI = 0; argI < argc; ++argI)
268 if (strcmp(argv[argI], "(") == 0)
273 else if (strcmp(argv[argI], ")") == 0)
281 args_[nArgs++] = tmpString;
287 args_[nArgs++] = argv[argI];
292 // quote each string element
294 tmpString += argv[argI];
299 args_[nArgs++] = argv[argI];
303 if (tmpString.size())
305 args_[nArgs++] = tmpString;
308 args_.setSize(nArgs);
314 void Foam::argList::getRootCase()
318 // [-case dir] specified
319 HashTable<string>::const_iterator iter = options_.find("case");
321 if (iter != options_.end())
326 if (casePath.empty() || casePath == ".")
328 // handle degenerate form and '-case .' like no -case specified
330 options_.erase("case");
332 else if (!casePath.isAbsolute() && casePath.name() == "..")
334 // avoid relative cases ending in '..' - makes for very ugly names
335 casePath = cwd()/casePath;
341 // nothing specified, use the current dir
345 rootPath_ = casePath.path();
346 globalCase_ = casePath.name();
350 // Set the case and case-name as an environment variable
351 if (rootPath_.isAbsolute())
353 // absolute path - use as-is
354 setEnv("FOAM_CASE", rootPath_/globalCase_, true);
355 setEnv("FOAM_CASENAME", globalCase_, true);
359 // qualify relative path
360 casePath = cwd()/rootPath_/globalCase_;
363 setEnv("FOAM_CASE", casePath, true);
364 setEnv("FOAM_CASENAME", casePath.name(), true);
369 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
371 Foam::argList::argList
382 // Check if this run is a parallel run by searching for any parallel option
383 // If found call runPar which might filter argv
384 for (int argI = 0; argI < argc; ++argI)
386 if (argv[argI][0] == '-')
388 const char *optionName = &argv[argI][1];
390 if (validParOptions.found(optionName))
392 parRunControl_.runPar(argc, argv);
398 // convert argv -> args_ and capture ( ... ) lists
399 // for normal arguments and for options
400 regroupArgv(argc, argv);
402 // Get executable name
403 args_[0] = fileName(argv[0]);
404 executable_ = fileName(argv[0]).name();
406 // Check arguments and options, we already have argv[0]
408 string argListString = args_[0];
410 for (int argI = 1; argI < args_.size(); ++argI)
412 argListString += ' ';
413 argListString += args_[argI];
415 if (args_[argI][0] == '-')
417 const char *optionName = &args_[argI][1];
422 validOptions.found(optionName)
423 && !validOptions[optionName].empty()
426 validParOptions.found(optionName)
427 && !validParOptions[optionName].empty()
432 if (argI >= args_.size())
435 <<"Option '-" << optionName
436 << "' requires an argument" << endl;
441 argListString += ' ';
442 argListString += args_[argI];
443 options_.insert(optionName, args_[argI]);
447 options_.insert(optionName, "");
454 args_[nArgs] = args_[argI];
460 args_.setSize(nArgs);
462 // Help/documentation options:
463 // -help print the usage
464 // -doc display application documentation in browser
465 // -srcDoc display source code in browser
468 options_.found("help")
469 || options_.found("doc")
470 || options_.found("srcDoc")
473 if (options_.found("help"))
478 // only display one or the other
479 if (options_.found("srcDoc"))
483 else if (options_.found("doc"))
491 // Print the usage message and exit if the number of arguments is incorrect
492 if (!check(checkArgs, checkOpts))
498 string dateString = clock::date();
499 string timeString = clock::clockTime();
501 // Print the banner once only for parallel runs
502 if (Pstream::master() && bannerEnabled)
504 IOobject::writeBanner(Info, true)
505 << "Build : " << Foam::FOAMbuild << nl
506 << "Exec : " << argListString.c_str() << nl
507 << "Date : " << dateString.c_str() << nl
508 << "Time : " << timeString.c_str() << nl
509 << "Host : " << hostName() << nl
510 << "PID : " << pid() << endl;
513 jobInfo.add("startDate", dateString);
514 jobInfo.add("startTime", timeString);
515 jobInfo.add("userName", userName());
516 jobInfo.add("foamVersion", word(FOAMversion));
517 jobInfo.add("code", executable_);
518 jobInfo.add("argList", argListString);
519 jobInfo.add("currentDir", cwd());
520 jobInfo.add("PPID", ppid());
521 jobInfo.add("PGID", pgid());
523 // add build information - only use the first word
525 std::string build(Foam::FOAMbuild);
526 std::string::size_type found = build.find(' ');
527 if (found != std::string::npos)
531 jobInfo.add("foamBuild", build);
535 // Case is a single processor run unless it is running parallel
538 // Roots if running distributed
542 // If this actually is a parallel run
543 if (parRunControl_.parRun())
546 if (Pstream::master())
548 // establish rootPath_/globalCase_/case_ for master
551 // See if running distributed (different roots for different procs)
552 label dictNProcs = -1;
555 if (options_.found("roots"))
558 IStringStream is(options_["roots"]);
559 roots = readList<fileName>(is);
561 if (roots.size() != 1)
563 dictNProcs = roots.size()+1;
568 source = rootPath_/globalCase_/"system/decomposeParDict";
569 IFstream decompDictStream(source);
571 if (!decompDictStream.good())
575 << decompDictStream.name()
579 dictionary decompDict(decompDictStream);
581 dictNProcs = readLabel
583 decompDict.lookup("numberOfSubdomains")
586 if (decompDict.lookupOrDefault("distributed", false))
588 decompDict.lookup("roots") >> roots;
593 // when a single root is specified, use it for all processes
594 if (roots.size() == 1)
596 const fileName rootName(roots[0]);
597 roots.setSize(Pstream::nProcs()-1, rootName);
599 // adjust dictNProcs for command-line '-roots' option
602 dictNProcs = roots.size()+1;
607 // Check number of processors.
608 // nProcs => number of actual procs
609 // dictNProcs => number of procs specified in decompositionDict
610 // nProcDirs => number of processor directories
611 // (n/a when running distributed)
613 // - normal running : nProcs = dictNProcs = nProcDirs
614 // - decomposition to more processors : nProcs = dictNProcs
615 // - decomposition to fewer processors : nProcs = nProcDirs
616 if (dictNProcs > Pstream::nProcs())
620 << " specifies " << dictNProcs
621 << " processors but job was started with "
622 << Pstream::nProcs() << " processors."
630 if (roots.size() != Pstream::nProcs()-1)
633 << "number of entries in roots "
635 << " is not equal to the number of slaves "
636 << Pstream::nProcs()-1
645 // Distribute the master's argument list (with new root)
646 bool hadCaseOpt = options_.found("case");
649 int slave = Pstream::firstSlave();
650 slave <= Pstream::lastSlave();
654 options_.set("case", roots[slave-1]/globalCase_);
656 OPstream toSlave(Pstream::scheduled, slave);
657 toSlave << args_ << options_;
659 options_.erase("case");
661 // restore [-case dir]
664 options_.set("case", rootPath_/globalCase_);
669 // Possibly going to fewer processors.
670 // Check if all procDirs are there.
671 if (dictNProcs < Pstream::nProcs())
678 rootPath_/globalCase_/"processor"
684 if (nProcDirs != Pstream::nProcs())
687 << "number of processor directories = "
689 << " is not equal to the number of processors = "
695 // Distribute the master's argument list (unaltered)
698 int slave = Pstream::firstSlave();
699 slave <= Pstream::lastSlave();
703 OPstream toSlave(Pstream::scheduled, slave);
704 toSlave << args_ << options_;
710 // Collect the master's argument list
711 IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
712 fromMaster >> args_ >> options_;
714 // establish rootPath_/globalCase_/case_ for slave
718 nProcs = Pstream::nProcs();
719 case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
723 // establish rootPath_/globalCase_/case_
731 // collect slave machine/pid
732 if (parRunControl_.parRun())
734 if (Pstream::master())
736 slaveProcs.setSize(Pstream::nProcs() - 1);
743 int slave = Pstream::firstSlave();
744 slave <= Pstream::lastSlave();
748 IPstream fromSlave(Pstream::scheduled, slave);
749 fromSlave >> slaveMachine >> slavePid;
751 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
756 OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
757 toMaster << hostName() << pid();
762 if (Pstream::master() && bannerEnabled)
764 Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
765 << "nProcs : " << nProcs << endl;
767 if (parRunControl_.parRun())
769 Info<< "Slaves : " << slaveProcs << nl;
772 Info<< "Roots : " << roots << nl;
774 Info<< "Pstream initialized with:" << nl
775 << " floatTransfer : " << Pstream::floatTransfer << nl
776 << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
778 << Pstream::commsTypeNames[Pstream::defaultCommsType]
783 jobInfo.add("root", rootPath_);
784 jobInfo.add("case", globalCase_);
785 jobInfo.add("nProcs", nProcs);
786 if (slaveProcs.size())
788 jobInfo.add("slaves", slaveProcs);
792 jobInfo.add("roots", roots);
796 // Switch on signal trapping. We have to wait until after Pstream::init
797 // since this sets up its own ones.
798 sigFpe_.set(bannerEnabled);
799 sigInt_.set(bannerEnabled);
800 sigQuit_.set(bannerEnabled);
801 sigSegv_.set(bannerEnabled);
805 Info<< "fileModificationChecking : "
806 << "Monitoring run-time modified files using "
807 << regIOobject::fileCheckTypesNames
809 regIOobject::fileModificationChecking
813 Info<< "allowSystemOperations : ";
814 if (dynamicCode::allowSystemOperations)
816 Info<< "Allowing user-supplied system call operations" << endl;
820 Info<< "Disallowing user-supplied system call operations" << endl;
824 if (Pstream::master() && bannerEnabled)
827 IOobject::writeDivider(Info);
832 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
834 Foam::argList::~argList()
840 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
842 bool Foam::argList::setOption(const word& opt, const string& param)
844 bool changed = false;
846 // only allow valid options
847 if (validOptions.found(opt))
849 // some options are to be protected
858 <<"used argList::setOption on a protected option: '"
859 << opt << "'" << endl;
863 if (validOptions[opt].empty())
868 // disallow change of type
870 <<"used argList::setOption to change bool to non-bool: '"
871 << opt << "'" << endl;
876 // did not previously exist
877 changed = !options_.found(opt);
885 // disallow change of type
887 <<"used argList::setOption to change non-bool to bool: '"
888 << opt << "'" << endl;
893 // existing value needs changing, or did not previously exist
894 changed = options_.found(opt) ? options_[opt] != param : true;
901 <<"used argList::setOption on an invalid option: '"
902 << opt << "'" << nl << "allowed are the following:"
903 << validOptions << endl;
907 // set/change the option as required
910 options_.set(opt, param);
917 bool Foam::argList::unsetOption(const word& opt)
919 // only allow valid options
920 if (validOptions.found(opt))
922 // some options are to be protected
931 <<"used argList::unsetOption on a protected option: '"
932 << opt << "'" << endl;
936 // remove the option, return true if state changed
937 return options_.erase(opt);
942 <<"used argList::unsetOption on an invalid option: '"
943 << opt << "'" << nl << "allowed are the following:"
944 << validOptions << endl;
952 void Foam::argList::printNotes() const
954 // output notes directly - no automatic text wrapping
958 forAllConstIter(SLList<string>, notes, iter)
960 Info<< iter().c_str() << nl;
966 void Foam::argList::printUsage() const
968 Info<< "\nUsage: " << executable_ << " [OPTIONS]";
970 forAllConstIter(SLList<string>, validArgs, iter)
972 Info<< " <" << iter().c_str() << '>';
975 Info<< "\noptions:\n";
977 wordList opts = validOptions.sortedToc();
980 const word& optionName = opts[optI];
982 HashTable<string>::const_iterator iter = validOptions.find(optionName);
983 Info<< " -" << optionName;
984 label len = optionName.size() + 3; // length includes leading ' -'
988 // length includes space and between option/param and '<>'
989 len += iter().size() + 3;
990 Info<< " <" << iter().c_str() << '>';
993 HashTable<string>::const_iterator usageIter =
994 optionUsage.find(optionName);
996 if (usageIter != optionUsage.end())
1011 // place srcDoc/doc/help options at the end
1017 "display source code in browser"
1024 "display application documentation in browser"
1038 <<"Using: OpenFOAM-" << Foam::FOAMversion
1039 << " (see www.OpenFOAM.com)" << nl
1040 <<"Build: " << Foam::FOAMbuild << nl
1045 void Foam::argList::displayDoc(bool source) const
1047 const dictionary& docDict = debug::controlDict().subDict("Documentation");
1048 List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
1049 List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
1051 // for source code: change foo_8C.html to foo_8C_source.html
1054 forAll(docExts, extI)
1056 docExts[extI].replace(".", "_source.");
1063 forAll(docDirs, dirI)
1065 forAll(docExts, extI)
1067 docFile = docDirs[dirI]/executable_ + docExts[extI];
1070 if (isFile(docFile))
1084 string docBrowser = getEnv("FOAM_DOC_BROWSER");
1085 if (docBrowser.empty())
1087 docDict.lookup("docBrowser") >> docBrowser;
1089 // can use FOAM_DOC_BROWSER='application file://%f' if required
1090 docBrowser.replaceAll("%f", docFile);
1092 Info<< "Show documentation: " << docBrowser.c_str() << endl;
1099 << "No documentation found for " << executable_
1100 << ", but you can use -help to display the usage\n" << endl;
1105 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
1109 if (Pstream::master())
1111 if (checkArgs && args_.size() - 1 != validArgs.size())
1114 << "Wrong number of arguments, expected " << validArgs.size()
1115 << " found " << args_.size() - 1 << endl;
1121 forAllConstIter(HashTable<string>, options_, iter)
1125 !validOptions.found(iter.key())
1126 && !validParOptions.found(iter.key())
1130 << "Invalid option: -" << iter.key() << endl;
1146 bool Foam::argList::checkRootCase() const
1148 if (!isDir(rootPath()))
1152 << ": cannot open root directory " << rootPath()
1158 if (!isDir(path()) && Pstream::master())
1160 // Allow slaves on non-existing processor directories, created later
1163 << ": cannot open case directory " << path()
1173 // ************************************************************************* //