scotch.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-2024 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 "scotch.H"
28 #include "floatScalar.H"
29 #include "Time.H"
30 #include "OFstream.H"
31 #include "globalIndex.H"
32 #include "SubField.H"
33 
34 extern "C"
35 {
36 #include "scotch.h"
37 }
38 
39 
40 // Hack: scotch generates floating point errors so need to switch of error
41 // trapping!
42 #ifdef __GLIBC__
43  #ifndef _GNU_SOURCE
44  #define _GNU_SOURCE
45  #endif
46  #include <fenv.h>
47 #endif
48 
49 
50 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
51 
52 namespace Foam
53 {
54 namespace decompositionMethods
55 {
56  defineTypeNameAndDebug(scotch, 0);
57 
59  (
60  decompositionMethod,
61  scotch,
62  decomposer
63  );
64 
66  (
67  decompositionMethod,
68  scotch,
69  distributor
70  );
71 }
72 }
73 
74 
75 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
76 
77 void Foam::decompositionMethods::scotch::check
78 (
79  const int retVal,
80  const char* str
81 )
82 {
83  if (retVal)
84  {
86  << "Call to scotch routine " << str << " failed."
87  << exit(FatalError);
88  }
89 }
90 
91 
93 (
94  const fileName& meshPath,
95  const labelList& adjncy,
96  const labelList& xadj,
97  const scalarField& cellWeights,
98  labelList& decomp
99 )
100 {
101  if (!Pstream::parRun())
102  {
103  decomposeOneProc
104  (
105  meshPath,
106  adjncy,
107  xadj,
108  cellWeights,
109  decomp
110  );
111  }
112  else
113  {
114  if (debug)
115  {
116  Info<< "scotch : running in parallel."
117  << " Decomposing all of graph on master processor." << endl;
118  }
119  globalIndex globalCells(xadj.size()-1);
120  label nTotalConnections = returnReduce(adjncy.size(), sumOp<label>());
121 
122  // Send all to master. Use scheduled to save some storage.
123  if (Pstream::master())
124  {
125  Field<label> allAdjncy(nTotalConnections);
126  Field<label> allXadj(globalCells.size()+1);
127  scalarField allWeights(globalCells.size());
128 
129  // Insert my own
130  label nTotalCells = 0;
131  forAll(cellWeights, celli)
132  {
133  allXadj[nTotalCells] = xadj[celli];
134  allWeights[nTotalCells++] = cellWeights[celli];
135  }
136  nTotalConnections = 0;
137  forAll(adjncy, i)
138  {
139  allAdjncy[nTotalConnections++] = adjncy[i];
140  }
141 
142  for (int slave=1; slave<Pstream::nProcs(); slave++)
143  {
144  IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
145  Field<label> nbrAdjncy(fromSlave);
146  Field<label> nbrXadj(fromSlave);
147  scalarField nbrWeights(fromSlave);
148 
149  // Append.
150  // label procStart = nTotalCells;
151  forAll(nbrXadj, celli)
152  {
153  allXadj[nTotalCells] = nTotalConnections+nbrXadj[celli];
154  allWeights[nTotalCells++] = nbrWeights[celli];
155  }
156  // No need to renumber xadj since already global.
157  forAll(nbrAdjncy, i)
158  {
159  allAdjncy[nTotalConnections++] = nbrAdjncy[i];
160  }
161  }
162  allXadj[nTotalCells] = nTotalConnections;
163 
164 
165  Field<label> allFinalDecomp;
166  decomposeOneProc
167  (
168  meshPath,
169  allAdjncy,
170  allXadj,
171  allWeights,
172  allFinalDecomp
173  );
174 
175 
176  // Send allFinalDecomp back
177  for (int slave=1; slave<Pstream::nProcs(); slave++)
178  {
179  OPstream toSlave(Pstream::commsTypes::scheduled, slave);
180  toSlave << SubField<label>
181  (
182  allFinalDecomp,
183  globalCells.localSize(slave),
184  globalCells.offset(slave)
185  );
186  }
187  // Get my own part (always first)
188  decomp = SubField<label>
189  (
190  allFinalDecomp,
191  globalCells.localSize()
192  );
193  }
194  else
195  {
196  // Send my part of the graph (already in global numbering)
197  {
198  OPstream toMaster
199  (
202  );
203  toMaster<< adjncy << SubField<label>(xadj, xadj.size()-1)
204  << cellWeights;
205  }
206 
207  // Receive back decomposition
208  IPstream fromMaster
209  (
212  );
213  fromMaster >> decomp;
214  }
215  }
216  return 0;
217 }
218 
219 
220 Foam::label Foam::decompositionMethods::scotch::decomposeOneProc
221 (
222  const fileName& meshPath,
223  const labelList& adjncy,
224  const labelList& xadj,
225  const scalarField& cellWeights,
226 
227  labelList& decomp
228 )
229 {
230  // Dump graph
231  if (decompositionDict_.found("scotchCoeffs"))
232  {
233  const dictionary& scotchCoeffs =
234  decompositionDict_.subDict("scotchCoeffs");
235 
236  if (scotchCoeffs.lookupOrDefault("writeGraph", false))
237  {
238  OFstream str(meshPath + ".grf");
239 
240  Info<< "Dumping Scotch graph file to " << str.name() << endl
241  << "Use this in combination with gpart." << endl;
242 
243  label version = 0;
244  str << version << nl;
245  // Number of vertices
246  str << xadj.size()-1 << ' ' << adjncy.size() << nl;
247  // Numbering starts from 0
248  label baseval = 0;
249  // Has weights?
250  label hasEdgeWeights = 0;
251  label hasVertexWeights = 0;
252  label numericflag = 10*hasEdgeWeights+hasVertexWeights;
253  str << baseval << ' ' << numericflag << nl;
254  for (label celli = 0; celli < xadj.size()-1; celli++)
255  {
256  label start = xadj[celli];
257  label end = xadj[celli+1];
258  str << end-start;
259 
260  for (label i = start; i < end; i++)
261  {
262  str << ' ' << adjncy[i];
263  }
264  str << nl;
265  }
266  }
267  }
268 
269  // Reset the seed of the pseudo-random generator used by the graph
270  // partitioning routines of the libScotch library. Two consecutive calls to
271  // the same libScotch partitioning routines, and separated by a call to
272  // SCOTCH randomReset, will always yield the same results, as if the
273  // equivalent standalone Scotch programs were used twice, independently,
274  SCOTCH_randomReset();
275 
276  // Strategy
277  // ~~~~~~~~
278 
279  // Default.
280  SCOTCH_Strat stradat;
281  check(SCOTCH_stratInit(&stradat), "SCOTCH_stratInit");
282 
283  if (decompositionDict_.found("scotchCoeffs"))
284  {
285  const dictionary& scotchCoeffs =
286  decompositionDict_.subDict("scotchCoeffs");
287 
288  string strategy;
289  if (scotchCoeffs.readIfPresent("strategy", strategy))
290  {
291  if (debug)
292  {
293  Info<< "scotch : Using strategy " << strategy << endl;
294  }
295  SCOTCH_stratGraphMap(&stradat, strategy.c_str());
296  }
297  }
298 
299 
300  // Graph
301  // ~~~~~
302 
303  labelList velotab;
304 
305  // Check for externally provided cellweights and if so initialise weights
306  if (!cellWeights.empty())
307  {
308  if (cellWeights.size() != xadj.size()-1)
309  {
311  << "Number of cell weights " << cellWeights.size()
312  << " does not equal number of cells " << xadj.size()-1
313  << exit(FatalError);
314  }
315 
316  label nWeights = 1;
317  velotab = scaleWeights(cellWeights, nWeights, false);
318  }
319 
320 
321  SCOTCH_Graph grafdat;
322  check(SCOTCH_graphInit(&grafdat), "SCOTCH_graphInit");
323  check
324  (
325  SCOTCH_graphBuild
326  (
327  &grafdat,
328  0, // baseval, c-style numbering
329  xadj.size()-1, // vertnbr, nCells
330  xadj.begin(), // verttab, start index per cell into adjncy
331  &xadj[1], // vendtab, end index ,,
332  velotab.begin(), // velotab, vertex weights
333  nullptr, // vlbltab
334  adjncy.size(), // edgenbr, number of arcs
335  adjncy.begin(), // edgetab
336  nullptr // edlotab, edge weights
337  ),
338  "SCOTCH_graphBuild"
339  );
340  check(SCOTCH_graphCheck(&grafdat), "SCOTCH_graphCheck");
341 
342 
343  // Architecture
344  // ~~~~~~~~~~~~
345  // (fully connected network topology since using switch)
346 
347  SCOTCH_Arch archdat;
348  check(SCOTCH_archInit(&archdat), "SCOTCH_archInit");
349 
350  labelList processorWeights;
351  if (decompositionDict_.found("scotchCoeffs"))
352  {
353  const dictionary& scotchCoeffs =
354  decompositionDict_.subDict("scotchCoeffs");
355 
356  scotchCoeffs.readIfPresent("processorWeights", processorWeights);
357  }
358  if (processorWeights.size())
359  {
360  if (debug)
361  {
362  Info<< "scotch : Using processor weights " << processorWeights
363  << endl;
364  }
365  check
366  (
367  SCOTCH_archCmpltw(&archdat, nProcessors_, processorWeights.begin()),
368  "SCOTCH_archCmpltw"
369  );
370  }
371  else
372  {
373  check
374  (
375  SCOTCH_archCmplt(&archdat, nProcessors_),
376  "SCOTCH_archCmplt"
377  );
378  }
379 
380  // Hack:switch off fpu error trapping
381  #ifdef FE_NOMASK_ENV
382  int oldExcepts = fedisableexcept
383  (
384  FE_DIVBYZERO
385  | FE_INVALID
386  | FE_OVERFLOW
387  );
388  #endif
389 
390  decomp.setSize(xadj.size()-1);
391  decomp = 0;
392  check
393  (
394  SCOTCH_graphMap
395  (
396  &grafdat,
397  &archdat,
398  &stradat, // const SCOTCH_Strat *
399  decomp.begin() // parttab
400  ),
401  "SCOTCH_graphMap"
402  );
403 
404  #ifdef FE_NOMASK_ENV
405  feenableexcept(oldExcepts);
406  #endif
407 
408  // Release storage for graph
409  SCOTCH_graphExit(&grafdat);
410 
411  // Release storage for strategy
412  SCOTCH_stratExit(&stradat);
413 
414  // Release storage for network topology
415  SCOTCH_archExit(&archdat);
416 
417  return 0;
418 }
419 
420 
421 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
422 
423 Foam::decompositionMethods::scotch::scotch(const dictionary& decompositionDict)
424 :
425  decompositionMethod(decompositionDict)
426 {}
427 
428 
429 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
430 
432 (
433  const polyMesh& mesh,
434  const pointField& points,
435  const scalarField& pointWeights
436 )
437 {
438  if (points.size() != mesh.nCells())
439  {
441  << "Can use this decomposition method only for the whole mesh"
442  << endl
443  << "and supply one coordinate (cellCentre) for every cell." << endl
444  << "The number of coordinates " << points.size() << endl
445  << "The number of cells in the mesh " << mesh.nCells()
446  << exit(FatalError);
447  }
448 
449  checkWeights(points, pointWeights);
450 
451  // Calculate local or global (if Pstream::parRun()) connectivity
452  CompactListList<label> cellCells;
454  (
455  mesh,
456  identityMap(mesh.nCells()),
457  mesh.nCells(),
458  true,
459  cellCells
460  );
461 
462  // Decompose using default weights
463  labelList decomp;
464  decompose
465  (
466  mesh.time().path()/mesh.name(),
467  cellCells.m(),
468  cellCells.offsets(),
469  pointWeights,
470  decomp
471  );
472 
473  return decomp;
474 }
475 
476 
478 (
479  const polyMesh& mesh,
480  const labelList& agglom,
481  const pointField& agglomPoints,
482  const scalarField& pointWeights
483 )
484 {
485  if (agglom.size() != mesh.nCells())
486  {
488  << "Size of cell-to-coarse map " << agglom.size()
489  << " differs from number of cells in mesh " << mesh.nCells()
490  << exit(FatalError);
491  }
492 
493  checkWeights(agglomPoints, pointWeights);
494 
495  // Calculate local or global (if Pstream::parRun()) connectivity
496  CompactListList<label> cellCells;
497  calcCellCells
498  (
499  mesh,
500  agglom,
501  agglomPoints.size(),
502  true,
503  cellCells
504  );
505 
506  // Decompose using weights
507  labelList decomp;
508  decompose
509  (
510  mesh.time().path()/mesh.name(),
511  cellCells.m(),
512  cellCells.offsets(),
513  pointWeights,
514  decomp
515  );
516 
517  // Rework back into decomposition for original mesh_
518  labelList fineDistribution(agglom.size());
519 
520  forAll(fineDistribution, i)
521  {
522  fineDistribution[i] = decomp[agglom[i]];
523  }
524 
525  return fineDistribution;
526 }
527 
528 
530 (
531  const labelListList& globalCellCells,
532  const pointField& cellCentres,
533  const scalarField& cellWeights
534 )
535 {
536  if (cellCentres.size() != globalCellCells.size())
537  {
539  << "Inconsistent number of cells (" << globalCellCells.size()
540  << ") and number of cell centres (" << cellCentres.size()
541  << ")." << exit(FatalError);
542  }
543 
544  checkWeights(cellCentres, cellWeights);
545 
546  // Make Metis CSR (Compressed Storage Format) storage
547  // adjncy : contains neighbours (= edges in graph)
548  // xadj(celli) : start of information in adjncy for celli
549 
550  CompactListList<label> cellCells(globalCellCells);
551 
552  // Decompose using weights
553  labelList decomp;
554  decompose
555  (
556  "scotch",
557  cellCells.m(),
558  cellCells.offsets(),
559  cellWeights,
560  decomp
561  );
562 
563  return decomp;
564 }
565 
566 
567 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
Macros for easy insertion into run-time selection tables.
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 label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:399
label checkWeights(const pointField &points, const scalarField &pointWeights) const
Check the weights against the points.
static void calcCellCells(const polyMesh &mesh, const labelList &agglom, const label nLocalCoarse, const bool global, CompactListList< label > &cellCells)
Helper: determine (local or global) cellCells from mesh.
virtual labelList decompose(const pointField &points, const scalarField &pointWeights)
Inherit decompose from decompositionMethod.
scotch(const dictionary &decompositionDict)
Construct given the decomposition dictionary and mesh.
Definition: dummyScotch.C:95
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
volScalarField scalarField(fieldObject, mesh)
const pointField & points
addToRunTimeSelectionTable(decompositionMethod, metis, decomposer)
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
List< label > labelList
A List of labels.
Definition: labelList.H:56
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
messageStream Info
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
error FatalError
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
static const char nl
Definition: Ostream.H:266