FreeFOAM The Cross-Platform CFD Toolkit
Hosted by SourceForge:
Get FreeFOAM at SourceForge.net.
            Fast, secure and Free Open Source software downloads

argList.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002   =========                 |
00003   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
00004    \\    /   O peration     |
00005     \\  /    A nd           | Copyright (C) 1991-2010 OpenCFD Ltd.
00006      \\/     M anipulation  |
00007 -------------------------------------------------------------------------------
00008 License
00009     This file is part of OpenFOAM.
00010 
00011     OpenFOAM is free software: you can redistribute it and/or modify it
00012     under the terms of the GNU General Public License as published by
00013     the Free Software Foundation, either version 3 of the License, or
00014     (at your option) any later version.
00015 
00016     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
00017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00019     for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
00023 
00024 \*---------------------------------------------------------------------------*/
00025 
00026 #include "argList.H"
00027 #include <OpenFOAM/OSspecific.H>
00028 #include <OpenFOAM/clock.H>
00029 #include <OpenFOAM/IFstream.H>
00030 #include <OpenFOAM/dictionary.H>
00031 #include <OpenFOAM/Switch.H>
00032 #include <OpenFOAM/IOobject.H>
00033 #include <OpenFOAM/JobInfo.H>
00034 #include <OpenFOAM/labelList.H>
00035 #include <OpenFOAM/ListOps.H>
00036 
00037 
00038 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
00039 
00040 Foam::SLList<Foam::string>    Foam::argList::validArgs;
00041 Foam::HashTable<Foam::string> Foam::argList::validOptions;
00042 Foam::HashTable<Foam::string> Foam::argList::validParOptions;
00043 bool Foam::argList::bannerEnabled(true);
00044 
00045 
00046 Foam::argList::initValidTables::initValidTables()
00047 {
00048     validOptions.set("case", "dir");
00049     validOptions.set("parallel", "");
00050     validParOptions.set("parallel", "");
00051 
00052     Pstream::addValidParOptions(validParOptions);
00053 }
00054 
00055 
00056 Foam::argList::initValidTables dummyInitValidTables;
00057 
00058 
00059 // convert argv -> args_
00060 // transform sequences with "(" ... ")" into string lists in the process
00061 bool Foam::argList::regroupArgv(int& argc, char**& argv)
00062 {
00063     int nArgs = 0;
00064     int listDepth = 0;
00065     string tmpString;
00066 
00067     // note: we also re-write directly into args_
00068     // and use a second pass to sort out args/options
00069     for (int argI = 0; argI < argc; argI++)
00070     {
00071         if (strcmp(argv[argI], "(") == 0)
00072         {
00073             listDepth++;
00074             tmpString += "(";
00075         }
00076         else if (strcmp(argv[argI], ")") == 0)
00077         {
00078             if (listDepth)
00079             {
00080                 listDepth--;
00081                 tmpString += ")";
00082                 if (listDepth == 0)
00083                 {
00084                     args_[nArgs++] = tmpString;
00085                     tmpString.clear();
00086                 }
00087             }
00088             else
00089             {
00090                 args_[nArgs++] = argv[argI];
00091             }
00092         }
00093         else if (listDepth)
00094         {
00095             // quote each string element
00096             tmpString += "\"";
00097             tmpString += argv[argI];
00098             tmpString += "\"";
00099         }
00100         else
00101         {
00102             args_[nArgs++] = argv[argI];
00103         }
00104     }
00105 
00106     if (tmpString.size())
00107     {
00108         args_[nArgs++] = tmpString;
00109     }
00110 
00111     args_.setSize(nArgs);
00112 
00113     return nArgs < argc;
00114 }
00115 
00116 
00117 // get rootPath_ / globalCase_ from one of the following forms
00118 //   * [-case dir]
00119 //   * cwd
00120 //
00121 // Also export FOAM_CASE and FOAM_CASENAME environment variables
00122 // so they can be used immediately (eg, in decomposeParDict)
00123 //
00124 void Foam::argList::getRootCase()
00125 {
00126     fileName casePath;
00127 
00128     // [-case dir] specified
00129     HashTable<string>::iterator iter = options_.find("case");
00130 
00131     if (iter != options_.end())
00132     {
00133         casePath = iter();
00134         casePath.clean();
00135 
00136         if (casePath.empty() || casePath == ".")
00137         {
00138             // handle degenerate form and '-case .' like no -case specified
00139             casePath = cwd();
00140             options_.erase("case");
00141         }
00142         else if (casePath[0] != '/' && casePath.name() == "..")
00143         {
00144             // avoid relative cases ending in '..' - makes for very ugly names
00145             casePath = cwd()/casePath;
00146             casePath.clean();
00147         }
00148     }
00149     else
00150     {
00151         // nothing specified, use the current dir
00152         casePath = cwd();
00153     }
00154 
00155     rootPath_   = casePath.path();
00156     globalCase_ = casePath.name();
00157     case_       = globalCase_;
00158 
00159 
00160     // Set the case and case-name as an environment variable
00161     if (rootPath_[0] == '/')
00162     {
00163         // absolute path - use as-is
00164         setEnv("FOAM_CASE", rootPath_/globalCase_, true);
00165         setEnv("FOAM_CASENAME", globalCase_, true);
00166     }
00167     else
00168     {
00169         // qualify relative path
00170         fileName casePath = cwd()/rootPath_/globalCase_;
00171         casePath.clean();
00172 
00173         setEnv("FOAM_CASE", casePath, true);
00174         setEnv("FOAM_CASENAME", casePath.name(), true);
00175     }
00176 
00177 
00178 }
00179 
00180 
00181 Foam::stringList::subList Foam::argList::additionalArgs() const
00182 {
00183     return stringList::subList(args_, args_.size() - 1, 1);
00184 }
00185 
00186 
00187 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
00188 
00189 Foam::argList::argList
00190 (
00191     int& argc,
00192     char**& argv,
00193     bool checkArgs,
00194     bool checkOpts
00195 )
00196 :
00197     args_(argc),
00198     options_(argc)
00199 {
00200     // Check if this run is a parallel run by searching for any parallel option
00201     // If found call runPar (might filter argv)
00202     for (int argI = 0; argI < argc; argI++)
00203     {
00204         if (argv[argI][0] == '-')
00205         {
00206             const char *optionName = &argv[argI][1];
00207 
00208             if (validParOptions.found(optionName))
00209             {
00210                 parRunControl_.runPar(argc, argv);
00211                 break;
00212             }
00213         }
00214     }
00215 
00216     // convert argv -> args_ and capture ( ... ) lists
00217     // for normal arguments and for options
00218     regroupArgv(argc, argv);
00219 
00220     // Get executable name
00221     args_[0]    = fileName(argv[0]);
00222     executable_ = fileName(argv[0]).name();
00223 
00224     // Check arguments and options, we already have argv[0]
00225     int nArgs = 1;
00226     string argListString = args_[0];
00227 
00228     for (int argI = 1; argI < args_.size(); argI++)
00229     {
00230         argListString += ' ';
00231         argListString += args_[argI];
00232 
00233         if (args_[argI][0] == '-')
00234         {
00235             const char *optionName = &args_[argI][1];
00236 
00237             if
00238             (
00239                 (
00240                     validOptions.found(optionName)
00241                  && validOptions[optionName] != ""
00242                 )
00243              || (
00244                     validParOptions.found(optionName)
00245                  && validParOptions[optionName] != ""
00246                 )
00247             )
00248             {
00249                 argI++;
00250                 if (argI >= args_.size())
00251                 {
00252                     FatalError
00253                         << "option " << "'-" << optionName << '\''
00254                         << " requires an argument"
00255                         << exit(FatalError);
00256                 }
00257 
00258                 argListString += ' ';
00259                 argListString += args_[argI];
00260                 options_.insert(optionName, args_[argI]);
00261             }
00262             else
00263             {
00264                 options_.insert(optionName, "");
00265             }
00266         }
00267         else
00268         {
00269             if (nArgs != argI)
00270             {
00271                 args_[nArgs] = args_[argI];
00272             }
00273             nArgs++;
00274         }
00275     }
00276 
00277     args_.setSize(nArgs);
00278 
00279     // Help/documentation options:
00280     //   -help    print the usage
00281     //   -doc     display application documentation in browser
00282     //   -srcDoc  display source code in browser
00283     if
00284     (
00285         options_.found("help")
00286      || options_.found("doc")
00287      || options_.found("srcDoc")
00288     )
00289     {
00290         if (options_.found("help"))
00291         {
00292             printUsage();
00293         }
00294 
00295         // only display one or the other
00296         if (options_.found("srcDoc"))
00297         {
00298             displayDoc(true);
00299         }
00300         else if (options_.found("doc"))
00301         {
00302             displayDoc(false);
00303         }
00304 
00305         ::exit(0);
00306     }
00307 
00308     // Print the usage message and exit if the number of arguments is incorrect
00309     if (!check(checkArgs, checkOpts))
00310     {
00311         FatalError.exit();
00312     }
00313 
00314 
00315     string dateString = clock::date();
00316     string timeString = clock::clockTime();
00317 
00318     // Print the banner once only for parallel runs
00319     if (Pstream::master() && bannerEnabled)
00320     {
00321         IOobject::writeBanner(Info, true)
00322             << "Build  : " << Foam::FOAMbuild << nl
00323             << "Exec   : " << argListString.c_str() << nl
00324             << "Date   : " << dateString.c_str() << nl
00325             << "Time   : " << timeString.c_str() << nl
00326             << "Host   : " << hostName() << nl
00327             << "PID    : " << pid() << endl;
00328     }
00329 
00330     jobInfo.add("startDate", dateString);
00331     jobInfo.add("startTime", timeString);
00332     jobInfo.add("userName", userName());
00333     jobInfo.add("foamVersion", word(FOAMfullVersion));
00334     jobInfo.add("foamBuild", Foam::FOAMbuild);
00335     jobInfo.add("foamUpstreamVersion", word(FOAMupstreamVersion));
00336     jobInfo.add("code", executable_);
00337     jobInfo.add("argList", argListString);
00338     jobInfo.add("currentDir", cwd());
00339     jobInfo.add("PPID", ppid());
00340     jobInfo.add("PGID", pgid());
00341 
00342 
00343     // Case is a single processor run unless it is running parallel
00344     int nProcs = 1;
00345 
00346     // If this actually is a parallel run
00347     if (parRunControl_.parRun())
00348     {
00349         // For the master
00350         if (Pstream::master())
00351         {
00352             // establish rootPath_/globalCase_/case_ for master
00353             getRootCase();
00354 
00355             IFstream decompDictStream
00356             (
00357                 rootPath_/globalCase_/"system/decomposeParDict"
00358             );
00359 
00360             if (!decompDictStream.good())
00361             {
00362                 FatalError
00363                     << "Cannot read "
00364                     << decompDictStream.name()
00365                     << exit(FatalError);
00366             }
00367 
00368             dictionary decompDict(decompDictStream);
00369 
00370             label dictNProcs
00371             (
00372                 readLabel
00373                 (
00374                     decompDict.lookup("numberOfSubdomains")
00375                 )
00376             );
00377 
00378             // Check number of processors.
00379             // nProcs     => number of actual procs
00380             // dictNProcs => number of procs specified in decompositionDict
00381             // nProcDirs  => number of processor directories
00382             //               (n/a when running distributed)
00383             //
00384             // - normal running : nProcs = dictNProcs = nProcDirs
00385             // - decomposition to more  processors : nProcs = dictNProcs
00386             // - decomposition to fewer processors : nProcs = nProcDirs
00387             if (dictNProcs > Pstream::nProcs())
00388             {
00389                 FatalError
00390                     << decompDictStream.name()
00391                     << " specifies " << dictNProcs
00392                     << " processors but job was started with "
00393                     << Pstream::nProcs() << " processors."
00394                     << exit(FatalError);
00395             }
00396 
00397             // distributed data
00398             if (decompDict.lookupOrDefault<Switch>("distributed", false))
00399             {
00400                 fileNameList roots;
00401                 decompDict.lookup("roots") >> roots;
00402 
00403                 if (roots.size() != Pstream::nProcs()-1)
00404                 {
00405                     FatalError
00406                         << "number of entries in decompositionDict::roots"
00407                         << " is not equal to the number of slaves "
00408                         << Pstream::nProcs()-1
00409                         << exit(FatalError);
00410                 }
00411 
00412                 // Distribute the master's argument list (with new root)
00413                 bool hadCaseOpt = options_.found("case");
00414                 for
00415                 (
00416                     int slave=Pstream::firstSlave();
00417                     slave<=Pstream::lastSlave();
00418                     slave++
00419                 )
00420                 {
00421                     options_.set
00422                     (
00423                         "case",
00424                         fileName(roots[slave-1])/globalCase_
00425                     );
00426 
00427                     OPstream toSlave(Pstream::scheduled, slave);
00428                     toSlave << args_ << options_;
00429                 }
00430                 options_.erase("case");
00431 
00432                 // restore [-case dir]
00433                 if (hadCaseOpt)
00434                 {
00435                     options_.set("case", rootPath_/globalCase_);
00436                 }
00437             }
00438             else
00439             {
00440                 // Possibly going to fewer processors.
00441                 // Check if all procDirs are there.
00442                 if (dictNProcs < Pstream::nProcs())
00443                 {
00444                     label nProcDirs = 0;
00445                     while
00446                     (
00447                         isDir
00448                         (
00449                             rootPath_/globalCase_/"processor"
00450                           + name(++nProcDirs)
00451                         )
00452                     )
00453                     {}
00454 
00455                     if (nProcDirs != Pstream::nProcs())
00456                     {
00457                         FatalError
00458                             << "number of processor directories = "
00459                             << nProcDirs
00460                             << " is not equal to the number of processors = "
00461                             << Pstream::nProcs()
00462                             << exit(FatalError);
00463                     }
00464                 }
00465 
00466                 // Distribute the master's argument list (unaltered)
00467                 for
00468                 (
00469                     int slave=Pstream::firstSlave();
00470                     slave<=Pstream::lastSlave();
00471                     slave++
00472                 )
00473                 {
00474                     OPstream toSlave(Pstream::scheduled, slave);
00475                     toSlave << args_ << options_;
00476                 }
00477             }
00478         }
00479         else
00480         {
00481             // Collect the master's argument list
00482             IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
00483             fromMaster >> args_ >> options_;
00484 
00485             // establish rootPath_/globalCase_/case_ for slave
00486             getRootCase();
00487         }
00488 
00489         nProcs = Pstream::nProcs();
00490         case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
00491     }
00492     else
00493     {
00494         // establish rootPath_/globalCase_/case_
00495         getRootCase();
00496         case_ = globalCase_;
00497     }
00498 
00499 
00500     wordList slaveProcs;
00501 
00502     // collect slave machine/pid
00503     if (parRunControl_.parRun())
00504     {
00505         if (Pstream::master())
00506         {
00507             slaveProcs.setSize(Pstream::nProcs() - 1);
00508             word  slaveMachine;
00509             label slavePid;
00510 
00511             label procI = 0;
00512             for
00513             (
00514                 int slave=Pstream::firstSlave();
00515                 slave<=Pstream::lastSlave();
00516                 slave++
00517             )
00518             {
00519                 IPstream fromSlave(Pstream::scheduled, slave);
00520                 fromSlave >> slaveMachine >> slavePid;
00521 
00522                 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
00523             }
00524         }
00525         else
00526         {
00527             OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
00528             toMaster << hostName() << pid();
00529         }
00530     }
00531 
00532 
00533     if (Pstream::master() && bannerEnabled)
00534     {
00535         Info<< "Case   : " << (rootPath_/globalCase_).c_str() << nl
00536             << "nProcs : " << nProcs << endl;
00537 
00538         if (parRunControl_.parRun())
00539         {
00540             Info<< "Slaves : " << slaveProcs << nl
00541                 << "Pstream initialized with:" << nl
00542                 << "    floatTransfer     : " << Pstream::floatTransfer << nl
00543                 << "    nProcsSimpleSum   : " << Pstream::nProcsSimpleSum << nl
00544                 << "    commsType         : "
00545                 << Pstream::commsTypeNames[Pstream::defaultCommsType]
00546                 << endl;
00547         }
00548     }
00549 
00550     jobInfo.add("root", rootPath_);
00551     jobInfo.add("case", globalCase_);
00552     jobInfo.add("nProcs", nProcs);
00553     if (slaveProcs.size())
00554     {
00555         jobInfo.add("slaves", slaveProcs);
00556     }
00557     jobInfo.write();
00558 
00559     // Switch on signal trapping. We have to wait until after Pstream::init
00560     // since this sets up its own ones.
00561     sigFpe_.set(bannerEnabled);
00562     sigInt_.set(bannerEnabled);
00563     sigQuit_.set(bannerEnabled);
00564     sigSegv_.set(bannerEnabled);
00565 
00566     if (Pstream::master() && bannerEnabled)
00567     {
00568         Info<< endl;
00569         IOobject::writeDivider(Info);
00570     }
00571 }
00572 
00573 
00574 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
00575 
00576 Foam::argList::~argList()
00577 {
00578     jobInfo.end();
00579 }
00580 
00581 
00582 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
00583 
00584 void Foam::argList::noBanner()
00585 {
00586     bannerEnabled = false;
00587 }
00588 
00589 
00590 void Foam::argList::noParallel()
00591 {
00592     validOptions.erase("parallel");
00593 }
00594 
00595 
00596 void Foam::argList::printUsage() const
00597 {
00598     Info<< nl
00599         << "Usage: " << executable_;
00600 
00601     for
00602     (
00603         SLList<string>::iterator iter = validArgs.begin();
00604         iter != validArgs.end();
00605         ++iter
00606     )
00607     {
00608         Info<< " <" << iter().c_str() << '>';
00609     }
00610 
00611     for
00612     (
00613         HashTable<string>::iterator iter = validOptions.begin();
00614         iter != validOptions.end();
00615         ++iter
00616     )
00617     {
00618         Info<< " [-" << iter.key();
00619 
00620         if (iter().size())
00621         {
00622             Info<< ' ' << iter().c_str();
00623         }
00624 
00625         Info<< ']';
00626     }
00627 
00628     // place help/doc/srcDoc options of the way at the end,
00629     // but with an extra space to separate it a little
00630     Info<< "  [-help] [-doc] [-srcDoc]\n" << endl;
00631 }
00632 
00633 
00634 void Foam::argList::displayDoc(bool source) const
00635 {
00636     const dictionary& docDict = debug::controlDict().subDict("Documentation");
00637     // List of doxygen documentation file indices
00638     List<fileName> docIndexFiles(docDict.lookup("doxyDocIndices"));
00639 
00640     bool found = false;
00641     fileName docFile;
00642 
00643     forAll(docIndexFiles,idxI)
00644     {
00645         IFstream indexFile(docIndexFiles[idxI]);
00646 
00647         if (!indexFile.good())
00648         {
00649             WarningIn("Foam::argList::displayDoc(bool)")
00650                 << "Cannot open documentation index file " << docIndexFiles[idxI]
00651                 << endl;
00652         }
00653         else
00654         {
00655             // the dictionary
00656             dictionary indexDict(indexFile);
00657             if (indexDict.found("docDir") && indexDict.found("docFiles"))
00658             {
00659                 fileName docDir;
00660                 dictionary docFiles;
00661                 // the documentation directory
00662                 indexDict.lookup("docDir") >> docDir;
00663                 // read the docFiles dictionary
00664                 docFiles = indexDict.subDict("docFiles");
00665                 // prefix docDir to all doc files
00666                 if (docFiles.found(executable_))
00667                 {
00668                     found = true;
00669                     docFile = docDir / FixedList<fileName,2>( docFiles.lookup(executable_) )[label(source)];
00670                 }
00671             }
00672             else
00673             {
00674                 WarningIn("Foam::argList::displayDoc(bool)")
00675                     << "The file " << docIndexFiles[idxI]
00676                     << "does not contain one or both of the entries"
00677                     << "\"docDir\" and \"docFiles\"."
00678                     << endl;
00679             }
00680         }
00681     }
00682 
00683     if (found)
00684     {
00685         string docBrowser(docDict.lookup("docBrowser"));
00686         if (docBrowser != "ECHO")
00687         {
00688             docBrowser.replaceAll("%f", docFile);
00689 
00690             Info<< "Show documentation: " << docBrowser.c_str() << endl;
00691 
00692             system(docBrowser);
00693         }
00694         else
00695         {
00696             Info<< "Documentation available at: " << docFile << endl;
00697         }
00698     }
00699     else
00700     {
00701         Info<< nl
00702             << "No documentation found for " << executable_
00703             << ", but you can use -help to display the usage\n" << endl;
00704     }
00705 }
00706 
00707 
00708 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
00709 {
00710     bool ok = true;
00711 
00712     if (Pstream::master())
00713     {
00714         if (checkArgs && args_.size() - 1 != validArgs.size())
00715         {
00716             FatalError
00717                 << "Wrong number of arguments, expected " << validArgs.size()
00718                 << " found " << args_.size() - 1 << endl;
00719             ok = false;
00720         }
00721 
00722         if (checkOpts)
00723         {
00724             forAllConstIter(HashTable<string>, options_, iter)
00725             {
00726                 if
00727                 (
00728                     !validOptions.found(iter.key())
00729                  && !validParOptions.found(iter.key())
00730                 )
00731                 {
00732                     FatalError
00733                         << "Invalid option: -" << iter.key() << endl;
00734                     ok = false;
00735                 }
00736             }
00737         }
00738 
00739         if (!ok)
00740         {
00741             printUsage();
00742         }
00743     }
00744 
00745     return ok;
00746 }
00747 
00748 
00749 bool Foam::argList::checkRootCase() const
00750 {
00751     if (!isDir(rootPath()))
00752     {
00753         FatalError
00754             << executable_
00755             << ": cannot open root directory " << rootPath()
00756             << endl;
00757 
00758         return false;
00759     }
00760 
00761     if (!isDir(path()) && Pstream::master())
00762     {
00763         // Allow slaves on non-existing processor directories, created later
00764         FatalError
00765             << executable_
00766             << ": cannot open case directory " << path()
00767             << endl;
00768 
00769         return false;
00770     }
00771 
00772     return true;
00773 }
00774 
00775 
00776 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines