ptscotch.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 "ptscotch.H"
28 #include "Time.H"
29 #include "OFstream.H"
30 #include "globalIndex.H"
31 #include "SubField.H"
32 #include "PstreamGlobals.H"
33 
34 extern "C"
35 {
36  #include <stdio.h>
37  #include <mpi.h>
38  #include "ptscotch.h"
39 }
40 
41 
42 // Hack: scotch generates floating point errors so need to switch of error
43 // trapping!
44 #ifdef __GLIBC__
45  #ifndef _GNU_SOURCE
46  #define _GNU_SOURCE
47  #endif
48  #include <fenv.h>
49 #endif
50 
51 
52 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
53 
54 namespace Foam
55 {
56 namespace decompositionMethods
57 {
58  defineTypeNameAndDebug(ptscotch, 0);
59 
61  (
62  decompositionMethod,
63  ptscotch,
64  distributor
65  );
66 }
67 }
68 
69 
70 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
71 
72 void Foam::decompositionMethods::ptscotch::check
73 (
74  const int retVal,
75  const char* str
76 )
77 {
78  if (retVal)
79  {
81  << "Call to scotch routine " << str << " failed."
82  << exit(FatalError);
83  }
84 }
85 
86 
88 (
89  const fileName& meshPath,
90  const labelList& adjncy,
91  const labelList& xadj,
92  const scalarField& cellWeights,
93  labelList& decomp
94 ) const
95 {
96  labelList dummyAdjncy(1);
97  labelList dummyXadj(1, label(0));
98 
99  return decompose
100  (
101  meshPath,
102  adjncy.size(),
103  (adjncy.size() ? adjncy.begin() : dummyAdjncy.begin()),
104  xadj.size(),
105  (xadj.size() ? xadj.begin() : dummyXadj.begin()),
106  cellWeights,
107  decomp
108  );
109 }
110 
111 
113 (
114  const fileName& meshPath,
115  const label adjncySize,
116  const label adjncy[],
117  const label xadjSize,
118  const label xadj[],
119  const scalarField& cellWeights,
120  labelList& decomp
121 ) const
122 {
123  if (debug)
124  {
125  Pout<< "ptscotch : entering with xadj:" << xadjSize << endl;
126  }
127 
128  // Dump graph
129  if (decompositionDict_.found("scotchCoeffs"))
130  {
131  const dictionary& scotchCoeffs =
132  decompositionDict_.subDict("scotchCoeffs");
133 
134  if (scotchCoeffs.lookupOrDefault("writeGraph", false))
135  {
136  OFstream str
137  (
138  meshPath + "_" + Foam::name(Pstream::myProcNo()) + ".dgr"
139  );
140 
141  Pout<< "Dumping Scotch graph file to " << str.name() << endl
142  << "Use this in combination with dgpart." << endl;
143 
144  globalIndex globalCells(xadjSize-1);
145 
146  // Distributed graph file (.grf)
147  label version = 2;
148  str << version << nl;
149  // Number of files (procglbnbr)
150  str << Pstream::nProcs();
151  // My file number (procloc)
152  str << ' ' << Pstream::myProcNo() << nl;
153 
154  // Total number of vertices (vertglbnbr)
155  str << globalCells.size();
156  // Total number of connections (edgeglbnbr)
157  str << ' ' << returnReduce(xadj[xadjSize-1], sumOp<label>())
158  << nl;
159  // Local number of vertices (vertlocnbr)
160  str << xadjSize-1;
161  // Local number of connections (edgelocnbr)
162  str << ' ' << xadj[xadjSize-1] << nl;
163  // Numbering starts from 0
164  label baseval = 0;
165  // 100*hasVertlabels+10*hasEdgeWeights+1*hasVertWeighs
166  str << baseval << ' ' << "000" << nl;
167  for (label celli = 0; celli < xadjSize-1; celli++)
168  {
169  label start = xadj[celli];
170  label end = xadj[celli+1];
171  str << end-start;
172 
173  for (label i = start; i < end; i++)
174  {
175  str << ' ' << adjncy[i];
176  }
177  str << nl;
178  }
179  }
180  }
181 
182  // Strategy
183  // ~~~~~~~~
184 
185  // Default.
186  SCOTCH_Strat stradat;
187  check(SCOTCH_stratInit(&stradat), "SCOTCH_stratInit");
188 
189  if (decompositionDict_.found("scotchCoeffs"))
190  {
191  const dictionary& scotchCoeffs =
192  decompositionDict_.subDict("scotchCoeffs");
193 
194  string strategy;
195  if (scotchCoeffs.readIfPresent("strategy", strategy))
196  {
197  if (debug)
198  {
199  Info<< "ptscotch : Using strategy " << strategy << endl;
200  }
201  SCOTCH_stratDgraphMap(&stradat, strategy.c_str());
202  }
203  }
204 
205 
206  // Graph
207  // ~~~~~
208 
209  labelList velotab;
210 
211  // Check for externally provided cellweights and if so initialise weights
212  if (returnReduce(cellWeights.size(), sumOp<label>()))
213  {
214  if (cellWeights.size() != xadjSize-1)
215  {
217  << "Number of cell weights " << cellWeights.size()
218  << " does not equal number of cells " << xadjSize-1
219  << exit(FatalError);
220  }
221 
222  label nWeights = 1;
223  velotab = scaleWeights(cellWeights, nWeights);
224 
225  if (nWeights == 1 && !cellWeights.size())
226  {
227  // Locally zero cells but not globally. Make sure we have
228  // some size so .begin() does not return null pointer. Data
229  // itself is never used.
230  velotab.setSize(1, 1);
231  }
232  }
233 
234 
235  if (debug)
236  {
237  Pout<< "SCOTCH_dgraphInit" << endl;
238  }
239 
240  SCOTCH_Dgraph grafdat;
241  check
242  (
243  SCOTCH_dgraphInit(&grafdat, PstreamGlobals::MPI_COMM_FOAM),
244  "SCOTCH_dgraphInit"
245  );
246 
247 
248  if (debug)
249  {
250  Pout<< "SCOTCH_dgraphBuild with:" << nl
251  << "xadjSize-1 : " << xadjSize-1 << nl
252  << "xadj : " << uintptr_t(xadj) << nl
253  << "velotab : " << uintptr_t(velotab.begin()) << nl
254  << "adjncySize : " << adjncySize << nl
255  << "adjncy : " << uintptr_t(adjncy) << nl
256  << endl;
257  }
258 
259  check
260  (
261  SCOTCH_dgraphBuild
262  (
263  &grafdat, // grafdat
264  0, // baseval, c-style numbering
265  xadjSize-1, // vertlocnbr, nCells
266  xadjSize-1, // vertlocmax
267  const_cast<SCOTCH_Num*>(xadj),
268  // vertloctab, start index per cell into
269  // adjncy
270  const_cast<SCOTCH_Num*>(xadj+1),// vendloctab, end index ,,
271 
272  const_cast<SCOTCH_Num*>(velotab.begin()),// veloloctab, vtx weights
273  nullptr, // vlblloctab
274 
275  adjncySize, // edgelocnbr, number of arcs
276  adjncySize, // edgelocsiz
277  const_cast<SCOTCH_Num*>(adjncy), // edgeloctab
278  nullptr, // edgegsttab
279  nullptr // edlotab, edge weights
280  ),
281  "SCOTCH_dgraphBuild"
282  );
283 
284 
285  if (debug)
286  {
287  Pout<< "SCOTCH_dgraphCheck" << endl;
288  }
289  check(SCOTCH_dgraphCheck(&grafdat), "SCOTCH_dgraphCheck");
290 
291 
292  // Architecture
293  // ~~~~~~~~~~~~
294  // (fully connected network topology since using switch)
295 
296  if (debug)
297  {
298  Pout<< "SCOTCH_archInit" << endl;
299  }
300 
301  SCOTCH_Arch archdat;
302  check(SCOTCH_archInit(&archdat), "SCOTCH_archInit");
303 
304  labelList processorWeights;
305  if (decompositionDict_.found("scotchCoeffs"))
306  {
307  const dictionary& scotchCoeffs =
308  decompositionDict_.subDict("scotchCoeffs");
309 
310  scotchCoeffs.readIfPresent("processorWeights", processorWeights);
311  }
312  if (processorWeights.size())
313  {
314  if (debug)
315  {
316  Info<< "ptscotch : Using processor weights "
317  << processorWeights
318  << endl;
319  }
320  check
321  (
322  SCOTCH_archCmpltw(&archdat, nProcessors_, processorWeights.begin()),
323  "SCOTCH_archCmpltw"
324  );
325  }
326  else
327  {
328  if (debug)
329  {
330  Pout<< "SCOTCH_archCmplt" << endl;
331  }
332  check
333  (
334  SCOTCH_archCmplt(&archdat, nProcessors_),
335  "SCOTCH_archCmplt"
336  );
337  }
338 
339 
340  // Hack:switch off fpu error trapping
341  #ifdef FE_NOMASK_ENV
342  int oldExcepts = fedisableexcept
343  (
344  FE_DIVBYZERO
345  | FE_INVALID
346  | FE_OVERFLOW
347  );
348  #endif
349 
350 
351  // Note: always provide allocated storage even if local size 0
352  decomp.setSize(max(1, xadjSize-1));
353  decomp = 0;
354 
355  if (debug)
356  {
357  Pout<< "SCOTCH_dgraphMap" << endl;
358  }
359  check
360  (
361  SCOTCH_dgraphMap
362  (
363  &grafdat,
364  &archdat,
365  &stradat, // const SCOTCH_Strat *
366  decomp.begin() // parttab
367  ),
368  "SCOTCH_graphMap"
369  );
370 
371  #ifdef FE_NOMASK_ENV
372  feenableexcept(oldExcepts);
373  #endif
374 
375  if (debug)
376  {
377  Pout<< "SCOTCH_dgraphExit" << endl;
378  }
379 
380  // Release storage for graph
381  SCOTCH_dgraphExit(&grafdat);
382 
383  // Release storage for strategy
384  SCOTCH_stratExit(&stradat);
385 
386  // Release storage for network topology
387  SCOTCH_archExit(&archdat);
388 
389  return 0;
390 }
391 
392 
393 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
394 
396 (
397  const dictionary& decompositionDict
398 )
399 :
400  decompositionMethod(decompositionDict)
401 {}
402 
403 
404 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
405 
407 (
408  const polyMesh& mesh,
409  const pointField& points,
410  const scalarField& pointWeights
411 )
412 {
413  if (points.size() != mesh.nCells())
414  {
416  << "Can use this decomposition method only for the whole mesh"
417  << endl
418  << "and supply one coordinate (cellCentre) for every cell." << endl
419  << "The number of coordinates " << points.size() << endl
420  << "The number of cells in the mesh " << mesh.nCells()
421  << exit(FatalError);
422  }
423 
424  checkWeights(points, pointWeights);
425 
426  // Make Metis CSR (Compressed Storage Format) storage
427  // adjncy : contains neighbours (= edges in graph)
428  // xadj(celli) : start of information in adjncy for celli
429  CompactListList<label> cellCells;
431  (
432  mesh,
433  identityMap(mesh.nCells()),
434  mesh.nCells(),
435  true,
436  cellCells
437  );
438 
439  // Decompose using default weights
440  labelList decomp;
441  decompose
442  (
443  mesh.time().path()/mesh.name(),
444  cellCells.m(),
445  cellCells.offsets(),
446  pointWeights,
447  decomp
448  );
449 
450  return decomp;
451 }
452 
453 
455 (
456  const polyMesh& mesh,
457  const labelList& agglom,
458  const pointField& agglomPoints,
459  const scalarField& pointWeights
460 )
461 {
462  if (agglom.size() != mesh.nCells())
463  {
465  << "Size of cell-to-coarse map " << agglom.size()
466  << " differs from number of cells in mesh " << mesh.nCells()
467  << exit(FatalError);
468  }
469 
470  checkWeights(agglomPoints, pointWeights);
471 
472  // Make Metis CSR (Compressed Storage Format) storage
473  // adjncy : contains neighbours (= edges in graph)
474  // xadj(celli) : start of information in adjncy for celli
475  CompactListList<label> cellCells;
476  calcCellCells
477  (
478  mesh,
479  agglom,
480  agglomPoints.size(),
481  true,
482  cellCells
483  );
484 
485  // Decompose using weights
486  labelList decomp;
487  decompose
488  (
489  mesh.time().path()/mesh.name(),
490  cellCells.m(),
491  cellCells.offsets(),
492  pointWeights,
493  decomp
494  );
495 
496  // Rework back into decomposition for original mesh
497  labelList fineDistribution(agglom.size());
498 
499  forAll(fineDistribution, i)
500  {
501  fineDistribution[i] = decomp[agglom[i]];
502  }
503 
504  return fineDistribution;
505 }
506 
507 
509 (
510  const labelListList& globalCellCells,
511  const pointField& cellCentres,
512  const scalarField& cellWeights
513 )
514 {
515  if (cellCentres.size() != globalCellCells.size())
516  {
518  << "Inconsistent number of cells (" << globalCellCells.size()
519  << ") and number of cell centres (" << cellCentres.size()
520  << ")." << exit(FatalError);
521  }
522 
523  checkWeights(cellCentres, cellWeights);
524 
525  // Make Metis CSR (Compressed Storage Format) storage
526  // adjncy : contains neighbours (= edges in graph)
527  // xadj(celli) : start of information in adjncy for celli
528  CompactListList<label> cellCells(globalCellCells);
529 
530  // Decompose using weights
531  labelList decomp;
532  decompose
533  (
534  "ptscotch",
535  cellCells.m(),
536  cellCells.offsets(),
537  cellWeights,
538  decomp
539  );
540 
541  return decomp;
542 }
543 
544 
545 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
Macros for easy insertion into run-time selection tables.
virtual const fileName & name() const
Return the name of the stream.
Definition: OSstream.H:85
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
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.
ptscotch(const dictionary &decompositionDict)
Construct given the decomposition dictionary.
#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
word name(const bool)
Return a word representation of a bool.
Definition: boolIO.C:39
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)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
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