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,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 "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)
92 static inline Foam::fileName getGraphPathBase(const polyMesh& mesh)
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 
317  // Do optional tree-like/multi-level decomposition by specifying
318  // domains/weights
319  List<SCOTCH_Num> domains;
320  List<scalar> dWeights;
321 
322  if
323  (
324  coeffsDict_.readIfPresent("processorWeights", procWeights)
325  && !procWeights.empty()
326  )
327  {
328  if (procWeights.size() != nDomains_)
329  {
331  << "processorWeights (" << procWeights.size()
332  << ") != number of domains (" << nDomains_ << ")" << nl
333  << exit(FatalIOError);
334  }
335 
336  DebugInfo
337  << "scotchDecomp : Using procesor weights "
338  << procWeights << endl;
339 
340  check
341  (
342  SCOTCH_archCmpltw(&archdat, nDomains_, procWeights.cdata()),
343  "SCOTCH_archCmpltw"
344  );
345  }
346  else if
347  (
348  coeffsDict_.readIfPresent("domains", domains, keyType::LITERAL)
349  && coeffsDict_.readIfPresent("domainWeights", dWeights, keyType::LITERAL)
350  )
351  {
352  // multi-level
353 
354  label nTotal = 1;
355  for (const label n : domains)
356  {
357  nTotal *= n;
358  }
359 
360  if (nTotal < nDomains())
361  {
362  const label sz = domains.size();
363  domains.setSize(sz+1);
364  dWeights.setSize(sz+1);
365  for (label i = sz-1; i >= 0; i--)
366  {
367  domains[i+1] = domains[i];
368  dWeights[i+1] = dWeights[i];
369  }
370 
371  if (nDomains() % nTotal)
372  {
374  << "Top level decomposition specifies " << nDomains()
375  << " domains which is not equal to the product of"
376  << " all sub domains " << nTotal
377  << exit(FatalError);
378  }
379 
380  domains[0] = nDomains() / nTotal;
381  dWeights[0] = scalar(1);
382  }
383 
384 
385  // Note: min, not gMin since routine runs on master only.
386  const scalar minWeights = min(dWeights);
387 
388  // Convert to integers.
389  List<SCOTCH_Num> weights(dWeights.size());
390 
391  forAll(weights, i)
392  {
393  weights[i] = static_cast<SCOTCH_Num>
394  (
395  (dWeights[i]/minWeights - 1) + 1
396  );
397  }
398 
399  check
400  (
401  SCOTCH_archTleaf
402  (
403  &archdat,
404  SCOTCH_Num(domains.size()),
405  domains.cdata(),
406  weights.cdata()
407  ),
408  "SCOTCH_archTleaf"
409  );
410  }
411  else
412  {
413  check
414  (
415  SCOTCH_archCmplt(&archdat, nDomains_),
416  "SCOTCH_archCmplt"
417  );
418 
419 
420  //- Hack to test clustering. Note that decomp is non-compact
421  // numbers!
422  //
424  //check
425  //(
426  // SCOTCH_archVcmplt(&archdat),
427  // "SCOTCH_archVcmplt"
428  //);
429  //
431  //SCOTCH_Num straval = 0;
434  //
437  //SCOTCH_Num agglomSize = 3;
438  //
440  //check
441  //(
442  // SCOTCH_stratGraphClusterBuild
443  // (
444  // &stradat, // strategy to build
445  // straval, // strategy flags
446  // agglomSize, // cells per cluster
447  // 1.0, // weight?
448  // 0.01 // max load imbalance
449  // ),
450  // "SCOTCH_stratGraphClusterBuild"
451  //);
452  }
453 
454 
455  //SCOTCH_Mapping mapdat;
456  //SCOTCH_graphMapInit(&grafdat, &mapdat, &archdat, nullptr);
457  //SCOTCH_graphMapCompute(&grafdat, &mapdat, &stradat); /* Perform mapping */
458  //SCOTCH_graphMapExit(&grafdat, &mapdat);
459 
460 
461  // Hack:switch off fpu error trapping
462  #ifdef FE_NOMASK_ENV
463  int oldExcepts = fedisableexcept
464  (
465  FE_DIVBYZERO
466  | FE_INVALID
467  | FE_OVERFLOW
468  );
469  #endif
470 
471  check
472  (
473  SCOTCH_graphMap
474  (
475  &grafdat,
476  &archdat,
477  &stradat, // const SCOTCH_Strat *
478  decomp_param.ref().data() // parttab
479  ),
480  "SCOTCH_graphMap"
481  );
482 
483  #ifdef FE_NOMASK_ENV
484  feenableexcept(oldExcepts);
485  #endif
486 
487  //decomp.resize(numCells);
488  //check
489  //(
490  // SCOTCH_graphPart
491  // (
492  // &grafdat,
493  // nDomains_, // partnbr
494  // &stradat, // const SCOTCH_Strat *
495  // decomp_param.ref().data() // parttab
496  // ),
497  // "SCOTCH_graphPart"
498  //);
499 
500  SCOTCH_graphExit(&grafdat); // Release storage for graph
501  SCOTCH_stratExit(&stradat); // Release storage for strategy
502  SCOTCH_archExit(&archdat); // Release storage for network topology
503 
504  return 0;
505 }
506 
507 
508 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
509 
511 (
512  const dictionary& decompDict,
513  const word& regionName
514 )
515 :
516  metisLikeDecomp(typeName, decompDict, regionName, selectionType::NULL_DICT)
517 {}
518 
519 
520 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
521 
523 (
524  const polyMesh& mesh,
525  const pointField& points,
526  const scalarField& pointWeights
527 ) const
528 {
529  // Where to write graph
530  graphPath_ = getGraphPathBase(mesh);
531 
533  (
534  mesh,
535  points,
536  pointWeights
537  );
538 }
539 
540 
542 (
543  const polyMesh& mesh,
544  const labelList& agglom,
545  const pointField& agglomPoints,
546  const scalarField& pointWeights
547 ) const
548 {
549  // Where to write graph
550  graphPath_ = getGraphPathBase(mesh);
551 
553  (
554  mesh,
555  agglom,
556  agglomPoints,
557  pointWeights
558  );
559 }
560 
561 
563 (
564  const labelListList& globalCellCells,
565  const pointField& cellCentres,
566  const scalarField& cWeights
567 ) const
568 {
569  // Where to write graph
570  graphPath_ = "scotch.grf";
571 
573  (
574  globalCellCells,
575  cellCentres,
576  cWeights
577  );
578 }
579 
580 
581 // ************************************************************************* //
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1)
fileName path() const
Return path.
Definition: Time.H:454
A class for handling file names.
Definition: fileName.H:71
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:132
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
label nDomains_
Number of domains for the decomposition.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:578
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:49
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
static Foam::fileName getGraphPathBase(const polyMesh &mesh)
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:362
const dictionary & coeffsDict_
Coefficients for all derived methods.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
Macros for easy insertion into run-time selection tables.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:414
Foam::word regionName(Foam::polyMesh::defaultRegion)
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
dynamicFvMesh & mesh
const pointField & points
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
virtual labelList decompose(const polyMesh &mesh, const pointField &points, const scalarField &pointWeights) const
Return for every coordinate the wanted processor number.
scotchDecomp(const scotchDecomp &)=delete
No copy construct.
virtual labelList decompose(const polyMesh &mesh, const pointField &points, const scalarField &pointWeights) const
Return for every coordinate the wanted processor number.
String literal.
Definition: keyType.H:82
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
label nDomains() const noexcept
Number of domains.
#define DebugInfo
Report an information message using Foam::Info.
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:194
defineTypeNameAndDebug(combustionModel, 0)
static void check(const int retVal, const char *what)
addToRunTimeSelectionTable(decompositionMethod, kahipDecomp, dictionary)
#define WarningInFunction
Report a warning using Foam::Warning.
const word & name() const
Return reference to name.
Definition: fvMesh.H:389
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:607
messageStream Info
Information stream (stdout output on master, null elsewhere)
label n
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
Namespace for OpenFOAM.
virtual label decomposeSerial(const labelList &adjncy, const labelList &xadj, const List< scalar > &cWeights, labelList &decomp) const
Decompose non-parallel.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...