/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2019-2025 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 "GAMGAgglomeration.H"
#include "GAMGInterface.H"
#include "processorGAMGInterface.H"
#include "cyclicLduInterface.H"
#include "PrecisionAdaptor.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::GAMGAgglomeration::agglomerateLduAddressing
(
const label fineLevelIndex
)
{
const lduMesh& fineMesh = meshLevel(fineLevelIndex);
const lduAddressing& fineMeshAddr = fineMesh.lduAddr();
const labelUList& upperAddr = fineMeshAddr.upperAddr();
const labelUList& lowerAddr = fineMeshAddr.lowerAddr();
const label nFineFaces = upperAddr.size();
// Get restriction map for current level
const labelField& restrictMap = restrictAddressing(fineLevelIndex);
if (min(restrictMap) == -1)
{
FatalErrorInFunction
<< "min(restrictMap) == -1" << exit(FatalError);
}
if (restrictMap.size() != fineMeshAddr.size())
{
FatalErrorInFunction
<< "restrict map does not correspond to fine level. " << endl
<< " Sizes: restrictMap: " << restrictMap.size()
<< " nEqns: " << fineMeshAddr.size()
<< abort(FatalError);
}
// Get the number of coarse cells
const label nCoarseCells = nCells_[fineLevelIndex];
// Storage for coarse cell neighbours and coefficients
// Guess initial maximum number of neighbours in coarse cell
label maxNnbrs = 10;
// Number of faces for each coarse-cell
labelList cCellnFaces(nCoarseCells, Zero);
// Setup initial packed storage for coarse-cell faces
labelList cCellFaces(maxNnbrs*nCoarseCells);
// Create face-restriction addressing
faceRestrictAddressing_.set(fineLevelIndex, new labelList(nFineFaces));
labelList& faceRestrictAddr = faceRestrictAddressing_[fineLevelIndex];
// Initial neighbour array (not in upper-triangle order)
labelList initCoarseNeighb(nFineFaces);
// Counter for coarse faces
label& nCoarseFaces = nFaces_[fineLevelIndex];
nCoarseFaces = 0;
// Loop through all fine faces
forAll(upperAddr, fineFacei)
{
label rmUpperAddr = restrictMap[upperAddr[fineFacei]];
label rmLowerAddr = restrictMap[lowerAddr[fineFacei]];
if (rmUpperAddr == rmLowerAddr)
{
// For each fine face inside of a coarse cell keep the address
// of the cell corresponding to the face in the faceRestrictAddr
// as a negative index
faceRestrictAddr[fineFacei] = -(rmUpperAddr + 1);
}
else
{
// this face is a part of a coarse face
label cOwn = rmUpperAddr;
label cNei = rmLowerAddr;
// get coarse owner and neighbour
if (rmUpperAddr > rmLowerAddr)
{
cOwn = rmLowerAddr;
cNei = rmUpperAddr;
}
// check the neighbour to see if this face has already been found
label* ccFaces = &cCellFaces[maxNnbrs*cOwn];
bool nbrFound = false;
label& ccnFaces = cCellnFaces[cOwn];
for (int i=0; i= maxNnbrs)
{
label oldMaxNnbrs = maxNnbrs;
maxNnbrs *= 2;
cCellFaces.setSize(maxNnbrs*nCoarseCells);
forAllReverse(cCellnFaces, i)
{
label* oldCcNbrs = &cCellFaces[oldMaxNnbrs*i];
label* newCcNbrs = &cCellFaces[maxNnbrs*i];
for (int j=0; j= 0)
{
faceRestrictAddr[fineFacei] =
coarseFaceMap[faceRestrictAddr[fineFacei]];
}
}
// Create face-flip status
faceFlipMap_.set(fineLevelIndex, new boolList(nFineFaces, false));
boolList& faceFlipMap = faceFlipMap_[fineLevelIndex];
forAll(faceRestrictAddr, fineFacei)
{
label coarseFacei = faceRestrictAddr[fineFacei];
if (coarseFacei >= 0)
{
// Maps to coarse face
label cOwn = coarseOwner[coarseFacei];
label cNei = coarseNeighbour[coarseFacei];
label rmUpperAddr = restrictMap[upperAddr[fineFacei]];
label rmLowerAddr = restrictMap[lowerAddr[fineFacei]];
if (cOwn == rmUpperAddr && cNei == rmLowerAddr)
{
faceFlipMap[fineFacei] = true;
}
else if (cOwn == rmLowerAddr && cNei == rmUpperAddr)
{
//faceFlipMap[fineFacei] = false;
}
else
{
FatalErrorInFunction
<< "problem."
<< " fineFacei:" << fineFacei
<< " rmUpperAddr:" << rmUpperAddr
<< " rmLowerAddr:" << rmLowerAddr
<< " coarseFacei:" << coarseFacei
<< " cOwn:" << cOwn
<< " cNei:" << cNei
<< exit(FatalError);
}
}
}
// Clear the temporary storage for the coarse cell data
cCellnFaces.clear();
cCellFaces.clear();
initCoarseNeighb.clear();
coarseFaceMap.clear();
// Create coarse-level interfaces
// Get reference to fine-level interfaces
const lduInterfacePtrsList& fineInterfaces = interfaceLevel(fineLevelIndex);
nPatchFaces_.set
(
fineLevelIndex,
new labelList(fineInterfaces.size(), Zero)
);
labelList& nPatchFaces = nPatchFaces_[fineLevelIndex];
patchFaceRestrictAddressing_.set
(
fineLevelIndex,
new labelListList(fineInterfaces.size())
);
labelListList& patchFineToCoarse =
patchFaceRestrictAddressing_[fineLevelIndex];
const label startOfRequests = UPstream::nRequests();
// Initialise transfer of restrict addressing on the interface
// The finest mesh uses patchAddr from the original lduAdressing.
// the coarser levels create their own adressing for faceCells
forAll(fineInterfaces, inti)
{
if (fineInterfaces.set(inti))
{
if (fineLevelIndex == 0)
{
fineInterfaces[inti].initInternalFieldTransfer
(
Pstream::commsTypes::nonBlocking,
restrictMap,
fineMeshAddr.patchAddr(inti)
);
}
else
{
fineInterfaces[inti].initInternalFieldTransfer
(
Pstream::commsTypes::nonBlocking,
restrictMap
);
}
}
}
UPstream::waitRequests(startOfRequests);
// Add the coarse level
meshLevels_.set
(
fineLevelIndex,
new lduPrimitiveMesh
(
nCoarseCells,
coarseOwner,
coarseNeighbour,
fineMesh.comm(),
true
)
);
lduInterfacePtrsList coarseInterfaces(fineInterfaces.size());
forAll(fineInterfaces, inti)
{
if (fineInterfaces.set(inti))
{
tmp restrictMapInternalField;
// The finest mesh uses patchAddr from the original lduAdressing.
// the coarser levels create their own adressing for faceCells
if (fineLevelIndex == 0)
{
restrictMapInternalField =
fineInterfaces[inti].interfaceInternalField
(
restrictMap,
fineMeshAddr.patchAddr(inti)
);
}
else
{
restrictMapInternalField =
fineInterfaces[inti].interfaceInternalField
(
restrictMap
);
}
tmp nbrRestrictMapInternalField =
fineInterfaces[inti].internalFieldTransfer
(
Pstream::commsTypes::nonBlocking,
restrictMap
);
coarseInterfaces.set
(
inti,
GAMGInterface::New
(
inti,
meshLevels_[fineLevelIndex].rawInterfaces(),
fineInterfaces[inti],
restrictMapInternalField(),
nbrRestrictMapInternalField(),
fineLevelIndex,
fineMesh.comm()
).ptr()
);
/* Same as below:
coarseInterfaces.set
(
inti,
GAMGInterface::New
(
inti,
meshLevels_[fineLevelIndex].rawInterfaces(),
fineInterfaces[inti],
fineInterfaces[inti].interfaceInternalField(restrictMap),
fineInterfaces[inti].internalFieldTransfer
(
Pstream::commsTypes::nonBlocking,
restrictMap
),
fineLevelIndex,
fineMesh.comm()
).ptr()
);
*/
nPatchFaces[inti] = coarseInterfaces[inti].faceCells().size();
patchFineToCoarse[inti] = refCast
(
coarseInterfaces[inti]
).faceRestrictAddressing();
}
}
meshLevels_[fineLevelIndex].addInterfaces
(
coarseInterfaces,
lduPrimitiveMesh::nonBlockingSchedule
(
coarseInterfaces
)
);
if (debug & 2)
{
const auto& coarseAddr = meshLevels_[fineLevelIndex].lduAddr();
Pout<< "GAMGAgglomeration :"
<< " agglomerated level " << fineLevelIndex
<< " from nCells:" << fineMeshAddr.size()
<< " nFaces:" << upperAddr.size()
<< " to nCells:" << nCoarseCells
<< " nFaces:" << nCoarseFaces << nl
<< " lower:" << flatOutput(coarseAddr.lowerAddr()) << nl
<< " upper:" << flatOutput(coarseAddr.upperAddr()) << nl
<< endl;
}
}
void Foam::GAMGAgglomeration::procAgglomerateLduAddressing
(
const label meshComm,
const labelList& procAgglomMap,
const labelList& procIDs,
const label allMeshComm,
const label levelIndex
)
{
// - Assemble all the procIDs in meshComm onto a single master
// (procIDs[0]). This constructs a new communicator ('comm') first.
// - The master communicates with neighbouring masters using
// allMeshComm
const lduMesh& myMesh = meshLevels_[levelIndex-1];
const label nOldInterfaces = myMesh.interfaces().size();
procAgglomMap_.set(levelIndex, new labelList(procAgglomMap));
agglomProcIDs_.set(levelIndex, new labelList(procIDs));
procCommunicator_[levelIndex] = allMeshComm;
procAgglomCommunicator_.set
(
levelIndex,
new UPstream::communicator
(
meshComm,
procIDs
)
);
const label comm = agglomCommunicator(levelIndex);
// These could only be set on the master procs but it is
// quite convenient to also have them on the slaves
procCellOffsets_.set(levelIndex, new labelList(0));
procFaceMap_.set(levelIndex, new labelListList(0));
procBoundaryMap_.set(levelIndex, new labelListList(0));
procBoundaryFaceMap_.set(levelIndex, new labelListListList(0));
// Collect meshes. Does no renumbering
PtrList otherMeshes;
lduPrimitiveMesh::gather(comm, myMesh, otherMeshes);
if (Pstream::myProcNo(meshComm) == procIDs[0])
{
// Combine all addressing
labelList procFaceOffsets;
meshLevels_.set
(
levelIndex-1,
new lduPrimitiveMesh
(
allMeshComm,
procAgglomMap,
procIDs,
myMesh,
otherMeshes,
procCellOffsets_[levelIndex],
procFaceOffsets,
procFaceMap_[levelIndex],
procBoundaryMap_[levelIndex],
procBoundaryFaceMap_[levelIndex]
)
);
}
// Scatter the procBoundaryMap back to the originating processor
// so it knows which proc boundaries are to be kept. This is used
// so we only send over interfaceFields on kept processors (see
// GAMGSolver::procAgglomerateMatrix)
// TBD: using sub-communicator here (instead of explicit procIDs). Should
// use sub-communicators more in other places.
{
const CompactListList