ptscotchDecomp.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2017 OpenFOAM Foundation
9  Copyright (C) 2015-2023 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "ptscotchDecomp.H"
31 #include "floatScalar.H"
32 #include "globalMeshData.H"
33 #include "Time.H"
35 #include "OFstream.H"
36 #include <cstdio>
37 #include <limits>
38 
39 // Include MPI without any C++ bindings
40 #ifndef MPICH_SKIP_MPICXX
41 #define MPICH_SKIP_MPICXX
42 #endif
43 #ifndef OMPI_SKIP_MPICXX
44 #define OMPI_SKIP_MPICXX
45 #endif
46 
47 // Avoid too many warnings from mpi.h
48 #pragma GCC diagnostic ignored "-Wold-style-cast"
49 
50 #include <mpi.h>
51 #include "ptscotch.h"
52 
53 // Hack: scotch generates floating point errors so need to switch off error
54 // trapping!
55 #ifdef __GLIBC__
56  #ifndef _GNU_SOURCE
57  #define _GNU_SOURCE
58  #endif
59  #include <fenv.h>
60 #endif
61 
62 // Error if we attempt narrowing
63 static_assert
64 (
65  sizeof(Foam::label) <= sizeof(SCOTCH_Num),
66  "SCOTCH_Num is too small for Foam::label, check your scotch headers"
67 );
68 
69 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
70 
71 namespace Foam
72 {
73  defineTypeNameAndDebug(ptscotchDecomp, 0);
75  (
76  decompositionMethod,
77  ptscotchDecomp,
78  dictionary
79  );
80 }
81 
82 
83 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
84 
85 namespace Foam
86 {
87 
88 // Check and print error message
89 static inline void check(const int retVal, const char* what)
90 {
91  if (retVal)
92  {
94  << "Call to scotch routine " << what
95  << " failed (" << retVal << ")\n"
96  << exit(FatalError);
97  }
98 }
99 
100 // The mesh-relative graph path/name (without extension)
101 static inline Foam::fileName getGraphPathBase(const polyMesh& mesh)
102 {
103  return mesh.time().path()/mesh.name();
104 }
105 
106 
107 } // End namespace Foam
108 
109 
110 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
111 
112 Foam::label Foam::ptscotchDecomp::decompose
113 (
114  const labelList& adjncy,
115  const labelList& xadj,
116  const List<scalar>& cWeights,
117  labelList& decomp
118 ) const
119 {
120  const SCOTCH_Num numCells = max(0, (xadj.size()-1));
121 
122  // Addressing
123  ConstPrecisionAdaptor<SCOTCH_Num, label, List> adjncy_param(adjncy);
124  ConstPrecisionAdaptor<SCOTCH_Num, label, List> xadj_param(xadj);
125 
126  // Output: cell -> processor addressing
127  decomp.resize_nocopy(numCells);
128  decomp = 0;
129  PrecisionAdaptor<SCOTCH_Num, label, List> decomp_param(decomp, false);
130 
131  // Avoid potential nullptr issues with zero-sized arrays
132  labelList adjncy_dummy, xadj_dummy, decomp_dummy;
133  if (!numCells)
134  {
135  adjncy_dummy.resize(1, 0);
136  adjncy_param.set(adjncy_dummy);
137 
138  xadj_dummy.resize(2, 0);
139  xadj_param.set(xadj_dummy);
140 
141  decomp_dummy.resize(1, 0);
142  decomp_param.clear(); // Avoid propagating spurious values
143  decomp_param.set(decomp_dummy);
144  }
145 
146 
147  if (debug & 2)
148  {
149  Pout<< "ptscotchDecomp : " << numCells << " cells" << endl;
150  }
151 
152  // Dump graph
153  if (coeffsDict_.getOrDefault("writeGraph", false))
154  {
155  OFstream str
156  (
157  graphPath_ + "_" + Foam::name(Pstream::myProcNo()) + ".dgr"
158  );
159 
160  Pout<< "Dumping Scotch graph file to " << str.name() << endl
161  << "Use this in combination with dgpart." << endl;
162 
163  const label numConnect = adjncy.size();
164  const label nTotCells = returnReduce(numCells, sumOp<label>());
165  const label nTotConnect = returnReduce(numConnect, sumOp<label>());
166 
167  // Version 2 = Distributed graph file (.dgrf)
168  str << "2" << nl;
169 
170  // Number of files (procglbnbr), my file number (procloc)
171  str << Pstream::nProcs() << ' ' << Pstream::myProcNo() << nl;
172 
173  // Total number of vertices (vertglbnbr),
174  // Total number of connections (edgeglbnbr)
175  str << nTotCells << ' ' << nTotConnect << nl;
176 
177  // Local number of vertices (vertlocnbr),
178  // Local number of connections (edgelocnbr)
179  str << numCells << ' ' << numConnect << nl;
180 
181  // Numbering starts from 0
182  // 100*hasVertlabels+10*hasEdgeWeights+1*hasVertWeights
183  str << "0 000" << nl;
184 
185  for (label celli = 0; celli < numCells; ++celli)
186  {
187  const label beg = xadj[celli];
188  const label end = xadj[celli+1];
189 
190  str << (end-beg); // size
191 
192  for (label i = beg; i < end; ++i)
193  {
194  str << ' ' << adjncy[i];
195  }
196  str << nl;
197  }
198  }
199 
200 
201  // Make repeatable
202  SCOTCH_randomReset();
203 
204  // Strategy
205  // ~~~~~~~~
206 
207  // Default.
208  SCOTCH_Strat stradat;
209  check
210  (
211  SCOTCH_stratInit(&stradat),
212  "SCOTCH_stratInit"
213  );
214 
215  string strategy;
216  if (coeffsDict_.readIfPresent("strategy", strategy))
217  {
218  DebugInfo
219  << "ptscotchDecomp : Using strategy " << strategy << endl;
220 
221  SCOTCH_stratDgraphMap(&stradat, strategy.c_str());
222  //fprintf(stdout, "S\tStrat=");
223  //SCOTCH_stratSave(&stradat, stdout);
224  //fprintf(stdout, "\n");
225  }
226 
227 
228  // Graph
229  // ~~~~~
230 
231  // Check for externally provided cellweights and if so initialise weights
232 
233  bool hasWeights = returnReduceOr(cWeights.size());
234 
235  const scalar minWeights = hasWeights ? gMin(cWeights) : scalar(1);
236 
237  if (minWeights <= 0)
238  {
239  hasWeights = false;
241  << "Illegal minimum weight " << minWeights
242  << " ... ignoring"
243  << endl;
244  }
245  else if (hasWeights && (cWeights.size() != numCells))
246  {
248  << "Number of cell weights " << cWeights.size()
249  << " does not equal number of cells " << numCells
250  << exit(FatalError);
251  }
252 
253 
254  List<SCOTCH_Num> velotab;
255 
256  if (hasWeights)
257  {
258  scalar rangeScale(1);
259 
260  const scalar velotabSum = gSum(cWeights)/minWeights;
261 
262  const scalar upperRange = static_cast<scalar>
263  (
265  );
266 
267  if (velotabSum > upperRange)
268  {
269  // 0.9 factor of safety to avoid floating point round-off in
270  // rangeScale tipping the subsequent sum over the integer limit.
271  rangeScale = 0.9*upperRange/velotabSum;
272 
274  << "Sum of weights overflows SCOTCH_Num: " << velotabSum
275  << ", compressing by factor " << rangeScale << endl;
276  }
277 
278  if (cWeights.size())
279  {
280  // Convert to integers.
281  velotab.resize(cWeights.size());
282 
283  forAll(velotab, i)
284  {
285  velotab[i] = static_cast<SCOTCH_Num>
286  (
287  ((cWeights[i]/minWeights - 1)*rangeScale) + 1
288  );
289  }
290  }
291  else
292  {
293  // Locally zero cells but not globally.
294  // Provide some size to avoid null pointer.
295  velotab.resize(1, 1);
296  }
297  }
298  else
299  {
300  // HACK: specify uniform weights
301  // - seems that scotch takes different code paths internally
302  // if weights are not specified (issue #3063)
303 
304  if (numCells > 0)
305  {
306  velotab.resize(numCells);
307  }
308  else
309  {
310  // Locally zero cells but not globally.
311  // Provide some size to avoid null pointer.
312  velotab.resize(1);
313  }
314 
315  velotab = static_cast<SCOTCH_Num>(1);
316  }
317 
318 
319  //
320  // Decomposition graph
321  //
322 
323  if (debug & 2)
324  {
325  Pout<< "SCOTCH_dgraphInit" << endl;
326  }
327  SCOTCH_Dgraph grafdat;
328  check
329  (
330  SCOTCH_dgraphInit(&grafdat, MPI_COMM_WORLD),
331  "SCOTCH_dgraphInit"
332  );
333 
334  if (debug & 2)
335  {
336  Pout<< "SCOTCH_dgraphBuild with:" << nl
337  << "numCells : " << numCells << nl
338  << "xadj : " << name(xadj_param().cdata()) << nl
339  << "velotab : " << name(velotab.cdata()) << nl
340  << "adjncySize : " << adjncy_param().size() << nl
341  << "adjncy : " << name(adjncy_param().cdata()) << nl
342  << endl;
343  }
344 
345  check
346  (
347  SCOTCH_dgraphBuild
348  (
349  &grafdat, // Graph to build
350  0, // Base for indexing (C-style)
351 
352  numCells, // vertlocnbr [== nCells]
353  numCells, // vertlocmax
354 
355  xadj_param.constCast().data(),
356  // vertloctab, start index per cell into
357  // adjncy
358  (xadj_param.constCast().data()+1),
359  // vendloctab, end index ,,
360 
361  velotab.data(), // veloloctab, vtx weights
362  nullptr, // vlblloctab
363 
364  adjncy.size(), // edgelocnbr, number of arcs
365  adjncy.size(), // edgelocsiz
366  adjncy_param.constCast().data(), // edgeloctab
367  nullptr, // edgegsttab
368  nullptr // edlotab, edge weights
369  ),
370  "SCOTCH_dgraphBuild"
371  );
372 
373  if (debug & 2)
374  {
375  Pout<< "SCOTCH_dgraphCheck" << endl;
376  }
377  check
378  (
379  SCOTCH_dgraphCheck(&grafdat),
380  "SCOTCH_dgraphCheck"
381  );
382 
383 
384  // Architecture
385  // ~~~~~~~~~~~~
386  // (fully connected network topology since using switch)
387 
388  if (debug & 2)
389  {
390  Pout<< "SCOTCH_archInit" << endl;
391  }
392  SCOTCH_Arch archdat;
393  check
394  (
395  SCOTCH_archInit(&archdat),
396  "SCOTCH_archInit"
397  );
398 
399  List<SCOTCH_Num> procWeights;
400  if
401  (
402  coeffsDict_.readIfPresent("processorWeights", procWeights)
403  && !procWeights.empty()
404  )
405  {
406  if (procWeights.size() != nDomains_)
407  {
408  FatalIOErrorInFunction(coeffsDict_)
409  << "processorWeights (" << procWeights.size()
410  << ") != number of domains (" << nDomains_ << ")" << nl
411  << exit(FatalIOError);
412  }
413 
414  DebugInfo
415  << "ptscotchDecomp : Using procesor weights "
416  << procWeights << endl;
417 
418  check
419  (
420  SCOTCH_archCmpltw(&archdat, nDomains_, procWeights.cdata()),
421  "SCOTCH_archCmpltw"
422  );
423  }
424  else
425  {
426  // Check for optional domains/weights
427  List<SCOTCH_Num> domains;
428  List<scalar> dWeights;
429  if
430  (
431  coeffsDict_.readIfPresent
432  (
433  "domains",
434  domains,
436  )
437  && coeffsDict_.readIfPresent
438  (
439  "domainWeights",
440  dWeights,
442  )
443  )
444  {
446  << "Ignoring multi-level decomposition since"
447  << " not supported by ptscotch."
448  << " It is supported by scotch" << endl;
449  }
450 
451  if (debug & 2)
452  {
453  Pout<< "SCOTCH_archCmplt" << endl;
454  }
455  check
456  (
457  SCOTCH_archCmplt(&archdat, nDomains_),
458  "SCOTCH_archCmplt"
459  );
460  }
461 
462 
463  // Hack:switch off fpu error trapping
464  #ifdef FE_NOMASK_ENV
465  int oldExcepts = fedisableexcept
466  (
467  FE_DIVBYZERO
468  | FE_INVALID
469  | FE_OVERFLOW
470  );
471  #endif
472 
473  if (debug & 2)
474  {
475  Pout<< "SCOTCH_dgraphMap" << endl;
476  }
477  check
478  (
479  SCOTCH_dgraphMap
480  (
481  &grafdat,
482  &archdat,
483  &stradat, // const SCOTCH_Strat *
484  decomp_param.ref().data() // parttab
485  ),
486  "SCOTCH_graphMap"
487  );
488 
489  #ifdef FE_NOMASK_ENV
490  feenableexcept(oldExcepts);
491  #endif
492 
493  //check
494  //(
495  // SCOTCH_dgraphPart
496  // (
497  // &grafdat,
498  // nDomains_, // partnbr
499  // &stradat, // const SCOTCH_Strat *
500  // decomp_param.ref().data() // parttab
501  // ),
502  // "SCOTCH_graphPart"
503  //);
504 
505  if (debug & 2)
506  {
507  Pout<< "SCOTCH_dgraphExit" << endl;
508  }
509 
510  SCOTCH_dgraphExit(&grafdat); // Release storage for graph
511  SCOTCH_stratExit(&stradat); // Release storage for strategy
512  SCOTCH_archExit(&archdat); // Release storage for network topology
514  return 0;
515 }
516 
517 
518 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
519 
520 Foam::ptscotchDecomp::ptscotchDecomp(const label numDomains)
521 :
522  decompositionMethod(numDomains),
523  coeffsDict_(dictionary::null)
524 {}
525 
526 
528 (
529  const dictionary& decompDict,
530  const word& regionName
531 )
532 :
533  decompositionMethod(decompDict, regionName),
534  coeffsDict_(findCoeffsDict("scotchCoeffs", selectionType::NULL_DICT))
535 {}
536 
537 
538 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
539 
540 Foam::labelList Foam::ptscotchDecomp::decompose
541 (
542  const polyMesh& mesh,
543  const pointField& points,
544  const scalarField& pointWeights
545 ) const
546 {
547  // Where to write graph
548  graphPath_ = getGraphPathBase(mesh);
549 
550  if (!points.empty() && (points.size() != mesh.nCells()))
551  {
553  << "Number of cell centres (" << points.size()
554  << ") != number of cells (" << mesh.nCells() << ")"
555  << exit(FatalError);
556  }
557 
558  // Make Metis CSR (Compressed Storage Format) storage
559  // adjncy : contains neighbours (= edges in graph)
560  // xadj(celli) : start of information in adjncy for celli
561 
562  CompactListList<label> cellCells;
564  (
565  mesh,
566  identity(mesh.nCells()),
567  mesh.nCells(),
568  true,
569  cellCells
570  );
571 
572  // Decompose using default weights
573  labelList decomp;
574  decompose
575  (
576  cellCells.values(),
577  cellCells.offsets(),
578  pointWeights,
579  decomp
580  );
581 
582  return decomp;
583 }
584 
585 
586 Foam::labelList Foam::ptscotchDecomp::decompose
587 (
588  const polyMesh& mesh,
589  const labelList& agglom,
590  const pointField& agglomPoints,
591  const scalarField& agglomWeights
592 ) const
593 {
594  // Where to write graph
595  graphPath_ = getGraphPathBase(mesh);
596 
597  if (agglom.size() != mesh.nCells())
598  {
600  << "Agglomeration size (" << agglom.size()
601  << ") != number of cells (" << mesh.nCells() << ")"
602  << exit(FatalError);
603  }
604 
605 
606  // Make Metis CSR (Compressed Storage Format) storage
607  // adjncy : contains neighbours (= edges in graph)
608  // xadj(celli) : start of information in adjncy for celli
609  CompactListList<label> cellCells;
611  (
612  mesh,
613  agglom,
614  agglomPoints.size(),
615  true,
616  cellCells
617  );
618 
619  // Decompose using weights
620  labelList decomp;
621  decompose
622  (
623  cellCells.values(),
624  cellCells.offsets(),
625  agglomWeights,
626  decomp
627  );
629  // From coarse back to fine for original mesh
630  return labelList(decomp, agglom);
631 }
632 
633 
634 Foam::labelList Foam::ptscotchDecomp::decompose
635 (
636  const CompactListList<label>& globalCellCells,
637  const pointField& cellCentres,
638  const scalarField& cWeights
639 ) const
640 {
641  // Where to write graph
642  graphPath_ = "ptscotch";
643 
644  if (!cellCentres.empty() && (cellCentres.size() != globalCellCells.size()))
645  {
647  << "Number of cell centres (" << cellCentres.size()
648  << ") != number of cells (" << globalCellCells.size() << ")"
649  << exit(FatalError);
650  }
651 
652  // CompactListList is already
653  // Metis CSR (Compressed Storage Format) storage
654  // adjncy : contains neighbours (= edges in graph)
655  // xadj(celli) : start of information in adjncy for celli
656 
657  // Decompose using default weights
658  labelList decomp;
659  decompose
660  (
661  globalCellCells.values(),
662  globalCellCells.offsets(),
663  cWeights,
664  decomp
665  );
666 
667  return decomp;
668 }
669 
670 
671 Foam::labelList Foam::ptscotchDecomp::decompose
672 (
673  const labelListList& globalCellCells,
674  const pointField& cellCentres,
675  const scalarField& cWeights
676 ) const
677 {
678  // Where to write graph
679  graphPath_ = "ptscotch";
680 
681  if (!cellCentres.empty() && (cellCentres.size() != globalCellCells.size()))
682  {
684  << "Number of cell centres (" << cellCentres.size()
685  << ") != number of cells (" << globalCellCells.size() << ")"
686  << exit(FatalError);
687  }
688 
689  // Make Metis CSR (Compressed Storage Format) storage
690  // adjncy : contains neighbours (= edges in graph)
691  // xadj(celli) : start of information in adjncy for celli
692 
693  auto cellCells(CompactListList<label>::pack(globalCellCells));
694 
695  // Decompose using default weights
696  labelList decomp;
697  decompose
698  (
699  cellCells.values(),
700  cellCells.offsets(),
701  cWeights,
702  decomp
703  );
704 
705  return decomp;
706 }
707 
708 
709 // ************************************************************************* //
selectionType
Selection type when handling the coefficients dictionary.
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
const labelList & offsets() const noexcept
Return the offset table (= size()+1)
fileName path() const
Return path = rootPath/caseName. Same as TimePaths::path()
Definition: Time.H:503
A class for handling file names.
Definition: fileName.H:72
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
label size() const noexcept
The primary size (the number of rows/sublists)
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:160
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
label nDomains_
Number of domains for the decomposition.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
Type gMin(const FieldField< Field, Type > &f)
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:666
static void calcCellCells(const polyMesh &mesh, const labelList &agglom, const label nLocalCoarse, const bool parallel, CompactListList< label > &cellCells)
Determine (local or global) cellCells from mesh agglomeration.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static Foam::fileName getGraphPathBase(const polyMesh &mesh)
static CompactListList< T > pack(const UList< SubListType > &lists, const bool checkOverflow=false)
Construct by packing together the list of lists.
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Can be negative if the process i...
Definition: UPstream.H:1074
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:360
virtual const fileName & name() const override
Get the name of the output serial stream. (eg, the name of the Fstream file name) ...
Definition: OSstream.H:128
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
Macros for easy insertion into run-time selection tables.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
ptscotchDecomp(const ptscotchDecomp &)=delete
No copy construct.
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1065
dynamicFvMesh & mesh
Type gSum(const FieldField< Field, Type > &f)
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
const pointField & points
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
Definition: labelLists.C:44
A class for handling words, derived from Foam::string.
Definition: word.H:63
Abstract base class for domain decomposition.
String literal.
Definition: keyType.H:82
#define DebugInfo
Report an information message using Foam::Info.
A packed storage of objects of type <T> using an offset table for access.
int debug
Static debugging option.
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry if present, and assign to T val. FatalIOError if it is found and the number of tokens i...
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:201
defineTypeNameAndDebug(combustionModel, 0)
static void check(const int retVal, const char *what)
#define WarningInFunction
Report a warning using Foam::Warning.
const word & name() const
Return reference to name.
Definition: fvMesh.H:387
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
Foam::word regionName(args.getOrDefault< word >("region", Foam::polyMesh::defaultRegion))
label nCells() const noexcept
Number of mesh cells.
const List< T > & values() const noexcept
Return the packed values.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value. FatalIOError if it is found and the number of...
List< label > labelList
A List of labels.
Definition: List.H:62
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Namespace for OpenFOAM.
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...