argList.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2021 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
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
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
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.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "argList.H"
27 #include "OSspecific.H"
28 #include "clock.H"
29 #include "IFstream.H"
30 #include "dictionary.H"
31 #include "IOobject.H"
32 #include "jobInfo.H"
33 #include "labelList.H"
34 #include "regIOobject.H"
35 #include "dynamicCode.H"
36 #include "fileOperation.H"
38 #include "stringListOps.H"
39 #include "dlLibraryTable.H"
40 
41 #include <cctype>
42 
43 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
44 
53 
55 {
57  (
58  "case", "dir",
59  "specify alternate case directory, default is the cwd"
60  );
61  argList::addBoolOption("parallel", "run in parallel");
62  validParOptions.set("parallel", "");
64  (
65  "roots", "(dir1 .. dirN)",
66  "slave root directories for distributed running"
67  );
68  validParOptions.set("roots", "(dir1 .. dirN)");
69 
71  (
72  "hostRoots", "(((host1 dir1) .. (hostN dirN))",
73  "slave root directories (per host) for distributed running"
74  );
75  validParOptions.set("hostRoots", "((host1 dir1) .. (hostN dirN))");
76 
78  (
79  "noFunctionObjects",
80  "do not execute functionObjects"
81  );
82 
84  (
85  "fileHandler",
86  "handler",
87  "override the fileHandler"
88  );
90  (
91  "libs",
92  "(lib1 .. libN)",
93  "pre-load libraries"
94  );
95 
97 }
98 
99 
101 {
102  argList::removeOption("case");
103  argList::removeOption("parallel");
104  argList::removeOption("roots");
105  argList::removeOption("hostRoots");
106  argList::removeOption("noFunctionObjects");
107  argList::removeOption("fileHandler");
108  argList::removeOption("libs");
109 }
110 
111 
113 
114 
115 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
116 
118 (
119  const word& opt,
120  const string& usage
121 )
122 {
123  addOption(opt, "", usage);
124 }
125 
126 
128 (
129  const word& opt,
130  const string& param,
131  const string& usage
132 )
133 {
134  validOptions.set(opt, param);
135  if (!usage.empty())
136  {
137  optionUsage.set(opt, usage);
138  }
139 }
140 
141 
143 (
144  const word& opt,
145  const string& usage
146 )
147 {
148  if (usage.empty())
149  {
150  optionUsage.erase(opt);
151  }
152  else
153  {
154  optionUsage.set(opt, usage);
155  }
156 }
157 
158 
159 void Foam::argList::addNote(const string& note)
160 {
161  if (!note.empty())
162  {
163  notes.append(note);
164  }
165 }
166 
167 
169 {
170  validOptions.erase(opt);
171  optionUsage.erase(opt);
172 }
173 
174 
176 {
177  removeOption("parallel");
178  removeOption("roots");
179  removeOption("hostRoots");
180  validParOptions.clear();
181 }
182 
183 
184 void Foam::argList::printOptionUsage
185 (
186  const label location,
187  const string& str
188 )
189 {
190  const string::size_type textWidth = usageMax - usageMin;
191  const string::size_type strLen = str.size();
192 
193  if (strLen)
194  {
195  // Minimum of 2 spaces between option and usage:
196  if (string::size_type(location) + 2 <= usageMin)
197  {
198  for (string::size_type i = location; i < usageMin; ++i)
199  {
200  Info<<' ';
201  }
202  }
203  else
204  {
205  // or start a new line
206  Info<< nl;
207  for (string::size_type i = 0; i < usageMin; ++i)
208  {
209  Info<<' ';
210  }
211  }
212 
213  // Text wrap
215  while (pos != string::npos && pos + textWidth < strLen)
216  {
217  // Potential end point and next point
218  string::size_type curr = pos + textWidth - 1;
219  string::size_type next = string::npos;
220 
221  if (isspace(str[curr]))
222  {
223  // We were lucky: ended on a space
224  next = str.find_first_not_of(" \t\n", curr);
225  }
226  else if (isspace(str[curr+1]))
227  {
228  // The next one is a space - so we are okay
229  curr++; // otherwise the length is wrong
230  next = str.find_first_not_of(" \t\n", curr);
231  }
232  else
233  {
234  // Search for end of a previous word break
235  string::size_type prev = str.find_last_of(" \t\n", curr);
236 
237  // Reposition to the end of previous word if possible
238  if (prev != string::npos && prev > pos)
239  {
240  curr = prev;
241  }
242  }
243 
244  if (next == string::npos)
245  {
246  next = curr + 1;
247  }
248 
249  // Indent following lines (not the first one)
250  if (pos)
251  {
252  for (string::size_type i = 0; i < usageMin; ++i)
253  {
254  Info<<' ';
255  }
256  }
257 
258  Info<< str.substr(pos, (curr - pos)).c_str() << nl;
259  pos = next;
260  }
261 
262  // Output the remainder of the string
263  if (pos != string::npos)
264  {
265  // Indent following lines (not the first one)
266  if (pos)
267  {
268  for (string::size_type i = 0; i < usageMin; ++i)
269  {
270  Info<<' ';
271  }
272  }
273 
274  Info<< str.substr(pos).c_str() << nl;
275  }
276  }
277  else
278  {
279  Info<< nl;
280  }
281 }
282 
283 
284 bool Foam::argList::postProcess(int argc, char *argv[])
285 {
286  bool postProcessOption = false;
287 
288  for (int i=1; i<argc; i++)
289  {
290  postProcessOption = argv[i] == '-' + postProcessOptionName;
291  if (postProcessOption) break;
292  }
293 
294  return postProcessOption;
295 }
296 
297 
298 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
299 
300 // Convert argv -> args_
301 // Transform sequences with "(" ... ")" into string lists in the process
302 bool Foam::argList::regroupArgv(int& argc, char**& argv)
303 {
304  int nArgs = 0;
305  int listDepth = 0;
306  string tmpString;
307 
308  // Note: we also re-write directly into args_
309  // and use a second pass to sort out args/options
310  for (int argi=0; argi<argc; argi++)
311  {
312  if (strcmp(argv[argi], "(") == 0)
313  {
314  ++listDepth;
315  tmpString += "(";
316  }
317  else if (strcmp(argv[argi], ")") == 0)
318  {
319  if (listDepth)
320  {
321  --listDepth;
322  tmpString += ")";
323  if (listDepth == 0)
324  {
325  args_[nArgs++] = tmpString;
326  tmpString.clear();
327  }
328  }
329  else
330  {
331  args_[nArgs++] = argv[argi];
332  }
333  }
334  else if (listDepth)
335  {
336  // Quote each string element
337  tmpString += "\"";
338  tmpString += argv[argi];
339  tmpString += "\"";
340  }
341  else
342  {
343  args_[nArgs++] = argv[argi];
344  }
345  }
346 
347  if (tmpString.size())
348  {
349  args_[nArgs++] = tmpString;
350  }
351 
352  args_.setSize(nArgs);
353 
354  return nArgs < argc;
355 }
356 
357 
358 void Foam::argList::getRootCase()
359 {
360  fileName casePath;
361 
362  // [-case dir] specified
363  HashTable<string>::const_iterator iter = options_.find("case");
364 
365  if (iter != options_.end())
366  {
367  casePath = iter();
368  casePath.clean();
369 
370  if (casePath.empty() || casePath == ".")
371  {
372  // Handle degenerate form and '-case .' like no -case specified
373  casePath = cwd();
374  options_.erase("case");
375  }
376  else if (!casePath.isAbsolute() && casePath.name() == "..")
377  {
378  // Avoid relative cases ending in '..' - makes for very ugly names
379  casePath = cwd()/casePath;
380  casePath.clean();
381  }
382  }
383  else
384  {
385  // Nothing specified, use the current dir
386  casePath = cwd();
387  }
388 
389  rootPath_ = casePath.path();
390  globalCase_ = casePath.name();
391  case_ = globalCase_;
392 
393 
394  // Set the case and case-name as an environment variable
395  if (rootPath_.isAbsolute())
396  {
397  // Absolute path - use as-is
398  setEnv("FOAM_CASE", rootPath_/globalCase_, true);
399  setEnv("FOAM_CASENAME", globalCase_, true);
400  }
401  else
402  {
403  // Qualify relative path
404  casePath = cwd()/rootPath_/globalCase_;
405  casePath.clean();
406 
407  setEnv("FOAM_CASE", casePath, true);
408  setEnv("FOAM_CASENAME", casePath.name(), true);
409  }
410 }
411 
412 
413 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
414 
416 (
417  int& argc,
418  char**& argv,
419  bool checkArgs,
420  bool checkOpts,
421  const bool initialise
422 )
423 :
424  args_(argc),
425  options_(argc)
426 {
427  // Pre-load any libraries
428  {
429  const string libsString(getEnv("FOAM_LIBS"));
430  if (!libsString.empty())
431  {
432  libs.open(fileNameList((IStringStream(libsString))()));
433  }
434 
435  for (int argi=0; argi<argc; argi++)
436  {
437  if (argv[argi][0] == '-')
438  {
439  const char *optionName = &argv[argi][1];
440  if (string(optionName) == "libs")
441  {
442  libs.open(fileNameList((IStringStream(argv[argi+1]))()));
443  break;
444  }
445  }
446  }
447  }
448 
449  // Check for fileHandler
450  word handlerType(getEnv("FOAM_FILEHANDLER"));
451  for (int argi=0; argi<argc; argi++)
452  {
453  if (argv[argi][0] == '-')
454  {
455  const char *optionName = &argv[argi][1];
456  if (string(optionName) == "fileHandler")
457  {
458  handlerType = argv[argi+1];
459  break;
460  }
461  }
462  }
463  if (handlerType.empty())
464  {
465  handlerType = fileOperation::defaultFileHandler;
466  }
467 
468  // Detect any parallel options
470  (
471  handlerType,
472  argc,
473  argv
474  )().needsThreading();
475 
476 
477  // Check if this run is a parallel run by searching for any parallel option
478  // If found call runPar which might filter argv
479  for (int argi=0; argi<argc; argi++)
480  {
481  if (argv[argi][0] == '-')
482  {
483  const char *optionName = &argv[argi][1];
484 
485  if (validParOptions.found(optionName))
486  {
487  parRunControl_.runPar(argc, argv, needsThread);
488  break;
489  }
490  }
491  }
492 
493  // Convert argv -> args_ and capture ( ... ) lists
494  // for normal arguments and for options
495  regroupArgv(argc, argv);
496 
497  // Get executable name
498  args_[0] = fileName(argv[0]);
499  executable_ = fileName(argv[0]).name();
500 
501  // Check arguments and options, we already have argv[0]
502  int nArgs = 1;
503  argListStr_ = args_[0];
504 
505  for (int argi=1; argi<args_.size(); argi++)
506  {
507  argListStr_ += ' ';
508  argListStr_ += args_[argi];
509 
510  if (args_[argi][0] == '-')
511  {
512  const char *optionName = &args_[argi][1];
513 
514  if
515  (
516  (
517  validOptions.found(optionName)
518  && !validOptions[optionName].empty()
519  )
520  || (
521  validParOptions.found(optionName)
522  && !validParOptions[optionName].empty()
523  )
524  )
525  {
526  ++argi;
527  if (argi >= args_.size())
528  {
529  FatalError
530  <<"Option '-" << optionName
531  << "' requires an argument" << endl;
532  printUsage();
533  FatalError.exit();
534  }
535 
536  argListStr_ += ' ';
537  argListStr_ += args_[argi];
538  options_.insert(optionName, args_[argi]);
539  }
540  else
541  {
542  options_.insert(optionName, "");
543  }
544  }
545  else
546  {
547  if (nArgs != argi)
548  {
549  args_[nArgs] = args_[argi];
550  }
551  ++nArgs;
552  }
553  }
554 
555  args_.setSize(nArgs);
556 
557  parse(checkArgs, checkOpts, initialise);
558 }
559 
560 
562 (
563  const argList& args,
564  const HashTable<string>& options,
565  bool checkArgs,
566  bool checkOpts,
567  bool initialise
568 )
569 :
570  parRunControl_(args.parRunControl_),
571  args_(args.args_),
572  options_(options),
573  executable_(args.executable_),
574  rootPath_(args.rootPath_),
575  globalCase_(args.globalCase_),
576  case_(args.case_),
577  argListStr_(args.argListStr_)
578 {
579  parse(checkArgs, checkOpts, initialise);
580 }
581 
582 
584 (
585  bool checkArgs,
586  bool checkOpts,
587  bool initialise
588 )
589 {
590  // Help/documentation options:
591  // -help print the usage
592  // -doc display application documentation in browser
593  // -srcDoc display source code in browser
594  if
595  (
596  options_.found("help")
597  || options_.found("doc")
598  || options_.found("srcDoc")
599  )
600  {
601  if (options_.found("help"))
602  {
603  printUsage();
604  }
605 
606  // Only display one or the other
607  if (options_.found("srcDoc"))
608  {
609  displayDoc(true);
610  }
611  else if (options_.found("doc"))
612  {
613  displayDoc(false);
614  }
615 
616  ::exit(0);
617  }
618 
619  // Print the usage message and exit if the number of arguments is incorrect
620  if (!check(checkArgs, checkOpts))
621  {
622  FatalError.exit();
623  }
624 
625 
626  if (initialise)
627  {
628  string dateString = clock::date();
629  string timeString = clock::clockTime();
630 
631  // Print the banner once only for parallel runs
633  {
635  << "Build : " << Foam::FOAMbuild << nl
636  << "Exec : " << argListStr_.c_str() << nl
637  << "Date : " << dateString.c_str() << nl
638  << "Time : " << timeString.c_str() << nl
639  << "Host : " << hostName() << nl
640  << "PID : " << pid() << endl;
641  }
642 
643  jobInfo_.add("startDate", dateString);
644  jobInfo_.add("startTime", timeString);
645  jobInfo_.add("userName", userName());
646  jobInfo_.add("foamVersion", word(FOAMversion));
647  jobInfo_.add("code", executable_);
648  jobInfo_.add("argList", argListStr_);
649  jobInfo_.add("currentDir", cwd());
650  jobInfo_.add("PPID", ppid());
651  jobInfo_.add("PGID", pgid());
652 
653  // Add build information - only use the first word
654  {
655  std::string build(Foam::FOAMbuild);
656  std::string::size_type found = build.find(' ');
657  if (found != std::string::npos)
658  {
659  build.resize(found);
660  }
661  jobInfo_.add("foamBuild", build);
662  }
663  }
664 
665 
666  // Set fileHandler. In increasing order of priority:
667  // 1. default = uncollated
668  // 2. environment var FOAM_FILEHANDLER
669  // 3. etc/controlDict optimisationSwitches 'fileHandler'
670  // 4. system/controlDict 'fileHandler' (not handled here; done in TimeIO.C)
671 
672  {
673  word handlerType(getEnv("FOAM_FILEHANDLER"));
674  HashTable<string>::const_iterator iter = options_.find("fileHandler");
675  if (iter != options_.end())
676  {
677  handlerType = iter();
678  }
679 
680  if (handlerType.empty())
681  {
682  handlerType = fileOperation::defaultFileHandler;
683  }
684 
685  autoPtr<fileOperation> handler
686  (
688  (
689  handlerType,
691  )
692  );
693  Foam::fileHandler(handler);
694  }
695 
696 
697  stringList slaveMachine;
698  stringList slaveProcs;
699 
700  // Collect slave machine/pid
701  if (parRunControl_.parRun())
702  {
703  if (Pstream::master())
704  {
705  slaveMachine.setSize(Pstream::nProcs() - 1);
706  slaveProcs.setSize(Pstream::nProcs() - 1);
707  label proci = 0;
708  for
709  (
710  int slave = Pstream::firstSlave();
711  slave <= Pstream::lastSlave();
712  slave++
713  )
714  {
715  IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
716 
717  string slaveBuild;
718  label slavePid;
719  fromSlave >> slaveBuild >> slaveMachine[proci] >> slavePid;
720 
721  slaveProcs[proci] = slaveMachine[proci]+"."+name(slavePid);
722  proci++;
723 
724  // Check build string to make sure all processors are running
725  // the same build
726  if (slaveBuild != Foam::FOAMbuild)
727  {
729  << "Master is running version " << Foam::FOAMbuild
730  << "; slave " << proci << " is running version "
731  << slaveBuild
732  << exit(FatalError);
733  }
734  }
735  }
736  else
737  {
738  OPstream toMaster
739  (
742  );
743  toMaster << string(Foam::FOAMbuild) << hostName() << pid();
744  }
745  }
746 
747 
748  // Case is a single processor run unless it is running parallel
749  int nProcs = 1;
750 
751  // Roots if running distributed
752  fileNameList roots;
753 
754  // If this actually is a parallel run
755  if (parRunControl_.parRun())
756  {
757  // For the master
758  if (Pstream::master())
759  {
760  // Establish rootPath_/globalCase_/case_ for master
761  getRootCase();
762 
763  // See if running distributed (different roots for different procs)
764  label dictNProcs = -1;
766 
767  if (options_.found("roots"))
768  {
769  source = "-roots";
770  IStringStream is(options_["roots"]);
771  roots = readList<fileName>(is);
772 
773  if (roots.size() != 1)
774  {
775  dictNProcs = roots.size()+1;
776  }
777  }
778  else if (options_.found("hostRoots"))
779  {
780  source = "-hostRoots";
781  IStringStream is(options_["hostRoots"]);
782  List<Tuple2<wordRe, fileName>> hostRoots(is);
783 
784  roots.setSize(Pstream::nProcs()-1);
785  forAll(hostRoots, i)
786  {
787  const Tuple2<wordRe, fileName>& hostRoot = hostRoots[i];
788  const wordRe& re = hostRoot.first();
789  labelList matchedRoots(findStrings(re, slaveMachine));
790  forAll(matchedRoots, matchi)
791  {
792  label slavei = matchedRoots[matchi];
793  if (roots[slavei] != wordRe())
794  {
796  << "Slave " << slaveMachine[slavei]
797  << " has multiple matching roots in "
798  << hostRoots << exit(FatalError);
799  }
800  else
801  {
802  roots[slavei] = hostRoot.second();
803  }
804  }
805  }
806 
807  // Check
808  forAll(roots, slavei)
809  {
810  if (roots[slavei] == wordRe())
811  {
813  << "Slave " << slaveMachine[slavei]
814  << " has no matching roots in "
815  << hostRoots << exit(FatalError);
816  }
817  }
818 
819  if (roots.size() != 1)
820  {
821  dictNProcs = roots.size()+1;
822  }
823  }
824  else
825  {
826  source = rootPath_/globalCase_/"system/decomposeParDict";
827  IFstream decompDictStream(source);
828 
829  if (!decompDictStream.good())
830  {
831  // Assume non-distributed running
832  dictNProcs = Pstream::nProcs();
833  }
834  else
835  {
836  dictionary decompDict(decompDictStream);
837 
838  dictNProcs = decompDict.lookup<label>("numberOfSubdomains");
839 
840  if (decompDict.lookupOrDefault("distributed", false))
841  {
842  decompDict.lookup("roots") >> roots;
843  }
844  }
845  }
846 
847  // Convenience:
848  // when a single root is specified, use it for all processes
849  if (roots.size() == 1)
850  {
851  const fileName rootName(roots[0]);
852  roots.setSize(Pstream::nProcs()-1, rootName);
853 
854  // adjust dictNProcs for command-line '-roots' option
855  if (dictNProcs < 0)
856  {
857  dictNProcs = roots.size()+1;
858  }
859  }
860 
861 
862  // Check number of processors.
863  // nProcs => number of actual procs
864  // dictNProcs => number of procs specified in decompositionDict
865  // nProcDirs => number of processor directories
866  // (n/a when running distributed)
867  //
868  // - normal running : nProcs = dictNProcs = nProcDirs
869  // - decomposition to more processors : nProcs = dictNProcs
870  // - decomposition to fewer processors : nProcs = nProcDirs
871  if (dictNProcs > Pstream::nProcs())
872  {
873  FatalError
874  << source
875  << " specifies " << dictNProcs
876  << " processors but job was started with "
877  << Pstream::nProcs() << " processors."
878  << exit(FatalError);
879  }
880 
881 
882  // Distributed data
883  if (roots.size())
884  {
885  if (roots.size() != Pstream::nProcs()-1)
886  {
887  FatalError
888  << "number of entries in roots "
889  << roots.size()
890  << " is not equal to the number of slaves "
891  << Pstream::nProcs()-1
892  << exit(FatalError);
893  }
894 
895  forAll(roots, i)
896  {
897  roots[i].expand();
898  }
899 
900  // Distribute the master's argument list (with new root)
901  bool hadCaseOpt = options_.found("case");
902  for
903  (
904  int slave = Pstream::firstSlave();
905  slave <= Pstream::lastSlave();
906  slave++
907  )
908  {
909  options_.set("case", roots[slave-1]/globalCase_);
910 
912  toSlave << args_ << options_;
913  }
914  options_.erase("case");
915 
916  // Restore [-case dir]
917  if (hadCaseOpt)
918  {
919  options_.set("case", rootPath_/globalCase_);
920  }
921  }
922  else
923  {
924  // Possibly going to fewer processors.
925  // Check if all procDirs are there.
926  if (dictNProcs < Pstream::nProcs())
927  {
928  label nProcDirs = 0;
929  while
930  (
931  isDir
932  (
933  rootPath_/globalCase_/"processor"
934  + name(++nProcDirs)
935  )
936  )
937  {}
938 
939  if (nProcDirs != Pstream::nProcs())
940  {
941  FatalError
942  << "number of processor directories = "
943  << nProcDirs
944  << " is not equal to the number of processors = "
945  << Pstream::nProcs()
946  << exit(FatalError);
947  }
948  }
949 
950  // Distribute the master's argument list (unaltered)
951  for
952  (
953  int slave = Pstream::firstSlave();
954  slave <= Pstream::lastSlave();
955  slave++
956  )
957  {
959  toSlave << args_ << options_;
960  }
961  }
962  }
963  else
964  {
965  // Collect the master's argument list
966  IPstream fromMaster
967  (
970  );
971  fromMaster >> args_ >> options_;
972 
973  // Establish rootPath_/globalCase_/case_ for slave
974  getRootCase();
975  }
976 
977  nProcs = Pstream::nProcs();
978  case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
979  }
980  else
981  {
982  // Establish rootPath_/globalCase_/case_
983  getRootCase();
984  case_ = globalCase_;
985  }
986 
988  {
989  Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
990  << "nProcs : " << nProcs << endl;
991 
992  if (parRunControl_.parRun())
993  {
994  Info<< "Slaves : " << slaveProcs << nl;
995  if (roots.size())
996  {
997  Info<< "Roots : " << roots << nl;
998  }
999  Info<< "Pstream initialised with:" << nl
1000  << " floatTransfer : " << Pstream::floatTransfer << nl
1001  << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
1002  << " commsType : "
1004  << " polling iterations : " << Pstream::nPollProcInterfaces
1005  << endl;
1006  }
1007  }
1008 
1009  if (initialise)
1010  {
1011  jobInfo_.add("root", rootPath_);
1012  jobInfo_.add("case", globalCase_);
1013  jobInfo_.add("nProcs", nProcs);
1014  if (slaveProcs.size())
1015  {
1016  jobInfo_.add("slaves", slaveProcs);
1017  }
1018  if (roots.size())
1019  {
1020  jobInfo_.add("roots", roots);
1021  }
1022  jobInfo_.write(executable_, rootPath_/globalCase_);
1023 
1024  // Switch on signal trapping. We have to wait until after Pstream::init
1025  // since this sets up its own ones.
1026  sigFpe_.set(writeInfoHeader);
1027  sigInt_.set(writeInfoHeader);
1028  sigQuit_.set(writeInfoHeader);
1029  sigSegv_.set(writeInfoHeader);
1030 
1031  if (writeInfoHeader)
1032  {
1033  Info<< "fileModificationChecking : "
1034  << "Monitoring run-time modified files using "
1036  [
1038  ];
1039  if
1040  (
1041  (
1044  )
1045  || (
1048  )
1049  )
1050  {
1051  Info<< " (fileModificationSkew "
1053  }
1054  Info<< endl;
1055 
1056  Info<< "allowSystemOperations : ";
1058  {
1059  Info<< "Allowing user-supplied system call operations" << endl;
1060  }
1061  else
1062  {
1063  Info<< "Disallowing user-supplied system call operations"
1064  << endl;
1065  }
1066  }
1067 
1069  {
1070  Info<< endl;
1072  }
1073  }
1074 }
1075 
1076 
1077 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1078 
1080 {
1081  jobInfo_.end();
1082 
1083  // Delete file handler to flush any remaining IO
1084  autoPtr<fileOperation> dummy(nullptr);
1085  fileHandler(dummy);
1086 }
1087 
1088 
1089 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1090 
1091 bool Foam::argList::setOption(const word& opt, const string& param)
1092 {
1093  bool changed = false;
1094 
1095  // Only allow valid options
1096  if (validOptions.found(opt))
1097  {
1098  // Some options are to be protected
1099  if
1100  (
1101  opt == "case"
1102  || opt == "parallel"
1103  || opt == "roots"
1104  || opt == "hostRoots"
1105  )
1106  {
1107  FatalError
1108  <<"used argList::setOption on a protected option: '"
1109  << opt << "'" << endl;
1110  FatalError.exit();
1111  }
1112 
1113  if (validOptions[opt].empty())
1114  {
1115  // Bool option
1116  if (!param.empty())
1117  {
1118  // Disallow change of type
1119  FatalError
1120  <<"used argList::setOption to change bool to non-bool: '"
1121  << opt << "'" << endl;
1122  FatalError.exit();
1123  }
1124  else
1125  {
1126  // Did not previously exist
1127  changed = !options_.found(opt);
1128  }
1129  }
1130  else
1131  {
1132  // Non-bool option
1133  if (param.empty())
1134  {
1135  // Disallow change of type
1136  FatalError
1137  <<"used argList::setOption to change non-bool to bool: '"
1138  << opt << "'" << endl;
1139  FatalError.exit();
1140  }
1141  else
1142  {
1143  // Existing value needs changing, or did not previously exist
1144  changed = options_.found(opt) ? options_[opt] != param : true;
1145  }
1146  }
1147  }
1148  else
1149  {
1150  FatalError
1151  <<"used argList::setOption on an invalid option: '"
1152  << opt << "'" << nl << "allowed are the following:"
1153  << validOptions << endl;
1154  FatalError.exit();
1155  }
1156 
1157  // Set/change the option as required
1158  if (changed)
1159  {
1160  options_.set(opt, param);
1161  }
1162 
1163  return changed;
1164 }
1165 
1166 
1168 {
1169  // Only allow valid options
1170  if (validOptions.found(opt))
1171  {
1172  // Some options are to be protected
1173  if
1174  (
1175  opt == "case"
1176  || opt == "parallel"
1177  || opt == "roots"
1178  || opt == "hostRoots"
1179  )
1180  {
1181  FatalError
1182  <<"used argList::unsetOption on a protected option: '"
1183  << opt << "'" << endl;
1184  FatalError.exit();
1185  }
1186 
1187  // Remove the option, return true if state changed
1188  return options_.erase(opt);
1189  }
1190  else
1191  {
1192  FatalError
1193  <<"used argList::unsetOption on an invalid option: '"
1194  << opt << "'" << nl << "allowed are the following:"
1195  << validOptions << endl;
1196  FatalError.exit();
1197  }
1198 
1199  return false;
1200 }
1201 
1202 
1204 {
1205  // Output notes directly - no automatic text wrapping
1206  if (!notes.empty())
1207  {
1208  Info<< nl;
1210  {
1211  Info<< iter().c_str() << nl;
1212  }
1213  }
1214 }
1215 
1216 
1218 {
1219  Info<< "\nUsage: " << executable_ << " [OPTIONS]";
1220 
1222  {
1223  Info<< " <" << iter().c_str() << '>';
1224  }
1225 
1226  Info<< "\noptions:\n";
1227 
1228  wordList opts = validOptions.sortedToc();
1229  forAll(opts, optI)
1230  {
1231  const word& optionName = opts[optI];
1232 
1233  HashTable<string>::const_iterator iter = validOptions.find(optionName);
1234  Info<< " -" << optionName;
1235  label len = optionName.size() + 3; // Length includes leading ' -'
1236 
1237  if (iter().size())
1238  {
1239  // Length includes space and between option/param and '<>'
1240  len += iter().size() + 3;
1241  Info<< " <" << iter().c_str() << '>';
1242  }
1243 
1245  optionUsage.find(optionName);
1246 
1247  if (usageIter != optionUsage.end())
1248  {
1249  printOptionUsage
1250  (
1251  len,
1252  usageIter()
1253  );
1254  }
1255  else
1256  {
1257  Info<< nl;
1258  }
1259  }
1260 
1261  // Place srcDoc/doc/help options at the end
1262  Info<< " -srcDoc";
1263  printOptionUsage
1264  (
1265  9,
1266  "display source code in browser"
1267  );
1268 
1269  Info<< " -doc";
1270  printOptionUsage
1271  (
1272  6,
1273  "display application documentation in browser"
1274  );
1275 
1276  Info<< " -help";
1277  printOptionUsage
1278  (
1279  7,
1280  "print the usage"
1281  );
1282 
1283 
1284  printNotes();
1285 
1286  Info<< nl
1287  <<"Using: OpenFOAM-" << Foam::FOAMversion
1288  << " (see https://openfoam.org)" << nl
1289  <<"Build: " << Foam::FOAMbuild << nl
1290  << endl;
1291 }
1292 
1293 
1295 {
1296  const dictionary& docDict = debug::controlDict().subDict("Documentation");
1297  List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
1298  fileName docExt(docDict.lookup("doxySourceFileExt"));
1299 
1300  // For source code: change foo_8C.html to foo_8C_source.html
1301  if (source)
1302  {
1303  docExt.replace(".", "_source.");
1304  }
1305 
1306  fileName docFile;
1307  fileName httpServer;
1308  bool found = false;
1309 
1310  forAll(docDirs, dirI)
1311  {
1312  // An HTTP server is treated as a special case ...
1313  if (docDirs[dirI].component(0) == "http:")
1314  {
1315  httpServer = docDirs[dirI]/executable_ + docExt;
1316  }
1317  else
1318  {
1319  // ... all other entries are treated as local directories
1320 
1321  // Remove the optional "file://"
1322  if (docDirs[dirI].component(0) == "file:")
1323  {
1324  docDirs[dirI].replace("file://", string::null);
1325  }
1326 
1327 
1328  // Expand the file name
1329  docFile = docDirs[dirI]/executable_ + docExt;
1330  docFile.expand();
1331 
1332  // Check the existence of the file
1333  if (isFile(docFile))
1334  {
1335  found = true;
1336  break;
1337  }
1338  }
1339  }
1340 
1341  if (found || httpServer != fileName::null)
1342  {
1343  string docBrowser = getEnv("FOAM_DOC_BROWSER");
1344  if (docBrowser.empty())
1345  {
1346  docDict.lookup("docBrowser") >> docBrowser;
1347  }
1348 
1349  if (found)
1350  {
1351  docBrowser += " file://" + docFile;
1352  }
1353  else
1354  {
1355  docBrowser += " " + httpServer;
1356  }
1357 
1358  Info<< "Show documentation: " << docBrowser.c_str() << endl;
1359 
1360  system(docBrowser);
1361  }
1362  else
1363  {
1364  Info<< nl
1365  << "No documentation found for " << executable_
1366  << ", but you can use -help to display the usage\n" << endl;
1367  }
1368 }
1369 
1370 
1371 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
1372 {
1373  bool ok = true;
1374 
1375  if (Pstream::master())
1376  {
1377  if (checkArgs && args_.size() - 1 != validArgs.size())
1378  {
1379  FatalError
1380  << "Wrong number of arguments, expected " << validArgs.size()
1381  << " found " << args_.size() - 1 << endl;
1382  ok = false;
1383  }
1384 
1385  if (checkOpts)
1386  {
1387  forAllConstIter(HashTable<string>, options_, iter)
1388  {
1389  if
1390  (
1391  !validOptions.found(iter.key())
1392  && !validParOptions.found(iter.key())
1393  )
1394  {
1395  FatalError
1396  << "Invalid option: -" << iter.key() << endl;
1397  ok = false;
1398  }
1399  }
1400  }
1401 
1402  if (!ok)
1403  {
1404  printUsage();
1405  }
1406  }
1407 
1408  return ok;
1409 }
1410 
1411 
1413 {
1414  if (!fileHandler().isDir(rootPath()))
1415  {
1416  FatalError
1417  << executable_
1418  << ": cannot open root directory " << rootPath()
1419  << endl;
1420 
1421  return false;
1422  }
1423 
1424  fileName pathDir(fileHandler().filePath(path()));
1425 
1426  if (pathDir.empty() && Pstream::master())
1427  {
1428  // Allow slaves on non-existing processor directories, created later
1429  // (e.g. redistributePar)
1430  FatalError
1431  << executable_
1432  << ": cannot open case directory " << path()
1433  << endl;
1434 
1435  return false;
1436  }
1437 
1438  return true;
1439 }
1440 
1441 
1442 // ************************************************************************* //
static bool floatTransfer
Should compact transfer be used in which floats replace doubles.
Definition: UPstream.H:265
string getEnv(const word &)
Return environment variable of given name.
Definition: POSIX.C:97
bool checkRootCase() const
Check root path and case path.
Definition: argList.C:1412
const word & executable() const
Name of executable without the path.
Definition: argListI.H:36
bool setOption(const word &opt, const string &param="")
Set option directly (use with caution)
Definition: argList.C:1091
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
static void removeOption(const word &opt)
Remove option from validOptions and from optionUsage.
Definition: argList.C:168
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
static int masterNo()
Process index of the master.
Definition: UPstream.H:417
A class for handling file names.
Definition: fileName.H:79
static Stream & writeBanner(Stream &os, bool noHint=false)
Write the standard OpenFOAM file/dictionary banner.
Definition: IOobjectI.H:65
An STL-conforming const_iterator.
Definition: HashTable.H:481
static word postProcessOptionName
Standard name for the post-processing option.
Definition: argList.H:174
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
bool unsetOption(const word &opt)
Unset option directly (use with caution)
Definition: argList.C:1167
static void addUsage(const word &opt, const string &usage)
Add option usage information to optionUsage.
Definition: argList.C:143
error FatalError
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:156
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:323
A 2-tuple for storing two objects of different types.
Definition: HashTable.H:65
bool writeInfoHeader
bool clean()
Cleanup file name.
Definition: fileName.C:93
argList(int &argc, char **&argv, bool checkArgs=true, bool checkOpts=true, bool initialise=true)
Construct from argc and argv.
Definition: argList.C:416
const fileName & rootPath() const
Return root path.
Definition: argListI.H:42
static int firstSlave()
Process index of first slave.
Definition: UPstream.H:446
const Type1 & first() const
Return first.
Definition: Tuple2.H:116
bool isFile(const fileName &, const bool checkVariants=true, const bool followLink=true)
Does the name exist as a file in the file system?
Definition: POSIX.C:555
Template class for non-intrusive linked lists.
Definition: LList.H:51
pid_t ppid()
Return the parent PID of this process.
Definition: POSIX.C:79
static Stream & writeDivider(Stream &os)
Write the standard file section divider.
Definition: IOobjectI.H:113
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
static const fileName null
An empty fileName.
Definition: fileName.H:97
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
static int nProcsSimpleSum
Number of processors at which the sum algorithm changes from linear.
Definition: UPstream.H:269
static SLList< string > notes
Additional notes for usage.
Definition: argList.H:165
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
Foam::argList::initValidTables dummyInitValidTables
Definition: argList.C:112
static void noParallel()
Remove the parallel options.
Definition: argList.C:175
static SLList< string > validArgs
A list of valid (mandatory) arguments.
Definition: argList.H:153
static bool postProcess(int argc, char *argv[])
Return true if the post-processing option is specified.
Definition: argList.C:284
Operations on lists of strings.
const char *const FOAMversion
jobInfo jobInfo_
Definition: jobInfo.C:44
virtual ~argList()
Destructor.
Definition: argList.C:1079
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:330
dlLibraryTable libs
Table of loaded dynamic libraries.
void exit(const int errNo=1)
Exit : can be called for any error to exit program.
Definition: error.C:168
bool add(entry *, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:1133
static autoPtr< fileOperation > New(const word &type, const bool verbose)
Select type.
bool findStrings(const wordReListMatcher &matcher, const std::string &str)
Return true if string matches one of the regular expressions.
Definition: stringListOps.H:52
const dictionary & subDict(const word &) const
Find and return a sub-dictionary.
Definition: dictionary.C:982
void parse(bool checkArgs, bool checkOpts, bool initialise)
Parse.
Definition: argList.C:584
Input inter-processor communications stream.
Definition: IPstream.H:50
static autoPtr< fileOperationInitialise > New(const word &type, int &argc, char **&argv)
Select type.
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
dimensionedScalar pos(const dimensionedScalar &ds)
void displayDoc(bool source=false) const
Display documentation in browser.
Definition: argList.C:1294
static int nPollProcInterfaces
Number of polling cycles in processor updates.
Definition: UPstream.H:275
string hostName(const bool full=false)
Return the system&#39;s host name, as per hostname(1)
Definition: POSIX.C:125
static HashTable< string > validParOptions
A list of valid parallel options.
Definition: argList.H:159
void printNotes() const
Print notes (if any)
Definition: argList.C:1203
bool isAbsolute() const
Return true if file name is absolute.
Definition: fileName.C:73
bool isDir(const fileName &, const bool followLink=true)
Does the name exist as a directory in the file system?
Definition: POSIX.C:539
A class for handling words, derived from string.
Definition: word.H:59
Extract command arguments and options from the supplied argc and argv parameters. ...
Definition: argList.H:102
static int allowSystemOperations
Flag if system operations are allowed.
Definition: dynamicCode.H:156
static void addOption(const word &opt, const string &param="", const string &usage="")
Add to an option to validOptions with usage information.
Definition: argList.C:128
word name() const
Return file name (part beyond last /)
Definition: fileName.C:195
string userName()
Return the user&#39;s login name.
Definition: POSIX.C:165
label size() const
Return the number of arguments.
Definition: argListI.H:90
const fileOperation & fileHandler()
Get current file handler.
static HashTable< string > optionUsage
Short usage information for validOptions.
Definition: argList.H:162
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:73
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
A wordRe is a word, but can also have a regular expression for matching words.
Definition: wordRe.H:74
static string clockTime()
Return the current wall-clock time as a string.
Definition: clock.C:93
static const string null
An empty string.
Definition: string.H:88
pid_t pid()
Return the PID of this process.
Definition: POSIX.C:73
static const char nl
Definition: Ostream.H:260
static fileCheckTypes fileModificationChecking
Type of file modification checking.
Definition: IOobject.H:219
Input from file stream.
Definition: IFstream.H:81
static const NamedEnum< commsTypes, 3 > commsTypeNames
Definition: UPstream.H:71
Output inter-processor communications stream.
Definition: OPstream.H:50
bool open(const fileName &libName, const bool verbose=true)
Open the named library, optionally with warnings if problems occur.
pid_t pgid()
Return the group PID of this process.
Definition: POSIX.C:85
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
fileName path() const
Return the path to the caseName.
Definition: argListI.H:66
static string date()
Return the current wall-clock date as a string.
Definition: clock.C:77
static const NamedEnum< fileCheckTypes, 4 > fileCheckTypesNames
Definition: IOobject.H:134
static commsTypes defaultCommsType
Default commsType.
Definition: UPstream.H:272
void setSize(const label)
Reset size of List.
Definition: List.C:281
string & replace(const string &oldStr, const string &newStr, size_type start=0)
Replace first occurrence of sub-string oldStr with newStr.
Definition: string.C:64
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
fvModels source(alpha1, mixture.thermo1().rho())
bool isspace(char c)
Definition: char.H:53
static word defaultFileHandler
Default fileHandler.
T lookupOrDefault(const word &, const T &, bool recursive=false, bool patternMatch=true) const
Find and return a T,.
string & expand(const bool allowEmpty=false)
Expand initial tildes and all occurrences of environment variables.
Definition: string.C:103
Input from memory buffer stream.
Definition: IStringStream.H:49
static void clear()
Clear the options table.
Definition: argList.C:100
fileName cwd()
Return current working directory path name.
Definition: POSIX.C:241
static float fileModificationSkew
Definition: regIOobject.H:101
const dimensionedScalar re
Classical electron radius: default SI units: [m].
messageStream Info
bool check(bool checkArgs=true, bool checkOpts=true) const
Check argument list.
Definition: argList.C:1371
const Type2 & second() const
Return second.
Definition: Tuple2.H:128
fileName path() const
Return directory path name (part before last /)
Definition: fileName.C:265
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: PtrList.H:52
static void addBoolOption(const word &opt, const string &usage="")
Add to a bool option to validOptions with usage information.
Definition: argList.C:118
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:159
static string::size_type usageMin
Min offset for displaying usage (default: 20)
Definition: argList.H:168
const char *const FOAMbuild
dictionary & controlDict()
The central control dictionary.
Definition: debug.C:120
List< fileName > fileNameList
A List of fileNames.
Definition: fileNameList.H:50
void printUsage() const
Print usage.
Definition: argList.C:1217
A class for handling character strings derived from std::string.
Definition: string.H:76
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
#define FatalErrorIn(functionName)
Report an error message using Foam::FatalError.
Definition: error.H:318
static string::size_type usageMax
Max screen width for displaying usage (default: 80)
Definition: argList.H:171
bool found
static HashTable< string > validOptions
A list of valid options.
Definition: argList.H:156
bool setEnv(const word &name, const std::string &value, const bool overwrite)
Set an environment variable.
Definition: POSIX.C:115
static void addValidParOptions(HashTable< string > &validParOptions)
Add the valid option this type of communications library.
Definition: UPstream.C:31
int system(const std::string &command)
Execute the specified command.
Definition: POSIX.C:1230
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:844
static int lastSlave(const label communicator=0)
Process index of last slave.
Definition: UPstream.H:452