scotchDecomp.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-2021 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 "scotchDecomp.H"
31 #include "floatScalar.H"
32 #include "Time.H"
33 #include "PrecisionAdaptor.H"
34 #include "OFstream.H"
35 #include <limits>
36 
37 // Probably not needed, but in case we pickup a ptscotch.h ...
38 #define MPICH_SKIP_MPICXX
39 #define OMPI_SKIP_MPICXX
40 
41 #include "scotch.h"
42 
43 // Hack: scotch generates floating point errors so need to switch off error
44 // trapping!
45 #ifdef __GLIBC__
46  #ifndef _GNU_SOURCE
47  #define _GNU_SOURCE
48  #endif
49  #include <fenv.h>
50 #endif
51 
52 // Error if we attempt narrowing
53 static_assert
54 (
55  sizeof(Foam::label) <= sizeof(SCOTCH_Num),
56  "SCOTCH_Num is too small for Foam::label, check your scotch headers"
57 );
58 
59 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
60 
61 namespace Foam
62 {
63  defineTypeNameAndDebug(scotchDecomp, 0);
65  (
66  decompositionMethod,
67  scotchDecomp,
68  dictionary
69  );
70 }
71 
72 
73 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
74 
75 namespace Foam
76 {
77 
78 // Check and print error message
79 static inline void check(const int retVal, const char* what)
80 {
81  if (retVal)
82  {
84  << "Call to scotch routine " << what
85  << " failed (" << retVal << ")\n"
86  << exit(FatalError);
87  }
88 }
89 
90 
91 // The mesh-relative graph path/name (without extension)
93 {
94  return mesh.time().path()/mesh.name();
95 }
96 
97 
98 } // End namespace Foam
99 
100 
101 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
102 
104 (
105  const labelList& adjncy,
106  const labelList& xadj,
107  const List<scalar>& cWeights,
108  labelList& decomp
109 ) const
110 {
111  const SCOTCH_Num numCells = max(0, (xadj.size()-1));
112 
113  // Addressing
114  ConstPrecisionAdaptor<SCOTCH_Num, label, List> adjncy_param(adjncy);
115  ConstPrecisionAdaptor<SCOTCH_Num, label, List> xadj_param(xadj);
116 
117  // Output: cell -> processor addressing
118  decomp.resize(numCells);
119  decomp = 0;
120  PrecisionAdaptor<SCOTCH_Num, label, List> decomp_param(decomp, false);
121 
122  // Avoid potential nullptr issues with zero-sized arrays
123  labelList adjncy_dummy, xadj_dummy, decomp_dummy;
124  if (!numCells)
125  {
126  adjncy_dummy.resize(1, 0);
127  adjncy_param.set(adjncy_dummy);
128 
129  xadj_dummy.resize(2, 0);
130  xadj_param.set(xadj_dummy);
131 
132  decomp_dummy.resize(1, 0);
133  decomp_param.clear(); // Avoid propagating spurious values
134  decomp_param.set(decomp_dummy);
135  }
136 
137 
138  // Dump graph
139  if (coeffsDict_.getOrDefault("writeGraph", false))
140  {
141  OFstream str(graphPath_ + ".grf");
142 
143  Info<< "Dumping Scotch graph file to " << str.name() << nl
144  << "Use this in combination with gpart." << endl;
145 
146  const label numConnect = adjncy.size();
147 
148  // Version 0 = Graph file (.grf)
149  str << "0" << nl;
150 
151  // Number of vertices,
152  // number of edges (connections)
153  str << numCells << ' ' << numConnect << nl;
154 
155  // Numbering starts from 0
156  // 100*hasVertlabels+10*hasEdgeWeights+1*hasVertWeights
157  str << "0 000" << nl;
158 
159  for (label celli = 0; celli < numCells; ++celli)
160  {
161  const label beg = xadj[celli];
162  const label end = xadj[celli+1];
163 
164  str << (end-beg); // size
165 
166  for (label i = beg; i < end; ++i)
167  {
168  str << ' ' << adjncy[i];
169  }
170  str << nl;
171  }
172  }
173 
174 
175  // Make repeatable
176  SCOTCH_randomReset();
177 
178  // Strategy
179  // ~~~~~~~~
180 
181  // Default.
182  SCOTCH_Strat stradat;
183  check
184  (
185  SCOTCH_stratInit(&stradat),
186  "SCOTCH_stratInit"
187  );
188 
189  string strategy;
190  if (coeffsDict_.readIfPresent("strategy", strategy))
191  {
192  DebugInfo << "scotchDecomp : Using strategy " << strategy << endl;
193 
194  SCOTCH_stratGraphMap(&stradat, strategy.c_str());
195  //fprintf(stdout, "S\tStrat=");
196  //SCOTCH_stratSave(&stradat, stdout);
197  //fprintf(stdout, "\n");
198  }
199 
200 
201  // Graph
202  // ~~~~~
203 
204  // Check for externally provided cellweights and if so initialise weights
205 
206  bool hasWeights = !cWeights.empty();
207 
208  // Note: min, not gMin since routine runs on master only.
209  const scalar minWeights = hasWeights ? min(cWeights) : scalar(1);
210 
211  if (minWeights <= 0)
212  {
213  hasWeights = false;
215  << "Illegal minimum weight " << minWeights
216  << " ... ignoring"
217  << endl;
218  }
219  else if (hasWeights && (cWeights.size() != numCells))
220  {
222  << "Number of cell weights " << cWeights.size()
223  << " does not equal number of cells " << numCells
224  << exit(FatalError);
225  }
226 
227 
228  List<SCOTCH_Num> velotab;
229 
230  if (hasWeights)
231  {
232  scalar rangeScale(1);
233 
234  const scalar velotabSum = sum(cWeights)/minWeights;
235 
236  const scalar upperRange = static_cast<scalar>
237  (
239  );
240 
241  if (velotabSum > upperRange)
242  {
243  // 0.9 factor of safety to avoid floating point round-off in
244  // rangeScale tipping the subsequent sum over the integer limit.
245  rangeScale = 0.9*upperRange/velotabSum;
246 
248  << "Sum of weights overflows SCOTCH_Num: " << velotabSum
249  << ", compressing by factor " << rangeScale << endl;
250  }
251 
252  {
253  // Convert to integers.
254  velotab.resize(cWeights.size());
255 
256  forAll(velotab, i)
257  {
258  velotab[i] = static_cast<SCOTCH_Num>
259  (
260  ((cWeights[i]/minWeights - 1)*rangeScale) + 1
261  );
262  }
263  }
264  }
265 
266 
267  //
268  // Decomposition graph
269  //
270 
271  SCOTCH_Graph grafdat;
272  check
273  (
274  SCOTCH_graphInit(&grafdat),
275  "SCOTCH_graphInit"
276  );
277  check
278  (
279  SCOTCH_graphBuild
280  (
281  &grafdat, // Graph to build
282  0, // Base for indexing (C-style)
283 
284  numCells, // Number of vertices [== nCells]
285  xadj_param().cdata(), // verttab, start index per cell into adjncy
286  nullptr, // vendtab, end index (nullptr == automatic)
287 
288  velotab.cdata(), // velotab, vertex weights
289  nullptr, // Vertex labels (nullptr == ignore)
290 
291  adjncy.size(), // Number of graph edges
292  adjncy_param().cdata(), // Edge array
293  nullptr // Edge weights (nullptr == ignore)
294  ),
295  "SCOTCH_graphBuild"
296  );
297  check
298  (
299  SCOTCH_graphCheck(&grafdat),
300  "SCOTCH_graphCheck"
301  );
302 
303 
304  // Architecture
305  // ~~~~~~~~~~~~
306  // (fully connected network topology since using switch)
307 
308  SCOTCH_Arch archdat;
309  check
310  (
311  SCOTCH_archInit(&archdat),
312  "SCOTCH_archInit"
313  );
314 
315  List<SCOTCH_Num> procWeights;
316  if
317  (
318  coeffsDict_.readIfPresent("processorWeights", procWeights)
319  && !procWeights.empty()
320  )
321  {
322  if (procWeights.size() != nDomains_)
323  {
325  << "processorWeights (" << procWeights.size()
326  << ") != number of domains (" << nDomains_ << ")" << nl
327  << exit(FatalIOError);
328  }
329 
330  DebugInfo
331  << "scotchDecomp : Using procesor weights "
332  << procWeights << endl;
333 
334  check
335  (
336  SCOTCH_archCmpltw(&archdat, nDomains_, procWeights.cdata()),
337  "SCOTCH_archCmpltw"
338  );
339  }
340  else
341  {
342  check
343  (
344  SCOTCH_archCmplt(&archdat, nDomains_),
345  "SCOTCH_archCmplt"
346  );
347 
348 
349  //- Hack to test clustering. Note that decomp is non-compact
350  // numbers!
351  //
353  //check
354  //(
355  // SCOTCH_archVcmplt(&archdat),
356  // "SCOTCH_archVcmplt"
357  //);
358  //
360  //SCOTCH_Num straval = 0;
363  //
366  //SCOTCH_Num agglomSize = 3;
367  //
369  //check
370  //(
371  // SCOTCH_stratGraphClusterBuild
372  // (
373  // &stradat, // strategy to build
374  // straval, // strategy flags
375  // agglomSize, // cells per cluster
376  // 1.0, // weight?
377  // 0.01 // max load imbalance
378  // ),
379  // "SCOTCH_stratGraphClusterBuild"
380  //);
381  }
382 
383 
384  //SCOTCH_Mapping mapdat;
385  //SCOTCH_graphMapInit(&grafdat, &mapdat, &archdat, nullptr);
386  //SCOTCH_graphMapCompute(&grafdat, &mapdat, &stradat); /* Perform mapping */
387  //SCOTCH_graphMapExit(&grafdat, &mapdat);
388 
389 
390  // Hack:switch off fpu error trapping
391  #ifdef FE_NOMASK_ENV
392  int oldExcepts = fedisableexcept
393  (
394  FE_DIVBYZERO
395  | FE_INVALID
396  | FE_OVERFLOW
397  );
398  #endif
399 
400  check
401  (
402  SCOTCH_graphMap
403  (
404  &grafdat,
405  &archdat,
406  &stradat, // const SCOTCH_Strat *
407  decomp_param.ref().data() // parttab
408  ),
409  "SCOTCH_graphMap"
410  );
411 
412  #ifdef FE_NOMASK_ENV
413  feenableexcept(oldExcepts);
414  #endif
415 
416  //decomp.resize(numCells);
417  //check
418  //(
419  // SCOTCH_graphPart
420  // (
421  // &grafdat,
422  // nDomains_, // partnbr
423  // &stradat, // const SCOTCH_Strat *
424  // decomp_param.ref().data() // parttab
425  // ),
426  // "SCOTCH_graphPart"
427  //);
428 
429  SCOTCH_graphExit(&grafdat); // Release storage for graph
430  SCOTCH_stratExit(&stradat); // Release storage for strategy
431  SCOTCH_archExit(&archdat); // Release storage for network topology
432 
433  return 0;
434 }
435 
436 
437 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
438 
440 (
441  const dictionary& decompDict,
442  const word& regionName
443 )
444 :
445  metisLikeDecomp(typeName, decompDict, regionName, selectionType::NULL_DICT)
446 {}
447 
448 
449 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
450 
452 (
453  const polyMesh& mesh,
454  const pointField& points,
455  const scalarField& pointWeights
456 ) const
457 {
458  // Where to write graph
459  graphPath_ = getGraphPathBase(mesh);
460 
462  (
463  mesh,
464  points,
465  pointWeights
466  );
467 }
468 
469 
471 (
472  const polyMesh& mesh,
473  const labelList& agglom,
474  const pointField& agglomPoints,
475  const scalarField& pointWeights
476 ) const
477 {
478  // Where to write graph
479  graphPath_ = getGraphPathBase(mesh);
480 
482  (
483  mesh,
484  agglom,
485  agglomPoints,
486  pointWeights
487  );
488 }
489 
490 
492 (
493  const labelListList& globalCellCells,
494  const pointField& cellCentres,
495  const scalarField& cWeights
496 ) const
497 {
498  // Where to write graph
499  graphPath_ = "scotch.grf";
500 
502  (
503  globalCellCells,
504  cellCentres,
505  cWeights
506  );
507 }
508 
509 
510 // ************************************************************************* //
Foam::addToRunTimeSelectionTable
addToRunTimeSelectionTable(decompositionMethod, kahipDecomp, dictionary)
Foam::decompositionMethod::nDomains_
label nDomains_
Number of domains for the decomposition.
Definition: decompositionMethod.H:95
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:67
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
Foam::scalarField
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Definition: primitiveFieldsFwd.H:52
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
Foam::List::resize
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:139
Foam::metisLikeDecomp::coeffsDict_
const dictionary & coeffsDict_
Coefficients for all derived methods.
Definition: metisLikeDecomp.H:60
PrecisionAdaptor.H
Foam::FatalIOError
IOerror FatalIOError
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::scotchDecomp::decomposeSerial
virtual label decomposeSerial(const labelList &adjncy, const labelList &xadj, const List< scalar > &cWeights, labelList &decomp) const
Decompose non-parallel.
Definition: dummyScotchDecomp.C:60
Foam::min
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
Foam::polyMesh
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:77
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
OFstream.H
regionName
Foam::word regionName
Definition: createNamedDynamicFvMesh.H:1
Foam::check
static void check(const int retVal, const char *what)
Definition: ptscotchDecomp.C:80
Foam::Info
messageStream Info
Information stream (stdout output on master, null elsewhere)
scotchDecomp.H
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
Foam::FatalError
error FatalError
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:6
addToRunTimeSelectionTable.H
Macros for easy insertion into run-time selection tables.
stdFoam::end
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:121
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::metisLikeDecomp::decompose
virtual labelList decompose(const polyMesh &mesh, const pointField &points, const scalarField &pointWeights) const
Return for every coordinate the wanted processor number.
Definition: metisLikeDecomp.C:173
Foam::scotchDecomp::scotchDecomp
scotchDecomp(const scotchDecomp &)=delete
No copy construct.
Time.H
Foam::labelListList
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
DebugInfo
#define DebugInfo
Report an information message using Foam::Info.
Definition: messageStream.H:382
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::Time::path
fileName path() const
Return path.
Definition: Time.H:358
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:63
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::sum
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
Definition: DimensionedFieldFunctions.C:327
floatScalar.H
Foam::scotchDecomp::decompose
virtual labelList decompose(const polyMesh &mesh, const pointField &points, const scalarField &pointWeights) const
Return for every coordinate the wanted processor number.
Definition: dummyScotchDecomp.C:90
Foam::fvMesh::time
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:280
FatalIOErrorInFunction
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:473
Foam::getGraphPathBase
static Foam::fileName getGraphPathBase(const polyMesh &mesh)
Definition: ptscotchDecomp.C:92
Foam::dictionary::getOrDefault
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Definition: dictionaryTemplates.C:148
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:328
Foam::dictionary::readIfPresent
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Definition: dictionaryTemplates.C:405
Foam::fvMesh::name
const word & name() const
Return reference to name.
Definition: fvMesh.H:300