/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
\*---------------------------------------------------------------------------*/
#include "metisDecomp.H"
#include "addToRunTimeSelectionTable.H"
#include "Time.H"
#include "PrecisionAdaptor.H"
// Probably not needed...
#ifndef MPICH_SKIP_MPICXX
#define MPICH_SKIP_MPICXX
#endif
#ifndef OMPI_SKIP_MPICXX
#define OMPI_SKIP_MPICXX
#endif
#include "metis.h"
// Provide a clear error message if we have a severe size mismatch
// Allow widening, but not narrowing
//
// Metis has an 'idx_t' type, but the IDXTYPEWIDTH define is perhaps
// more future-proof?
//#ifdef IDXTYPEWIDTH
//static_assert
//(
// sizeof(Foam::label) > (IDXTYPEWIDTH/8),
// "sizeof(Foam::label) > (IDXTYPEWIDTH/8), check your metis headers"
//);
//#endif
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(metisDecomp, 0);
addToRunTimeSelectionTable
(
decompositionMethod,
metisDecomp,
dictionary
);
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
Foam::label Foam::metisDecomp::decomposeSerial
(
const labelList& adjncy,
const labelList& xadj,
const List& cWeights,
labelList& decomp
) const
{
// Method of decomposition
// recursive: multi-level recursive bisection (default)
// k-way: multi-level k-way
word method("recursive");
const dictionary* coeffsDictPtr = decompDict_.findDict("metisCoeffs");
idx_t numCells = Foam::max(0, (xadj.size()-1));
// Decomposition options
List options(METIS_NOPTIONS);
METIS_SetDefaultOptions(options.data());
// Processor weights initialised with no size, only used if specified in
// a file
Field procWeights;
// Cell weights (so on the vertices of the dual)
List cellWeights;
// Face weights (so on the edges of the dual)
List faceWeights;
bool hasWeights = !cWeights.empty();
// Note: min, not gMin since routine runs on master only.
const scalar minWeights = hasWeights ? min(cWeights) : scalar(1);
if (minWeights <= 0)
{
hasWeights = false;
WarningInFunction
<< "Illegal minimum weight " << minWeights
<< " ... ignoring"
<< endl;
}
else if (hasWeights && (cWeights.size() != numCells))
{
FatalErrorInFunction
<< "Number of weights (" << cWeights.size()
<< ") != number of cells (" << numCells << ")"
<< exit(FatalError);
}
if (hasWeights)
{
// Convert to integers.
cellWeights.resize_nocopy(cWeights.size());
forAll(cellWeights, i)
{
cellWeights[i] = idx_t(cWeights[i]/minWeights);
}
}
// Check for user supplied weights and decomp options
if (coeffsDictPtr)
{
const dictionary& coeffDict = *coeffsDictPtr;
word weightsFile;
if (coeffDict.readIfPresent("method", method))
{
if (method != "recursive" && method != "k-way")
{
FatalErrorInFunction
<< "Method " << method << " in metisCoeffs in dictionary : "
<< decompDict_.name()
<< " should be 'recursive' or 'k-way'"
<< exit(FatalError);
}
Info<< "metisDecomp : Using Metis method " << method
<< nl << endl;
}
if (coeffDict.readIfPresent("options", options))
{
if (options.size() != METIS_NOPTIONS)
{
FatalErrorInFunction
<< "Number of options in metisCoeffs in dictionary : "
<< decompDict_.name()
<< " should be " << METIS_NOPTIONS
<< exit(FatalError);
}
Info<< "metisDecomp : Using Metis options " << options
<< nl << endl;
}
if (coeffDict.readIfPresent("processorWeights", procWeights))
{
if (procWeights.size() != nDomains_)
{
FatalIOErrorInFunction(coeffDict)
<< "processorWeights (" << procWeights.size()
<< ") != number of domains (" << nDomains_ << ")" << nl
<< exit(FatalIOError);
}
procWeights /= sum(procWeights);
}
}
idx_t ncon = 1;
idx_t nProcs = nDomains_;
// Addressing
ConstPrecisionAdaptor xadj_param(xadj);
ConstPrecisionAdaptor adjncy_param(adjncy);
// Output: cell -> processor addressing
decomp.resize(numCells);
PrecisionAdaptor decomp_param(decomp, false);
// Avoid potential nullptr issues with zero-sized arrays
labelList adjncy_dummy, xadj_dummy, decomp_dummy;
if (!numCells)
{
adjncy_dummy.resize(1, 0);
adjncy_param.set(adjncy_dummy);
xadj_dummy.resize(2, 0);
xadj_param.set(xadj_dummy);
decomp_dummy.resize(1, 0);
decomp_param.clear(); // Avoid propagating spurious values
decomp_param.set(decomp_dummy);
}
//
// Decompose
//
// Output: number of cut edges
idx_t edgeCut = 0;
if (method == "recursive")
{
METIS_PartGraphRecursive
(
&numCells, // num vertices in graph
&ncon, // num balancing constraints
xadj_param.constCast().data(), // indexing into adjncy
adjncy_param.constCast().data(), // neighbour info
cellWeights.data(), // vertex wts
nullptr, // vsize: total communication vol
faceWeights.data(), // edge wts
&nProcs, // nParts
procWeights.data(), // tpwgts
nullptr, // ubvec: processor imbalance (default)
options.data(),
&edgeCut,
decomp_param.ref().data()
);
}
else
{
METIS_PartGraphKway
(
&numCells, // num vertices in graph
&ncon, // num balancing constraints
xadj_param.constCast().data(), // indexing into adjncy
adjncy_param.constCast().data(), // neighbour info
cellWeights.data(), // vertex wts
nullptr, // vsize: total communication vol
faceWeights.data(), // edge wts
&nProcs, // nParts
procWeights.data(), // tpwgts
nullptr, // ubvec: processor imbalance (default)
options.data(),
&edgeCut,
decomp_param.ref().data()
);
}
return edgeCut;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::metisDecomp::metisDecomp(const label numDomains)
:
metisLikeDecomp(numDomains)
{}
Foam::metisDecomp::metisDecomp
(
const dictionary& decompDict,
const word& regionName
)
:
metisLikeDecomp(typeName, decompDict, regionName, selectionType::NULL_DICT)
{}
// ************************************************************************* //