Formatting
[foam-extend-3.2.git] / src / foam / global / argList / argList.C
blob6fbc5f472a67166570f54a7427e2220ab705897b
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | foam-extend: Open Source CFD
4    \\    /   O peration     | Version:     3.2
5     \\  /    A nd           | Web:         http://www.foam-extend.org
6      \\/     M anipulation  | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
8 License
9     This file is part of foam-extend.
11     foam-extend 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 3 of the License, or (at your
14     option) any later version.
16     foam-extend is distributed in the hope that it will be useful, but
17     WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19     General Public License for more details.
21     You should have received a copy of the GNU General Public License
22     along with foam-extend.  If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "argList.H"
27 #include "OSspecific.H"
28 #include "clock.H"
29 #include "IFstream.H"
30 #include "dictionary.H"
31 #include "Switch.H"
32 #include "IOobject.H"
33 #include "JobInfo.H"
34 #include "labelList.H"
35 #include "SortableList.H"
37 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39 Foam::SLList<Foam::string> Foam::argList::validArgs;
40 Foam::HashTable<Foam::string> Foam::argList::validOptions;
41 Foam::HashTable<Foam::string> Foam::argList::validParOptions;
42 Foam::word Foam::argList::appDictName_("");
43 bool Foam::argList::bannerEnabled(true);
46 Foam::argList::initValidTables::initValidTables()
48     validOptions.set("case", "dir");
49     validOptions.set("parallel", "");
50     validParOptions.set("parallel", "");
51     validOptions.set("noFunctionObjects", "");
53     // Add the parameters for modifying the controlDict
54     // switches from the command-line
56     // Instantiate a NamedEnum for the controlDict switches names
57     const NamedEnum
58     <
59         debug::globalControlDictSwitchSet,
60         debug::DIM_GLOBAL_CONTROL_DICT_SWITCH_SET
61     >
62     globalControlDictSwitchSetNames;
64     forAll (globalControlDictSwitchSetNames, gI)
65     {
66         word switchSetName = globalControlDictSwitchSetNames.names[gI];
67         validOptions.set(switchSetName, "key1=val1,key2=val2,...");
68     }
70     validOptions.set("dumpControlSwitches", "");
72     Pstream::addValidParOptions(validParOptions);
76 Foam::argList::initValidTables dummyInitValidTables;
79 // convert argv -> args_
80 // transform sequences with "(" ... ")" into string lists in the process
81 bool Foam::argList::regroupArgv(int& argc, char**& argv)
83     int nArgs = 0;
84     int listDepth = 0;
85     string tmpString;
87     // note: we also re-write directly into args_
88     // and use a second pass to sort out args/options
89     for (int argI = 0; argI < argc; argI++)
90     {
91         if (strcmp(argv[argI], "(") == 0)
92         {
93             listDepth++;
94             tmpString += "(";
95         }
96         else if (strcmp(argv[argI], ")") == 0)
97         {
98             if (listDepth)
99             {
100                 listDepth--;
101                 tmpString += ")";
102                 if (listDepth == 0)
103                 {
104                     args_[nArgs++] = tmpString;
105                     tmpString.clear();
106                 }
107             }
108             else
109             {
110                 args_[nArgs++] = argv[argI];
111             }
112         }
113         else if (listDepth)
114         {
115             // quote each string element
116             tmpString += "\"";
117             tmpString += argv[argI];
118             tmpString += "\"";
119         }
120         else
121         {
122             args_[nArgs++] = argv[argI];
123         }
124     }
126     if (tmpString.size())
127     {
128         args_[nArgs++] = tmpString;
129     }
131     args_.setSize(nArgs);
133     return nArgs < argc;
137 // get rootPath_/globalCase_ from one of the following forms
138 //   * [-case dir]
139 //   * cwd
141 // Also export FOAM_CASE and FOAM_CASENAME environment variables
142 // so they can be used immediately (eg, in decomposeParDict)
144 void Foam::argList::getRootCase()
146     fileName casePath;
148     // [-case dir] specified
149     HashTable<string>::iterator iter = options_.find("case");
151     if (iter != options_.end())
152     {
153         casePath = iter();
154         casePath.clean();
156         if (casePath.empty() || casePath == ".")
157         {
158             // handle degenerate form and '-case .' like no -case specified
159             casePath = cwd();
160             options_.erase("case");
161         }
162         else if (casePath[0] != '/' && casePath.name() == "..")
163         {
164             // avoid relative cases ending in '..' - makes for very ugly names
165             casePath = cwd()/casePath;
166             casePath.clean();
167         }
168     }
169     else
170     {
171         // nothing specified, use the current dir
172         casePath = cwd();
173     }
175     rootPath_ = casePath.path();
176     globalCase_ = casePath.name();
177     case_ = globalCase_;
179     // Set the case and case-name as an environment variable
180     if (rootPath_[0] == '/')
181     {
182         // Absolute path - use as-is
183         setEnv("FOAM_CASE", rootPath_/globalCase_, true);
184         setEnv("FOAM_CASENAME", globalCase_, true);
185     }
186     else
187     {
188         // Qualify relative path
189         fileName casePath = cwd()/rootPath_/globalCase_;
190         casePath.clean();
192         setEnv("FOAM_CASE", casePath, true);
193         setEnv("FOAM_CASENAME", casePath.name(), true);
194     }
198 Foam::stringList::subList Foam::argList::additionalArgs() const
200     return stringList::subList(args_, args_.size() - 1, 1);
204 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
206 Foam::argList::argList
208     int& argc,
209     char**& argv,
210     bool checkArgs,
211     bool checkOpts
214     args_(argc),
215     options_(argc)
217     // Check if this run is a parallel run by searching for any parallel option
218     // If found call runPar (might filter argv)
219     for (int argI = 0; argI < argc; argI++)
220     {
221         if (argv[argI][0] == '-')
222         {
223             const char *optionName = &argv[argI][1];
225             if (validParOptions.found(optionName))
226             {
227                 parRunControl_.runPar(argc, argv);
228                 break;
229             }
230         }
231     }
233     // convert argv -> args_ and capture ( ... ) lists
234     // for normal arguments and for options
235     regroupArgv(argc, argv);
237     // Get executable name
238     args_[0] = fileName(argv[0]);
239     executable_ = fileName(argv[0]).name();
241     // Check arguments and options, we already have argv[0]
242     int nArgs = 1;
243     string argListString = args_[0];
245     for (int argI = 1; argI < args_.size(); argI++)
246     {
247         argListString += ' ';
248         argListString += args_[argI];
250         if (args_[argI][0] == '-')
251         {
252             const char *optionName = &args_[argI][1];
254             if
255             (
256                 (
257                     validOptions.found(optionName)
258                  && validOptions[optionName] != ""
259                 )
260              || (
261                     validParOptions.found(optionName)
262                  && validParOptions[optionName] != ""
263                 )
264             )
265             {
266                 argI++;
267                 if (argI >= args_.size())
268                 {
269                     FatalError
270                         << "option " << "'-" << optionName << '\''
271                         << " requires an argument"
272                         << exit(FatalError);
273                 }
275                 argListString += ' ';
276                 argListString += args_[argI];
277                 options_.insert(optionName, args_[argI]);
278             }
279             else
280             {
281                 options_.insert(optionName, "");
282             }
283         }
284         else
285         {
286             if (nArgs != argI)
287             {
288                 args_[nArgs] = args_[argI];
289             }
290             nArgs++;
291         }
292     }
294     args_.setSize(nArgs);
296     // Help/documentation options:
297     //   -help    print the usage
298     //   -doc     display application documentation in browser
299     //   -srcDoc  display source code in browser
300     if
301     (
302         options_.found("help")
303      || options_.found("doc")
304      || options_.found("srcDoc")
305     )
306     {
307         if (options_.found("help"))
308         {
309             printUsage();
310         }
312         // only display one or the other
313         if (options_.found("srcDoc"))
314         {
315             displayDoc(true);
316         }
317         else if (options_.found("doc"))
318         {
319             displayDoc(false);
320         }
322         ::exit(0);
323     }
325     // Print the usage message and exit if the number of arguments is incorrect
326     if (!check(checkArgs, checkOpts))
327     {
328         FatalError.exit();
329     }
331     // From here, we consider the command-line arguments to be valid
333     string dateString = clock::date();
334     string timeString = clock::clockTime();
335     fileName ctrlDict = debug::controlDict().name();
336     string ctrlDictString = ctrlDict.path()/ctrlDict.name();
338     // Print the banner once only for parallel runs
339     if (Pstream::master() && bannerEnabled)
340     {
341         IOobject::writeBanner(Info, true)
342             << "Build    : " << Foam::FOAMbuild << nl
343             << "Exec     : " << argListString.c_str() << nl
344             << "Date     : " << dateString.c_str() << nl
345             << "Time     : " << timeString.c_str() << nl
346             << "Host     : " << hostName() << nl
347             << "PID      : " << pid()
348             << endl;
349     }
351     jobInfo.add("startDate", dateString);
352     jobInfo.add("startTime", timeString);
353     jobInfo.add("userName", userName());
354     jobInfo.add("foamVersion", word(FOAMversion));
355     jobInfo.add("foamBuild", Foam::FOAMbuild);
356     jobInfo.add("code", executable_);
357     jobInfo.add("argList", argListString);
358     jobInfo.add("currentDir", cwd());
359     jobInfo.add("PPID", ppid());
360     jobInfo.add("PGID", pgid());
363     // Case is a single processor run unless it is running parallel
364     int nProcs = 1;
366     // If this actually is a parallel run
367     if (parRunControl_.parRun())
368     {
369         // For the master
370         if (Pstream::master())
371         {
372             // establish rootPath_/globalCase_/case_ for master
373             getRootCase();
375             IFstream decompDictStream
376             (
377                 rootPath_/globalCase_/"system/decomposeParDict"
378             );
380             if (!decompDictStream.good())
381             {
382                 FatalError
383                     << "Cannot read "
384                     << decompDictStream.name()
385                     << exit(FatalError);
386             }
388             dictionary decompDict(decompDictStream);
390             label dictNProcs
391             (
392                 readLabel
393                 (
394                     decompDict.lookup("numberOfSubdomains")
395                 )
396             );
398             // Check number of processors.
399             // nProcs     => number of actual procs
400             // dictNProcs => number of procs specified in decompositionDict
401             // nProcDirs  => number of processor directories
402             //               (n/a when running distributed)
403             //
404             // - normal running : nProcs = dictNProcs = nProcDirs
405             // - decomposition to more  processors : nProcs = dictNProcs
406             // - decomposition to fewer processors : nProcs = nProcDirs
407             if (dictNProcs > Pstream::nProcs())
408             {
409                 FatalError
410                     << decompDictStream.name()
411                     << " specifies " << dictNProcs
412                     << " processors but job was started with "
413                     << Pstream::nProcs() << " processors."
414                     << exit(FatalError);
415             }
417             // distributed data
418             if (decompDict.lookupOrDefault<Switch>("distributed", false))
419             {
420                 fileNameList roots;
421                 decompDict.lookup("roots") >> roots;
423                 if (roots.size() != Pstream::nProcs() - 1)
424                 {
425                     FatalError
426                         << "number of entries in decompositionDict::roots"
427                         << " is not equal to the number of slaves "
428                         << Pstream::nProcs() - 1
429                         << exit(FatalError);
430                 }
432                 // Distribute the master's argument list (with new root)
433                 bool hadCaseOpt = options_.found("case");
434                 for
435                 (
436                     int slave = Pstream::firstSlave();
437                     slave <= Pstream::lastSlave();
438                     slave++
439                 )
440                 {
441                     options_.set
442                     (
443                         "case",
444                         fileName(roots[slave-1])/globalCase_
445                     );
447                     OPstream toSlave(Pstream::scheduled, slave);
448                     toSlave << args_ << options_;
449                 }
450                 options_.erase("case");
452                 // restore [-case dir]
453                 if (hadCaseOpt)
454                 {
455                     options_.set("case", rootPath_/globalCase_);
456                 }
457             }
458             else
459             {
460                 // Possibly going to fewer processors.
461                 // Check if all procDirs are there.
462                 if (dictNProcs < Pstream::nProcs())
463                 {
464                     label nProcDirs = 0;
465                     while
466                     (
467                         isDir
468                         (
469                             rootPath_/globalCase_/"processor"
470                           + name(++nProcDirs)
471                         )
472                     )
473                     {}
475                     if (nProcDirs != Pstream::nProcs())
476                     {
477                         FatalError
478                             << "number of processor directories = "
479                             << nProcDirs
480                             << " is not equal to the number of processors = "
481                             << Pstream::nProcs()
482                             << exit(FatalError);
483                     }
484                 }
486                 // Distribute the master's argument list (unaltered)
487                 for
488                 (
489                     int slave = Pstream::firstSlave();
490                     slave <= Pstream::lastSlave();
491                     slave++
492                 )
493                 {
494                     OPstream toSlave(Pstream::scheduled, slave);
495                     toSlave << args_ << options_;
496                 }
497             }
498         }
499         else
500         {
501             // Collect the master's argument list
502             IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
503             fromMaster >> args_ >> options_;
505             // establish rootPath_/globalCase_/case_ for slave
506             getRootCase();
507         }
509         nProcs = Pstream::nProcs();
510         case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
511     }
512     else
513     {
514         // establish rootPath_/globalCase_/case_
515         getRootCase();
516         case_ = globalCase_;
517     }
519     // Managing the overrides for the global control switches:
520     //
521     // Here is the order of precedence for the definition/overriding of the
522     // control switches, from lowest to highest:
523     //  - source code definitions from the various libraries/solvers
524     //  - file specified by the env. variable FOAM_GLOBAL_CONTROLDICT
525     //  - case's system/controlDict file
526     //  - command-line parameters
527     //
528     // First, we allow the users to specify the location of a centralized
529     // global controlDict dictionary using the environment variable
530     // FOAM_GLOBAL_CONTROLDICT.
531     fileName optionalGlobControlDictFileName =
532     getEnv("FOAM_GLOBAL_CONTROLDICT");
534     if (optionalGlobControlDictFileName.size() )
535     {
536         debug::updateCentralDictVars
537         (
538             optionalGlobControlDictFileName,
539             Pstream::master() && bannerEnabled
540         );
541     }
543     // Now that the rootPath_/globalCase_ directory is known (following the
544     // call to getRootCase()), we grab any global control switches overrides
545     // from the current case's controlDict.
547     debug::updateCentralDictVars
548     (
549         rootPath_/globalCase_/"system/controlDict",
550         Pstream::master() && bannerEnabled
551     );
553     // Finally, a command-line override for central controlDict's variables.
554     // This is the ultimate override for the global control switches.
556     // Instantiate a NamedEnum for the controlDict switches names
557     const NamedEnum
558     <
559         debug::globalControlDictSwitchSet,
560         debug::DIM_GLOBAL_CONTROL_DICT_SWITCH_SET
561     >
562     globalControlDictSwitchSetNames;
564     forAll (globalControlDictSwitchSetNames, gI)
565     {
566         word switchSetName = globalControlDictSwitchSetNames.names[gI];
568         if (optionFound(switchSetName))
569         {
570             debug::updateCentralDictVars
571             (
572                 globalControlDictSwitchSetNames[switchSetName],
573                 option(switchSetName)
574             );
575         }
576     }
578     if ( optionFound("dumpControlSwitches") )
579     {
580         if (Pstream::master())
581         {
582             // Dumping the application's control switches.
583             // We dump the full information to the console using a standard
584             // dictionary format, so one can copy/paste this information
585             //  directly into a case's system/controlDict file to
586             //  override some switches values without having to always
587             // use the command-line options.
588             debug::dumpControlSwitchesToConsole();
589         }
591         ::exit(0);
592     }
594     wordList slaveProcs;
596     // collect slave machine/pid
597     if (parRunControl_.parRun())
598     {
599         if (Pstream::master())
600         {
601             slaveProcs.setSize(Pstream::nProcs() - 1);
602             word slaveMachine;
603             label slavePid;
605             label procI = 0;
606             for
607             (
608                 int slave = Pstream::firstSlave();
609                 slave <= Pstream::lastSlave();
610                 slave++
611             )
612             {
613                 IPstream fromSlave(Pstream::scheduled, slave);
614                 fromSlave >> slaveMachine >> slavePid;
616                 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
617             }
618         }
619         else
620         {
621             OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
622             toMaster << hostName() << pid();
623         }
624     }
627     if (Pstream::master() && bannerEnabled)
628     {
629         Info<< "Case     : " << (rootPath_/globalCase_).c_str() << nl
630             << "nProcs   : " << nProcs << endl;
632         if (parRunControl_.parRun())
633         {
634             Info<< "Slaves : " << slaveProcs << nl
635                 << "Pstream initialized with:" << nl
636                 << "    floatTransfer     : "
637                 << Pstream::floatTransfer << nl
638                 << "    nProcsSimpleSum   : "
639                 << Pstream::nProcsSimpleSum() << nl
640                 << "    commsType         : "
641                 << Pstream::commsTypeNames[Pstream::defaultCommsType()]
642                 << endl;
643         }
644     }
646     jobInfo.add("root", rootPath_);
647     jobInfo.add("case", globalCase_);
648     jobInfo.add("nProcs", nProcs);
649     if (slaveProcs.size())
650     {
651         jobInfo.add("slaves", slaveProcs);
652     }
653     jobInfo.write();
655     // Switch on signal trapping. We have to wait until after Pstream::init
656     // since this sets up its own ones.
657     sigFpe_.set(bannerEnabled);
658     sigInt_.set(bannerEnabled);
659     sigQuit_.set(bannerEnabled);
660     sigSegv_.set(bannerEnabled);
662     if (Pstream::master() && bannerEnabled)
663     {
664         Info<< endl;
665         IOobject::writeDivider(Info);
666     }
668     // If the macro AppSpecificDictionary is used, one can
669     // modify the application-specific dictionnary using the
670     // command-line parameter -appDict
671     if (appDictName_ != "")
672     {
673         optionReadIfPresent("appDict", appDictName_);
674     }
678 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
680 Foam::argList::~argList()
682     jobInfo.end();
686 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
688 void Foam::argList::noBanner()
690     bannerEnabled = false;
694 void Foam::argList::noParallel()
696     validOptions.erase("parallel");
700 void Foam::argList::printUsage() const
702     Info<< nl
703         << "Usage: " << executable_;
705     for
706     (
707         SLList<string>::iterator iter = validArgs.begin();
708         iter != validArgs.end();
709         ++iter
710     )
711     {
712         Info<< " <" << iter().c_str() << '>';
713     }
715     int i = 0;
716     SortableList<Foam::string> sortedValidOptions(validOptions.size());
718     for
719     (
720         HashTable<string>::iterator iter = validOptions.begin();
721         iter != validOptions.end();
722         ++iter, ++i
723     )
724     {
725         OStringStream keyValuePair;
726         keyValuePair << "[-" << iter.key();
728         if (iter().size())
729         {
730             keyValuePair<< ' ' << iter().c_str();
731         }
733         keyValuePair<< ']';
734         sortedValidOptions[i]= keyValuePair.str();
735     }
736     sortedValidOptions.sort();
738     forAll (sortedValidOptions, sI)
739     Info<< " " << sortedValidOptions[sI].c_str();
741     // place help/doc/srcDoc options of the way at the end,
742     // but with an extra space to separate it a little
743     Info<< "  [-help] [-doc] [-srcDoc]\n" << endl;
747 void Foam::argList::displayDoc(bool source) const
749     const dictionary& docDict = debug::controlDict().subDict("Documentation");
750     List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
751     List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
753     // for source code: change foo_8C.html to foo_8C_source.html
754     if (source)
755     {
756         forAll (docExts, extI)
757         {
758             docExts[extI].replace(".", "_source.");
759         }
760     }
762     fileName docFile;
763     bool found = false;
765     forAll (docDirs, dirI)
766     {
767         forAll (docExts, extI)
768         {
769             docFile = docDirs[dirI]/executable_ + docExts[extI];
770             docFile.expand();
772             if (isFile(docFile))
773             {
774                 found = true;
775                 break;
776             }
777         }
778         if (found)
779         {
780             break;
781         }
782     }
784     if (found)
785     {
786         string docBrowser(docDict.lookup("docBrowser"));
787         docBrowser.replaceAll("%f", docFile);
789         Info<< "Show documentation: " << docBrowser.c_str() << endl;
791         system(docBrowser);
792     }
793     else
794     {
795         Info<< nl
796             << "No documentation found for " << executable_
797             << ", but you can use -help to display the usage\n" << endl;
798     }
802 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
804     bool ok = true;
806     if (Pstream::master())
807     {
808         if (checkArgs && args_.size() - 1 != validArgs.size())
809         {
810             FatalError
811                 << "Wrong number of arguments, expected " << validArgs.size()
812                 << " found " << args_.size() - 1 << endl;
813             ok = false;
814         }
816         if (checkOpts)
817         {
818             forAllConstIter(HashTable<string>, options_, iter)
819             {
820                 if
821                 (
822                     !validOptions.found(iter.key())
823                  && !validParOptions.found(iter.key())
824                 )
825                 {
826                     FatalError
827                         << "Invalid option: -" << iter.key() << endl;
828                     ok = false;
829                 }
830             }
831         }
833         if (!ok)
834         {
835             printUsage();
836         }
837     }
839     return ok;
843 bool Foam::argList::checkRootCase() const
845     if (!isDir(rootPath()))
846     {
847         FatalError
848             << executable_
849             << ": cannot open root directory " << rootPath()
850             << endl;
852         return false;
853     }
855     if (!isDir(path()) && Pstream::master())
856     {
857         // Allow slaves on non-existing processor directories, created later
858         FatalError
859             << executable_
860             << ": cannot open case directory " << path()
861             << endl;
863         return false;
864     }
866     return true;
870 // ************************************************************************* //