fvMeshDistributors: New library for mesh redistribution and load-balancing

Basic support is now provided for dynamic mesh redistribution, particularly for
load-balancing.  The mesh distributor is selected in the optional 'distributor'
entry in dynamicMeshDict, for example in the
multiphase/interFoam/RAS/floatingObject tutorial case when run in parallel using
the new Allrun-parallel script

distributor
{
    type            decomposer;

    libs            ("libfvMeshDistributors.so");

    redistributionInterval  10;
}

in which the 'decomposer' form of redistribution is selected to call the mesh
decomposition method specified in decomposeParDict to re-decompose the mesh for
redistribution.  The redistributionInterval entry specifies how frequently mesh
redistribution takes place, in the above every 10th time-step.  An optional
maxImbalance entry is also provided to control redistribution based on the cell
distribution imbalance:

Class
    Foam::fvMeshDistributor::decomposer

Description
    Dynamic mesh redistribution using the decomposer

Usage
    Example of single field based refinement in all cells:
    \verbatim
    distributor
    {
        type            decomposer;

        libs            ("libfvMeshDistributors.so");

        // How often to redistribute
        redistributionInterval  10;

        // Maximum fractional cell distribution imbalance
        // before rebalancing
        maxImbalance    0.1;
    }
    \endverbatim

Currently mesh refinement/unrefinement and motion with redistribution is
supported but many aspects of OpenFOAM are not yet and will require further
development, in particular fvModels and Lagrangian.

Also only the geometry-based simple and hierarchical decomposition method are
well behaved for redistribution, scotch and ptScotch cause dramatic changes in
mesh distribution with a corresponding heavy communications overhead limiting
their usefulness or at least the frequency with which they should be called to
redistribute the mesh.
This commit is contained in:
Henry Weller
2021-12-09 14:06:45 +00:00
parent 9d6f708f4d
commit bf5f056296
22 changed files with 443 additions and 117 deletions

View File

@ -48,6 +48,7 @@ parallel/Allwmake $targetType $*
wmake $targetType fvMeshMovers
wmake $targetType fvMeshTopoChangers
wmake $targetType fvMeshDistributors
wmake $targetType conversion
wmake $targetType sampling

View File

@ -206,9 +206,6 @@ Foam::polyMesh::readUpdateState Foam::polyMesh::readUpdate()
if (boundaryChanged)
{
WarningInFunction
<< "boundary changed, proceed with care." << endl;
boundary_.clear();
boundary_.setSize(newBoundary.size());

View File

@ -218,7 +218,6 @@ Foam::labelList Foam::fvMeshDistribute::select
}
// Check all procs have same names and in exactly same order.
void Foam::fvMeshDistribute::checkEqualWordList
(
const string& msg,
@ -264,7 +263,6 @@ Foam::wordList Foam::fvMeshDistribute::mergeWordList(const wordList& procNames)
}
// Print some info on mesh.
void Foam::fvMeshDistribute::printMeshInfo(const fvMesh& mesh)
{
Pout<< "Primitives:" << nl
@ -351,7 +349,6 @@ void Foam::fvMeshDistribute::printCoupleInfo
}
// Finds (non-empty) patch that exposed internal and proc faces can be put into.
Foam::label Foam::fvMeshDistribute::findInternalPatch() const
{
const polyBoundaryMesh& patches = mesh_.boundaryMesh();
@ -369,42 +366,23 @@ Foam::label Foam::fvMeshDistribute::findInternalPatch() const
}
}
if (internalPatchi != -1)
{
return internalPatchi;
}
label nonEmptyPatchi = -1;
forAllReverse(patches, patchi)
{
const polyPatch& pp = patches[patchi];
if (!isA<emptyPolyPatch>(pp) && !pp.coupled())
{
nonEmptyPatchi = patchi;
break;
}
}
if (nonEmptyPatchi == -1)
if (internalPatchi == -1)
{
FatalErrorInFunction
<< "Cannot find a patch which is neither of type empty nor"
<< " coupled in patches " << patches.names() << endl
<< "There has to be at least one such patch for"
<< " distribution to work" << abort(FatalError);
<< "Cannot find a internal patch in " << patches.names() << nl
<< " of types " << patches.types() << nl
<< " An internal patch must be provided for the exposed "
" internal faces." << exit(FatalError);
}
if (debug)
{
Pout<< "findInternalPatch : using patch " << nonEmptyPatchi
<< " name:" << patches[nonEmptyPatchi].name()
<< " type:" << patches[nonEmptyPatchi].type()
<< " to put exposed faces into." << endl;
Pout<< "findInternalPatch : using patch " << internalPatchi
<< " name:" << patches[internalPatchi].name()
<< " type:" << patches[internalPatchi].type()
<< " for the exposed internal faces." << endl;
}
// Do additional test for processor patches intermingled with non-proc
// patches.
label procPatchi = -1;
@ -427,12 +405,10 @@ Foam::label Foam::fvMeshDistribute::findInternalPatch() const
}
}
return nonEmptyPatchi;
return internalPatchi;
}
// Delete all processor patches. Move any processor faces into the last
// non-processor patch.
Foam::autoPtr<Foam::mapPolyMesh> Foam::fvMeshDistribute::deleteProcPatches
(
const label destinationPatch
@ -504,7 +480,6 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::fvMeshDistribute::deleteProcPatches
}
// Repatch the mesh.
Foam::autoPtr<Foam::mapPolyMesh> Foam::fvMeshDistribute::repatch
(
const labelList& newPatchID, // per boundary face -1 or new patchID
@ -621,11 +596,6 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::fvMeshDistribute::repatch
}
// Detect shared points. Need processor patches to be present.
// Background: when adding bits of mesh one can get points which
// share the same position but are only detectable to be topologically
// the same point when doing parallel analysis. This routine will
// merge those points.
Foam::autoPtr<Foam::mapPolyMesh> Foam::fvMeshDistribute::mergeSharedPoints
(
const labelList& pointToGlobalMaster,
@ -1006,7 +976,6 @@ void Foam::fvMeshDistribute::getCouplingData
}
// Subset the neighbourCell/neighbourProc fields
void Foam::fvMeshDistribute::subsetCouplingData
(
const fvMesh& mesh,
@ -1085,8 +1054,6 @@ void Foam::fvMeshDistribute::subsetCouplingData
}
// Find cells on mesh whose faceID/procID match the neighbour cell/proc of
// domainMesh. Store the matching face.
void Foam::fvMeshDistribute::findCouples
(
const primitiveMesh& mesh,
@ -1161,7 +1128,6 @@ void Foam::fvMeshDistribute::findCouples
}
// Map data on boundary faces to new mesh (resulting from adding two meshes)
Foam::labelList Foam::fvMeshDistribute::mapBoundaryData
(
const primitiveMesh& mesh, // mesh after adding
@ -1235,7 +1201,6 @@ Foam::labelList Foam::fvMeshDistribute::mapPointData
}
// Remove cells. Add all exposed faces to patch oldInternalPatchi
Foam::autoPtr<Foam::mapPolyMesh> Foam::fvMeshDistribute::doRemoveCells
(
const labelList& cellsToRemove,
@ -1320,8 +1285,6 @@ void Foam::fvMeshDistribute::mapFields(const mapPolyMesh& map)
}
// Delete and add processor patches. Changes mesh and returns per neighbour proc
// the processor patchID.
void Foam::fvMeshDistribute::addProcPatches
(
const labelList& nbrProc, // Processor that neighbour is now on
@ -1424,7 +1387,6 @@ void Foam::fvMeshDistribute::addProcPatches
}
// Get boundary faces to be repatched. Is -1 or new patchID
Foam::labelList Foam::fvMeshDistribute::getBoundaryPatch
(
const labelList& nbrProc, // new processor per boundary face
@ -1455,7 +1417,6 @@ Foam::labelList Foam::fvMeshDistribute::getBoundaryPatch
}
// Send mesh and coupling data.
void Foam::fvMeshDistribute::sendMesh
(
const label domain,
@ -1624,7 +1585,6 @@ void Foam::fvMeshDistribute::sendMesh
}
// Receive mesh. Opposite of sendMesh
Foam::autoPtr<Foam::fvMesh> Foam::fvMeshDistribute::receiveMesh
(
const label domain,
@ -1749,7 +1709,6 @@ Foam::autoPtr<Foam::fvMesh> Foam::fvMeshDistribute::receiveMesh
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
// Construct from components
Foam::fvMeshDistribute::fvMeshDistribute(fvMesh& mesh)
:
mesh_(mesh)
@ -2002,12 +1961,8 @@ Foam::autoPtr<Foam::mapDistributePolyMesh> Foam::fvMeshDistribute::distribute
const wordList dimTensors(mesh_.names(volTensorField::Internal::typeName));
checkEqualWordList("volTensorField::Internal", dimTensors);
// Find patch to temporarily put exposed and processor faces into.
label oldInternalPatchi = findInternalPatch();
const label oldInternalPatchi = findInternalPatch();
// Delete processor patches, starting from the back. Move all faces into
// oldInternalPatchi.

View File

@ -103,7 +103,7 @@ class fvMeshDistribute
// Patch handling
//- Find patch to put exposed internal faces into
//- Find internal patch to put exposed internal faces into
label findInternalPatch() const;
//- Save boundary fields
@ -138,8 +138,11 @@ class fvMeshDistribute
void correctProcessorPatchFields();
//- Delete all processor patches. Move any processor faces into
// patchi.
autoPtr<mapPolyMesh> deleteProcPatches(const label patchi);
// destinationPatch
autoPtr<mapPolyMesh> deleteProcPatches
(
const label destinationPatch
);
//- Repatch the mesh. This is only necessary for the proc
// boundary faces. newPatchID is over all boundary faces: -1 or
@ -154,6 +157,12 @@ class fvMeshDistribute
//- Merge any local points that were remotely coupled.
// constructPointMap is adapted for the new point labels.
//
// Detect shared points. Need processor patches to be present.
// Background: when adding bits of mesh one can get points which
// share the same position but are only detectable to be
// topologically the same point when doing parallel analysis. This
// routine will merge those points.
autoPtr<mapPolyMesh> mergeSharedPoints
(
const labelList& pointToGlobalMaster,

View File

@ -313,31 +313,6 @@ void Foam::fvMeshDistribute::correctProcessorPatchFields()
}
namespace Foam
{
template<class Type>
inline Type max
(
const pointPatchField<Type>& f
)
{
return pTraits<Type>::min;
}
template<class Type>
inline Type min
(
const pointPatchField<Type>& f
)
{
return pTraits<Type>::max;
}
}
template<class GeoField>
void Foam::fvMeshDistribute::sendFields
(

View File

@ -0,0 +1,3 @@
decomposer/fvMeshDistributorDecomposer.C
LIB = $(FOAM_LIBBIN)/libfvMeshDistributors

View File

@ -0,0 +1,14 @@
EXE_INC = \
-I$(LIB_SRC)/parallel/decompose/decompositionMethods/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/dynamicMesh/lnInclude \
-I$(LIB_SRC)/finiteVolume/lnInclude
LIB_LIBS = \
-ldecompositionMethods \
-L$(FOAM_LIBBIN)/dummy -lscotchDecomp -lptscotchDecomp \
-ltriSurface \
-lmeshTools \
-ldynamicMesh \
-lfiniteVolume

View File

@ -0,0 +1,200 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2021 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "fvMeshDistributorDecomposer.H"
#include "decompositionMethod.H"
#include "fvMeshDistribute.H"
#include "mapDistributePolyMesh.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace fvMeshDistributors
{
defineTypeNameAndDebug(decomposer, 0);
addToRunTimeSelectionTable
(
fvMeshDistributor,
decomposer,
fvMesh
);
}
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
void Foam::fvMeshDistributors::decomposer::readDict()
{
const dictionary& decomposerDict(dict());
redistributionInterval_ =
decomposerDict.lookupOrDefault("redistributionInterval", 10);
maxImbalance_ =
decomposerDict.lookupOrDefault<scalar>("maxImbalance", 0.1);
}
void Foam::fvMeshDistributors::decomposer::redecompose()
{
fvMesh& mesh = this->mesh();
IOdictionary decompositionDict
(
IOobject
(
"decomposeParDict",
mesh.time().system(),
mesh,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
)
);
labelList finalDecomp;
// Create decompositionMethod and new decomposition
{
autoPtr<decompositionMethod> decomposer
(
decompositionMethod::New
(
decompositionDict
)
);
if (!decomposer().parallelAware())
{
WarningInFunction
<< "You have selected decomposition method "
<< decomposer().typeName
<< " which does" << endl
<< "not synchronise the decomposition across"
<< " processor patches." << endl
<< " You might want to select a decomposition method which"
<< " is aware of this. Continuing."
<< endl;
}
finalDecomp = decomposer().decompose(mesh, mesh.cellCentres());
}
// Mesh distribution engine
fvMeshDistribute distributor(mesh);
// Do actual sending/receiving of mesh
autoPtr<mapDistributePolyMesh> map = distributor.distribute(finalDecomp);
mesh.distribute(map);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fvMeshDistributors::decomposer::decomposer(fvMesh& mesh)
:
fvMeshDistributor(mesh),
redistributionInterval_(1),
maxImbalance_(0.1),
timeIndex_(-1)
{
readDict();
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::fvMeshDistributors::decomposer::~decomposer()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::fvMeshDistributors::decomposer::update()
{
if
(
Pstream::nProcs() > 1
&& mesh().time().timeIndex() > 1
&& timeIndex_ != mesh().time().timeIndex()
&& mesh().time().timeIndex() % redistributionInterval_ == 0
)
{
timeIndex_ = mesh().time().timeIndex();
const scalar idealNCells =
mesh().globalData().nTotalCells()/Pstream::nProcs();
const scalar imbalance = returnReduce
(
mag(1 - mesh().nCells()/idealNCells),
maxOp<scalar>()
);
if (imbalance > maxImbalance_)
{
if (debug)
{
Info<< "Redistributing mesh with imbalance "
<< imbalance << endl;
}
redecompose();
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
void Foam::fvMeshDistributors::decomposer::updateMesh(const mapPolyMesh&)
{}
void Foam::fvMeshDistributors::decomposer::distribute
(
const mapDistributePolyMesh&
)
{}
bool Foam::fvMeshDistributors::decomposer::write(const bool write) const
{
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,148 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2021 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Class
Foam::fvMeshDistributor::decomposer
Description
Dynamic mesh redistribution using the decomposer
Usage
Example of single field based refinement in all cells:
\verbatim
distributor
{
type decomposer;
libs ("libfvMeshDistributors.so");
// How often to redistribute
redistributionInterval 10;
// Maximum fractional cell distribution imbalance
// before rebalancing
maxImbalance 0.1;
}
\endverbatim
SourceFiles
fvMeshDistributorDecomposer.C
\*---------------------------------------------------------------------------*/
#ifndef fvMeshDistributorDecomposer_H
#define fvMeshDistributorDecomposer_H
#include "fvMeshDistributor.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace fvMeshDistributors
{
/*---------------------------------------------------------------------------*\
Class decomposer Declaration
\*---------------------------------------------------------------------------*/
class decomposer
:
public fvMeshDistributor
{
// Private Member Data
//- Time-step interval between redistribution calls
label redistributionInterval_;
//- Maximum imbalance between the ideal number of cells per processor
// and the maximum or minimum as a ratio mag(1 - nCells/idealNcells)
scalar maxImbalance_;
//- The time index used for updating
label timeIndex_;
// Private Member Functions
//- Read the projection parameters from dictionary
void readDict();
//- Re-decompose the mesh for redistribution
void redecompose();
public:
//- Runtime type information
TypeName("decomposer");
// Constructors
//- Construct from fvMesh
explicit decomposer(fvMesh& mesh);
//- Disallow default bitwise copy construction
decomposer(const decomposer&) = delete;
//- Destructor
virtual ~decomposer();
// Member Functions
//- Distribute the mesh
virtual bool update();
//- Update corresponding to the given map
virtual void updateMesh(const mapPolyMesh&);
//- Update corresponding to the given distribution map
virtual void distribute(const mapDistributePolyMesh&);
// Writing
//- Write using given format, version and compression
virtual bool write(const bool write = true) const;
// Member Operators
//- Disallow default bitwise assignment
void operator=(const decomposer&) = delete;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace fvMeshDistributors
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -161,33 +161,10 @@ Foam::fvMesh::readUpdateState Foam::processorMeshes::readUpdate()
// Check if any new meshes need to be read.
fvMesh::readUpdateState procStat = meshes_[proci].readUpdate();
/*
if (procStat != fvMesh::UNCHANGED)
{
Info<< "Processor " << proci
<< " at time " << databases_[proci].timeName()
<< " detected mesh change " << procStat
<< endl;
}
*/
// Combine into overall mesh change status
if (stat == fvMesh::UNCHANGED)
if (procStat > stat)
{
stat = procStat;
}
else if (stat != procStat)
{
FatalErrorInFunction
<< "Processor " << proci
<< " has a different polyMesh at time "
<< databases_[proci].timeName()
<< " compared to any previous processors." << nl
<< "Please check time " << databases_[proci].timeName()
<< " directories on all processors for consistent"
<< " mesh files."
<< exit(FatalError);
}
}
if
@ -199,6 +176,7 @@ Foam::fvMesh::readUpdateState Foam::processorMeshes::readUpdate()
// Reread all meshes and addressing
read();
}
return stat;
}

View File

@ -20,6 +20,8 @@ internalField uniform (0 0 0);
boundaryField
{
#includeEtc "caseDicts/setConstraintTypes"
stationaryWalls
{
type noSlip;

View File

@ -20,6 +20,8 @@ internalField uniform 0;
boundaryField
{
#includeEtc "caseDicts/setConstraintTypes"
stationaryWalls
{
type zeroGradient;

View File

@ -20,6 +20,8 @@ internalField uniform 0.1;
boundaryField
{
#includeEtc "caseDicts/setConstraintTypes"
stationaryWalls
{
type epsilonWallFunction;

View File

@ -20,6 +20,8 @@ internalField uniform 0.1;
boundaryField
{
#includeEtc "caseDicts/setConstraintTypes"
stationaryWalls
{
type kqRWallFunction;

View File

@ -20,6 +20,8 @@ internalField uniform 0;
boundaryField
{
#includeEtc "caseDicts/setConstraintTypes"
stationaryWalls
{
type nutkWallFunction;

View File

@ -20,6 +20,8 @@ internalField uniform 0;
boundaryField
{
#includeEtc "caseDicts/setConstraintTypes"
stationaryWalls
{
type fixedFluxPressure;

View File

@ -20,6 +20,8 @@ internalField uniform (0 0 0);
boundaryField
{
#includeEtc "caseDicts/setConstraintTypes"
stationaryWalls
{
type fixedValue;

View File

@ -4,13 +4,10 @@ cd ${0%/*} || exit 1 # Run from this directory
# Source tutorial run functions
. $WM_PROJECT_DIR/bin/tools/RunFunctions
# Set application name
application=$(getApplication)
runApplication blockMesh
runApplication topoSet
runApplication subsetMesh -overwrite c0 -patch floatingObject -noFields
runApplication setFields
runApplication $application
runApplication $(getApplication)
#------------------------------------------------------------------------------

View File

@ -0,0 +1,17 @@
#!/bin/sh
cd ${0%/*} || exit 1 # Run from this directory
# Source tutorial run functions
. $WM_PROJECT_DIR/bin/tools/RunFunctions
runApplication blockMesh
runApplication topoSet
runApplication subsetMesh -overwrite c0 -patch floatingObject -noFields
runApplication setFields
runApplication decomposePar
runParallel $(getApplication)
runApplication reconstructParMesh
runApplication reconstructPar
#------------------------------------------------------------------------------

View File

@ -120,4 +120,14 @@ topoChanger
}
distributor
{
type decomposer;
libs ("libfvMeshDistributors.so");
redistributionInterval 10;
}
// ************************************************************************* //

View File

@ -46,6 +46,7 @@ boundary
(0 4 7 3)
);
}
atmosphere
{
type patch;
@ -54,11 +55,18 @@ boundary
(4 5 6 7)
);
}
floatingObject
{
type wall;
faces ();
}
internal
{
type internal;
faces ();
}
);

View File

@ -16,7 +16,7 @@ FoamFile
numberOfSubdomains 8;
method scotch;
method hierarchical;
simpleCoeffs
{
@ -25,7 +25,7 @@ simpleCoeffs
hierarchicalCoeffs
{
n (2 2 1);
n (2 2 2);
order xyz;
}