sync'ing files with internal line
[OpenFOAM-1.6.x.git] / src / OpenFOAM / global / argList / argList.C
blob16f216adf6f58368c4cce88ff19d875925917121
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         if (casePath.empty() || casePath == ".")
133         {
134             // handle degenerate form and '-case .' like no -case specified
135             casePath = cwd();
136             options_.erase("case");
137         }
138         else if (casePath[0] != '/' && casePath.name() == "..")
139         {
140             // avoid relative cases ending in '..' - makes for very ugly names
141             casePath = cwd()/casePath;
142             casePath.clean();
143         }
144     }
145     else
146     {
147         // nothing specified, use the current dir
148         casePath = cwd();
149     }
151     rootPath_   = casePath.path();
152     globalCase_ = casePath.name();
153     case_       = globalCase_;
157 Foam::stringList::subList Foam::argList::additionalArgs() const
159     return stringList::subList(args_, args_.size() - 1, 1);
163 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
165 Foam::argList::argList
167     int& argc,
168     char**& argv,
169     bool checkArgs,
170     bool checkOpts
173     args_(argc),
174     options_(argc)
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++)
179     {
180         if (argv[argI][0] == '-')
181         {
182             const char *optionName = &argv[argI][1];
184             if (validParOptions.found(optionName))
185             {
186                 parRunControl_.runPar(argc, argv);
187                 break;
188             }
189         }
190     }
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]
201     int nArgs = 1;
202     string argListString = args_[0];
204     for (int argI = 1; argI < args_.size(); argI++)
205     {
206         argListString += ' ';
207         argListString += args_[argI];
209         if (args_[argI][0] == '-')
210         {
211             const char *optionName = &args_[argI][1];
213             if
214             (
215                 (
216                     validOptions.found(optionName)
217                  && validOptions[optionName] != ""
218                 )
219              || (
220                     validParOptions.found(optionName)
221                  && validParOptions[optionName] != ""
222                 )
223             )
224             {
225                 argI++;
226                 if (argI >= args_.size())
227                 {
228                     FatalError
229                         << "option " << "'-" << optionName << '\''
230                         << " requires an argument"
231                         << exit(FatalError);
232                 }
234                 argListString += ' ';
235                 argListString += args_[argI];
236                 options_.insert(optionName, args_[argI]);
237             }
238             else
239             {
240                 options_.insert(optionName, "");
241             }
242         }
243         else
244         {
245             if (nArgs != argI)
246             {
247                 args_[nArgs] = args_[argI];
248             }
249             nArgs++;
250         }
251     }
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
259     if
260     (
261         options_.found("help")
262      || options_.found("doc")
263      || options_.found("srcDoc")
264     )
265     {
266         if (options_.found("help"))
267         {
268             printUsage();
269         }
271         // only display one or the other
272         if (options_.found("srcDoc"))
273         {
274             displayDoc(true);
275         }
276         else if (options_.found("doc"))
277         {
278             displayDoc(false);
279         }
281         ::exit(0);
282     }
284     // Print the usage message and exit if the number of arguments is incorrect
285     if (!check(checkArgs, checkOpts))
286     {
287         FatalError.exit();
288     }
291     string dateString = clock::date();
292     string timeString = clock::clockTime();
294     // Print the banner once only for parallel runs
295     if (Pstream::master() && bannerEnabled)
296     {
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;
304     }
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
319     int nProcs = 1;
321     // If this actually is a parallel run
322     if (parRunControl_.parRun())
323     {
324         // For the master
325         if (Pstream::master())
326         {
327             // establish rootPath_/globalCase_/case_ for master
328             getRootCase();
330             IFstream decompDictStream
331             (
332                 rootPath_/globalCase_/"system/decomposeParDict"
333             );
335             if (!decompDictStream.good())
336             {
337                 FatalError
338                     << "Cannot read "
339                     << decompDictStream.name()
340                     << exit(FatalError);
341             }
343             dictionary decompDict(decompDictStream);
345             label dictNProcs
346             (
347                 readLabel
348                 (
349                     decompDict.lookup("numberOfSubdomains")
350                 )
351             );
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)
358             //
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())
363             {
364                 FatalError
365                     << decompDictStream.name()
366                     << " specifies " << dictNProcs
367                     << " processors but job was started with "
368                     << Pstream::nProcs() << " processors."
369                     << exit(FatalError);
370             }
372             // distributed data
373             if (decompDict.lookupOrDefault<Switch>("distributed", false))
374             {
375                 fileNameList roots;
376                 decompDict.lookup("roots") >> roots;
378                 if (roots.size() != Pstream::nProcs()-1)
379                 {
380                     FatalError
381                         << "number of entries in decompositionDict::roots"
382                         << " is not equal to the number of slaves "
383                         << Pstream::nProcs()-1
384                         << exit(FatalError);
385                 }
387                 // Distribute the master's argument list (with new root)
388                 bool hadCaseOpt = options_.found("case");
389                 for
390                 (
391                     int slave=Pstream::firstSlave();
392                     slave<=Pstream::lastSlave();
393                     slave++
394                 )
395                 {
396                     options_.set
397                     (
398                         "case",
399                         fileName(roots[slave-1])/globalCase_
400                     );
402                     OPstream toSlave(Pstream::scheduled, slave);
403                     toSlave << args_ << options_;
404                 }
405                 options_.erase("case");
407                 // restore [-case dir]
408                 if (hadCaseOpt)
409                 {
410                     options_.set("case", rootPath_/globalCase_);
411                 }
412             }
413             else
414             {
415                 // Possibly going to fewer processors.
416                 // Check if all procDirs are there.
417                 if (dictNProcs < Pstream::nProcs())
418                 {
419                     label nProcDirs = 0;
420                     while
421                     (
422                         isDir
423                         (
424                             rootPath_/globalCase_/"processor"
425                           + name(++nProcDirs)
426                         )
427                     )
428                     {}
430                     if (nProcDirs != Pstream::nProcs())
431                     {
432                         FatalError
433                             << "number of processor directories = "
434                             << nProcDirs
435                             << " is not equal to the number of processors = "
436                             << Pstream::nProcs()
437                             << exit(FatalError);
438                     }
439                 }
441                 // Distribute the master's argument list (unaltered)
442                 for
443                 (
444                     int slave=Pstream::firstSlave();
445                     slave<=Pstream::lastSlave();
446                     slave++
447                 )
448                 {
449                     OPstream toSlave(Pstream::scheduled, slave);
450                     toSlave << args_ << options_;
451                 }
452             }
453         }
454         else
455         {
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
461             getRootCase();
462         }
464         nProcs = Pstream::nProcs();
465         case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
466     }
467     else
468     {
469         // establish rootPath_/globalCase_/case_
470         getRootCase();
471         case_ = globalCase_;
472     }
475     wordList slaveProcs;
477     // collect slave machine/pid
478     if (parRunControl_.parRun())
479     {
480         if (Pstream::master())
481         {
482             slaveProcs.setSize(Pstream::nProcs() - 1);
483             word  slaveMachine;
484             label slavePid;
486             label procI = 0;
487             for
488             (
489                 int slave=Pstream::firstSlave();
490                 slave<=Pstream::lastSlave();
491                 slave++
492             )
493             {
494                 IPstream fromSlave(Pstream::scheduled, slave);
495                 fromSlave >> slaveMachine >> slavePid;
497                 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
498             }
499         }
500         else
501         {
502             OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
503             toMaster << hostName() << pid();
504         }
505     }
508     if (Pstream::master() && bannerEnabled)
509     {
510         Info<< "Case   : " << (rootPath_/globalCase_).c_str() << nl
511             << "nProcs : " << nProcs << endl;
513         if (parRunControl_.parRun())
514         {
515             Info<< "Slaves : " << slaveProcs << nl
516                 << "Pstream initialized with:" << nl
517                 << "    floatTransfer     : " << Pstream::floatTransfer << nl
518                 << "    nProcsSimpleSum   : " << Pstream::nProcsSimpleSum << nl
519                 << "    commsType         : "
520                 << Pstream::commsTypeNames[Pstream::defaultCommsType]
521                 << endl;
522         }
523     }
525     jobInfo.add("root", rootPath_);
526     jobInfo.add("case", globalCase_);
527     jobInfo.add("nProcs", nProcs);
528     if (slaveProcs.size())
529     {
530         jobInfo.add("slaves", slaveProcs);
531     }
532     jobInfo.write();
535     // Set the case and case-name as an environment variable
536     if (rootPath_[0] == '/')
537     {
538         // absolute path - use as-is
539         setEnv("FOAM_CASE", rootPath_/globalCase_, true);
540         setEnv("FOAM_CASENAME", globalCase_, true);
541     }
542     else
543     {
544         // qualify relative path
545         fileName casePath = cwd()/rootPath_/globalCase_;
546         casePath.clean();
548         setEnv("FOAM_CASE", casePath, true);
549         setEnv("FOAM_CASENAME", casePath.name(), true);
550     }
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)
560     {
561         Info<< endl;
562         IOobject::writeDivider(Info);
563     }
567 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
569 Foam::argList::~argList()
571     jobInfo.end();
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
591     Info<< nl
592         << "Usage: " << executable_;
594     for
595     (
596         SLList<string>::iterator iter = validArgs.begin();
597         iter != validArgs.end();
598         ++iter
599     )
600     {
601         Info<< " <" << iter().c_str() << '>';
602     }
604     for
605     (
606         HashTable<string>::iterator iter = validOptions.begin();
607         iter != validOptions.end();
608         ++iter
609     )
610     {
611         Info<< " [-" << iter.key();
613         if (iter().size())
614         {
615             Info<< ' ' << iter().c_str();
616         }
618         Info<< ']';
619     }
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
634     if (source)
635     {
636         forAll(docExts, extI)
637         {
638             docExts[extI].replace(".", "-source.");
639         }
640     }
642     fileName docFile;
643     bool found = false;
645     forAll(docDirs, dirI)
646     {
647         forAll(docExts, extI)
648         {
649             docFile = docDirs[dirI]/executable_ + docExts[extI];
650             docFile.expand();
652             if (isFile(docFile))
653             {
654                 found = true;
655                 break;
656             }
657         }
658         if (found)
659         {
660             break;
661         }
662     }
664     if (found)
665     {
666         string docBrowser(docDict.lookup("docBrowser"));
667         docBrowser.replaceAll("%f", docFile);
669         Info<< "Show documentation: " << docBrowser.c_str() << endl;
671         system(docBrowser);
672     }
673     else
674     {
675         Info<< nl
676             << "No documentation found for " << executable_
677             << ", but you can use -help to display the usage\n" << endl;
678     }
682 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
684     bool ok = true;
686     if (Pstream::master())
687     {
688         if (checkArgs && args_.size() - 1 != validArgs.size())
689         {
690             FatalError
691                 << "Wrong number of arguments, expected " << validArgs.size()
692                 << " found " << args_.size() - 1 << endl;
693             ok = false;
694         }
696         if (checkOpts)
697         {
698             forAllConstIter(HashTable<string>, options_, iter)
699             {
700                 if
701                 (
702                     !validOptions.found(iter.key())
703                  && !validParOptions.found(iter.key())
704                 )
705                 {
706                     FatalError
707                         << "Invalid option: -" << iter.key() << endl;
708                     ok = false;
709                 }
710             }
711         }
713         if (!ok)
714         {
715             printUsage();
716         }
717     }
719     return ok;
723 bool Foam::argList::checkRootCase() const
725     if (!isDir(rootPath()))
726     {
727         FatalError
728             << executable_
729             << ": cannot open root directory " << rootPath()
730             << endl;
732         return false;
733     }
735     if (!isDir(path()) && Pstream::master())
736     {
737         // Allow slaves on non-existing processor directories, created later
738         FatalError
739             << executable_
740             << ": cannot open case directory " << path()
741             << endl;
743         return false;
744     }
746     return true;
750 // ************************************************************************* //