initial commit for version 1.6.x patch release
[OpenFOAM-1.6.x.git] / src / OpenFOAM / global / argList / argList.C
blob37caa6ddb3cec81d85afdc60507e7c2288d88bdb
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 1991-2009 OpenCFD Ltd.
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
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
19     for more details.
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 \*---------------------------------------------------------------------------*/
27 #include "argList.H"
28 #include "OSspecific.H"
29 #include "clock.H"
30 #include "IFstream.H"
31 #include "dictionary.H"
32 #include "Switch.H"
33 #include "IOobject.H"
34 #include "JobInfo.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)
63     int level = 0;
64     int nArgs = 0;
65     string tmpString;
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++)
70     {
71         if (strcmp(argv[argi], "(") == 0)
72         {
73             level++;
74             tmpString += "(";
75         }
76         else if (strcmp(argv[argi], ")") == 0)
77         {
78             if (level >= 1)
79             {
80                 level--;
81                 tmpString += ")";
82                 if (level == 0)
83                 {
84                     args_[nArgs++] = tmpString;
85                     tmpString.clear();
86                 }
87             }
88             else
89             {
90                 args_[nArgs++] = argv[argi];
91             }
92         }
93         else if (level)
94         {
95             // quote each string element
96             tmpString += "\"";
97             tmpString += argv[argi];
98             tmpString += "\"";
99         }
100         else
101         {
102             args_[nArgs++] = argv[argi];
103         }
104     }
106     if (tmpString.size())
107     {
108         args_[nArgs++] = tmpString;
109     }
111     args_.setSize(nArgs);
113     return nArgs < argc;
117 // get rootPath_ / globalCase_ from one of the following forms
118 //   * [-case dir]
119 //   * cwd
120 void Foam::argList::getRootCase()
122     fileName casePath;
124     // [-case dir] specified
125     HashTable<string>::iterator iter = options_.find("case");
127     if (iter != options_.end())
128     {
129         casePath = iter();
130         casePath.clean();
132         // handle degenerate form and '-case .' like no -case specified
133         if (casePath.empty() || casePath == ".")
134         {
135             casePath = cwd();
136             options_.erase("case");
137         }
138     }
139     else
140     {
141         // nothing specified, use the current dir
142         casePath = cwd();
143     }
145     rootPath_   = casePath.path();
146     globalCase_ = casePath.name();
147     case_       = globalCase_;
151 Foam::stringList::subList Foam::argList::additionalArgs() const
153     return stringList::subList(args_, args_.size() - 1, 1);
157 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
159 Foam::argList::argList
161     int& argc,
162     char**& argv,
163     bool checkArgs,
164     bool checkOpts
167     args_(argc),
168     options_(argc)
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++)
173     {
174         if (argv[argi][0] == '-')
175         {
176             const char *optionName = &argv[argi][1];
178             if (validParOptions.found(optionName))
179             {
180                 parRunControl_.runPar(argc, argv);
181                 break;
182             }
183         }
184     }
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]
195     int nArgs = 1;
196     string argListString = args_[0];
198     for (int argi = 1; argi < args_.size(); argi++)
199     {
200         argListString += ' ';
201         argListString += args_[argi];
203         if (args_[argi][0] == '-')
204         {
205             const char *optionName = &args_[argi][1];
207             if
208             (
209                 (
210                     validOptions.found(optionName)
211                  && validOptions[optionName] != ""
212                 )
213              || (
214                     validParOptions.found(optionName)
215                  && validParOptions[optionName] != ""
216                 )
217             )
218             {
219                 argi++;
220                 if (argi >= args_.size())
221                 {
222                     FatalError
223                         << "option " << "'-" << optionName << '\''
224                         << " requires an argument"
225                         << exit(FatalError);
226                 }
228                 argListString += ' ';
229                 argListString += args_[argi];
230                 options_.insert(optionName, args_[argi]);
231             }
232             else
233             {
234                 options_.insert(optionName, "");
235             }
236         }
237         else
238         {
239             if (nArgs != argi)
240             {
241                 args_[nArgs] = args_[argi];
242             }
243             nArgs++;
244         }
245     }
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
253     if
254     (
255         options_.found("help")
256      || options_.found("doc")
257      || options_.found("srcDoc")
258     )
259     {
260         if (options_.found("help"))
261         {
262             printUsage();
263         }
265         // only display one or the other
266         if (options_.found("srcDoc"))
267         {
268             displayDoc(true);
269         }
270         else if (options_.found("doc"))
271         {
272             displayDoc(false);
273         }
275         ::exit(0);
276     }
278     // Print the usage message and exit if the number of arguments is incorrect
279     if (!check(checkArgs, checkOpts))
280     {
281         FatalError.exit();
282     }
285     string dateString = clock::date();
286     string timeString = clock::clockTime();
288     // Print the banner once only for parallel runs
289     if (Pstream::master() && bannerEnabled)
290     {
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;
298     }
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
313     int nProcs = 1;
315     // If this actually is a parallel run
316     if (parRunControl_.parRun())
317     {
318         // For the master
319         if (Pstream::master())
320         {
321             // establish rootPath_/globalCase_/case_ for master
322             getRootCase();
324             IFstream decompDictStream
325             (
326                 rootPath_/globalCase_/"system/decomposeParDict"
327             );
329             if (!decompDictStream.good())
330             {
331                 FatalError
332                     << "Cannot read "
333                     << decompDictStream.name()
334                     << exit(FatalError);
335             }
337             dictionary decompDict(decompDictStream);
339             label dictNProcs
340             (
341                 readLabel
342                 (
343                     decompDict.lookup("numberOfSubdomains")
344                 )
345             );
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)
352             //
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())
357             {
358                 FatalError
359                     << decompDictStream.name()
360                     << " specifies " << dictNProcs
361                     << " processors but job was started with "
362                     << Pstream::nProcs() << " processors."
363                     << exit(FatalError);
364             }
366             // distributed data
367             if (decompDict.lookupOrDefault<Switch>("distributed", false))
368             {
369                 fileNameList roots;
370                 decompDict.lookup("roots") >> roots;
372                 if (roots.size() != Pstream::nProcs()-1)
373                 {
374                     FatalError
375                         << "number of entries in decompositionDict::roots"
376                         << " is not equal to the number of slaves "
377                         << Pstream::nProcs()-1
378                         << exit(FatalError);
379                 }
381                 // Distribute the master's argument list (with new root)
382                 bool hadCaseOpt = options_.found("case");
383                 for
384                 (
385                     int slave=Pstream::firstSlave();
386                     slave<=Pstream::lastSlave();
387                     slave++
388                 )
389                 {
390                     options_.set
391                     (
392                         "case",
393                         fileName(roots[slave-1])/globalCase_
394                     );
396                     OPstream toSlave(Pstream::scheduled, slave);
397                     toSlave << args_ << options_;
398                 }
399                 options_.erase("case");
401                 // restore [-case dir]
402                 if (hadCaseOpt)
403                 {
404                     options_.set("case", rootPath_/globalCase_);
405                 }
406             }
407             else
408             {
409                 // Possibly going to fewer processors.
410                 // Check if all procDirs are there.
411                 if (dictNProcs < Pstream::nProcs())
412                 {
413                     label nProcDirs = 0;
414                     while
415                     (
416                         isDir
417                         (
418                             rootPath_/globalCase_/"processor"
419                           + name(++nProcDirs)
420                         )
421                     )
422                     {}
424                     if (nProcDirs != Pstream::nProcs())
425                     {
426                         FatalError
427                             << "number of processor directories = "
428                             << nProcDirs
429                             << " is not equal to the number of processors = "
430                             << Pstream::nProcs()
431                             << exit(FatalError);
432                     }
433                 }
435                 // Distribute the master's argument list (unaltered)
436                 for
437                 (
438                     int slave=Pstream::firstSlave();
439                     slave<=Pstream::lastSlave();
440                     slave++
441                 )
442                 {
443                     OPstream toSlave(Pstream::scheduled, slave);
444                     toSlave << args_ << options_;
445                 }
446             }
447         }
448         else
449         {
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
455             getRootCase();
456         }
458         nProcs = Pstream::nProcs();
459         case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
460     }
461     else
462     {
463         // establish rootPath_/globalCase_/case_
464         getRootCase();
465         case_ = globalCase_;
466     }
469     wordList slaveProcs;
471     // collect slave machine/pid
472     if (parRunControl_.parRun())
473     {
474         if (Pstream::master())
475         {
476             slaveProcs.setSize(Pstream::nProcs() - 1);
477             word  slaveMachine;
478             label slavePid;
480             label procI = 0;
481             for
482             (
483                 int slave=Pstream::firstSlave();
484                 slave<=Pstream::lastSlave();
485                 slave++
486             )
487             {
488                 IPstream fromSlave(Pstream::scheduled, slave);
489                 fromSlave >> slaveMachine >> slavePid;
491                 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
492             }
493         }
494         else
495         {
496             OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
497             toMaster << hostName() << pid();
498         }
499     }
502     if (Pstream::master() && bannerEnabled)
503     {
504         Info<< "Case   : " << (rootPath_/globalCase_).c_str() << nl
505             << "nProcs : " << nProcs << endl;
507         if (parRunControl_.parRun())
508         {
509             Info<< "Slaves : " << slaveProcs << nl
510                 << "Pstream initialized with:" << nl
511                 << "    floatTransfer     : " << Pstream::floatTransfer << nl
512                 << "    nProcsSimpleSum   : " << Pstream::nProcsSimpleSum << nl
513                 << "    commsType         : "
514                 << Pstream::commsTypeNames[Pstream::defaultCommsType]
515                 << endl;
516         }
517     }
519     jobInfo.add("root", rootPath_);
520     jobInfo.add("case", globalCase_);
521     jobInfo.add("nProcs", nProcs);
522     if (slaveProcs.size())
523     {
524         jobInfo.add("slaves", slaveProcs);
525     }
526     jobInfo.write();
529     // Set the case and case-name as an environment variable
530     if (rootPath_[0] == '/')
531     {
532         // absolute path
533         setEnv("FOAM_CASE", rootPath_/globalCase_, true);
534     }
535     else if (rootPath_ == ".")
536     {
537         // relative to the current working directory
538         setEnv("FOAM_CASE", cwd()/globalCase_, true);
539     }
540     else
541     {
542         // qualify relative path
543         setEnv("FOAM_CASE", cwd()/rootPath_/globalCase_, true);
544     }
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)
556     {
557         Info<< endl;
558         IOobject::writeDivider(Info);
559     }
563 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
565 Foam::argList::~argList()
567     jobInfo.end();
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
587     Info<< nl
588         << "Usage: " << executable_;
590     for
591     (
592         SLList<string>::iterator iter = validArgs.begin();
593         iter != validArgs.end();
594         ++iter
595     )
596     {
597         Info<< " <" << iter().c_str() << '>';
598     }
600     for
601     (
602         HashTable<string>::iterator iter = validOptions.begin();
603         iter != validOptions.end();
604         ++iter
605     )
606     {
607         Info<< " [-" << iter.key();
609         if (iter().size())
610         {
611             Info<< ' ' << iter().c_str();
612         }
614         Info<< ']';
615     }
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
630     if (source)
631     {
632         forAll(docExts, extI)
633         {
634             docExts[extI].replace(".", "-source.");
635         }
636     }
638     fileName docFile;
639     bool found = false;
641     forAll(docDirs, dirI)
642     {
643         forAll(docExts, extI)
644         {
645             docFile = docDirs[dirI]/executable_ + docExts[extI];
646             docFile.expand();
648             if (isFile(docFile))
649             {
650                 found = true;
651                 break;
652             }
653         }
654         if (found)
655         {
656             break;
657         }
658     }
660     if (found)
661     {
662         string docBrowser(docDict.lookup("docBrowser"));
663         docBrowser.replaceAll("%f", docFile);
665         Info<< "Show documentation: " << docBrowser.c_str() << endl;
667         system(docBrowser);
668     }
669     else
670     {
671         Info<< nl
672             << "No documentation found for " << executable_
673             << ", but you can use -help to display the usage\n" << endl;
674     }
678 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
680     bool ok = true;
682     if (Pstream::master())
683     {
684         if (checkArgs && args_.size() - 1 != validArgs.size())
685         {
686             FatalError
687                 << "Wrong number of arguments, expected " << validArgs.size()
688                 << " found " << args_.size() - 1 << endl;
689             ok = false;
690         }
692         if (checkOpts)
693         {
694             forAllConstIter(HashTable<string>, options_, iter)
695             {
696                 if
697                 (
698                     !validOptions.found(iter.key())
699                  && !validParOptions.found(iter.key())
700                 )
701                 {
702                     FatalError
703                         << "Invalid option: -" << iter.key() << endl;
704                     ok = false;
705                 }
706             }
707         }
709         if (!ok)
710         {
711             printUsage();
712         }
713     }
715     return ok;
719 bool Foam::argList::checkRootCase() const
721     if (!isDir(rootPath()))
722     {
723         FatalError
724             << executable_
725             << ": cannot open root directory " << rootPath()
726             << endl;
728         return false;
729     }
731     if (!isDir(path()) && Pstream::master())
732     {
733         // Allow slaves on non-existing processor directories, created later
734         FatalError
735             << executable_
736             << ": cannot open case directory " << path()
737             << endl;
739         return false;
740     }
742     return true;
746 // ************************************************************************* //