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