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 | Copyright (C) 2011-2016 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 "scotchDecomp.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  defineTypeNameAndDebug(scotchDecomp, 0);
55 
57  (
58  decompositionMethod,
59  scotchDecomp,
60  dictionary
61  );
62 }
63 
64 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
65 
66 void Foam::scotchDecomp::check(const int retVal, const char* str)
67 {
68  if (retVal)
69  {
71  << "Call to scotch routine " << str << " failed."
72  << exit(FatalError);
73  }
74 }
75 
76 
77 Foam::label Foam::scotchDecomp::decompose
78 (
79  const fileName& meshPath,
80  const List<label>& adjncy,
81  const List<label>& xadj,
82  const scalarField& cWeights,
83 
84  List<label>& finalDecomp
85 )
86 {
87  if (!Pstream::parRun())
88  {
89  decomposeOneProc
90  (
91  meshPath,
92  adjncy,
93  xadj,
94  cWeights,
95  finalDecomp
96  );
97  }
98  else
99  {
100  if (debug)
101  {
102  Info<< "scotchDecomp : running in parallel."
103  << " Decomposing all of graph on master processor." << endl;
104  }
105  globalIndex globalCells(xadj.size()-1);
106  label nTotalConnections = returnReduce(adjncy.size(), sumOp<label>());
107 
108  // Send all to master. Use scheduled to save some storage.
109  if (Pstream::master())
110  {
111  Field<label> allAdjncy(nTotalConnections);
112  Field<label> allXadj(globalCells.size()+1);
113  scalarField allWeights(globalCells.size());
114 
115  // Insert my own
116  label nTotalCells = 0;
117  forAll(cWeights, celli)
118  {
119  allXadj[nTotalCells] = xadj[celli];
120  allWeights[nTotalCells++] = cWeights[celli];
121  }
122  nTotalConnections = 0;
123  forAll(adjncy, i)
124  {
125  allAdjncy[nTotalConnections++] = adjncy[i];
126  }
127 
128  for (int slave=1; slave<Pstream::nProcs(); slave++)
129  {
130  IPstream fromSlave(Pstream::scheduled, slave);
131  Field<label> nbrAdjncy(fromSlave);
132  Field<label> nbrXadj(fromSlave);
133  scalarField nbrWeights(fromSlave);
134 
135  // Append.
136  //label procStart = nTotalCells;
137  forAll(nbrXadj, celli)
138  {
139  allXadj[nTotalCells] = nTotalConnections+nbrXadj[celli];
140  allWeights[nTotalCells++] = nbrWeights[celli];
141  }
142  // No need to renumber xadj since already global.
143  forAll(nbrAdjncy, i)
144  {
145  allAdjncy[nTotalConnections++] = nbrAdjncy[i];
146  }
147  }
148  allXadj[nTotalCells] = nTotalConnections;
149 
150 
151  Field<label> allFinalDecomp;
152  decomposeOneProc
153  (
154  meshPath,
155  allAdjncy,
156  allXadj,
157  allWeights,
158  allFinalDecomp
159  );
160 
161 
162  // Send allFinalDecomp back
163  for (int slave=1; slave<Pstream::nProcs(); slave++)
164  {
165  OPstream toSlave(Pstream::scheduled, slave);
166  toSlave << SubField<label>
167  (
168  allFinalDecomp,
169  globalCells.localSize(slave),
170  globalCells.offset(slave)
171  );
172  }
173  // Get my own part (always first)
174  finalDecomp = SubField<label>
175  (
176  allFinalDecomp,
177  globalCells.localSize()
178  );
179  }
180  else
181  {
182  // Send my part of the graph (already in global numbering)
183  {
184  OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
185  toMaster<< adjncy << SubField<label>(xadj, xadj.size()-1)
186  << cWeights;
187  }
188 
189  // Receive back decomposition
190  IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
191  fromMaster >> finalDecomp;
192  }
193  }
194  return 0;
195 }
196 
197 
198 // Call scotch with options from dictionary.
199 Foam::label Foam::scotchDecomp::decomposeOneProc
200 (
201  const fileName& meshPath,
202  const List<label>& adjncy,
203  const List<label>& xadj,
204  const scalarField& cWeights,
205 
206  List<label>& finalDecomp
207 )
208 {
209  // Dump graph
210  if (decompositionDict_.found("scotchCoeffs"))
211  {
212  const dictionary& scotchCoeffs =
213  decompositionDict_.subDict("scotchCoeffs");
214 
215  if (scotchCoeffs.lookupOrDefault("writeGraph", false))
216  {
217  OFstream str(meshPath + ".grf");
218 
219  Info<< "Dumping Scotch graph file to " << str.name() << endl
220  << "Use this in combination with gpart." << endl;
221 
222  label version = 0;
223  str << version << nl;
224  // Numer of vertices
225  str << xadj.size()-1 << ' ' << adjncy.size() << nl;
226  // Numbering starts from 0
227  label baseval = 0;
228  // Has weights?
229  label hasEdgeWeights = 0;
230  label hasVertexWeights = 0;
231  label numericflag = 10*hasEdgeWeights+hasVertexWeights;
232  str << baseval << ' ' << numericflag << nl;
233  for (label celli = 0; celli < xadj.size()-1; celli++)
234  {
235  label start = xadj[celli];
236  label end = xadj[celli+1];
237  str << end-start;
238 
239  for (label i = start; i < end; i++)
240  {
241  str << ' ' << adjncy[i];
242  }
243  str << nl;
244  }
245  }
246  }
247 
248 
249  // Strategy
250  // ~~~~~~~~
251 
252  // Default.
253  SCOTCH_Strat stradat;
254  check(SCOTCH_stratInit(&stradat), "SCOTCH_stratInit");
255 
256  if (decompositionDict_.found("scotchCoeffs"))
257  {
258  const dictionary& scotchCoeffs =
259  decompositionDict_.subDict("scotchCoeffs");
260 
261  string strategy;
262  if (scotchCoeffs.readIfPresent("strategy", strategy))
263  {
264  if (debug)
265  {
266  Info<< "scotchDecomp : Using strategy " << strategy << endl;
267  }
268  SCOTCH_stratGraphMap(&stradat, strategy.c_str());
269  //fprintf(stdout, "S\tStrat=");
270  //SCOTCH_stratSave(&stradat, stdout);
271  //fprintf(stdout, "\n");
272  }
273  }
274 
275 
276  // Graph
277  // ~~~~~
278 
279  List<label> velotab;
280 
281 
282  // Check for externally provided cellweights and if so initialise weights
283  // Note: min, not gMin since routine runs on master only.
284  scalar minWeights = min(cWeights);
285  if (!cWeights.empty())
286  {
287  if (minWeights <= 0)
288  {
290  << "Illegal minimum weight " << minWeights
291  << endl;
292  }
293 
294  if (cWeights.size() != xadj.size()-1)
295  {
297  << "Number of cell weights " << cWeights.size()
298  << " does not equal number of cells " << xadj.size()-1
299  << exit(FatalError);
300  }
301 
302  scalar velotabSum = sum(cWeights)/minWeights;
303 
304  scalar rangeScale(1.0);
305 
306  if (velotabSum > scalar(labelMax - 1))
307  {
308  // 0.9 factor of safety to avoid floating point round-off in
309  // rangeScale tipping the subsequent sum over the integer limit.
310  rangeScale = 0.9*scalar(labelMax - 1)/velotabSum;
311 
313  << "Sum of weights has overflowed integer: " << velotabSum
314  << ", compressing weight scale by a factor of " << rangeScale
315  << endl;
316  }
317 
318  // Convert to integers.
319  velotab.setSize(cWeights.size());
320 
321  forAll(velotab, i)
322  {
323  velotab[i] = int((cWeights[i]/minWeights - 1)*rangeScale) + 1;
324  }
325  }
326 
327 
328 
329  SCOTCH_Graph grafdat;
330  check(SCOTCH_graphInit(&grafdat), "SCOTCH_graphInit");
331  check
332  (
333  SCOTCH_graphBuild
334  (
335  &grafdat,
336  0, // baseval, c-style numbering
337  xadj.size()-1, // vertnbr, nCells
338  xadj.begin(), // verttab, start index per cell into adjncy
339  &xadj[1], // vendtab, end index ,,
340  velotab.begin(), // velotab, vertex weights
341  NULL, // vlbltab
342  adjncy.size(), // edgenbr, number of arcs
343  adjncy.begin(), // edgetab
344  NULL // edlotab, edge weights
345  ),
346  "SCOTCH_graphBuild"
347  );
348  check(SCOTCH_graphCheck(&grafdat), "SCOTCH_graphCheck");
349 
350 
351  // Architecture
352  // ~~~~~~~~~~~~
353  // (fully connected network topology since using switch)
354 
355  SCOTCH_Arch archdat;
356  check(SCOTCH_archInit(&archdat), "SCOTCH_archInit");
357 
358  List<label> processorWeights;
359  if (decompositionDict_.found("scotchCoeffs"))
360  {
361  const dictionary& scotchCoeffs =
362  decompositionDict_.subDict("scotchCoeffs");
363 
364  scotchCoeffs.readIfPresent("processorWeights", processorWeights);
365  }
366  if (processorWeights.size())
367  {
368  if (debug)
369  {
370  Info<< "scotchDecomp : Using procesor weights " << processorWeights
371  << endl;
372  }
373  check
374  (
375  SCOTCH_archCmpltw(&archdat, nProcessors_, processorWeights.begin()),
376  "SCOTCH_archCmpltw"
377  );
378  }
379  else
380  {
381  check
382  (
383  SCOTCH_archCmplt(&archdat, nProcessors_),
384  "SCOTCH_archCmplt"
385  );
386 
387 
388  //- Hack to test clustering. Note that finalDecomp is non-compact
389  // numbers!
390  //
392  //check
393  //(
394  // SCOTCH_archVcmplt(&archdat),
395  // "SCOTCH_archVcmplt"
396  //);
397  //
399  //SCOTCH_Num straval = 0;
402  //
405  //SCOTCH_Num agglomSize = 3;
406  //
408  //check
409  //(
410  // SCOTCH_stratGraphClusterBuild
411  // (
412  // &stradat, // strategy to build
413  // straval, // strategy flags
414  // agglomSize, // cells per cluster
415  // 1.0, // weight?
416  // 0.01 // max load imbalance
417  // ),
418  // "SCOTCH_stratGraphClusterBuild"
419  //);
420  }
421 
422 
423  //SCOTCH_Mapping mapdat;
424  //SCOTCH_graphMapInit(&grafdat, &mapdat, &archdat, NULL);
425  //SCOTCH_graphMapCompute(&grafdat, &mapdat, &stradat); /* Perform mapping */
426  //SCOTCH_graphMapExit(&grafdat, &mapdat);
427 
428 
429  // Hack:switch off fpu error trapping
430  #ifdef FE_NOMASK_ENV
431  int oldExcepts = fedisableexcept
432  (
433  FE_DIVBYZERO
434  | FE_INVALID
435  | FE_OVERFLOW
436  );
437  #endif
438 
439  finalDecomp.setSize(xadj.size()-1);
440  finalDecomp = 0;
441  check
442  (
443  SCOTCH_graphMap
444  (
445  &grafdat,
446  &archdat,
447  &stradat, // const SCOTCH_Strat *
448  finalDecomp.begin() // parttab
449  ),
450  "SCOTCH_graphMap"
451  );
452 
453  #ifdef FE_NOMASK_ENV
454  feenableexcept(oldExcepts);
455  #endif
456 
457 
458 
459  //finalDecomp.setSize(xadj.size()-1);
460  //check
461  //(
462  // SCOTCH_graphPart
463  // (
464  // &grafdat,
465  // nProcessors_, // partnbr
466  // &stradat, // const SCOTCH_Strat *
467  // finalDecomp.begin() // parttab
468  // ),
469  // "SCOTCH_graphPart"
470  //);
471 
472  // Release storage for graph
473  SCOTCH_graphExit(&grafdat);
474  // Release storage for strategy
475  SCOTCH_stratExit(&stradat);
476  // Release storage for network topology
477  SCOTCH_archExit(&archdat);
478 
479  return 0;
480 }
481 
482 
483 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
484 
485 Foam::scotchDecomp::scotchDecomp(const dictionary& decompositionDict)
486 :
487  decompositionMethod(decompositionDict)
488 {}
489 
490 
491 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
492 
493 Foam::labelList Foam::scotchDecomp::decompose
494 (
495  const polyMesh& mesh,
496  const pointField& points,
497  const scalarField& pointWeights
498 )
499 {
500  if (points.size() != mesh.nCells())
501  {
503  << "Can use this decomposition method only for the whole mesh"
504  << endl
505  << "and supply one coordinate (cellCentre) for every cell." << endl
506  << "The number of coordinates " << points.size() << endl
507  << "The number of cells in the mesh " << mesh.nCells()
508  << exit(FatalError);
509  }
510 
511  // Calculate local or global (if Pstream::parRun()) connectivity
512  CompactListList<label> cellCells;
514  (
515  mesh,
516  identity(mesh.nCells()),
517  mesh.nCells(),
518  true,
519  cellCells
520  );
521 
522  // Decompose using default weights
523  List<label> finalDecomp;
524  decompose
525  (
526  mesh.time().path()/mesh.name(),
527  cellCells.m(),
528  cellCells.offsets(),
529  pointWeights,
530  finalDecomp
531  );
532 
533  // Copy back to labelList
534  labelList decomp(finalDecomp.size());
535  forAll(decomp, i)
536  {
537  decomp[i] = finalDecomp[i];
538  }
539  return decomp;
540 }
541 
542 
543 Foam::labelList Foam::scotchDecomp::decompose
544 (
545  const polyMesh& mesh,
546  const labelList& agglom,
547  const pointField& agglomPoints,
548  const scalarField& pointWeights
549 )
550 {
551  if (agglom.size() != mesh.nCells())
552  {
554  << "Size of cell-to-coarse map " << agglom.size()
555  << " differs from number of cells in mesh " << mesh.nCells()
556  << exit(FatalError);
557  }
558 
559  // Calculate local or global (if Pstream::parRun()) connectivity
560  CompactListList<label> cellCells;
562  (
563  mesh,
564  agglom,
565  agglomPoints.size(),
566  true,
567  cellCells
568  );
569 
570  // Decompose using weights
571  List<label> finalDecomp;
572  decompose
573  (
574  mesh.time().path()/mesh.name(),
575  cellCells.m(),
576  cellCells.offsets(),
577  pointWeights,
578  finalDecomp
579  );
580 
581  // Rework back into decomposition for original mesh_
582  labelList fineDistribution(agglom.size());
583 
584  forAll(fineDistribution, i)
585  {
586  fineDistribution[i] = finalDecomp[agglom[i]];
587  }
588 
589  return fineDistribution;
590 }
591 
592 
593 Foam::labelList Foam::scotchDecomp::decompose
594 (
595  const labelListList& globalCellCells,
596  const pointField& cellCentres,
597  const scalarField& cWeights
598 )
599 {
600  if (cellCentres.size() != globalCellCells.size())
601  {
603  << "Inconsistent number of cells (" << globalCellCells.size()
604  << ") and number of cell centres (" << cellCentres.size()
605  << ")." << exit(FatalError);
606  }
607 
608 
609  // Make Metis CSR (Compressed Storage Format) storage
610  // adjncy : contains neighbours (= edges in graph)
611  // xadj(celli) : start of information in adjncy for celli
612 
613  CompactListList<label> cellCells(globalCellCells);
614 
615  // Decompose using weights
616  List<label> finalDecomp;
617  decompose
618  (
619  "scotch",
620  cellCells.m(),
621  cellCells.offsets(),
622  cWeights,
623  finalDecomp
624  );
625 
626  // Copy back to labelList
627  labelList decomp(finalDecomp.size());
628  forAll(decomp, i)
629  {
630  decomp[i] = finalDecomp[i];
631  }
632  return decomp;
633 }
634 
635 
636 // ************************************************************************* //
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:428
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
static int masterNo()
Process index of the master.
Definition: UPstream.H:405
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
error FatalError
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:253
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:411
labelList identity(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
Macros for easy insertion into run-time selection tables.
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
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.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
static const label labelMax
Definition: label.H:62
List< label > labelList
A List of labels.
Definition: labelList.H:56
addToRunTimeSelectionTable(ensightPart, ensightPartCells, istream)
static const char nl
Definition: Ostream.H:262
defineTypeNameAndDebug(combustionModel, 0)
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:393
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:399
#define WarningInFunction
Report a warning using Foam::Warning.
messageStream Info
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Namespace for OpenFOAM.