initial commit for version 1.5.x patch release
[OpenFOAM-1.5.x.git] / src / OpenFOAM / global / argList / argList.C
bloba2faa64133a164011173bf5a2168accea365b3cc
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 1991-2008 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 namespace Foam
42     SLList<string>    argList::validArgs;
43     HashTable<string> argList::validOptions;
44     HashTable<string> argList::validParOptions;
48 Foam::argList::initValidTables::initValidTables()
50     validOptions.insert("case", "dir");
51     validOptions.insert("parallel", "");
52     validParOptions.insert("parallel", "");
54     Pstream::addValidParOptions(validParOptions);
58 Foam::argList::initValidTables dummyInitValidTables;
61 // convert argv -> args_
62 // transform sequences with "(" ... ")" into string lists in the process
63 bool Foam::argList::regroupArgv(int& argc, char**& argv)
65     int level = 0;
66     int nArgs = 0;
67     string tmpString;
69     // note: we also re-write directly into args_
70     // and use a second pass to sort out args/options
71     for (int argi=0; argi < argc; argi++)
72     {
73         if (strcmp(argv[argi], "(") == 0)
74         {
75             level++;
76             tmpString += "(";
77         }
78         else if (strcmp(argv[argi], ")") == 0)
79         {
80             if (level >= 1)
81             {
82                 level--;
83                 tmpString += ")";
84                 if (level == 0)
85                 {
86                     args_[nArgs++] = tmpString;
87                     tmpString.clear();
88                 }
89             }
90             else
91             {
92                 args_[nArgs++] = argv[argi];
93             }
94         }
95         else if (level)
96         {
97             // quote each string element
98             tmpString += "\"";
99             tmpString += argv[argi];
100             tmpString += "\"";
101         }
102         else
103         {
104             args_[nArgs++] = argv[argi];
105         }
106     }
108     if (tmpString.size())
109     {
110         args_[nArgs++] = tmpString;
111     }
113     args_.setSize(nArgs);
115     return nArgs < argc;
119 // get rootPath_ / globalCase_ from one of the following forms
120 //   * [-case dir]
121 //   * cwd
122 void Foam::argList::getRootCase()
124     fileName casePath;
126     // [-case dir] specified
127     HashTable<string>::iterator iter = options_.find("case");
129     if (iter != options_.end())
130     {
131         casePath = iter();
132         casePath.removeRepeated('/');
133         casePath.removeTrailing('/');
134     }
135     else
136     {
137         // nothing specified, use the current dir
138         casePath = cwd();
140         // we could add this back in as '-case'?
141         // options_.insert("case", casePath);
142     }
144     rootPath_   = casePath.path();
145     globalCase_ = casePath.name();
146     case_       = globalCase_;
150 Foam::stringList::subList Foam::argList::additionalArgs() const
152     return stringList::subList(args_, args_.size() - 1, 1);
156 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
158 Foam::argList::argList
160     int& argc,
161     char**& argv,
162     bool checkArgs,
163     bool checkOpts
166     args_(argc),
167     options_(argc)
169     // Check if this run is a parallel run by searching for any parallel option
170     // If found call runPar (might filter argv)
171     for (int argi=0; argi<argc; argi++)
172     {
173         if (argv[argi][0] == '-')
174         {
175             const char *optionName = &argv[argi][1];
177             if (validParOptions.found(optionName))
178             {
179                 parRunControl_.runPar(argc, argv);
180                 break;
181             }
182         }
183     }
185     // convert argv -> args_ and capture ( ... ) lists
186     // for normal arguments and for options
187     regroupArgv(argc, argv);
189     // Get executable name
190     args_[0]    = fileName(argv[0]);
191     executable_ = fileName(argv[0]).name();
193     // Check arguments and options, we already have argv[0]
194     int nArgs = 1;
195     string argListString = args_[0];
197     for (int argi = 1; argi < args_.size(); argi++)
198     {
199         argListString += ' ';
200         argListString += args_[argi];
202         if (args_[argi][0] == '-')
203         {
204             const char *optionName = &args_[argi][1];
206             if
207             (
208                 (
209                     validOptions.found(optionName)
210                  && validOptions[optionName] != ""
211                 )
212              || (
213                     validParOptions.found(optionName)
214                  && validParOptions[optionName] != ""
215                 )
216             )
217             {
218                 argi++;
219                 if (argi >= args_.size())
220                 {
221                     FatalError
222                         << "option " << "'-" << optionName << '\''
223                         << " requires an argument"
224                         << exit(FatalError);
225                 }
227                 argListString += ' ';
228                 argListString += args_[argi];
229                 options_.insert(optionName, args_[argi]);
230             }
231             else
232             {
233                 options_.insert(optionName, "");
234             }
235         }
236         else
237         {
238             if (nArgs != argi)
239             {
240                 args_[nArgs] = args_[argi];
241             }
242             nArgs++;
243         }
244     }
246     args_.setSize(nArgs);
248     // Help/documentation options:
249     //   -help    print the usage
250     //   -doc     display application documentation in browser
251     //   -srcDoc  display source code in browser
252     if
253     (
254         options_.found("help")
255      || options_.found("doc")
256      || options_.found("srcDoc")
257     )
258     {
259         if (options_.found("help"))
260         {
261             printUsage();
262         }
264         // only display one or the other
265         if (options_.found("srcDoc"))
266         {
267             displayDoc(true);
268         }
269         else if (options_.found("doc"))
270         {
271             displayDoc(false);
272         }
274         ::exit(0);
275     }
277     // Print the usage message and exit if the number of arguments is incorrect
278     if (!check(checkArgs, checkOpts))
279     {
280         FatalError.exit();
281     }
284     string dateString = clock::date();
285     string timeString = clock::clockTime();
287     // Print the banner once only for parallel runs
288     if (Pstream::master())
289     {
290         IOobject::writeBanner(Info, true);
291         Info<< "Exec   : " << argListString.c_str() << nl
292             << "Date   : " << dateString.c_str() << nl
293             << "Time   : " << timeString.c_str() << nl
294             << "Host   : " << hostName() << nl
295             << "PID    : " << pid() << endl;
296     }
298     jobInfo.add("startDate", dateString);
299     jobInfo.add("startTime", timeString);
300     jobInfo.add("userName", userName());
301     jobInfo.add("foamVersion", word(FOAMversion));
302     jobInfo.add("code", executable_);
303     jobInfo.add("argList", argListString);
304     jobInfo.add("currentDir", cwd());
305     jobInfo.add("PPID", ppid());
306     jobInfo.add("PGID", pgid());
309     // Case is a single processor run unless it is running parallel
310     int nProcs = 1;
312     // If this actually is a parallel run
313     if (parRunControl_.parRun())
314     {
315         // For the master
316         if (Pstream::master())
317         {
318             fileNameList roots;
320             // establish rootPath_/globalCase_/case_ for master
321             getRootCase();
323             IFstream decompDictStream
324             (
325                 rootPath_/globalCase_/"system/decomposeParDict"
326             );
328             if (!decompDictStream.good())
329             {
330                 FatalError
331                     << "Cannot read "
332                     << decompDictStream.name()
333                     << exit(FatalError);
334             }
336             dictionary decompositionDict(decompDictStream);
338             Switch distributed(false);
340             if
341             (
342                 decompositionDict.readIfPresent("distributed", distributed)
343              && distributed
344             )
345             {
346                 decompositionDict.lookup("roots") >> roots;
348                 if (roots.size() != Pstream::nProcs()-1)
349                 {
350                     FatalError
351                         << "number of entries in decompositionDict::roots"
352                         << " is not equal to the number of slaves "
353                         << Pstream::nProcs()-1
354                         << exit(FatalError);
355                 }
356             }
359             label dictNProcs
360             (
361                 readLabel
362                 (
363                     decompositionDict.lookup("numberOfSubdomains")
364                 )
365             );
367             // Check number of processors. We have nProcs(number of
368             // actual processes), dictNProcs(wanted number of processes read
369             // from decompositionDict) and nProcDirs(number of processor
370             // directories - n/a when running distributed)
371             //
372             // - normal running : nProcs = dictNProcs = nProcDirs
373             // - decomposition to more processors : nProcs = dictNProcs
374             // - decomposition to less processors : nProcs = nProcDirs
375             if (dictNProcs > Pstream::nProcs())
376             {
377                 FatalError
378                     << decompDictStream.name()
379                     << " specifies " << dictNProcs
380                     << " processors but job was started with "
381                     << Pstream::nProcs() << " processors."
382                     << exit(FatalError);
383             }
385             if (!distributed && dictNProcs < Pstream::nProcs())
386             {
387                 // Possibly going to fewer processors.
388                 // Check if all procDirs are there.
389                 label nProcDirs = 0;
390                 while
391                 (
392                     dir
393                     (
394                         rootPath_/globalCase_/"processor"
395                       + name(++nProcDirs)
396                     )
397                 )
398                 {}
400                 if (nProcDirs != Pstream::nProcs())
401                 {
402                     FatalError
403                         << "number of processor directories = "
404                         << nProcDirs
405                         << " is not equal to the number of processors = "
406                         << Pstream::nProcs()
407                         << exit(FatalError);
408                 }
409             }
411             // distributed data
412             if (roots.size())
413             {
414                 bool hadOptCase = options_.found("case");
416                 // Distribute the master's argument list (with new root)
417                 for
418                 (
419                     int slave=Pstream::firstSlave();
420                     slave<=Pstream::lastSlave();
421                     slave++
422                 )
423                 {
424                     options_.erase("case");
425                     options_.insert
426                     (
427                         "case",
428                         fileName(roots[slave-1])/globalCase_
429                     );
431                     OPstream toSlave(Pstream::scheduled, slave);
432                     toSlave << args_ << options_;
433                 }
435                 options_.erase("case");
437                 // restore [-case dir]
438                 if (hadOptCase)
439                 {
440                     options_.insert("case", rootPath_/globalCase_);
441                 }
442             }
443             else
444             {
445                 // Distribute the master's argument list (unaltered)
446                 for
447                 (
448                     int slave=Pstream::firstSlave();
449                     slave<=Pstream::lastSlave();
450                     slave++
451                 )
452                 {
453                     OPstream toSlave(Pstream::scheduled, slave);
454                     toSlave << args_ << options_;
455                 }
456             }
457         }
458         else
459         {
460             // Collect the master's argument list
461             IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
462             fromMaster >> args_ >> options_;
464             // establish rootPath_/globalCase_/case_ for slave
465             getRootCase();
466         }
468         nProcs = Pstream::nProcs();
469         case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
470     }
471     else
472     {
473         // establish rootPath_/globalCase_/case_
474         getRootCase();
476         case_ = globalCase_;
477     }
480     wordList slaveProcs;
482     // collect slave machine/pid
483     if (parRunControl_.parRun())
484     {
485         if (Pstream::master())
486         {
487             slaveProcs.setSize(Pstream::nProcs() - 1);
488             word  slaveMachine;
489             label slavePid;
491             label procI = 0;
492             for
493             (
494                 int slave=Pstream::firstSlave();
495                 slave<=Pstream::lastSlave();
496                 slave++
497             )
498             {
499                 IPstream fromSlave(Pstream::scheduled, slave);
500                 fromSlave >> slaveMachine >> slavePid;
502                 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
503             }
504         }
505         else
506         {
507             OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
508             toMaster << hostName() << pid();
509         }
510     }
513     if (Pstream::master())
514     {
515         Info<< "Case   : " << (rootPath_/globalCase_).c_str() << nl
516             << "nProcs : " << nProcs << endl;
517     }
519     if (parRunControl_.parRun() && Pstream::master())
520     {
521         Info<< "Slaves : " << slaveProcs << nl
522             << "Pstream initialized with:" << nl
523             << "    floatTransfer     : " << Pstream::floatTransfer << nl
524             << "    nProcsSimpleSum   : " << Pstream::nProcsSimpleSum << nl
525             << "    commsType         : "
526             << Pstream::commsTypeNames[Pstream::defaultCommsType]
527             << endl;
528     }
530     jobInfo.add("root", rootPath_);
531     jobInfo.add("case", globalCase_);
532     jobInfo.add("nProcs", nProcs);
533     if (slaveProcs.size())
534     {
535         jobInfo.add("slaves", slaveProcs);
536     }
537     jobInfo.write();
539     // Set the case as an environment variable
540     setEnv("FOAM_CASE", rootPath_/globalCase_, true);
542     // Switch on signal trapping. We have to wait until after Pstream::init
543     // since this sets up its own ones.
544     sigFpe_.set();
545     sigInt_.set();
546     sigQuit_.set();
547     sigSegv_.set();
549     if (Pstream::master())
550     {
551         Info<< endl;
552         IOobject::writeDivider(Info);
553     }
557 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
559 Foam::argList::~argList()
561     jobInfo.end();
565 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
567 void Foam::argList::noParallel()
569     validOptions.erase("parallel");
573 void Foam::argList::printUsage() const
575     Info<< nl
576         << "Usage: " << executable_;
578     for
579     (
580         SLList<string>::iterator iter = validArgs.begin();
581         iter != validArgs.end();
582         ++iter
583     )
584     {
585         Info<< " <" << iter().c_str() << '>';
586     }
588     for
589     (
590         HashTable<string>::iterator iter = validOptions.begin();
591         iter != validOptions.end();
592         ++iter
593     )
594     {
595         Info<< " [-" << iter.key();
597         if (iter().size())
598         {
599             Info<< ' ' << iter().c_str();
600         }
602         Info<< ']';
603     }
605     // place help/doc/srcDoc options of the way at the end,
606     // but with an extra space to separate it a little
607     Info<< "  [-help] [-doc] [-srcDoc]\n" << endl;
611 void Foam::argList::displayDoc(bool source) const
613     const dictionary& docDict = debug::controlDict().subDict("Documentation");
614     List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
615     List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
617     // for source code: change foo_8C.html to foo_8C-source.html
618     if (source)
619     {
620         forAll(docExts, extI)
621         {
622             docExts[extI].replace(".", "-source.");
623         }
624     }
626     fileName docFile;
627     bool found = false;
629     forAll(docDirs, dirI)
630     {
631         forAll(docExts, extI)
632         {
633             docFile = docDirs[dirI]/executable_ + docExts[extI];
634             docFile.expand();
636             if (exists(docFile))
637             {
638                 found = true;
639                 break;
640             }
641         }
642         if (found)
643         {
644             break;
645         }
646     }
648     if (found)
649     {
650         string docBrowser(docDict.lookup("docBrowser"));
651         docBrowser.replaceAll("%f", docFile);
653         Info<< "Show documentation: " << docBrowser.c_str() << endl;
655         system(docBrowser);
656     }
657     else
658     {
659         Info<< nl
660             << "No documentation found for " << executable_
661             << ", but you can use -help to display the usage\n" << endl;
662     }
666 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
668     bool ok = true;
670     if (Pstream::master())
671     {
672         if (checkArgs && args_.size() - 1 != validArgs.size())
673         {
674             FatalError
675                 << "Wrong number of arguments, expected " << validArgs.size()
676                 << " found " << args_.size() - 1 << endl;
677             ok = false;
678         }
680         if (checkOpts)
681         {
682             forAllConstIter(HashTable<string>, options_, iter)
683             {
684                 if
685                 (
686                     !validOptions.found(iter.key())
687                  && !validParOptions.found(iter.key())
688                 )
689                 {
690                     FatalError
691                         << "Invalid option: -" << iter.key() << endl;
692                     ok = false;
693                 }
694             }
695         }
697         if (!ok)
698         {
699             printUsage();
700         }
701     }
703     return ok;
707 bool Foam::argList::checkRootCase() const
709     if (!dir(rootPath()))
710     {
711         FatalError
712             << executable_
713             << ": cannot open root directory " << rootPath()
714             << endl;
716         return false;
717     }
719     if (!dir(path()) && Pstream::master())
720     {
721         // Allow slaves on non-existing processor directories, created later
722         FatalError
723             << executable_
724             << ": cannot open case directory " << path()
725             << endl;
727         return false;
728     }
730     return true;
734 // ************************************************************************* //