1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
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.
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
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/>.
26 By: Francois PELLEGRINI RE: Graph mapping 'strategy' string [ reply ]
27 2008-08-22 10:09 Strategy handling in Scotch is a bit tricky. In order
28 not to be confused, you must have a clear view of how they are built.
31 1- Strategies are made up of "methods" which are combined by means of
34 2- A method is of the form "m{param=value,param=value,...}", where "m"
35 is a single character (this is your first error: "f" is a method name,
36 not a parameter name).
38 3- There exist different sort of strategies : bipartitioning strategies,
39 mapping strategies, ordering strategies, which cannot be mixed. For
40 instance, you cannot build a bipartitioning strategy and feed it to a
41 mapping method (this is your second error).
43 To use the "mapCompute" routine, you must create a mapping strategy, not
44 a bipartitioning one, and so use stratGraphMap() and not
45 stratGraphBipart(). Your mapping strategy should however be based on the
46 "recursive bipartitioning" method ("b"). For instance, a simple (and
47 hence not very efficient) mapping strategy can be :
51 which computes mappings with the recursive bipartitioning method "b",
52 this latter using the Fiduccia-Mattheyses method "f" to compute its
55 If you want an exact partition (see your previous post), try
58 However, these strategies are not the most efficient, as they do not
59 make use of the multi-level framework.
61 To use the multi-level framework, try for instance:
63 "b{sep=m{vert=100,low=h,asc=f}x}"
65 The current default mapping strategy in Scotch can be seen by using the
66 "-vs" option of program gmap. It is, to date:
81 d{pass=40,dif=1,rem=1}
84 f{move=80,pass=-1,bal=0.002491},
85 org=f{move=80,pass=-1,bal=0.002491},
89 f{move=80,pass=-1,bal=0.002491},
100 d{pass=40,dif=1,rem=1}
103 f{move=80,pass=-1,bal=0.002491},
104 org=f{move=80,pass=-1,bal=0.002491},
108 f{move=80,pass=-1,bal=0.002491},
117 Note: instead of gmap run gpart <nProcs> -vs <grfFile>
118 where <grfFile> can be obtained by running with 'writeGraph=true'
120 \*---------------------------------------------------------------------------*/
122 #include "scotchDecomp.H"
123 #include "addToRunTimeSelectionTable.H"
124 #include "floatScalar.H"
126 #include "OFstream.H"
127 #include "globalIndex.H"
128 #include "SubField.H"
136 // Hack: scotch generates floating point errors so need to switch of error
138 #if defined(linux) || defined(linuxAMD64) || defined(linuxIA64)
142 #if defined(LINUX) && defined(__GNUC__)
154 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
158 defineTypeNameAndDebug(scotchDecomp, 0);
160 addToRunTimeSelectionTable
168 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
170 void Foam::scotchDecomp::check(const int retVal, const char* str)
174 FatalErrorIn("scotchDecomp::decompose(..)")
175 << "Call to scotch routine " << str << " failed."
181 Foam::label Foam::scotchDecomp::decompose
183 const fileName& meshPath,
184 const List<int>& adjncy,
185 const List<int>& xadj,
186 const scalarField& cWeights,
188 List<int>& finalDecomp
191 if (!Pstream::parRun())
206 Info<< "scotchDecomp : running in parallel."
207 << " Decomposing all of graph on master processor." << endl;
209 globalIndex globalCells(xadj.size()-1);
210 label nTotalConnections = returnReduce(adjncy.size(), sumOp<label>());
212 // Send all to master. Use scheduled to save some storage.
213 if (Pstream::master())
215 Field<int> allAdjncy(nTotalConnections);
216 Field<int> allXadj(globalCells.size()+1);
217 scalarField allWeights(globalCells.size());
220 label nTotalCells = 0;
221 forAll(cWeights, cellI)
223 allXadj[nTotalCells] = xadj[cellI];
224 allWeights[nTotalCells++] = cWeights[cellI];
226 nTotalConnections = 0;
229 allAdjncy[nTotalConnections++] = adjncy[i];
232 for (int slave=1; slave<Pstream::nProcs(); slave++)
234 IPstream fromSlave(Pstream::scheduled, slave);
235 Field<int> nbrAdjncy(fromSlave);
236 Field<int> nbrXadj(fromSlave);
237 scalarField nbrWeights(fromSlave);
240 //label procStart = nTotalCells;
241 forAll(nbrXadj, cellI)
243 allXadj[nTotalCells] = nTotalConnections+nbrXadj[cellI];
244 allWeights[nTotalCells++] = nbrWeights[cellI];
246 // No need to renumber xadj since already global.
249 allAdjncy[nTotalConnections++] = nbrAdjncy[i];
252 allXadj[nTotalCells] = nTotalConnections;
255 Field<int> allFinalDecomp;
266 // Send allFinalDecomp back
267 for (int slave=1; slave<Pstream::nProcs(); slave++)
269 OPstream toSlave(Pstream::scheduled, slave);
270 toSlave << SubField<int>
273 globalCells.localSize(slave),
274 globalCells.offset(slave)
277 // Get my own part (always first)
278 finalDecomp = SubField<int>
281 globalCells.localSize()
286 // Send my part of the graph (already in global numbering)
288 OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
289 toMaster<< adjncy << SubField<int>(xadj, xadj.size()-1)
293 // Receive back decomposition
294 IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
295 fromMaster >> finalDecomp;
302 // Call scotch with options from dictionary.
303 Foam::label Foam::scotchDecomp::decomposeOneProc
305 const fileName& meshPath,
306 const List<int>& adjncy,
307 const List<int>& xadj,
308 const scalarField& cWeights,
310 List<int>& finalDecomp
314 if (decompositionDict_.found("scotchCoeffs"))
316 const dictionary& scotchCoeffs =
317 decompositionDict_.subDict("scotchCoeffs");
319 if (scotchCoeffs.lookupOrDefault("writeGraph", false))
321 OFstream str(meshPath + ".grf");
323 Info<< "Dumping Scotch graph file to " << str.name() << endl
324 << "Use this in combination with gpart." << endl;
327 str << version << nl;
329 str << xadj.size()-1 << ' ' << adjncy.size() << nl;
330 // Numbering starts from 0
333 label hasEdgeWeights = 0;
334 label hasVertexWeights = 0;
335 label numericflag = 10*hasEdgeWeights+hasVertexWeights;
336 str << baseval << ' ' << numericflag << nl;
337 for (label cellI = 0; cellI < xadj.size()-1; cellI++)
339 label start = xadj[cellI];
340 label end = xadj[cellI+1];
343 for (label i = start; i < end; i++)
345 str << ' ' << adjncy[i];
357 SCOTCH_Strat stradat;
358 check(SCOTCH_stratInit(&stradat), "SCOTCH_stratInit");
360 if (decompositionDict_.found("scotchCoeffs"))
362 const dictionary& scotchCoeffs =
363 decompositionDict_.subDict("scotchCoeffs");
366 if (scotchCoeffs.readIfPresent("strategy", strategy))
370 Info<< "scotchDecomp : Using strategy " << strategy << endl;
372 SCOTCH_stratGraphMap(&stradat, strategy.c_str());
373 //fprintf(stdout, "S\tStrat=");
374 //SCOTCH_stratSave(&stradat, stdout);
375 //fprintf(stdout, "\n");
386 // Check for externally provided cellweights and if so initialise weights
387 // Note: min, not gMin since routine runs on master only.
388 scalar minWeights = min(cWeights);
389 if (cWeights.size() > 0)
395 "scotchDecomp::decompose"
396 "(const pointField&, const scalarField&)"
397 ) << "Illegal minimum weight " << minWeights
401 if (cWeights.size() != xadj.size()-1)
405 "scotchDecomp::decompose"
406 "(const pointField&, const scalarField&)"
407 ) << "Number of cell weights " << cWeights.size()
408 << " does not equal number of cells " << xadj.size()-1
412 scalar velotabSum = sum(cWeights)/minWeights;
414 scalar rangeScale(1.0);
416 if (velotabSum > scalar(INT_MAX - 1))
418 // 0.9 factor of safety to avoid floating point round-off in
419 // rangeScale tipping the subsequent sum over the integer limit.
420 rangeScale = 0.9*scalar(INT_MAX - 1)/velotabSum;
424 "scotchDecomp::decompose"
425 "(const pointField&, const scalarField&)"
426 ) << "Sum of weights has overflowed integer: " << velotabSum
427 << ", compressing weight scale by a factor of " << rangeScale
431 // Convert to integers.
432 velotab.setSize(cWeights.size());
437 int((cWeights[i]/minWeights - 1)*rangeScale) + 1;
443 SCOTCH_Graph grafdat;
444 check(SCOTCH_graphInit(&grafdat), "SCOTCH_graphInit");
450 0, // baseval, c-style numbering
451 xadj.size()-1, // vertnbr, nCells
452 xadj.begin(), // verttab, start index per cell into adjncy
453 &xadj[1], // vendtab, end index ,,
454 velotab.begin(), // velotab, vertex weights
456 adjncy.size(), // edgenbr, number of arcs
457 adjncy.begin(), // edgetab
458 NULL // edlotab, edge weights
462 check(SCOTCH_graphCheck(&grafdat), "SCOTCH_graphCheck");
467 // (fully connected network topology since using switch)
470 check(SCOTCH_archInit(&archdat), "SCOTCH_archInit");
472 List<label> processorWeights;
473 if (decompositionDict_.found("scotchCoeffs"))
475 const dictionary& scotchCoeffs =
476 decompositionDict_.subDict("scotchCoeffs");
478 scotchCoeffs.readIfPresent("processorWeights", processorWeights);
480 if (processorWeights.size())
484 Info<< "scotchDecomp : Using procesor weights " << processorWeights
489 SCOTCH_archCmpltw(&archdat, nProcessors_, processorWeights.begin()),
497 SCOTCH_archCmplt(&archdat, nProcessors_),
503 //SCOTCH_Mapping mapdat;
504 //SCOTCH_graphMapInit(&grafdat, &mapdat, &archdat, NULL);
505 //SCOTCH_graphMapCompute(&grafdat, &mapdat, &stradat); /* Perform mapping */
506 //SCOTCH_graphMapExit(&grafdat, &mapdat);
509 // Hack:switch off fpu error trapping
511 int oldExcepts = fedisableexcept
519 finalDecomp.setSize(xadj.size()-1);
527 &stradat, // const SCOTCH_Strat *
528 finalDecomp.begin() // parttab
534 feenableexcept(oldExcepts);
539 //finalDecomp.setSize(xadj.size()-1);
545 // nProcessors_, // partnbr
546 // &stradat, // const SCOTCH_Strat *
547 // finalDecomp.begin() // parttab
549 // "SCOTCH_graphPart"
552 // Release storage for graph
553 SCOTCH_graphExit(&grafdat);
554 // Release storage for strategy
555 SCOTCH_stratExit(&stradat);
556 // Release storage for network topology
557 SCOTCH_archExit(&archdat);
563 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
565 Foam::scotchDecomp::scotchDecomp(const dictionary& decompositionDict)
567 decompositionMethod(decompositionDict)
571 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
573 Foam::labelList Foam::scotchDecomp::decompose
575 const polyMesh& mesh,
576 const pointField& points,
577 const scalarField& pointWeights
580 if (points.size() != mesh.nCells())
584 "scotchDecomp::decompose(const polyMesh&, const pointField&"
585 ", const scalarField&)"
586 ) << "Can use this decomposition method only for the whole mesh"
588 << "and supply one coordinate (cellCentre) for every cell." << endl
589 << "The number of coordinates " << points.size() << endl
590 << "The number of cells in the mesh " << mesh.nCells()
594 // Calculate local or global (if Pstream::parRun()) connectivity
595 CompactListList<label> cellCells;
596 calcCellCells(mesh, identity(mesh.nCells()), mesh.nCells(), cellCells);
598 // Decompose using default weights
599 List<int> finalDecomp;
602 mesh.time().path()/mesh.name(),
609 // Copy back to labelList
610 labelList decomp(finalDecomp.size());
613 decomp[i] = finalDecomp[i];
619 Foam::labelList Foam::scotchDecomp::decompose
621 const polyMesh& mesh,
622 const labelList& agglom,
623 const pointField& agglomPoints,
624 const scalarField& pointWeights
627 if (agglom.size() != mesh.nCells())
631 "scotchDecomp::decompose"
632 "(const polyMesh&, const labelList&, const pointField&"
633 ", const scalarField&)"
634 ) << "Size of cell-to-coarse map " << agglom.size()
635 << " differs from number of cells in mesh " << mesh.nCells()
639 // Calculate local or global (if Pstream::parRun()) connectivity
640 CompactListList<label> cellCells;
641 calcCellCells(mesh, agglom, agglomPoints.size(), cellCells);
643 // Decompose using weights
644 List<int> finalDecomp;
647 mesh.time().path()/mesh.name(),
654 // Rework back into decomposition for original mesh_
655 labelList fineDistribution(agglom.size());
657 forAll(fineDistribution, i)
659 fineDistribution[i] = finalDecomp[agglom[i]];
662 return fineDistribution;
666 Foam::labelList Foam::scotchDecomp::decompose
668 const labelListList& globalCellCells,
669 const pointField& cellCentres,
670 const scalarField& cWeights
673 if (cellCentres.size() != globalCellCells.size())
677 "scotchDecomp::decompose"
678 "(const labelListList&, const pointField&, const scalarField&)"
679 ) << "Inconsistent number of cells (" << globalCellCells.size()
680 << ") and number of cell centres (" << cellCentres.size()
681 << ")." << exit(FatalError);
685 // Make Metis CSR (Compressed Storage Format) storage
686 // adjncy : contains neighbours (= edges in graph)
687 // xadj(celli) : start of information in adjncy for celli
689 CompactListList<label> cellCells(globalCellCells);
691 // Decompose using weights
692 List<int> finalDecomp;
702 // Copy back to labelList
703 labelList decomp(finalDecomp.size());
706 decomp[i] = finalDecomp[i];
712 // ************************************************************************* //