Merge branch 'upstream/OpenFOAM' into master
[freefoam.git] / src / OpenFOAM / global / argList / argList.C
blob9c19439a6db92b4e2d70334e9db3273f7073d412
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 <OpenFOAM/OSspecific.H>
29 #include <OpenFOAM/clock.H>
30 #include <OpenFOAM/IFstream.H>
31 #include <OpenFOAM/dictionary.H>
32 #include <OpenFOAM/Switch.H>
33 #include <OpenFOAM/IOobject.H>
34 #include <OpenFOAM/JobInfo.H>
35 #include <OpenFOAM/labelList.H>
36 #include <OpenFOAM/ListOps.H>
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41 Foam::SLList<Foam::string>    Foam::argList::validArgs;
42 Foam::HashTable<Foam::string> Foam::argList::validOptions;
43 Foam::HashTable<Foam::string> Foam::argList::validParOptions;
44 bool Foam::argList::bannerEnabled(true);
47 Foam::argList::initValidTables::initValidTables()
49     validOptions.set("case", "dir");
50     validOptions.set("parallel", "");
51     validParOptions.set("parallel", "");
53     Pstream::addValidParOptions(validParOptions);
57 Foam::argList::initValidTables dummyInitValidTables;
60 // convert argv -> args_
61 // transform sequences with "(" ... ")" into string lists in the process
62 bool Foam::argList::regroupArgv(int& argc, char**& argv)
64     int nArgs = 0;
65     int listDepth = 0;
66     string tmpString;
68     // note: we also re-write directly into args_
69     // and use a second pass to sort out args/options
70     for (int argI = 0; argI < argc; argI++)
71     {
72         if (strcmp(argv[argI], "(") == 0)
73         {
74             listDepth++;
75             tmpString += "(";
76         }
77         else if (strcmp(argv[argI], ")") == 0)
78         {
79             if (listDepth)
80             {
81                 listDepth--;
82                 tmpString += ")";
83                 if (listDepth == 0)
84                 {
85                     args_[nArgs++] = tmpString;
86                     tmpString.clear();
87                 }
88             }
89             else
90             {
91                 args_[nArgs++] = argv[argI];
92             }
93         }
94         else if (listDepth)
95         {
96             // quote each string element
97             tmpString += "\"";
98             tmpString += argv[argI];
99             tmpString += "\"";
100         }
101         else
102         {
103             args_[nArgs++] = argv[argI];
104         }
105     }
107     if (tmpString.size())
108     {
109         args_[nArgs++] = tmpString;
110     }
112     args_.setSize(nArgs);
114     return nArgs < argc;
118 // get rootPath_ / globalCase_ from one of the following forms
119 //   * [-case dir]
120 //   * cwd
122 // Also export FOAM_CASE and FOAM_CASENAME environment variables
123 // so they can be used immediately (eg, in decomposeParDict)
125 void Foam::argList::getRootCase()
127     fileName casePath;
129     // [-case dir] specified
130     HashTable<string>::iterator iter = options_.find("case");
132     if (iter != options_.end())
133     {
134         casePath = iter();
135         casePath.clean();
137         if (casePath.empty() || casePath == ".")
138         {
139             // handle degenerate form and '-case .' like no -case specified
140             casePath = cwd();
141             options_.erase("case");
142         }
143         else if (casePath[0] != '/' && casePath.name() == "..")
144         {
145             // avoid relative cases ending in '..' - makes for very ugly names
146             casePath = cwd()/casePath;
147             casePath.clean();
148         }
149     }
150     else
151     {
152         // nothing specified, use the current dir
153         casePath = cwd();
154     }
156     rootPath_   = casePath.path();
157     globalCase_ = casePath.name();
158     case_       = globalCase_;
161     // Set the case and case-name as an environment variable
162     if (rootPath_[0] == '/')
163     {
164         // absolute path - use as-is
165         setEnv("FOAM_CASE", rootPath_/globalCase_, true);
166         setEnv("FOAM_CASENAME", globalCase_, true);
167     }
168     else
169     {
170         // qualify relative path
171         fileName casePath = cwd()/rootPath_/globalCase_;
172         casePath.clean();
174         setEnv("FOAM_CASE", casePath, true);
175         setEnv("FOAM_CASENAME", casePath.name(), true);
176     }
182 Foam::stringList::subList Foam::argList::additionalArgs() const
184     return stringList::subList(args_, args_.size() - 1, 1);
188 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
190 Foam::argList::argList
192     int& argc,
193     char**& argv,
194     bool checkArgs,
195     bool checkOpts
198     args_(argc),
199     options_(argc)
201     // Check if this run is a parallel run by searching for any parallel option
202     // If found call runPar (might filter argv)
203     for (int argI = 0; argI < argc; argI++)
204     {
205         if (argv[argI][0] == '-')
206         {
207             const char *optionName = &argv[argI][1];
209             if (validParOptions.found(optionName))
210             {
211                 parRunControl_.runPar(argc, argv);
212                 break;
213             }
214         }
215     }
217     // convert argv -> args_ and capture ( ... ) lists
218     // for normal arguments and for options
219     regroupArgv(argc, argv);
221     // Get executable name
222     args_[0]    = fileName(argv[0]);
223     executable_ = fileName(argv[0]).name();
225     // Check arguments and options, we already have argv[0]
226     int nArgs = 1;
227     string argListString = args_[0];
229     for (int argI = 1; argI < args_.size(); argI++)
230     {
231         argListString += ' ';
232         argListString += args_[argI];
234         if (args_[argI][0] == '-')
235         {
236             const char *optionName = &args_[argI][1];
238             if
239             (
240                 (
241                     validOptions.found(optionName)
242                  && validOptions[optionName] != ""
243                 )
244              || (
245                     validParOptions.found(optionName)
246                  && validParOptions[optionName] != ""
247                 )
248             )
249             {
250                 argI++;
251                 if (argI >= args_.size())
252                 {
253                     FatalError
254                         << "option " << "'-" << optionName << '\''
255                         << " requires an argument"
256                         << exit(FatalError);
257                 }
259                 argListString += ' ';
260                 argListString += args_[argI];
261                 options_.insert(optionName, args_[argI]);
262             }
263             else
264             {
265                 options_.insert(optionName, "");
266             }
267         }
268         else
269         {
270             if (nArgs != argI)
271             {
272                 args_[nArgs] = args_[argI];
273             }
274             nArgs++;
275         }
276     }
278     args_.setSize(nArgs);
280     // Help/documentation options:
281     //   -help    print the usage
282     //   -doc     display application documentation in browser
283     //   -srcDoc  display source code in browser
284     if
285     (
286         options_.found("help")
287      || options_.found("doc")
288      || options_.found("srcDoc")
289     )
290     {
291         if (options_.found("help"))
292         {
293             printUsage();
294         }
296         // only display one or the other
297         if (options_.found("srcDoc"))
298         {
299             displayDoc(true);
300         }
301         else if (options_.found("doc"))
302         {
303             displayDoc(false);
304         }
306         ::exit(0);
307     }
309     // Print the usage message and exit if the number of arguments is incorrect
310     if (!check(checkArgs, checkOpts))
311     {
312         FatalError.exit();
313     }
316     string dateString = clock::date();
317     string timeString = clock::clockTime();
319     // Print the banner once only for parallel runs
320     if (Pstream::master() && bannerEnabled)
321     {
322         IOobject::writeBanner(Info, true)
323             << "Build  : " << Foam::FOAMbuild << nl
324             << "Exec   : " << argListString.c_str() << nl
325             << "Date   : " << dateString.c_str() << nl
326             << "Time   : " << timeString.c_str() << nl
327             << "Host   : " << hostName() << nl
328             << "PID    : " << pid() << endl;
329     }
331     jobInfo.add("startDate", dateString);
332     jobInfo.add("startTime", timeString);
333     jobInfo.add("userName", userName());
334     jobInfo.add("foamVersion", word(FOAMfullVersion));
335     jobInfo.add("foamBuild", Foam::FOAMbuild);
336     jobInfo.add("code", executable_);
337     jobInfo.add("argList", argListString);
338     jobInfo.add("currentDir", cwd());
339     jobInfo.add("PPID", ppid());
340     jobInfo.add("PGID", pgid());
343     // Case is a single processor run unless it is running parallel
344     int nProcs = 1;
346     // If this actually is a parallel run
347     if (parRunControl_.parRun())
348     {
349         // For the master
350         if (Pstream::master())
351         {
352             // establish rootPath_/globalCase_/case_ for master
353             getRootCase();
355             IFstream decompDictStream
356             (
357                 rootPath_/globalCase_/"system/decomposeParDict"
358             );
360             if (!decompDictStream.good())
361             {
362                 FatalError
363                     << "Cannot read "
364                     << decompDictStream.name()
365                     << exit(FatalError);
366             }
368             dictionary decompDict(decompDictStream);
370             label dictNProcs
371             (
372                 readLabel
373                 (
374                     decompDict.lookup("numberOfSubdomains")
375                 )
376             );
378             // Check number of processors.
379             // nProcs     => number of actual procs
380             // dictNProcs => number of procs specified in decompositionDict
381             // nProcDirs  => number of processor directories
382             //               (n/a when running distributed)
383             //
384             // - normal running : nProcs = dictNProcs = nProcDirs
385             // - decomposition to more  processors : nProcs = dictNProcs
386             // - decomposition to fewer processors : nProcs = nProcDirs
387             if (dictNProcs > Pstream::nProcs())
388             {
389                 FatalError
390                     << decompDictStream.name()
391                     << " specifies " << dictNProcs
392                     << " processors but job was started with "
393                     << Pstream::nProcs() << " processors."
394                     << exit(FatalError);
395             }
397             // distributed data
398             if (decompDict.lookupOrDefault<Switch>("distributed", false))
399             {
400                 fileNameList roots;
401                 decompDict.lookup("roots") >> roots;
403                 if (roots.size() != Pstream::nProcs()-1)
404                 {
405                     FatalError
406                         << "number of entries in decompositionDict::roots"
407                         << " is not equal to the number of slaves "
408                         << Pstream::nProcs()-1
409                         << exit(FatalError);
410                 }
412                 // Distribute the master's argument list (with new root)
413                 bool hadCaseOpt = options_.found("case");
414                 for
415                 (
416                     int slave=Pstream::firstSlave();
417                     slave<=Pstream::lastSlave();
418                     slave++
419                 )
420                 {
421                     options_.set
422                     (
423                         "case",
424                         fileName(roots[slave-1])/globalCase_
425                     );
427                     OPstream toSlave(Pstream::scheduled, slave);
428                     toSlave << args_ << options_;
429                 }
430                 options_.erase("case");
432                 // restore [-case dir]
433                 if (hadCaseOpt)
434                 {
435                     options_.set("case", rootPath_/globalCase_);
436                 }
437             }
438             else
439             {
440                 // Possibly going to fewer processors.
441                 // Check if all procDirs are there.
442                 if (dictNProcs < Pstream::nProcs())
443                 {
444                     label nProcDirs = 0;
445                     while
446                     (
447                         isDir
448                         (
449                             rootPath_/globalCase_/"processor"
450                           + name(++nProcDirs)
451                         )
452                     )
453                     {}
455                     if (nProcDirs != Pstream::nProcs())
456                     {
457                         FatalError
458                             << "number of processor directories = "
459                             << nProcDirs
460                             << " is not equal to the number of processors = "
461                             << Pstream::nProcs()
462                             << exit(FatalError);
463                     }
464                 }
466                 // Distribute the master's argument list (unaltered)
467                 for
468                 (
469                     int slave=Pstream::firstSlave();
470                     slave<=Pstream::lastSlave();
471                     slave++
472                 )
473                 {
474                     OPstream toSlave(Pstream::scheduled, slave);
475                     toSlave << args_ << options_;
476                 }
477             }
478         }
479         else
480         {
481             // Collect the master's argument list
482             IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
483             fromMaster >> args_ >> options_;
485             // establish rootPath_/globalCase_/case_ for slave
486             getRootCase();
487         }
489         nProcs = Pstream::nProcs();
490         case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
491     }
492     else
493     {
494         // establish rootPath_/globalCase_/case_
495         getRootCase();
496         case_ = globalCase_;
497     }
500     wordList slaveProcs;
502     // collect slave machine/pid
503     if (parRunControl_.parRun())
504     {
505         if (Pstream::master())
506         {
507             slaveProcs.setSize(Pstream::nProcs() - 1);
508             word  slaveMachine;
509             label slavePid;
511             label procI = 0;
512             for
513             (
514                 int slave=Pstream::firstSlave();
515                 slave<=Pstream::lastSlave();
516                 slave++
517             )
518             {
519                 IPstream fromSlave(Pstream::scheduled, slave);
520                 fromSlave >> slaveMachine >> slavePid;
522                 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
523             }
524         }
525         else
526         {
527             OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
528             toMaster << hostName() << pid();
529         }
530     }
533     if (Pstream::master() && bannerEnabled)
534     {
535         Info<< "Case   : " << (rootPath_/globalCase_).c_str() << nl
536             << "nProcs : " << nProcs << endl;
538         if (parRunControl_.parRun())
539         {
540             Info<< "Slaves : " << slaveProcs << nl
541                 << "Pstream initialized with:" << nl
542                 << "    floatTransfer     : " << Pstream::floatTransfer << nl
543                 << "    nProcsSimpleSum   : " << Pstream::nProcsSimpleSum << nl
544                 << "    commsType         : "
545                 << Pstream::commsTypeNames[Pstream::defaultCommsType]
546                 << endl;
547         }
548     }
550     jobInfo.add("root", rootPath_);
551     jobInfo.add("case", globalCase_);
552     jobInfo.add("nProcs", nProcs);
553     if (slaveProcs.size())
554     {
555         jobInfo.add("slaves", slaveProcs);
556     }
557     jobInfo.write();
559     // Switch on signal trapping. We have to wait until after Pstream::init
560     // since this sets up its own ones.
561     sigFpe_.set(bannerEnabled);
562     sigInt_.set(bannerEnabled);
563     sigQuit_.set(bannerEnabled);
564     sigSegv_.set(bannerEnabled);
566     if (Pstream::master() && bannerEnabled)
567     {
568         Info<< endl;
569         IOobject::writeDivider(Info);
570     }
574 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
576 Foam::argList::~argList()
578     jobInfo.end();
582 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
584 void Foam::argList::noBanner()
586     bannerEnabled = false;
590 void Foam::argList::noParallel()
592     validOptions.erase("parallel");
596 void Foam::argList::printUsage() const
598     Info<< nl
599         << "Usage: " << executable_;
601     for
602     (
603         SLList<string>::iterator iter = validArgs.begin();
604         iter != validArgs.end();
605         ++iter
606     )
607     {
608         Info<< " <" << iter().c_str() << '>';
609     }
611     for
612     (
613         HashTable<string>::iterator iter = validOptions.begin();
614         iter != validOptions.end();
615         ++iter
616     )
617     {
618         Info<< " [-" << iter.key();
620         if (iter().size())
621         {
622             Info<< ' ' << iter().c_str();
623         }
625         Info<< ']';
626     }
628     // place help/doc/srcDoc options of the way at the end,
629     // but with an extra space to separate it a little
630     Info<< "  [-help] [-doc] [-srcDoc]\n" << endl;
634 void Foam::argList::displayDoc(bool source) const
636     const dictionary& docDict = debug::controlDict().subDict("Documentation");
637     // List of doxygen documentation file indices
638     List<fileName> docIndexFiles(docDict.lookup("doxyDocIndices"));
640     bool found = false;
641     fileName docFile;
643     forAll(docIndexFiles,idxI)
644     {
645         IFstream indexFile(docIndexFiles[idxI]);
647         if (!indexFile.good())
648         {
649             WarningIn("Foam::argList::displayDoc(bool)")
650                 << "Cannot open documentation index file " << docIndexFiles[idxI]
651                 << endl;
652         }
653         else
654         {
655             // the dictionary
656             dictionary indexDict(indexFile);
657             if (indexDict.found("docDir") && indexDict.found("docFiles"))
658             {
659                 fileName docDir;
660                 dictionary docFiles;
661                 // the documentation directory
662                 indexDict.lookup("docDir") >> docDir;
663                 // read the docFiles dictionary
664                 docFiles = indexDict.subDict("docFiles");
665                 // prefix docDir to all doc files
666                 if (docFiles.found(executable_))
667                 {
668                     found = true;
669                     docFile = docDir / FixedList<fileName,2>( docFiles.lookup(executable_) )[label(source)];
670                 }
671             }
672             else
673             {
674                 WarningIn("Foam::argList::displayDoc(bool)")
675                     << "The file " << docIndexFiles[idxI]
676                     << "does not contain one or both of the entries"
677                     << "\"docDir\" and \"docFiles\"."
678                     << endl;
679             }
680         }
681     }
683     if (found)
684     {
685         string docBrowser(docDict.lookup("docBrowser"));
686         if (docBrowser != "ECHO")
687         {
688             docBrowser.replaceAll("%f", docFile);
690             Info<< "Show documentation: " << docBrowser.c_str() << endl;
692             system(docBrowser);
693         }
694         else
695         {
696             Info<< "Documentation available at: " << docFile << endl;
697         }
698     }
699     else
700     {
701         Info<< nl
702             << "No documentation found for " << executable_
703             << ", but you can use -help to display the usage\n" << endl;
704     }
708 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
710     bool ok = true;
712     if (Pstream::master())
713     {
714         if (checkArgs && args_.size() - 1 != validArgs.size())
715         {
716             FatalError
717                 << "Wrong number of arguments, expected " << validArgs.size()
718                 << " found " << args_.size() - 1 << endl;
719             ok = false;
720         }
722         if (checkOpts)
723         {
724             forAllConstIter(HashTable<string>, options_, iter)
725             {
726                 if
727                 (
728                     !validOptions.found(iter.key())
729                  && !validParOptions.found(iter.key())
730                 )
731                 {
732                     FatalError
733                         << "Invalid option: -" << iter.key() << endl;
734                     ok = false;
735                 }
736             }
737         }
739         if (!ok)
740         {
741             printUsage();
742         }
743     }
745     return ok;
749 bool Foam::argList::checkRootCase() const
751     if (!isDir(rootPath()))
752     {
753         FatalError
754             << executable_
755             << ": cannot open root directory " << rootPath()
756             << endl;
758         return false;
759     }
761     if (!isDir(path()) && Pstream::master())
762     {
763         // Allow slaves on non-existing processor directories, created later
764         FatalError
765             << executable_
766             << ": cannot open case directory " << path()
767             << endl;
769         return false;
770     }
772     return true;
776 // ************************ vim: set sw=4 sts=4 et: ************************ //