1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2010 OpenCFD Ltd.
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/>.
24 \*---------------------------------------------------------------------------*/
26 #include "parMetisDecomp.H"
27 #include "metisDecomp.H"
28 #include "scotchDecomp.H"
29 #include "syncTools.H"
30 #include "addToRunTimeSelectionTable.H"
31 #include "floatScalar.H"
34 #include "labelIOField.H"
35 #include "globalIndex.H"
41 # include "parmetis.h"
44 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
48 defineTypeNameAndDebug(parMetisDecomp, 0);
50 addToRunTimeSelectionTable
59 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
61 //- Does prevention of 0 cell domains and calls parmetis.
62 Foam::label Foam::parMetisDecomp::decompose
66 const pointField& cellCentres,
67 Field<int>& cellWeights,
68 Field<int>& faceWeights,
69 const List<int>& options,
71 List<int>& finalDecomp
77 // Number of dimensions
81 if (cellCentres.size() != xadj.size()-1)
83 FatalErrorIn("parMetisDecomp::decompose(..)")
84 << "cellCentres:" << cellCentres.size()
85 << " xadj:" << xadj.size()
90 // Get number of cells on all processors
91 List<int> nLocalCells(Pstream::nProcs());
92 nLocalCells[Pstream::myProcNo()] = xadj.size()-1;
93 Pstream::gatherList(nLocalCells);
94 Pstream::scatterList(nLocalCells);
97 List<int> cellOffsets(Pstream::nProcs()+1);
99 forAll(nLocalCells, procI)
101 cellOffsets[procI] = nGlobalCells;
102 nGlobalCells += nLocalCells[procI];
104 cellOffsets[Pstream::nProcs()] = nGlobalCells;
106 // Convert pointField into float
107 Field<floatScalar> xyz(3*cellCentres.size());
109 forAll(cellCentres, cellI)
111 const point& cc = cellCentres[cellI];
112 xyz[compI++] = float(cc.x());
113 xyz[compI++] = float(cc.y());
114 xyz[compI++] = float(cc.z());
117 // Make sure every domain has at least one cell
118 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
119 // (Metis falls over with zero sized domains)
120 // Trickle cells from processors that have them up to those that
124 // Number of cells to send to the next processor
125 // (is same as number of cells next processor has to receive)
126 List<int> nSendCells(Pstream::nProcs(), 0);
128 for (label procI = nLocalCells.size()-1; procI >=1; procI--)
130 if (nLocalCells[procI]-nSendCells[procI] < 1)
132 nSendCells[procI-1] = nSendCells[procI]-nLocalCells[procI]+1;
136 // First receive (so increasing the sizes of all arrays)
138 if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0)
140 // Receive cells from previous processor
141 IPstream fromPrevProc(Pstream::blocking, Pstream::myProcNo()-1);
143 Field<int> prevXadj(fromPrevProc);
144 Field<int> prevAdjncy(fromPrevProc);
145 Field<floatScalar> prevXyz(fromPrevProc);
146 Field<int> prevCellWeights(fromPrevProc);
147 Field<int> prevFaceWeights(fromPrevProc);
149 if (prevXadj.size() != nSendCells[Pstream::myProcNo()-1])
151 FatalErrorIn("parMetisDecomp::decompose(..)")
152 << "Expected from processor " << Pstream::myProcNo()-1
153 << " connectivity for " << nSendCells[Pstream::myProcNo()-1]
154 << " nCells but only received " << prevXadj.size()
155 << abort(FatalError);
159 prepend(prevAdjncy, adjncy);
160 // Adapt offsets and prepend xadj
161 xadj += prevAdjncy.size();
162 prepend(prevXadj, xadj);
164 prepend(prevXyz, xyz);
166 prepend(prevCellWeights, cellWeights);
167 prepend(prevFaceWeights, faceWeights);
171 // Send to my next processor
173 if (nSendCells[Pstream::myProcNo()] > 0)
175 // Send cells to next processor
176 OPstream toNextProc(Pstream::blocking, Pstream::myProcNo()+1);
178 int nCells = nSendCells[Pstream::myProcNo()];
179 int startCell = xadj.size()-1 - nCells;
180 int startFace = xadj[startCell];
181 int nFaces = adjncy.size()-startFace;
183 // Send for all cell data: last nCells elements
184 // Send for all face data: last nFaces elements
186 << Field<int>::subField(xadj, nCells, startCell)-startFace
187 << Field<int>::subField(adjncy, nFaces, startFace)
188 << SubField<floatScalar>(xyz, nDims*nCells, nDims*startCell)
192 ? static_cast<const Field<int>&>
194 Field<int>::subField(cellWeights, nCells, startCell)
201 ? static_cast<const Field<int>&>
203 Field<int>::subField(faceWeights, nFaces, startFace)
208 // Remove data that has been sent
209 if (faceWeights.size())
211 faceWeights.setSize(faceWeights.size()-nFaces);
213 if (cellWeights.size())
215 cellWeights.setSize(cellWeights.size()-nCells);
217 xyz.setSize(xyz.size()-nDims*nCells);
218 adjncy.setSize(adjncy.size()-nFaces);
219 xadj.setSize(xadj.size() - nCells);
224 // Adapt number of cells
225 forAll(nSendCells, procI)
228 nLocalCells[procI] -= nSendCells[procI];
233 nLocalCells[procI] += nSendCells[procI-1];
238 forAll(nLocalCells, procI)
240 cellOffsets[procI] = nGlobalCells;
241 nGlobalCells += nLocalCells[procI];
245 if (nLocalCells[Pstream::myProcNo()] != (xadj.size()-1))
247 FatalErrorIn("parMetisDecomp::decompose(..)")
248 << "Have connectivity for " << xadj.size()-1
249 << " cells but nLocalCells:" << nLocalCells[Pstream::myProcNo()]
250 << abort(FatalError);
256 int* adjwgtPtr = NULL;
258 if (cellWeights.size())
260 vwgtPtr = cellWeights.begin();
261 wgtFlag += 2; // Weights on vertices
263 if (faceWeights.size())
265 adjwgtPtr = faceWeights.begin();
266 wgtFlag += 1; // Weights on edges
270 // Number of weights or balance constraints
272 // Per processor, per constraint the weight
273 Field<floatScalar> tpwgts(nCon*nProcessors_, 1./nProcessors_);
274 // Imbalance tolerance
275 Field<floatScalar> ubvec(nCon, 1.02);
276 if (nProcessors_ == 1)
278 // If only one processor there is no imbalance.
282 MPI_Comm comm = MPI_COMM_WORLD;
284 // output: cell -> processor addressing
285 finalDecomp.setSize(nLocalCells[Pstream::myProcNo()]);
287 // output: number of cut edges
291 ParMETIS_V3_PartGeomKway
293 cellOffsets.begin(), // vtxDist
296 vwgtPtr, // vertexweights
297 adjwgtPtr, // edgeweights
303 &nProcessors_, // nParts
306 const_cast<List<int>&>(options).begin(),
313 // If we sent cells across make sure we undo it
314 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
316 // Receive back from next processor if I sent something
317 if (nSendCells[Pstream::myProcNo()] > 0)
319 IPstream fromNextProc(Pstream::blocking, Pstream::myProcNo()+1);
321 List<int> nextFinalDecomp(fromNextProc);
323 if (nextFinalDecomp.size() != nSendCells[Pstream::myProcNo()])
325 FatalErrorIn("parMetisDecomp::decompose(..)")
326 << "Expected from processor " << Pstream::myProcNo()+1
327 << " decomposition for " << nSendCells[Pstream::myProcNo()]
328 << " nCells but only received " << nextFinalDecomp.size()
329 << abort(FatalError);
332 append(nextFinalDecomp, finalDecomp);
335 // Send back to previous processor.
336 if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0)
338 OPstream toPrevProc(Pstream::blocking, Pstream::myProcNo()-1);
340 int nToPrevious = nSendCells[Pstream::myProcNo()-1];
347 finalDecomp.size()-nToPrevious
350 // Remove locally what has been sent
351 finalDecomp.setSize(finalDecomp.size()-nToPrevious);
358 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
360 Foam::parMetisDecomp::parMetisDecomp
362 const dictionary& decompositionDict,
366 decompositionMethod(decompositionDict),
371 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
373 Foam::labelList Foam::parMetisDecomp::decompose
375 const pointField& cc,
376 const scalarField& cWeights
379 if (cc.size() != mesh_.nCells())
383 "parMetisDecomp::decompose"
384 "(const pointField&, const scalarField&)"
385 ) << "Can use this decomposition method only for the whole mesh"
387 << "and supply one coordinate (cellCentre) for every cell." << endl
388 << "The number of coordinates " << cc.size() << endl
389 << "The number of cells in the mesh " << mesh_.nCells()
393 // For running sequential ...
394 if (Pstream::nProcs() <= 1)
396 return metisDecomp(decompositionDict_, mesh_).decompose
406 // Offsets into adjncy
408 calcMetisDistributedCSR
416 // decomposition options. 0 = use defaults
417 List<int> options(3, 0);
418 //options[0] = 1; // don't use defaults but use values below
419 //options[1] = -1; // full debug info
420 //options[2] = 15; // random number seed
422 // cell weights (so on the vertices of the dual)
423 Field<int> cellWeights;
425 // face weights (so on the edges of the dual)
426 Field<int> faceWeights;
429 // Check for externally provided cellweights and if so initialise weights
430 scalar minWeights = gMin(cWeights);
431 if (cWeights.size() > 0)
437 "metisDecomp::decompose"
438 "(const pointField&, const scalarField&)"
439 ) << "Illegal minimum weight " << minWeights
443 if (cWeights.size() != mesh_.nCells())
447 "parMetisDecomp::decompose"
448 "(const pointField&, const scalarField&)"
449 ) << "Number of cell weights " << cWeights.size()
450 << " does not equal number of cells " << mesh_.nCells()
454 // Convert to integers.
455 cellWeights.setSize(cWeights.size());
456 forAll(cellWeights, i)
458 cellWeights[i] = int(cWeights[i]/minWeights);
463 // Check for user supplied weights and decomp options
464 if (decompositionDict_.found("metisCoeffs"))
466 const dictionary& metisCoeffs =
467 decompositionDict_.subDict("metisCoeffs");
470 if (metisCoeffs.readIfPresent("cellWeightsFile", weightsFile))
472 Info<< "parMetisDecomp : Using cell-based weights read from "
473 << weightsFile << endl;
475 labelIOField cellIOWeights
480 mesh_.time().timeName(),
486 cellWeights.transfer(cellIOWeights);
488 if (cellWeights.size() != mesh_.nCells())
492 "parMetisDecomp::decompose"
493 "(const pointField&, const scalarField&)"
494 ) << "Number of cell weights " << cellWeights.size()
495 << " read from " << cellIOWeights.objectPath()
496 << " does not equal number of cells " << mesh_.nCells()
501 if (metisCoeffs.readIfPresent("faceWeightsFile", weightsFile))
503 Info<< "parMetisDecomp : Using face-based weights read from "
504 << weightsFile << endl;
511 mesh_.time().timeName(),
518 if (weights.size() != mesh_.nFaces())
522 "parMetisDecomp::decompose"
523 "(const pointField&, const scalarField&)"
524 ) << "Number of face weights " << weights.size()
525 << " does not equal number of internal and boundary faces "
530 faceWeights.setSize(adjncy.size());
532 // Assume symmetric weights. Keep same ordering as adjncy.
533 List<int> nFacesPerCell(mesh_.nCells(), 0);
535 // Handle internal faces
536 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
538 label w = weights[faceI];
540 label own = mesh_.faceOwner()[faceI];
541 label nei = mesh_.faceNeighbour()[faceI];
543 faceWeights[xadj[own] + nFacesPerCell[own]++] = w;
544 faceWeights[xadj[nei] + nFacesPerCell[nei]++] = w;
546 // Coupled boundary faces
547 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
549 forAll(patches, patchI)
551 const polyPatch& pp = patches[patchI];
555 label faceI = pp.start();
559 label w = weights[faceI];
560 label own = mesh_.faceOwner()[faceI];
561 faceWeights[xadj[own] + nFacesPerCell[own]++] = w;
568 if (metisCoeffs.readIfPresent("options", options))
570 Info<< "Using Metis options " << options
573 if (options.size() != 3)
577 "parMetisDecomp::decompose"
578 "(const pointField&, const scalarField&)"
579 ) << "Number of options " << options.size()
580 << " should be three." << exit(FatalError);
586 // Do actual decomposition
587 List<int> finalDecomp;
600 // Copy back to labelList
601 labelList decomp(finalDecomp.size());
604 decomp[i] = finalDecomp[i];
610 Foam::labelList Foam::parMetisDecomp::decompose
612 const labelList& cellToRegion,
613 const pointField& regionPoints,
614 const scalarField& regionWeights
617 const labelList& faceOwner = mesh_.faceOwner();
618 const labelList& faceNeighbour = mesh_.faceNeighbour();
619 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
621 if (cellToRegion.size() != mesh_.nCells())
625 "parMetisDecomp::decompose(const labelList&, const pointField&)"
626 ) << "Size of cell-to-coarse map " << cellToRegion.size()
627 << " differs from number of cells in mesh " << mesh_.nCells()
632 // Global region numbering engine
633 globalIndex globalRegions(regionPoints.size());
636 // Get renumbered owner region on other side of coupled faces
637 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
639 List<int> globalNeighbour(mesh_.nFaces()-mesh_.nInternalFaces());
641 forAll(patches, patchI)
643 const polyPatch& pp = patches[patchI];
647 label faceI = pp.start();
648 label bFaceI = pp.start() - mesh_.nInternalFaces();
652 label ownRegion = cellToRegion[faceOwner[faceI]];
653 globalNeighbour[bFaceI++] = globalRegions.toGlobal(ownRegion);
659 // Get the cell on the other side of coupled patches
660 syncTools::swapBoundaryFaceList(mesh_, globalNeighbour, false);
663 // Get globalCellCells on coarse mesh
664 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
666 labelListList globalRegionRegions;
668 List<DynamicList<label> > dynRegionRegions(regionPoints.size());
670 // Internal faces first
671 forAll(faceNeighbour, faceI)
673 label ownRegion = cellToRegion[faceOwner[faceI]];
674 label neiRegion = cellToRegion[faceNeighbour[faceI]];
676 if (ownRegion != neiRegion)
678 label globalOwn = globalRegions.toGlobal(ownRegion);
679 label globalNei = globalRegions.toGlobal(neiRegion);
681 if (findIndex(dynRegionRegions[ownRegion], globalNei) == -1)
683 dynRegionRegions[ownRegion].append(globalNei);
685 if (findIndex(dynRegionRegions[neiRegion], globalOwn) == -1)
687 dynRegionRegions[neiRegion].append(globalOwn);
692 // Coupled boundary faces
693 forAll(patches, patchI)
695 const polyPatch& pp = patches[patchI];
699 label faceI = pp.start();
700 label bFaceI = pp.start() - mesh_.nInternalFaces();
704 label ownRegion = cellToRegion[faceOwner[faceI]];
705 label globalNei = globalNeighbour[bFaceI++];
708 if (findIndex(dynRegionRegions[ownRegion], globalNei) == -1)
710 dynRegionRegions[ownRegion].append(globalNei);
716 globalRegionRegions.setSize(dynRegionRegions.size());
717 forAll(dynRegionRegions, i)
719 globalRegionRegions[i].transfer(dynRegionRegions[i]);
723 labelList regionDecomp
733 // Rework back into decomposition for original mesh_
734 labelList cellDistribution(cellToRegion.size());
736 forAll(cellDistribution, cellI)
738 cellDistribution[cellI] = regionDecomp[cellToRegion[cellI]];
740 return cellDistribution;
744 Foam::labelList Foam::parMetisDecomp::decompose
746 const labelListList& globalCellCells,
747 const pointField& cellCentres,
748 const scalarField& cWeights
751 if (cellCentres.size() != globalCellCells.size())
755 "parMetisDecomp::decompose(const labelListList&"
756 ", const pointField&, const scalarField&)"
757 ) << "Inconsistent number of cells (" << globalCellCells.size()
758 << ") and number of cell centres (" << cellCentres.size()
759 << ") or weights (" << cWeights.size() << ")." << exit(FatalError);
762 // For running sequential ...
763 if (Pstream::nProcs() <= 1)
765 return metisDecomp(decompositionDict_, mesh_)
766 .decompose(globalCellCells, cellCentres, cWeights);
770 // Make Metis Distributed CSR (Compressed Storage Format) storage
774 // Offsets into adjncy
776 scotchDecomp::calcCSR(globalCellCells, adjncy, xadj);
778 // decomposition options. 0 = use defaults
779 List<int> options(3, 0);
780 //options[0] = 1; // don't use defaults but use values below
781 //options[1] = -1; // full debug info
782 //options[2] = 15; // random number seed
784 // cell weights (so on the vertices of the dual)
785 Field<int> cellWeights;
787 // face weights (so on the edges of the dual)
788 Field<int> faceWeights;
791 // Check for externally provided cellweights and if so initialise weights
792 scalar minWeights = gMin(cWeights);
793 if (cWeights.size() > 0)
799 "parMetisDecomp::decompose(const labelListList&"
800 ", const pointField&, const scalarField&)"
801 ) << "Illegal minimum weight " << minWeights
805 if (cWeights.size() != globalCellCells.size())
809 "parMetisDecomp::decompose(const labelListList&"
810 ", const pointField&, const scalarField&)"
811 ) << "Number of cell weights " << cWeights.size()
812 << " does not equal number of cells " << globalCellCells.size()
816 // Convert to integers.
817 cellWeights.setSize(cWeights.size());
818 forAll(cellWeights, i)
820 cellWeights[i] = int(cWeights[i]/minWeights);
825 // Check for user supplied weights and decomp options
826 if (decompositionDict_.found("metisCoeffs"))
828 const dictionary& metisCoeffs =
829 decompositionDict_.subDict("metisCoeffs");
831 if (metisCoeffs.readIfPresent("options", options))
833 Info<< "Using Metis options " << options
836 if (options.size() != 3)
840 "parMetisDecomp::decompose(const labelListList&"
841 ", const pointField&, const scalarField&)"
842 ) << "Number of options " << options.size()
843 << " should be three." << exit(FatalError);
849 // Do actual decomposition
850 List<int> finalDecomp;
863 // Copy back to labelList
864 labelList decomp(finalDecomp.size());
867 decomp[i] = finalDecomp[i];
873 void Foam::parMetisDecomp::calcMetisDistributedCSR
875 const polyMesh& mesh,
880 // Create global cell numbers
881 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
883 globalIndex globalCells(mesh.nCells());
887 // Make Metis Distributed CSR (Compressed Storage Format) storage
888 // adjncy : contains cellCells (= edges in graph)
889 // xadj(celli) : start of information in adjncy for celli
893 const labelList& faceOwner = mesh.faceOwner();
894 const labelList& faceNeighbour = mesh.faceNeighbour();
895 const polyBoundaryMesh& patches = mesh.boundaryMesh();
898 // Get renumbered owner on other side of coupled faces
899 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
901 List<int> globalNeighbour(mesh.nFaces()-mesh.nInternalFaces());
903 forAll(patches, patchI)
905 const polyPatch& pp = patches[patchI];
909 label faceI = pp.start();
910 label bFaceI = pp.start() - mesh.nInternalFaces();
914 globalNeighbour[bFaceI++] = globalCells.toGlobal
922 // Get the cell on the other side of coupled patches
923 syncTools::swapBoundaryFaceList(mesh, globalNeighbour, false);
926 // Count number of faces (internal + coupled)
927 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
929 // Number of faces per cell
930 List<int> nFacesPerCell(mesh.nCells(), 0);
932 // Number of coupled faces
933 label nCoupledFaces = 0;
935 for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
937 nFacesPerCell[faceOwner[faceI]]++;
938 nFacesPerCell[faceNeighbour[faceI]]++;
940 // Handle coupled faces
941 HashSet<edge, Hash<edge> > cellPair(mesh.nFaces()-mesh.nInternalFaces());
943 forAll(patches, patchI)
945 const polyPatch& pp = patches[patchI];
949 label faceI = pp.start();
953 label own = faceOwner[faceI];
954 label globalNei = globalNeighbour[faceI-mesh.nInternalFaces()];
956 if (cellPair.insert(edge(own, globalNei)))
959 nFacesPerCell[faceOwner[faceI]]++;
970 xadj.setSize(mesh.nCells()+1);
974 for (label cellI = 0; cellI < mesh.nCells(); cellI++)
976 xadj[cellI] = freeAdj;
978 freeAdj += nFacesPerCell[cellI];
980 xadj[mesh.nCells()] = freeAdj;
987 adjncy.setSize(2*mesh.nInternalFaces() + nCoupledFaces);
991 // For internal faces is just offsetted owner and neighbour
992 for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
994 label own = faceOwner[faceI];
995 label nei = faceNeighbour[faceI];
997 adjncy[xadj[own] + nFacesPerCell[own]++] = globalCells.toGlobal(nei);
998 adjncy[xadj[nei] + nFacesPerCell[nei]++] = globalCells.toGlobal(own);
1000 // For boundary faces is offsetted coupled neighbour
1002 forAll(patches, patchI)
1004 const polyPatch& pp = patches[patchI];
1008 label faceI = pp.start();
1009 label bFaceI = pp.start()-mesh.nInternalFaces();
1013 label own = faceOwner[faceI];
1014 label globalNei = globalNeighbour[bFaceI];
1016 if (cellPair.insert(edge(own, globalNei)))
1018 adjncy[xadj[own] + nFacesPerCell[own]++] =
1019 globalNeighbour[bFaceI];
1030 // ************************************************************************* //