1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
5 \\ / A nd | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation, either version 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "parMetisDecomp.H"
27 #include "metisDecomp.H"
28 #include "addToRunTimeSelectionTable.H"
29 #include "floatScalar.H"
31 #include "labelIOField.H"
32 #include "syncTools.H"
33 #include "globalIndex.H"
39 # include "parmetis.h"
42 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
46 defineTypeNameAndDebug(parMetisDecomp, 0);
48 addToRunTimeSelectionTable
57 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
59 //- Does prevention of 0 cell domains and calls parmetis.
60 Foam::label Foam::parMetisDecomp::decompose
64 const pointField& cellCentres,
65 Field<int>& cellWeights,
66 Field<int>& faceWeights,
67 const List<int>& options,
68 List<int>& finalDecomp
74 // Number of dimensions
78 if (cellCentres.size() != xadj.size()-1)
80 FatalErrorIn("parMetisDecomp::decompose(..)")
81 << "cellCentres:" << cellCentres.size()
82 << " xadj:" << xadj.size()
87 // Get number of cells on all processors
88 List<int> nLocalCells(Pstream::nProcs());
89 nLocalCells[Pstream::myProcNo()] = xadj.size()-1;
90 Pstream::gatherList(nLocalCells);
91 Pstream::scatterList(nLocalCells);
94 List<int> cellOffsets(Pstream::nProcs()+1);
96 forAll(nLocalCells, procI)
98 cellOffsets[procI] = nGlobalCells;
99 nGlobalCells += nLocalCells[procI];
101 cellOffsets[Pstream::nProcs()] = nGlobalCells;
103 // Convert pointField into the data type parMetis expects (float or double)
104 Field<real_t> xyz(3*cellCentres.size());
106 forAll(cellCentres, cellI)
108 const point& cc = cellCentres[cellI];
109 xyz[compI++] = float(cc.x());
110 xyz[compI++] = float(cc.y());
111 xyz[compI++] = float(cc.z());
114 // Make sure every domain has at least one cell
115 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
116 // (Metis falls over with zero sized domains)
117 // Trickle cells from processors that have them up to those that
121 // Number of cells to send to the next processor
122 // (is same as number of cells next processor has to receive)
123 List<int> nSendCells(Pstream::nProcs(), 0);
125 for (label procI = nLocalCells.size()-1; procI >=1; procI--)
127 if (nLocalCells[procI]-nSendCells[procI] < 1)
129 nSendCells[procI-1] = nSendCells[procI]-nLocalCells[procI]+1;
133 // First receive (so increasing the sizes of all arrays)
135 if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0)
137 // Receive cells from previous processor
138 IPstream fromPrevProc(Pstream::blocking, Pstream::myProcNo()-1);
140 Field<int> prevXadj(fromPrevProc);
141 Field<int> prevAdjncy(fromPrevProc);
142 Field<real_t> prevXyz(fromPrevProc);
143 Field<int> prevCellWeights(fromPrevProc);
144 Field<int> prevFaceWeights(fromPrevProc);
146 if (prevXadj.size() != nSendCells[Pstream::myProcNo()-1])
148 FatalErrorIn("parMetisDecomp::decompose(..)")
149 << "Expected from processor " << Pstream::myProcNo()-1
150 << " connectivity for " << nSendCells[Pstream::myProcNo()-1]
151 << " nCells but only received " << prevXadj.size()
152 << abort(FatalError);
156 prepend(prevAdjncy, adjncy);
157 // Adapt offsets and prepend xadj
158 xadj += prevAdjncy.size();
159 prepend(prevXadj, xadj);
161 prepend(prevXyz, xyz);
163 prepend(prevCellWeights, cellWeights);
164 prepend(prevFaceWeights, faceWeights);
168 // Send to my next processor
170 if (nSendCells[Pstream::myProcNo()] > 0)
172 // Send cells to next processor
173 OPstream toNextProc(Pstream::blocking, Pstream::myProcNo()+1);
175 int nCells = nSendCells[Pstream::myProcNo()];
176 int startCell = xadj.size()-1 - nCells;
177 int startFace = xadj[startCell];
178 int nFaces = adjncy.size()-startFace;
180 // Send for all cell data: last nCells elements
181 // Send for all face data: last nFaces elements
183 << Field<int>::subField(xadj, nCells, startCell)-startFace
184 << Field<int>::subField(adjncy, nFaces, startFace)
185 << SubField<real_t>(xyz, nDims*nCells, nDims*startCell)
189 ? static_cast<const Field<int>&>
191 Field<int>::subField(cellWeights, nCells, startCell)
198 ? static_cast<const Field<int>&>
200 Field<int>::subField(faceWeights, nFaces, startFace)
205 // Remove data that has been sent
206 if (faceWeights.size())
208 faceWeights.setSize(faceWeights.size()-nFaces);
210 if (cellWeights.size())
212 cellWeights.setSize(cellWeights.size()-nCells);
214 xyz.setSize(xyz.size()-nDims*nCells);
215 adjncy.setSize(adjncy.size()-nFaces);
216 xadj.setSize(xadj.size() - nCells);
221 // Adapt number of cells
222 forAll(nSendCells, procI)
225 nLocalCells[procI] -= nSendCells[procI];
230 nLocalCells[procI] += nSendCells[procI-1];
235 forAll(nLocalCells, procI)
237 cellOffsets[procI] = nGlobalCells;
238 nGlobalCells += nLocalCells[procI];
242 if (nLocalCells[Pstream::myProcNo()] != (xadj.size()-1))
244 FatalErrorIn("parMetisDecomp::decompose(..)")
245 << "Have connectivity for " << xadj.size()-1
246 << " cells but nLocalCells:" << nLocalCells[Pstream::myProcNo()]
247 << abort(FatalError);
253 int* adjwgtPtr = NULL;
255 if (cellWeights.size())
257 vwgtPtr = cellWeights.begin();
258 wgtFlag += 2; // Weights on vertices
260 if (faceWeights.size())
262 adjwgtPtr = faceWeights.begin();
263 wgtFlag += 1; // Weights on edges
267 // Number of weights or balance constraints
269 // Per processor, per constraint the weight
270 Field<real_t> tpwgts(nCon*nProcessors_, 1./nProcessors_);
271 // Imbalance tolerance
272 Field<real_t> ubvec(nCon, 1.02);
273 if (nProcessors_ == 1)
275 // If only one processor there is no imbalance.
279 MPI_Comm comm = MPI_COMM_WORLD;
281 // output: cell -> processor addressing
282 finalDecomp.setSize(nLocalCells[Pstream::myProcNo()]);
284 // output: number of cut edges
288 ParMETIS_V3_PartGeomKway
290 cellOffsets.begin(), // vtxDist
293 vwgtPtr, // vertexweights
294 adjwgtPtr, // edgeweights
300 &nProcessors_, // nParts
303 const_cast<List<int>&>(options).begin(),
310 // If we sent cells across make sure we undo it
311 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
313 // Receive back from next processor if I sent something
314 if (nSendCells[Pstream::myProcNo()] > 0)
316 IPstream fromNextProc(Pstream::blocking, Pstream::myProcNo()+1);
318 List<int> nextFinalDecomp(fromNextProc);
320 if (nextFinalDecomp.size() != nSendCells[Pstream::myProcNo()])
322 FatalErrorIn("parMetisDecomp::decompose(..)")
323 << "Expected from processor " << Pstream::myProcNo()+1
324 << " decomposition for " << nSendCells[Pstream::myProcNo()]
325 << " nCells but only received " << nextFinalDecomp.size()
326 << abort(FatalError);
329 append(nextFinalDecomp, finalDecomp);
332 // Send back to previous processor.
333 if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0)
335 OPstream toPrevProc(Pstream::blocking, Pstream::myProcNo()-1);
337 int nToPrevious = nSendCells[Pstream::myProcNo()-1];
344 finalDecomp.size()-nToPrevious
347 // Remove locally what has been sent
348 finalDecomp.setSize(finalDecomp.size()-nToPrevious);
355 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
357 Foam::parMetisDecomp::parMetisDecomp
359 const dictionary& decompositionDict,
363 decompositionMethod(decompositionDict),
368 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
370 Foam::labelList Foam::parMetisDecomp::decompose
372 const pointField& cc,
373 const scalarField& cWeights
376 if (cc.size() != mesh_.nCells())
380 "parMetisDecomp::decompose"
381 "(const pointField&, const scalarField&)"
382 ) << "Can use this decomposition method only for the whole mesh"
384 << "and supply one coordinate (cellCentre) for every cell." << endl
385 << "The number of coordinates " << cc.size() << endl
386 << "The number of cells in the mesh " << mesh_.nCells()
390 // For running sequential ...
391 if (Pstream::nProcs() <= 1)
393 return metisDecomp(decompositionDict_, mesh_).decompose
403 // Offsets into adjncy
413 // decomposition options. 0 = use defaults
414 List<int> options(3, 0);
415 //options[0] = 1; // don't use defaults but use values below
416 //options[1] = -1; // full debug info
417 //options[2] = 15; // random number seed
419 // cell weights (so on the vertices of the dual)
420 Field<int> cellWeights;
422 // face weights (so on the edges of the dual)
423 Field<int> faceWeights;
426 // Check for externally provided cellweights and if so initialise weights
427 scalar minWeights = gMin(cWeights);
428 if (cWeights.size() > 0)
434 "metisDecomp::decompose"
435 "(const pointField&, const scalarField&)"
436 ) << "Illegal minimum weight " << minWeights
440 if (cWeights.size() != mesh_.nCells())
444 "parMetisDecomp::decompose"
445 "(const pointField&, const scalarField&)"
446 ) << "Number of cell weights " << cWeights.size()
447 << " does not equal number of cells " << mesh_.nCells()
451 // Convert to integers.
452 cellWeights.setSize(cWeights.size());
453 forAll(cellWeights, i)
455 cellWeights[i] = int(cWeights[i]/minWeights);
460 // Check for user supplied weights and decomp options
461 if (decompositionDict_.found("metisCoeffs"))
463 const dictionary& metisCoeffs =
464 decompositionDict_.subDict("metisCoeffs");
467 if (metisCoeffs.readIfPresent("cellWeightsFile", weightsFile))
469 Info<< "parMetisDecomp : Using cell-based weights read from "
470 << weightsFile << endl;
472 labelIOField cellIOWeights
477 mesh_.time().timeName(),
483 cellWeights.transfer(cellIOWeights);
485 if (cellWeights.size() != mesh_.nCells())
489 "parMetisDecomp::decompose"
490 "(const pointField&, const scalarField&)"
491 ) << "Number of cell weights " << cellWeights.size()
492 << " read from " << cellIOWeights.objectPath()
493 << " does not equal number of cells " << mesh_.nCells()
498 if (metisCoeffs.readIfPresent("faceWeightsFile", weightsFile))
500 Info<< "parMetisDecomp : Using face-based weights read from "
501 << weightsFile << endl;
508 mesh_.time().timeName(),
515 if (weights.size() != mesh_.nFaces())
519 "parMetisDecomp::decompose"
520 "(const pointField&, const scalarField&)"
521 ) << "Number of face weights " << weights.size()
522 << " does not equal number of internal and boundary faces "
527 faceWeights.setSize(adjncy.size());
529 // Assume symmetric weights. Keep same ordering as adjncy.
530 List<int> nFacesPerCell(mesh_.nCells(), 0);
532 // Handle internal faces
533 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
535 label w = weights[faceI];
537 label own = mesh_.faceOwner()[faceI];
538 label nei = mesh_.faceNeighbour()[faceI];
540 faceWeights[xadj[own] + nFacesPerCell[own]++] = w;
541 faceWeights[xadj[nei] + nFacesPerCell[nei]++] = w;
543 // Coupled boundary faces
544 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
546 forAll(patches, patchI)
548 const polyPatch& pp = patches[patchI];
552 label faceI = pp.start();
556 label w = weights[faceI];
557 label own = mesh_.faceOwner()[faceI];
558 faceWeights[xadj[own] + nFacesPerCell[own]++] = w;
565 if (metisCoeffs.readIfPresent("options", options))
567 Info<< "Using Metis options " << options
570 if (options.size() != 3)
574 "parMetisDecomp::decompose"
575 "(const pointField&, const scalarField&)"
576 ) << "Number of options " << options.size()
577 << " should be three." << exit(FatalError);
583 // Do actual decomposition
584 List<int> finalDecomp;
596 // Copy back to labelList
597 labelList decomp(finalDecomp.size());
600 decomp[i] = finalDecomp[i];
606 Foam::labelList Foam::parMetisDecomp::decompose
608 const labelList& cellToRegion,
609 const pointField& regionPoints,
610 const scalarField& regionWeights
613 const labelList& faceOwner = mesh_.faceOwner();
614 const labelList& faceNeighbour = mesh_.faceNeighbour();
615 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
617 if (cellToRegion.size() != mesh_.nCells())
621 "parMetisDecomp::decompose(const labelList&, const pointField&)"
622 ) << "Size of cell-to-coarse map " << cellToRegion.size()
623 << " differs from number of cells in mesh " << mesh_.nCells()
628 // Global region numbering engine
629 globalIndex globalRegions(regionPoints.size());
632 // Get renumbered owner region on other side of coupled faces
633 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
635 List<int> globalNeighbour(mesh_.nFaces()-mesh_.nInternalFaces());
637 forAll(patches, patchI)
639 const polyPatch& pp = patches[patchI];
643 label faceI = pp.start();
644 label bFaceI = pp.start() - mesh_.nInternalFaces();
648 label ownRegion = cellToRegion[faceOwner[faceI]];
649 globalNeighbour[bFaceI++] = globalRegions.toGlobal(ownRegion);
655 // Get the cell on the other side of coupled patches
656 syncTools::swapBoundaryFaceList(mesh_, globalNeighbour, false);
659 // Get globalCellCells on coarse mesh
660 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
662 labelListList globalRegionRegions;
664 List<DynamicList<label> > dynRegionRegions(regionPoints.size());
666 // Internal faces first
667 forAll(faceNeighbour, faceI)
669 label ownRegion = cellToRegion[faceOwner[faceI]];
670 label neiRegion = cellToRegion[faceNeighbour[faceI]];
672 if (ownRegion != neiRegion)
674 label globalOwn = globalRegions.toGlobal(ownRegion);
675 label globalNei = globalRegions.toGlobal(neiRegion);
677 if (findIndex(dynRegionRegions[ownRegion], globalNei) == -1)
679 dynRegionRegions[ownRegion].append(globalNei);
681 if (findIndex(dynRegionRegions[neiRegion], globalOwn) == -1)
683 dynRegionRegions[neiRegion].append(globalOwn);
688 // Coupled boundary faces
689 forAll(patches, patchI)
691 const polyPatch& pp = patches[patchI];
695 label faceI = pp.start();
696 label bFaceI = pp.start() - mesh_.nInternalFaces();
700 label ownRegion = cellToRegion[faceOwner[faceI]];
701 label globalNei = globalNeighbour[bFaceI++];
706 findIndex(dynRegionRegions[ownRegion], globalNei)
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]];
741 return cellDistribution;
745 Foam::labelList Foam::parMetisDecomp::decompose
747 const labelListList& globalCellCells,
748 const pointField& cellCentres,
749 const scalarField& cWeights
752 if (cellCentres.size() != globalCellCells.size())
756 "parMetisDecomp::decompose(const labelListList&"
757 ", const pointField&, const scalarField&)"
758 ) << "Inconsistent number of cells (" << globalCellCells.size()
759 << ") and number of cell centres (" << cellCentres.size()
760 << ") or weights (" << cWeights.size() << ")." << exit(FatalError);
763 // For running sequential ...
764 if (Pstream::nProcs() <= 1)
766 return metisDecomp(decompositionDict_, mesh_).decompose
775 // Make Metis Distributed CSR (Compressed Storage Format) storage
780 // Offsets into adjncy
783 calcCSR(globalCellCells, adjncy, xadj);
785 // decomposition options. 0 = use defaults
786 List<int> options(3, 0);
787 //options[0] = 1; // don't use defaults but use values below
788 //options[1] = -1; // full debug info
789 //options[2] = 15; // random number seed
791 // cell weights (so on the vertices of the dual)
792 Field<int> cellWeights;
794 // face weights (so on the edges of the dual)
795 Field<int> faceWeights;
798 // Check for externally provided cellweights and if so initialise weights
799 scalar minWeights = gMin(cWeights);
800 if (cWeights.size() > 0)
806 "parMetisDecomp::decompose(const labelListList&"
807 ", const pointField&, const scalarField&)"
808 ) << "Illegal minimum weight " << minWeights
812 if (cWeights.size() != globalCellCells.size())
816 "parMetisDecomp::decompose(const labelListList&"
817 ", const pointField&, const scalarField&)"
818 ) << "Number of cell weights " << cWeights.size()
819 << " does not equal number of cells " << globalCellCells.size()
823 // Convert to integers.
824 cellWeights.setSize(cWeights.size());
825 forAll(cellWeights, i)
827 cellWeights[i] = int(cWeights[i]/minWeights);
832 // Check for user supplied weights and decomp options
833 if (decompositionDict_.found("metisCoeffs"))
835 const dictionary& metisCoeffs =
836 decompositionDict_.subDict("metisCoeffs");
838 if (metisCoeffs.readIfPresent("options", options))
840 Info<< "Using Metis options " << options
843 if (options.size() != 3)
847 "parMetisDecomp::decompose(const labelListList&"
848 ", const pointField&, const scalarField&)"
849 ) << "Number of options " << options.size()
850 << " should be three." << exit(FatalError);
856 // Do actual decomposition
857 List<int> finalDecomp;
869 // Copy back to labelList
870 labelList decomp(finalDecomp.size());
873 decomp[i] = finalDecomp[i];
879 // ************************************************************************* //