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 (!methodDict_.empty())
130  {
131  if (methodDict_.lookupOrDefault("writeGraph", false))
132  {
133  OFstream str
134  (
135  meshPath + "_" + Foam::name(Pstream::myProcNo()) + ".dgr"
136  );
137 
138  Pout<< "Dumping Scotch graph file to " << str.name() << endl
139  << "Use this in combination with dgpart." << endl;
140 
141  globalIndex globalCells(xadjSize-1);
142 
143  // Distributed graph file (.grf)
144  label version = 2;
145  str << version << nl;
146  // Number of files (procglbnbr)
147  str << Pstream::nProcs();
148  // My file number (procloc)
149  str << ' ' << Pstream::myProcNo() << nl;
150 
151  // Total number of vertices (vertglbnbr)
152  str << globalCells.size();
153  // Total number of connections (edgeglbnbr)
154  str << ' ' << returnReduce(xadj[xadjSize-1], sumOp<label>())
155  << nl;
156  // Local number of vertices (vertlocnbr)
157  str << xadjSize-1;
158  // Local number of connections (edgelocnbr)
159  str << ' ' << xadj[xadjSize-1] << nl;
160  // Numbering starts from 0
161  label baseval = 0;
162  // 100*hasVertlabels+10*hasEdgeWeights+1*hasVertWeighs
163  str << baseval << ' ' << "000" << nl;
164  for (label celli = 0; celli < xadjSize-1; celli++)
165  {
166  label start = xadj[celli];
167  label end = xadj[celli+1];
168  str << end-start;
169 
170  for (label i = start; i < end; i++)
171  {
172  str << ' ' << adjncy[i];
173  }
174  str << nl;
175  }
176  }
177  }
178 
179  // Strategy
180  // ~~~~~~~~
181 
182  // Default.
183  SCOTCH_Strat stradat;
184  check(SCOTCH_stratInit(&stradat), "SCOTCH_stratInit");
185 
186  if (!methodDict_.empty())
187  {
188  string strategy;
189  if (methodDict_.readIfPresent("strategy", strategy))
190  {
191  if (debug)
192  {
193  Info<< "ptscotch : Using strategy " << strategy << endl;
194  }
195  SCOTCH_stratDgraphMap(&stradat, strategy.c_str());
196  }
197  }
198 
199 
200  // Graph
201  // ~~~~~
202 
203  labelList velotab;
204 
205  // Check for externally provided cellweights and if so initialise weights
206  if (returnReduce(cellWeights.size(), sumOp<label>()))
207  {
208  if (cellWeights.size() != xadjSize-1)
209  {
211  << "Number of cell weights " << cellWeights.size()
212  << " does not equal number of cells " << xadjSize-1
213  << exit(FatalError);
214  }
215 
216  label nWeights = 1;
217  velotab = scaleWeights(cellWeights, nWeights);
218 
219  if (nWeights == 1 && !cellWeights.size())
220  {
221  // Locally zero cells but not globally. Make sure we have
222  // some size so .begin() does not return null pointer. Data
223  // itself is never used.
224  velotab.setSize(1, 1);
225  }
226  }
227 
228 
229  if (debug)
230  {
231  Pout<< "SCOTCH_dgraphInit" << endl;
232  }
233 
234  SCOTCH_Dgraph grafdat;
235  check
236  (
237  SCOTCH_dgraphInit(&grafdat, PstreamGlobals::MPI_COMM_FOAM),
238  "SCOTCH_dgraphInit"
239  );
240 
241 
242  if (debug)
243  {
244  Pout<< "SCOTCH_dgraphBuild with:" << nl
245  << "xadjSize-1 : " << xadjSize-1 << nl
246  << "xadj : " << uintptr_t(xadj) << nl
247  << "velotab : " << uintptr_t(velotab.begin()) << nl
248  << "adjncySize : " << adjncySize << nl
249  << "adjncy : " << uintptr_t(adjncy) << nl
250  << endl;
251  }
252 
253  check
254  (
255  SCOTCH_dgraphBuild
256  (
257  &grafdat, // grafdat
258  0, // baseval, c-style numbering
259  xadjSize-1, // vertlocnbr, nCells
260  xadjSize-1, // vertlocmax
261  const_cast<SCOTCH_Num*>(xadj),
262  // vertloctab, start index per cell into
263  // adjncy
264  const_cast<SCOTCH_Num*>(xadj+1),// vendloctab, end index ,,
265 
266  const_cast<SCOTCH_Num*>(velotab.begin()),// veloloctab, vtx weights
267  nullptr, // vlblloctab
268 
269  adjncySize, // edgelocnbr, number of arcs
270  adjncySize, // edgelocsiz
271  const_cast<SCOTCH_Num*>(adjncy), // edgeloctab
272  nullptr, // edgegsttab
273  nullptr // edlotab, edge weights
274  ),
275  "SCOTCH_dgraphBuild"
276  );
277 
278 
279  if (debug)
280  {
281  Pout<< "SCOTCH_dgraphCheck" << endl;
282  }
283  check(SCOTCH_dgraphCheck(&grafdat), "SCOTCH_dgraphCheck");
284 
285 
286  // Architecture
287  // ~~~~~~~~~~~~
288  // (fully connected network topology since using switch)
289 
290  if (debug)
291  {
292  Pout<< "SCOTCH_archInit" << endl;
293  }
294 
295  SCOTCH_Arch archdat;
296  check(SCOTCH_archInit(&archdat), "SCOTCH_archInit");
297 
298  labelList processorWeights;
299  if (!methodDict_.empty())
300  {
301  methodDict_.readIfPresent("processorWeights", processorWeights);
302  }
303  if (processorWeights.size())
304  {
305  if (debug)
306  {
307  Info<< "ptscotch : Using processor weights "
308  << processorWeights
309  << endl;
310  }
311  check
312  (
313  SCOTCH_archCmpltw(&archdat, nProcessors_, processorWeights.begin()),
314  "SCOTCH_archCmpltw"
315  );
316  }
317  else
318  {
319  if (debug)
320  {
321  Pout<< "SCOTCH_archCmplt" << endl;
322  }
323  check
324  (
325  SCOTCH_archCmplt(&archdat, nProcessors_),
326  "SCOTCH_archCmplt"
327  );
328  }
329 
330 
331  // Hack:switch off fpu error trapping
332  #ifdef FE_NOMASK_ENV
333  int oldExcepts = fedisableexcept
334  (
335  FE_DIVBYZERO
336  | FE_INVALID
337  | FE_OVERFLOW
338  );
339  #endif
340 
341 
342  // Note: always provide allocated storage even if local size 0
343  decomp.setSize(max(1, xadjSize-1));
344  decomp = 0;
345 
346  if (debug)
347  {
348  Pout<< "SCOTCH_dgraphMap" << endl;
349  }
350  check
351  (
352  SCOTCH_dgraphMap
353  (
354  &grafdat,
355  &archdat,
356  &stradat, // const SCOTCH_Strat *
357  decomp.begin() // parttab
358  ),
359  "SCOTCH_graphMap"
360  );
361 
362  #ifdef FE_NOMASK_ENV
363  feenableexcept(oldExcepts);
364  #endif
365 
366  if (debug)
367  {
368  Pout<< "SCOTCH_dgraphExit" << endl;
369  }
370 
371  // Release storage for graph
372  SCOTCH_dgraphExit(&grafdat);
373 
374  // Release storage for strategy
375  SCOTCH_stratExit(&stradat);
376 
377  // Release storage for network topology
378  SCOTCH_archExit(&archdat);
379 
380  return 0;
381 }
382 
383 
384 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
385 
387 (
388  const dictionary& decompositionDict,
389  const dictionary& methodDict
390 )
391 :
392  decompositionMethod(decompositionDict),
393  methodDict_(methodDict)
394 {}
395 
396 
397 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
398 
400 (
401  const polyMesh& mesh,
402  const pointField& points,
403  const scalarField& pointWeights
404 )
405 {
406  if (points.size() != mesh.nCells())
407  {
409  << "Can use this decomposition method only for the whole mesh"
410  << endl
411  << "and supply one coordinate (cellCentre) for every cell." << endl
412  << "The number of coordinates " << points.size() << endl
413  << "The number of cells in the mesh " << mesh.nCells()
414  << exit(FatalError);
415  }
416 
417  checkWeights(points, pointWeights);
418 
419  // Make Metis CSR (Compressed Storage Format) storage
420  // adjncy : contains neighbours (= edges in graph)
421  // xadj(celli) : start of information in adjncy for celli
422  CompactListList<label> cellCells;
424  (
425  mesh,
427  mesh.nCells(),
428  true,
429  cellCells
430  );
431 
432  // Decompose using default weights
433  labelList decomp;
434  decompose
435  (
436  mesh.time().path()/mesh.name(),
437  cellCells.m(),
438  cellCells.offsets(),
439  pointWeights,
440  decomp
441  );
442 
443  return decomp;
444 }
445 
446 
448 (
449  const polyMesh& mesh,
450  const labelList& agglom,
451  const pointField& agglomPoints,
452  const scalarField& pointWeights
453 )
454 {
455  if (agglom.size() != mesh.nCells())
456  {
458  << "Size of cell-to-coarse map " << agglom.size()
459  << " differs from number of cells in mesh " << mesh.nCells()
460  << exit(FatalError);
461  }
462 
463  checkWeights(agglomPoints, pointWeights);
464 
465  // Make Metis CSR (Compressed Storage Format) storage
466  // adjncy : contains neighbours (= edges in graph)
467  // xadj(celli) : start of information in adjncy for celli
468  CompactListList<label> cellCells;
469  calcCellCells
470  (
471  mesh,
472  agglom,
473  agglomPoints.size(),
474  true,
475  cellCells
476  );
477 
478  // Decompose using weights
479  labelList decomp;
480  decompose
481  (
482  mesh.time().path()/mesh.name(),
483  cellCells.m(),
484  cellCells.offsets(),
485  pointWeights,
486  decomp
487  );
488 
489  // Rework back into decomposition for original mesh
490  labelList fineDistribution(agglom.size());
491 
492  forAll(fineDistribution, i)
493  {
494  fineDistribution[i] = decomp[agglom[i]];
495  }
496 
497  return fineDistribution;
498 }
499 
500 
502 (
503  const labelListList& globalCellCells,
504  const pointField& cellCentres,
505  const scalarField& cellWeights
506 )
507 {
508  if (cellCentres.size() != globalCellCells.size())
509  {
511  << "Inconsistent number of cells (" << globalCellCells.size()
512  << ") and number of cell centres (" << cellCentres.size()
513  << ")." << exit(FatalError);
514  }
515 
516  checkWeights(cellCentres, cellWeights);
517 
518  // Make Metis CSR (Compressed Storage Format) storage
519  // adjncy : contains neighbours (= edges in graph)
520  // xadj(celli) : start of information in adjncy for celli
521  CompactListList<label> cellCells(globalCellCells);
522 
523  // Decompose using weights
524  labelList decomp;
525  decompose
526  (
527  "ptscotch",
528  cellCells.m(),
529  cellCells.offsets(),
530  cellWeights,
531  decomp
532  );
533 
534  return decomp;
535 }
536 
537 
538 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
Macros for easy insertion into run-time selection tables.
label size() const
Return number of elements in table.
Definition: HashTableI.H:65
virtual const fileName & name() const
Return the name of the stream.
Definition: OSstream.H:85
fileName path() const
Explicitly inherit path from TimePaths to disambiguate from.
Definition: TimePaths.H:138
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, const dictionary &methodDict)
Construct given the decomposition dictionary.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:420
const word & name() const
Return reference to name.
Definition: fvMesh.H:434
label nCells() const
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#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:258
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)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
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:267
word meshPath
Definition: setMeshPath.H:1