Additional overset fringe strategy. Vuko Vukcevic

cuttingPatchFringe (based on cutting patches)
This commit is contained in:
Hrvoje Jasak 2019-07-11 13:42:23 +01:00
commit 1416e3c96c
14 changed files with 1963 additions and 28 deletions

View file

@ -4,6 +4,8 @@ oversetFringe/oversetFringe/oversetFringe.C
oversetFringe/oversetFringe/newOversetFringe.C oversetFringe/oversetFringe/newOversetFringe.C
oversetFringe/manualFringe/manualFringe.C oversetFringe/manualFringe/manualFringe.C
oversetFringe/faceCellsFringe/faceCellsFringe.C oversetFringe/faceCellsFringe/faceCellsFringe.C
oversetFringe/donorBasedLayeredOverlapFringe/donorBasedLayeredOverlapFringe.C
oversetFringe/cuttingPatchFringe/cuttingPatchFringe.C
oversetFringe/overlapFringe/overlapFringe/overlapFringe.C oversetFringe/overlapFringe/overlapFringe/overlapFringe.C
oversetFringe/overlapFringe/layeredOverlapFringe/layeredOverlapFringe.C oversetFringe/overlapFringe/layeredOverlapFringe/layeredOverlapFringe.C
oversetFringe/overlapFringe/adaptiveOverlapFringe/adaptiveOverlapFringe.C oversetFringe/overlapFringe/adaptiveOverlapFringe/adaptiveOverlapFringe.C

View file

@ -2,12 +2,10 @@ EXE_INC = \
-I$(LIB_SRC)/finiteVolume/lnInclude \ -I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \ -I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude \ -I$(LIB_SRC)/surfMesh/lnInclude \
-I$(LIB_SRC)/sampling/lnInclude \ -I$(LIB_SRC)/sampling/lnInclude
-I$(LIB_SRC)/dynamicMesh/dynamicMesh/lnInclude
EXE_LIBS = \ EXE_LIBS = \
-lfiniteVolume \ -lfiniteVolume \
-lmeshTools \ -lmeshTools \
-lsurfMesh \ -lsurfMesh \
-lsampling \ -lsampling
-ldynamicMesh

View file

@ -59,7 +59,7 @@ void Foam::oversetAdjustPhi
const unallocLabelList& neighbour = mesh.neighbour(); const unallocLabelList& neighbour = mesh.neighbour();
// Get region split to identify separate mesh components // Get region split to identify separate mesh components
const labelList& regionID = om.regionID(); const scalarField& regionID = om.regionID().internalField();
// Sum up incoming and outgoing flux // Sum up incoming and outgoing flux
scalar fringeIn = 0; scalar fringeIn = 0;
@ -80,8 +80,12 @@ void Foam::oversetAdjustPhi
{ {
// Internal face // Internal face
// Debug check // Debug check. Note: use notEqual since we are comparing two
if (regionID[owner[curFace]] != regionID[neighbour[curFace]]) // scalars
if
(
notEqual(regionID[owner[curFace]], regionID[neighbour[curFace]])
)
{ {
FatalErrorIn FatalErrorIn
( (

View file

@ -60,7 +60,7 @@ void Foam::regionWiseOversetAdjustPhi
const unallocLabelList& neighbour = mesh.neighbour(); const unallocLabelList& neighbour = mesh.neighbour();
// Get region split to identify separate mesh components // Get region split to identify separate mesh components
const labelList& regionID = om.regionID(); const scalarField& regionID = om.regionID().internalField();
// Incoming and outgoing region fluxes // Incoming and outgoing region fluxes
scalarField regionIn(om.regions().size(), 0); scalarField regionIn(om.regions().size(), 0);
@ -94,7 +94,7 @@ void Foam::regionWiseOversetAdjustPhi
// scaled // scaled
forAll (phip, i) forAll (phip, i)
{ {
// Get current region index // Get current region index. Note: conversion to label
const label curRegion = regionID[fc[i]]; const label curRegion = regionID[fc[i]];
if (phip[i] < 0.0) if (phip[i] < 0.0)
@ -124,11 +124,12 @@ void Foam::regionWiseOversetAdjustPhi
{ {
// Internal face // Internal face
// Get region index // Get region index. Note: conversion to label
const label curRegion = regionID[owner[curFace]]; const label curRegion = regionID[owner[curFace]];
// Check whether owner and neighbour belong to the same region // Check whether owner and neighbour belong to the same region.
if (curRegion != regionID[neighbour[curFace]]) // Note: use notEqual function since we are comparing two scalars
if (notEqual(curRegion, regionID[neighbour[curFace]]))
{ {
FatalErrorIn FatalErrorIn
( (
@ -210,7 +211,7 @@ void Foam::regionWiseOversetAdjustPhi
{ {
// Processor patch, master side // Processor patch, master side
// Get region index // Get region index. Note: conversion to label
const label curRegion = const label curRegion =
regionID[mesh.boundary()[patchI].faceCells()[faceI]]; regionID[mesh.boundary()[patchI].faceCells()[faceI]];
@ -321,7 +322,7 @@ void Foam::regionWiseOversetAdjustPhi
{ {
// Internal face // Internal face
// Get region index // Get region index. Note: conversion to label
const label curRegion = regionID[owner[curFace]]; const label curRegion = regionID[owner[curFace]];
// Get reference to the flux for scaling // Get reference to the flux for scaling
@ -358,7 +359,7 @@ void Foam::regionWiseOversetAdjustPhi
if (procPatch.master()) // Owner side if (procPatch.master()) // Owner side
{ {
// Get region index // Get region index. Note: conversion to label
const label curRegion = const label curRegion =
regionID[mesh.boundary()[patchI].faceCells()[faceI]]; regionID[mesh.boundary()[patchI].faceCells()[faceI]];
@ -374,7 +375,7 @@ void Foam::regionWiseOversetAdjustPhi
} }
else // Neighbouring processor side else // Neighbouring processor side
{ {
// Get region index // Get region index. Note: conversion to label
const label curRegion = const label curRegion =
regionID[mesh.boundary()[patchI].faceCells()[faceI]]; regionID[mesh.boundary()[patchI].faceCells()[faceI]];

View file

@ -0,0 +1,649 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | foam-extend: Open Source CFD
\\ / O peration | Version: 4.1
\\ / A nd | Web: http://www.foam-extend.org
\\/ M anipulation | For copyright notice see file Copyright
-------------------------------------------------------------------------------
License
This file is part of foam-extend.
foam-extend 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.
foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "cuttingPatchFringe.H"
#include "oversetMesh.H"
#include "oversetRegion.H"
#include "polyPatchID.H"
#include "addToRunTimeSelectionTable.H"
#include "syncTools.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(cuttingPatchFringe, 0);
addToRunTimeSelectionTable
(
oversetFringe,
cuttingPatchFringe,
dictionary
);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::cuttingPatchFringe::calcAddressing() const
{
// Make sure that either acceptorsPtr is unnalocated or if it is allocated,
// that it is empty
if (acceptorsPtr_ && !acceptorsPtr_->empty())
{
FatalErrorIn
(
"void Foam::cuttingPatchFringe::calcAddressing() const"
) << "Addressing already calculated"
<< abort(FatalError);
}
// Get polyMesh
const polyMesh& mesh = this->mesh();
// Collect all cutting patches
labelHashSet patchIDs(cuttingPatchNames_.size());
forAll (cuttingPatchNames_, nameI)
{
// Get polyPatchID and check if valid
const polyPatchID cutPatch
(
cuttingPatchNames_[nameI],
mesh.boundaryMesh()
);
if (!cutPatch.active())
{
FatalErrorIn
(
"void cuttingPatchFringe::calcAddressing const"
) << "Cutting patch " << cuttingPatchNames_[nameI]
<< " cannot be found."
<< abort(FatalError);
}
// Store patch ID in the set
patchIDs.insert(cutPatch.index());
}
if (debug)
{
Info<< "Starting cutting patch fringe assembly..." << endl;
}
// Note: similar code as in oversetRegion::calcHoleTriMesh. Consider
// refactoring. VV, 20/May/2019
// Make and invert local triSurface
triFaceList triFaces;
pointField triPoints;
// Memory management
{
triSurface ts = triSurfaceTools::triangulate
(
mesh.boundaryMesh(),
patchIDs
);
// Clean mutiple points and zero-sized triangles
ts.cleanup(false);
triFaces.setSize(ts.size());
triPoints = ts.points();
forAll (ts, tsI)
{
// Bugfix: no need to reverse the face because the normals point in
// the correct direction already. VV, 20/May/2019.
triFaces[tsI] = ts[tsI];
}
}
if (Pstream::parRun())
{
// Combine all faces and points into a single list
List<triFaceList> allTriFaces(Pstream::nProcs());
List<pointField> allTriPoints(Pstream::nProcs());
allTriFaces[Pstream::myProcNo()] = triFaces;
allTriPoints[Pstream::myProcNo()] = triPoints;
Pstream::gatherList(allTriFaces);
Pstream::scatterList(allTriFaces);
Pstream::gatherList(allTriPoints);
Pstream::scatterList(allTriPoints);
// Re-pack points and faces
label nTris = 0;
label nPoints = 0;
forAll (allTriFaces, procI)
{
nTris += allTriFaces[procI].size();
nPoints += allTriPoints[procI].size();
}
// Pack points
triPoints.setSize(nPoints);
// Prepare point renumbering array
labelListList renumberPoints(Pstream::nProcs());
nPoints = 0;
forAll (allTriPoints, procI)
{
const pointField& ptp = allTriPoints[procI];
renumberPoints[procI].setSize(ptp.size());
labelList& procRenumberPoints = renumberPoints[procI];
forAll (ptp, ptpI)
{
triPoints[nPoints] = ptp[ptpI];
procRenumberPoints[ptpI] = nPoints;
nPoints++;
}
}
// Pack triangles and renumber into complete points on the fly
triFaces.setSize(nTris);
nTris = 0;
forAll (allTriFaces, procI)
{
const triFaceList& ptf = allTriFaces[procI];
const labelList& procRenumberPoints = renumberPoints[procI];
forAll (ptf, ptfI)
{
const triFace& procFace = ptf[ptfI];
triFace& renumberFace = triFaces[nTris];
forAll (renumberFace, rfI)
{
renumberFace[rfI] =
procRenumberPoints[procFace[rfI]];
}
nTris++;
}
}
}
// Make a complete triSurface from local data
triSurface patchTriMesh(triFaces, triPoints);
// Clean up duplicate points and zero sized triangles
patchTriMesh.cleanup(false);
// Get this region
const oversetRegion& myRegion = this->region();
// Debug: write down the tri mesh
if (Pstream::master())
{
patchTriMesh.write
(
word
(
"patchTriSurface_region" + myRegion.name() + ".vtk"
)
);
}
// Create the tri surface search object
triSurfaceSearch patchTriSearch(patchTriMesh);
// Get cells in this region
const labelList& myRegionCells = myRegion.regionCells();
// Get cell centres for inside-outside search using search object
vectorField myCC(myRegionCells.size());
// Cell centres from polyMesh
const vectorField& cc = mesh.cellCentres();
forAll (myCC, i)
{
myCC[i] = cc[myRegionCells[i]];
}
// Inside mask: all cells within search object will be marked
boolList insideMask(mesh.nCells(), false);
// Get inside cells for cells in my region only
boolList myRegionInsideMask = patchTriSearch.calcInside(myCC);
// Note: insideMask has the size of all mesh cells and
// myRegionInsideMask has the size of cells in this region
forAll (myRegionInsideMask, i)
{
insideMask[myRegionCells[i]] = myRegionInsideMask[i];
}
// Make sure that the cut holes for this region are also properly marked as
// "inside". This may not be the case automatically for e.g. simulations
// with appendages
const labelList& cutRegionHoles = myRegion.cutHoles();
forAll (cutRegionHoles, i)
{
insideMask[cutRegionHoles[i]] = true;
}
// Get necessary mesh data (from polyMesh/primitiveMesh)
const cellList& meshCells = mesh.cells();
const unallocLabelList& owner = mesh.faceOwner();
const unallocLabelList& neighbour = mesh.faceNeighbour();
// Bool list for collecting faces with at least one unmarked
// cell (to determine the acceptors for the first iteration)
boolList hasUnmarkedCell(mesh.nFaces(), false);
// Loop through all cells
forAll (insideMask, cellI)
{
if (!insideMask[cellI])
{
// This cell is not inside (it is unmarked). Loop through
// its faces and set the flag
const cell& cFaces = meshCells[cellI];
forAll (cFaces, i)
{
// Set the mark for this global face
hasUnmarkedCell[cFaces[i]] = true;
}
}
}
// Sync the face list across processor boundaries
syncTools::syncFaceList
(
mesh,
hasUnmarkedCell,
orEqOp<bool>(),
true
);
// Mark-up for all inside faces
boolList insideFaceMask(mesh.nFaces(), false);
// Collect all acceptors for the first iteration (the cells that
// have at least one neighbour cell that is not marked)
labelHashSet acceptors(myRegionCells.size()/10);
// Loop again through all cells and collect marked ones into
// acceptors or holes, depending on whether they have unmarked cell
// as a neighbour (indicating an acceptor)
forAll (insideMask, cellI)
{
if (insideMask[cellI])
{
// This cell is inside the covered region
const cell& cFaces = meshCells[cellI];
forAll (cFaces, i)
{
// Get global face index
const label& faceI = cFaces[i];
// Check whether this neighbour is unmarked
if (hasUnmarkedCell[faceI])
{
// This cell has unmarked neighbour, collect it into
// the acceptor list
acceptors.insert(cellI);
// This cell is no longer "inside cell"
insideMask[cellI] = false;
}
else
{
// This is an "inside" face, mark it
insideFaceMask[faceI] = true;
}
} // End for all faces
} // End if cell is inside
} // End for all cells
// Note: insideFaceMask already synced across processors because it relies
// on hasUnmarkedCell list, which has been synced just above
// Hash set containing new acceptors (for successive iterations)
labelHashSet newAcceptors(acceptors.size());
// Now that we have the initial set of acceptors (and holes), loop
// nLayers away from initial donors
for (label i = 0; i < nLayers_; ++i)
{
// Face markup for propagation
boolList propagateFace(mesh.nFaces(), false);
// Loop through all acceptors and mark faces that are around hole
// cells. This way, we make sure that we go towards the correct,
// inside direction
forAllConstIter (labelHashSet, acceptors, iter)
{
// Get the cell index and the cell
const label& cellI = iter.key();
const cell& cFaces = meshCells[cellI];
// Loop through all faces of the cell
forAll (cFaces, i)
{
// Get face index (global)
const label& faceI = cFaces[i];
if (insideFaceMask[faceI])
{
// This is a hole face, we are moving in the right
// direction. Mark the face for propagation
propagateFace[faceI] = true;
}
} // End for all faces of the cell
} // End for all donor cells
// Sync the face list across processor boundaries
syncTools::syncFaceList
(
mesh,
propagateFace,
orEqOp<bool>(),
false
);
// Loop through all internal faces and append acceptors
for (label faceI = 0; faceI < mesh.nInternalFaces(); ++faceI)
{
if (propagateFace[faceI])
{
// Face is marked, select owner or neighbour
const label& own = owner[faceI];
const label& nei = neighbour[faceI];
// Either owner or neighbour may be hole, not both
if (insideMask[own])
{
// Owner cell is a hole, insert it
newAcceptors.insert(own);
// Update hole mask
insideMask[own] = false;
}
else if (insideMask[nei])
{
// Neighbour cell is a hole, insert it
newAcceptors.insert(nei);
// Update hole mask
insideMask[nei] = false;
}
// Also update hole face mask for next iteration
insideFaceMask[faceI] = false;
}
}
// Loop through boundary faces
for
(
label faceI = mesh.nInternalFaces();
faceI < mesh.nFaces();
++faceI
)
{
if (propagateFace[faceI])
{
// Face is marked, select owner if this is the right
// side. Neighbour handled on the other side
const label& own = owner[faceI];
if (insideMask[own])
{
// Face cell is a hole, insert it
newAcceptors.insert(own);
// Update hole mask
insideMask[own] = false;
}
// Also update hole face mask for next iteration
insideFaceMask[faceI] = false;
}
}
// Transfer newAcceptors into acceptors for next iteration or
// for final assembly. Resize newAcceptors accordingly
acceptors.transfer(newAcceptors);
newAcceptors.resize(acceptors.size());
} // End for specified number of layers
// At this point, we have the final set of acceptors and we marked
// all cells that should be holes. Collect them into the list
dynamicLabelList fringeHoles(myRegionCells.size()/10);
forAll (insideMask, cellI)
{
if (insideMask[cellI])
{
fringeHoles.append(cellI);
}
}
// Set acceptors and holes from the data for all regions
acceptorsPtr_ = new labelList(acceptors.sortedToc());
fringeHolesPtr_ = new labelList(fringeHoles.xfer());
if (debug)
{
Info<< "In cuttingPatchFringe::calcAddressing() const" << nl
<< "Found " << acceptorsPtr_->size() << " acceptors." << nl
<< "Found " << fringeHolesPtr_->size() << " fringe holes." << endl;
}
}
void Foam::cuttingPatchFringe::clearAddressing() const
{
deleteDemandDrivenData(fringeHolesPtr_);
deleteDemandDrivenData(acceptorsPtr_);
deleteDemandDrivenData(finalDonorAcceptorsPtr_);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
// Construct from dictionary
Foam::cuttingPatchFringe::cuttingPatchFringe
(
const fvMesh& mesh,
const oversetRegion& region,
const dictionary& dict
)
:
oversetFringe(mesh, region, dict),
cuttingPatchNames_(dict.lookup("cuttingPatches")),
nLayers_(readLabel(dict.lookup("nLayers"))),
fringeHolesPtr_(nullptr),
acceptorsPtr_(nullptr),
finalDonorAcceptorsPtr_(nullptr)
{
// Sanity check number of layers: must be greater than 0
if (nLayers_ < 1)
{
FatalIOErrorIn
(
"cuttingPatchFringe::"
"cuttingPatchFringe\n"
"(\n"
" const fvMesh& mesh,\n"
" const oversetRegion& region,\n"
" const dictionary& dict\n"
")",
dict
) << "Invalid number of layers specified, nLayers = " << nLayers_
<< nl
<< "The number should be greater than 0."
<< exit(FatalError);
}
// Preferably, the number of layers should be at least 2
if (nLayers_ == 1)
{
WarningIn
(
"cuttingPatchFringe::"
"cuttingPatchFringe\n"
"(\n"
" const fvMesh& mesh,\n"
" const oversetRegion& region,\n"
" const dictionary& dict\n"
")"
) << "You have specified nLayers = " << nLayers_
<< nl
<< "We strongly advise to use at least 2 layers to avoid" << nl
<< "possibility of having acceptors that cannot find decent" << nl
<< "donors on the other side."
<< endl;
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::cuttingPatchFringe::~cuttingPatchFringe()
{
clearAddressing();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::cuttingPatchFringe::updateIteration
(
donorAcceptorList& donorAcceptorRegionData
) const
{
// If the donorAcceptor list has been allocated, something went wrong with
// the iteration procedure (not-updated flag): this function has been called
// more than once, which should not happen for cuttingPatchFringe
if (finalDonorAcceptorsPtr_)
{
FatalErrorIn
(
"cuttingPatchFringe::"
"updateIteration(donorAcceptorList&) const"
) << "finalDonorAcceptorPtr_ already allocated. Something went "
<< "wrong with the iteration procedure (flag was not updated)."
<< nl
<< "This should not happen for cuttingPatchFringe."
<< abort(FatalError);
}
// Allocate the list by reusing the argument list
finalDonorAcceptorsPtr_ = new donorAcceptorList
(
donorAcceptorRegionData,
true
);
// Set the flag to true and return
updateSuitableOverlapFlag(true);
return foundSuitableOverlap();
}
const Foam::labelList& Foam::cuttingPatchFringe::fringeHoles() const
{
if (!fringeHolesPtr_)
{
calcAddressing();
}
return *fringeHolesPtr_;
}
const Foam::labelList& Foam::cuttingPatchFringe::candidateAcceptors() const
{
if (!acceptorsPtr_)
{
calcAddressing();
}
return *acceptorsPtr_;
}
Foam::donorAcceptorList&
Foam::cuttingPatchFringe::finalDonorAcceptors() const
{
if (!finalDonorAcceptorsPtr_)
{
FatalErrorIn("cuttingPatchFringe::finalDonorAcceptors()")
<< "finalDonorAcceptorPtr_ not allocated. Make sure you have"
<< " called cuttingPatchFringe::updateIteration() before"
<< " asking for final set of donor/acceptor pairs."
<< abort(FatalError);
}
if (!foundSuitableOverlap())
{
FatalErrorIn("cuttingPatchFringe::finalDonorAcceptors()")
<< "Attemted to access finalDonorAcceptors but suitable overlap "
<< "has not been found. This is not allowed. "
<< abort(FatalError);
}
return *finalDonorAcceptorsPtr_;
}
void Foam::cuttingPatchFringe::update() const
{
Info<< "cuttingPatchFringe::update() const" << endl;
// Clear out
clearAddressing();
// Set flag to false and clear final donor/acceptors only
deleteDemandDrivenData(finalDonorAcceptorsPtr_);
updateSuitableOverlapFlag(false);
}
// ************************************************************************* //

View file

@ -0,0 +1,151 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | foam-extend: Open Source CFD
\\ / O peration | Version: 4.1
\\ / A nd | Web: http://www.foam-extend.org
\\/ M anipulation | For copyright notice see file Copyright
-------------------------------------------------------------------------------
License
This file is part of foam-extend.
foam-extend 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.
foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>.
Class
cuttingPatchFringe
Description
Fringe algorithm based on collecting all the cells cut by its donor region
patch (assuming faceCells algorithm is used). Inside cells are marked and
moved nLayers towards the interior to define the acceptors as robustly as
possible.
Author
Vuko Vukcevic, Wikki Ltd. All rights reserved.
SourceFiles
cuttingPatchFringe.C
\*---------------------------------------------------------------------------*/
#ifndef cuttingPatchFringe_H
#define cuttingPatchFringe_H
#include "oversetFringe.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class cuttingPatchFringe Declaration
\*---------------------------------------------------------------------------*/
class cuttingPatchFringe
:
public oversetFringe
{
// Private data
//- Names of the cuttingPatches
wordList cuttingPatchNames_;
//- How many layers to move away from connected region donors to define
// acceptor (and holes)
label nLayers_;
//- Fringe hole cells
mutable labelList* fringeHolesPtr_;
//- Acceptor cells
mutable labelList* acceptorsPtr_;
//- Final donor/acceptor pairs for this region (fringe)
mutable donorAcceptorList* finalDonorAcceptorsPtr_;
// Private Member Functions
//- Calculate hole and acceptor addressing
void calcAddressing() const;
//- Clear addressing
void clearAddressing() const;
public:
//- Runtime type information
TypeName("cuttingPatch");
// Constructors
//- Construct from dictionary
cuttingPatchFringe
(
const fvMesh& mesh,
const oversetRegion& region,
const dictionary& dict
);
//- Disallow default bitwise copy construct
cuttingPatchFringe
(
const cuttingPatchFringe&
) = delete;
//- Disallow default bitwise assignment
void operator=(const cuttingPatchFringe&) = delete;
// Destructor
virtual ~cuttingPatchFringe();
// Member Functions
//- Update iteration. Note: invalidates parameter
virtual bool updateIteration
(
donorAcceptorList& donorAcceptorRegionData
) const;
//- Return list of deactivated (hole) cells
// Fringe hole cells are collected in addition to geometric hole
// cells, which fall outside of all donor regions
virtual const labelList& fringeHoles() const;
//- Return list of acceptor cells
virtual const labelList& candidateAcceptors() const;
//- Return list of final donor acceptor pairs. Note: caller may
// invalidate finalDonorAcceptorsPtr_ for optimisation purposes
virtual donorAcceptorList& finalDonorAcceptors() const;
//- Update the fringe
virtual void update() const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View file

@ -0,0 +1,887 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | foam-extend: Open Source CFD
\\ / O peration | Version: 4.1
\\ / A nd | Web: http://www.foam-extend.org
\\/ M anipulation | For copyright notice see file Copyright
-------------------------------------------------------------------------------
License
This file is part of foam-extend.
foam-extend 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.
foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "donorBasedLayeredOverlapFringe.H"
#include "oversetMesh.H"
#include "oversetRegion.H"
#include "faceCellsFringe.H"
#include "oversetRegion.H"
#include "addToRunTimeSelectionTable.H"
#include "syncTools.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(donorBasedLayeredOverlapFringe, 0);
addToRunTimeSelectionTable
(
oversetFringe,
donorBasedLayeredOverlapFringe,
dictionary
);
}
const Foam::debug::tolerancesSwitch
Foam::donorBasedLayeredOverlapFringe::distTol_
(
"donorBasedLayeredOverlapDistanceTolerance",
0.0
);
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::donorBasedLayeredOverlapFringe::init() const
{
// Set size of the list containing IDs
connectedRegionIDs_.setSize(connectedRegionNames_.size());
// Get list of all overset regions
const PtrList<oversetRegion>& allRegions =
this->region().overset().regions();
// Create list of all region names for easy lookup
wordList allRegionNames(allRegions.size());
forAll (allRegionNames, arI)
{
allRegionNames[arI] = allRegions[arI].name();
}
// Loop through all regions and check whether the overlap has been found
forAll (connectedRegionNames_, crI)
{
// Get name of this connected region
const word& crName = connectedRegionNames_[crI];
// Find this region in the list of all regions
const label regionID = findIndex(allRegionNames, crName);
if (regionID == -1)
{
FatalErrorIn("void donorBasedLayeredOverlapFringe::init() const")
<< "Region " << crName << " not found in list of regions."
<< "List of overset regions: " << allRegionNames
<< abort(FatalError);
}
// Collect the region index in the list
connectedRegionIDs_[crI] = regionID;
// Sanity check: if the specified connected donor region has more than 1
// donor regions, this fringe algorithm is attempted to be used for
// something that's not intended. Issue an error
if (allRegions[regionID].donorRegions().size() != 1)
{
FatalErrorIn("void donorBasedLayeredOverlapFringe::init() const")
<< "Region " << crName << " specified as connected region, but"
<< " that region has "
<< allRegions[regionID].donorRegions().size()
<< " donor regions."
<< abort(FatalError);
}
// Sanity check whether the donor region of connected region is actually
// this region
if (allRegions[regionID].donorRegions()[0] != this->region().index())
{
FatalErrorIn("void donorBasedLayeredOverlapFringe::init() const")
<< "The donor region of region " << crName
<< " should be only region " << this->region().name()
<< abort(FatalError);
}
}
// Sanity check: number of (optionally) specified centre points must be
// equal to the number of connected regions
if
(
!regionCentrePoints_.empty()
&& (regionCentrePoints_.size() != connectedRegionIDs_.size())
)
{
FatalErrorIn("void donorBasedLayeredOverlapFringe::init() const")
<< "You have specified "
<< regionCentrePoints_.size()
<< " regionCentrePoints, while specifying "
<< connectedRegionIDs_.size()
<< " connectedRegions."
<< nl
<< "If you'd like to avoid using automatic centre point detection,"
<< " make sure to specify centre points for all connected regions."
<< abort(FatalError);
}
}
void Foam::donorBasedLayeredOverlapFringe::calcAddressing() const
{
// Make sure that either acceptorsPtr is unnalocated or if it is allocated,
// that it is empty
if (acceptorsPtr_ && !acceptorsPtr_->empty())
{
FatalErrorIn
(
"void Foam::donorBasedLayeredOverlapFringe::calcAddressing() const"
) << "Addressing already calculated"
<< abort(FatalError);
}
if (!isInitialized_)
{
// This is the first call, initialize the data and set flag to true
init();
isInitialized_ = true;
}
// Get list of all overset regions
const PtrList<oversetRegion>& allRegions =
this->region().overset().regions();
// Loop through all connected regions and check whether the fringe overlap
// has been found for all of them
bool allFringesReady = true;
forAll (connectedRegionIDs_, crI)
{
// Get ID of this region
const label& regionID = connectedRegionIDs_[crI];
// Get the overset region
const oversetRegion& region = allRegions[regionID];
// Get the fringe algorithm from the region
const oversetFringe& fringe = region.fringe();
// If this is not faceCells fringe, issue a Warning. This fringe
// selection algorithm is intended to work only with faceCells fringe on
// the other side. VV, 9/Apr/2019
if (!isA<faceCellsFringe>(fringe))
{
WarningIn
(
"void Foam::donorBasedLayeredOverlapFringe::"
"updateIteration(donorAcceptorList&) const"
) << "donorBasedLayeredOverlap fringe is designed to work"
<< " with faceCells fringe as a connected region fringe."
<< nl
<< "Connected overset region " << region.name()
<< " has " << fringe.type() << " fringe type. "
<< nl
<< "Proceed with care!"
<< endl;
}
// Update flag collecting whether all connected regions found the
// overlap
allFringesReady &= fringe.foundSuitableOverlap();
}
// Sets containing all acceptors and all holes for all connected regions
const polyMesh& mesh = this->mesh();
labelHashSet allAcceptors(0.02*mesh.nCells());
labelHashSet allFringeHoles(0.02*mesh.nCells());
// Communicate state across processors
reduce(allFringesReady, andOp<bool>());
if (allFringesReady)
{
if (debug)
{
Info<< "All dependent fringes are ready."
<< " Starting donor based layered overlap assembly..." << endl;
}
// Loop through connected regions
forAll (connectedRegionIDs_, crI)
{
// Get ID of this region
const label& regionID = connectedRegionIDs_[crI];
// Get fringe of the connected region
const oversetFringe& fringe = allRegions[regionID].fringe();
// The fringe should be finalized, which means we may take a const
// reference to its final donor acceptors
const donorAcceptorList& crDonorAcceptorPairs =
fringe.finalDonorAcceptors();
// Need to gather/scatter the donor-acceptor pairs across all
// processors because these pairs only represent acceptors found on
// my processor. It would be possible to optimize this a bit using
// the mapDistribute tool, but I don't think it will represent a big
// overhead, especially since it's done once.
List<donorAcceptorList> allDonorAcceptorPairs(Pstream::nProcs());
// Fill in my part and communicate
allDonorAcceptorPairs[Pstream::myProcNo()] = crDonorAcceptorPairs;
Pstream::gatherList(allDonorAcceptorPairs);
Pstream::scatterList(allDonorAcceptorPairs);
// Count approximate number of acceptors to guess the size for the
// hash set containing donors
label nAllAcceptors = 0;
forAll (allDonorAcceptorPairs, procI)
{
nAllAcceptors += allDonorAcceptorPairs[procI].size();
}
// Hash set containing donors
labelHashSet donors(6*nAllAcceptors);
// Initialize centre of the donors of this connected region in order
// to search in a given direction
vector centrePoint(vector::zero);
// Loop through all processors
forAll (allDonorAcceptorPairs, procI)
{
// Get all donor/acceptor pairs found on this processor
const donorAcceptorList& procDonorAcceptorPairs =
allDonorAcceptorPairs[procI];
// Loop through all donor/acceptors
forAll (procDonorAcceptorPairs, daI)
{
// Get this donor/acceptor pair
const donorAcceptor& daPair = procDonorAcceptorPairs[daI];
// Check whether all donors have been found
if (!daPair.donorFound())
{
FatalErrorIn
(
"donorBasedLayeredOverlapFringe::"
"updateIteration(donorAcceptorList&) const"
) << "Donor not found for donor/acceptor pair " << daI
<< nl
<< "Donor/acceptor data: " << daPair
<< nl
<< "In connected region: "
<< allRegions[regionID].name()
<< abort(FatalError);
}
// Mark donors on my processor from this connected region.
// Note that the check has been made in constructor to make
// sure that this region is the only donor region for the
// connected region
if (daPair.donorProcNo() == Pstream::myProcNo())
{
// Get donor index
const label& dI = daPair.donorCell();
// Insert donor into the hash set
if (donors.insert(dI))
{
// Donor has been inserted (not previously found in
// the hash set), add donor point to centre point
// (the centre point will be calculated later on as
// arithmetic mean)
centrePoint += daPair.donorPoint();
}
// Loop through extended donor cells
const donorAcceptor::DynamicLabelList& extDonors =
daPair.extendedDonorCells();
const donorAcceptor::DynamicPointList& extDonorPoints =
daPair.extendedDonorPoints();
forAll (extDonors, i)
{
// Get donor index
const label& edI = extDonors[i];
// Inser extended donor into the hash set
if (donors.insert(edI))
{
// Donor has been inserted (not previously found
// in the hash set), add extended donor point as
// well
centrePoint += extDonorPoints[i];
}
} // End for all extended donors
} // End if this donor is on my processor
} // End for all (master) donor cells
} // End for all processors
// Use the centre point as specified by the user if it was specified
// (if the regionCentrePoints_ list is not empty). This avoids
// parallel communication as well.
if (!regionCentrePoints_.empty())
{
// Use specified centre point, discarding the data we calculated
// above
centrePoint = regionCentrePoints_[crI];
}
else
{
// User did not specify centre points and the centre point holds
// the sum of all the points. Reduce centre point and divide it
// with global number of unique donors
reduce(centrePoint, sumOp<vector>());
centrePoint /= returnReduce(donors.size(), sumOp<label>());
}
if (debug)
{
Info<< "Centre point for donors for region "
<< allRegions[regionID].name() << " is : " << centrePoint
<< endl;
}
// We now have a collection of all donors for this connected region
// and the centre point to move to. Let's collect the acceptors
labelHashSet acceptors(donors.size()); // Reasonable size estimate
// Get necessary mesh data (from polyMesh/primitiveMesh)
const vectorField& cc = mesh.cellCentres();
const vectorField& fc = mesh.faceCentres();
const cellList& meshCells = mesh.cells();
const unallocLabelList& owner = mesh.faceOwner();
const unallocLabelList& neighbour = mesh.faceNeighbour();
// Get bounding box of this region for additional check when marking
// acceptors
const boundBox& bb = allRegions[regionID].globalBounds();
// Mark cells that are eligible to be acceptors (not donors)
boolList eligibleCells(mesh.nCells(), true);
forAllConstIter (labelHashSet, donors, iter)
{
eligibleCells[iter.key()] = false;
}
// Loop nLayers away from initial donors
for (label i = 0; i < nLayers_; ++i)
{
// Face markup for propagation
boolList propagateFace(mesh.nFaces(), false);
// Loop through all donors and mark faces that are pointing
// towards the centre point and have an eligible neighbour
forAllConstIter (labelHashSet, donors, iter)
{
// Get the cell index and the cell
const label& cellI = iter.key();
const cell& cFaces = meshCells[cellI];
// Get cell centre of this donor and calculate distance to
// centre point
const vector& donorCentre = cc[cellI];
const scalar donorCentreToRegionCentreDist =
mag(donorCentre - centrePoint);
// Loop through all faces of the cell
forAll (cFaces, i)
{
// Get face index (global)
const label& faceI = cFaces[i];
// Get face centre and calculate distance to centre
// point
const vector& faceCentre = fc[faceI];
const scalar faceCentreToRegionCentreDist =
mag(faceCentre - centrePoint);
if
(
// First, distance based criterium
(
faceCentreToRegionCentreDist
- donorCentreToRegionCentreDist
< distTol_
)
// Second, bounding box based criterium
&& (bb.contains(faceCentre))
)
{
// Face is closer to the centre point than cell: we
// are moving in the right direction. Mark the face
propagateFace[faceI] = true;
}
} // End for all faces of the cell
} // End for all donor cells
// Sync the face list across processor boundaries
syncTools::syncFaceList
(
mesh,
propagateFace,
orOp<bool>(),
false
);
// Loop through all faces and append acceptors
for (label faceI = 0; faceI < mesh.nInternalFaces(); ++faceI)
{
if (propagateFace[faceI])
{
// Face is marked, select owner or neighbour
const label& own = owner[faceI];
const label& nei = neighbour[faceI];
// Either owner or neighbour may be eligible, not both
if (eligibleCells[own])
{
// Owner cell is not a donor, insert it
acceptors.insert(own);
// Mark as ineligible
eligibleCells[own] = false;
}
else if (eligibleCells[nei])
{
// Neighbour cell is not a donor, insert it
acceptors.insert(nei);
// Mark as ineligible
eligibleCells[nei] = false;
}
}
}
// Loop through boundary faces
for
(
label faceI = mesh.nInternalFaces();
faceI < mesh.nFaces();
++faceI
)
{
if (propagateFace[faceI])
{
// Face is marked, select owner if this is the right
// side. Neighbour handled on the other side
const label& own = owner[faceI];
if (eligibleCells[own])
{
// Face cell is not a donor, insert it
acceptors.insert(own);
// Mark as ineligible
eligibleCells[own] = false;
}
}
}
// Special treatment for all non-final iterations
if (i < nLayers_ - 1)
{
// This is not the last iteration, transfer acceptors into
// donors
donors.transfer(acceptors);
// Resize acceptors list
acceptors.resize(donors.size());
}
} // End for specified number of layers
// At this point, we have the final set of acceptors and we marked
// all cells that are ineligible (either donor or acceptor). The
// remaining thing to do is to mark the interior holes
// Create a hash set that will contain fringe holes
labelHashSet fringeHoles(10*acceptors.size());
// Collect holes until there are no holes to collect. Note: for the
// first iteration, we will start with acceptors, otherwise, we will
// start from all fringe holes
label nAddedHoles;
bool firstIteration = true;
do
{
// Reset number of newly added holes
nAddedHoles = 0;
// Face markup for propagation
boolList propagateFace(mesh.nFaces(), false);
// Get collection of cells from which to start the search:
// acceptors for the first iteration and fringe holes otherwise
labelHashSet* curSetPtr = nullptr;
if (firstIteration)
{
// Point current set to acceptors and switch off the flag
curSetPtr = &acceptors;
firstIteration = false;
}
else
{
// Point current set to fringe holes
curSetPtr = &fringeHoles;
}
const labelHashSet& curSet = *curSetPtr;
// Loop through all cells in the set and mark faces that are
// pointing towards the centre point and have eligible neighbour
// (not an acceptor or donor).
forAllConstIter (labelHashSet, curSet, iter)
{
// Get the cell index and the cell
const label& cellI = iter.key();
const cell& cFaces = meshCells[cellI];
// Note: there's no need to check for the distance here
// because there's always at least one "buffer" layer
// towards the outer side that consists of donors, which are
// marked as ineligible at the beginning
// Loop through all faces of the cell and mark all of them
// for propagation
forAll (cFaces, i)
{
propagateFace[cFaces[i]] = true;
}
}
// Sync the face list across processor boundaries
syncTools::syncFaceList
(
mesh,
propagateFace,
orOp<bool>(),
false
);
// Loop through all faces and append interior holes
for (label faceI = 0; faceI < mesh.nInternalFaces(); ++faceI)
{
if (propagateFace[faceI])
{
// Face is marked, select owner or neighbour
const label& own = owner[faceI];
const label& nei = neighbour[faceI];
// Either owner or neighbour may be eligible, not both
if (eligibleCells[own])
{
// Owner cell is not a hole (or an acceptor in the
// first iteration), insert it
if (fringeHoles.insert(own))
{
// Count number of added holes in this iteration
++nAddedHoles;
}
// Mark as ineligible
eligibleCells[own] = false;
}
else if (eligibleCells[nei])
{
// Neighbour cell is not a hole (or an acceptor in
// the first iteration), insert it
if (fringeHoles.insert(nei))
{
// Count number of added holes in this iteration
++nAddedHoles;
}
// Mark as ineligible
eligibleCells[nei] = false;
}
}
}
// Loop through boundary faces
for
(
label faceI = mesh.nInternalFaces();
faceI < mesh.nFaces();
++faceI
)
{
if (propagateFace[faceI])
{
// Face is marked, select owner if this is the right
// side. Neighbour handled on the other side
const label& own = owner[faceI];
if (eligibleCells[own])
{
// Face cell is not a hole (or an acceptor in the
// first iteration), inser it
if (fringeHoles.insert(own))
{
++nAddedHoles;
}
// Mark as ineligible
eligibleCells[own] = false;
}
}
}
// Need to reduce nAddedHoles in order to have synced loops in
// parallel
reduce(nAddedHoles, sumOp<label>());
// We moved one layer "inside" the fringe. Keep going until
// there are no more holes to add
} while (nAddedHoles != 0);
// Finally, we have collected all the fringe holes and acceptors
// from this connected region. Append them to the global sets
allAcceptors += acceptors;
allFringeHoles += fringeHoles;
} // End for all connected regions
// Set acceptors and holes from the data for all regions
acceptorsPtr_ = new labelList(allAcceptors.sortedToc());
fringeHolesPtr_ = new labelList(allFringeHoles.sortedToc());
}
else
{
// Connected fringes are not ready, allocate empty lists for acceptors
// and holes, which will be deleted when asked for again from the
// iterative procedure (see candidateAcceptors() and fringeHoles())
acceptorsPtr_ = new labelList;
fringeHolesPtr_ = new labelList;
}
if (debug)
{
Info<< "In donorBasedLayeredOverlapFringe::calcAddressing() const" << nl
<< "Found " << acceptorsPtr_->size() << " acceptors." << nl
<< "Found " << fringeHolesPtr_->size() << " fringe holes." << endl;
}
}
void Foam::donorBasedLayeredOverlapFringe::clearAddressing() const
{
deleteDemandDrivenData(fringeHolesPtr_);
deleteDemandDrivenData(acceptorsPtr_);
deleteDemandDrivenData(finalDonorAcceptorsPtr_);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
// Construct from dictionary
Foam::donorBasedLayeredOverlapFringe::donorBasedLayeredOverlapFringe
(
const fvMesh& mesh,
const oversetRegion& region,
const dictionary& dict
)
:
oversetFringe(mesh, region, dict),
connectedRegionNames_(dict.lookup("connectedRegions")),
connectedRegionIDs_(),
regionCentrePoints_
(
dict.lookupOrDefault<List<point> >
(
"regionCentrePoints",
List<point>(0)
)
),
nLayers_(readLabel(dict.lookup("nLayers"))),
fringeHolesPtr_(nullptr),
acceptorsPtr_(nullptr),
finalDonorAcceptorsPtr_(nullptr),
isInitialized_(false)
{
// Sanity check number of layers: must be greater than 0
if (nLayers_ < 1)
{
FatalIOErrorIn
(
"donorBasedLayeredOverlapFringe::"
"donorBasedLayeredOverlapFringe\n"
"(\n"
" const fvMesh& mesh,\n"
" const oversetRegion& region,\n"
" const dictionary& dict\n"
")",
dict
) << "Invalid number of layers specified, nLayers = " << nLayers_
<< nl
<< "The number should be greater than 0."
<< abort(FatalError);
}
// Recommendation: use at least two layers in order to avoid going defining
// acceptors on the wrong side and filling in the whole region with holes
if (nLayers_ == 1)
{
WarningIn
(
"donorBasedLayeredOverlapFringe::"
"donorBasedLayeredOverlapFringe\n"
"(\n"
" const fvMesh& mesh,\n"
" const oversetRegion& region,\n"
" const dictionary& dict\n"
")"
) << "It is recommended to use at least 2 layers (nLayers = 2) in"
<< " order to avoid defining acceptor cells on the wrong side"
<< " of the region."
<< nl
<< "Be sure to check the fringe layer for this region."
<< endl;
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::donorBasedLayeredOverlapFringe::~donorBasedLayeredOverlapFringe()
{
clearAddressing();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::donorBasedLayeredOverlapFringe::updateIteration
(
donorAcceptorList& donorAcceptorRegionData
) const
{
// If the donorAcceptor list has been allocated, something went wrong with
// the iteration procedure (not-updated flag): this function has been called
// more than once, which should not happen for
// donorBasedLayeredOverlapFringe
if (finalDonorAcceptorsPtr_)
{
FatalErrorIn
(
"donorBasedLayeredOverlapFringe::"
"updateIteration(donorAcceptorList&) const"
) << "finalDonorAcceptorPtr_ already allocated. Something went "
<< "wrong with the iteration procedure (flag was not updated)."
<< nl
<< "This should not happen for donorBasedLayeredOverlapFringe."
<< abort(FatalError);
}
if
(
fringeHolesPtr_ && acceptorsPtr_
&& returnReduce(!acceptorsPtr_->empty(), orOp<bool>())
)
{
// Note: we first check whether fringeHoles and acceptors pointers are
// allocated. If they are, there must be at least one processor with
// more than 0 acceptors in order for this fringe to be valid.
// Allocate the list by reusing the argument list
finalDonorAcceptorsPtr_ = new donorAcceptorList
(
donorAcceptorRegionData,
true
);
// Set the flag to true (for all processors due to reduction in the if
// statement)
updateSuitableOverlapFlag(true);
}
else
{
// Delete fringeHolesPtr and acceptorsPtr to trigger calculation of
// addressing, this time with other fringes up-to-date
deleteDemandDrivenData(fringeHolesPtr_);
deleteDemandDrivenData(acceptorsPtr_);
}
return foundSuitableOverlap();
}
const Foam::labelList& Foam::donorBasedLayeredOverlapFringe::fringeHoles() const
{
// Note: updateIteration deletes fringeHolesPtr if the suitable overlap is
// not found, thus preparing it for the next iteration when the other
// fringes should be ready
if (!fringeHolesPtr_)
{
calcAddressing();
}
return *fringeHolesPtr_;
}
const Foam::labelList&
Foam::donorBasedLayeredOverlapFringe::candidateAcceptors() const
{
// Note: updateIteration deletes acceptorsPtr if the suitable overlap is
// not found, thus preparing it for the next iteration when the other
// fringes should be ready
if (!acceptorsPtr_)
{
calcAddressing();
}
return *acceptorsPtr_;
}
Foam::donorAcceptorList&
Foam::donorBasedLayeredOverlapFringe::finalDonorAcceptors() const
{
if (!finalDonorAcceptorsPtr_)
{
FatalErrorIn("donorBasedLayeredOverlapFringe::finalDonorAcceptors()")
<< "finalDonorAcceptorPtr_ not allocated. Make sure you have"
<< " called donorBasedLayeredOverlapFringe::updateIteration() before"
<< " asking for final set of donor/acceptor pairs."
<< abort(FatalError);
}
if (!foundSuitableOverlap())
{
FatalErrorIn("donorBasedLayeredOverlapFringe::finalDonorAcceptors()")
<< "Attemted to access finalDonorAcceptors but suitable overlap "
<< "has not been found. This is not allowed. "
<< abort(FatalError);
}
return *finalDonorAcceptorsPtr_;
}
void Foam::donorBasedLayeredOverlapFringe::update() const
{
Info<< "donorBasedLayeredOverlapFringe::update() const" << endl;
// Clear out
clearAddressing();
// Set flag to false and clear final donor/acceptors only
deleteDemandDrivenData(finalDonorAcceptorsPtr_);
updateSuitableOverlapFlag(false);
}
// ************************************************************************* //

View file

@ -0,0 +1,190 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | foam-extend: Open Source CFD
\\ / O peration | Version: 4.1
\\ / A nd | Web: http://www.foam-extend.org
\\/ M anipulation | For copyright notice see file Copyright
-------------------------------------------------------------------------------
License
This file is part of foam-extend.
foam-extend 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.
foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>.
Class
donorBasedLayeredOverlapFringe
Description
Fringe algorithm based on donors from different regions. The algorithm waits
until the final donor/acceptor assembly has been performed for all regions
whose donor region is this region. Then, all donors are collected and the
acceptors are cells neighbouring the donors nLayers towards the interior.
Interior is defined either by user-specified points for each region or as a
centre of volume of the donor cells in particular region.
This fringe algorithm is intended to be used along with the faceCells fringe
on the other side, where the cell sizes are not significantly different from
each other (e.g. 10:1 cell ratio, where the finer cells are found on the
background mesh will probably be problematic to correctly set-up).
Note: based on distance tolerance (see distTol_ member), specified number of
layers and the bounding box of the connected donor region, it is possible
that acceptors end also on the wrong side (from the region centre as opposed
to towards the region centre). I do all I can to prevent this by looking at
the bounding box and hinting that at least 2 layers should be used. Be sure
to check the overset assembly for this region using calcOverset utility.
Author
Vuko Vukcevic, Wikki Ltd. All rights reserved.
SourceFiles
donorBasedLayeredOverlapFringe.C
\*---------------------------------------------------------------------------*/
#ifndef donorBasedLayeredOverlapFringe_H
#define donorBasedLayeredOverlapFringe_H
#include "oversetFringe.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class donorBasedLayeredOverlapFringe Declaration
\*---------------------------------------------------------------------------*/
class donorBasedLayeredOverlapFringe
:
public oversetFringe
{
// Private data
//- Names of connected regions. Looked up on construction
wordList connectedRegionNames_;
//- Regions IDs from which the donors will be collected as a starting
// point. Note: initialized in init private member function because we
// cannot initialize it in constructor. This is because certain overset
// regions (and their fringes) may not be initialized at this point.
mutable labelList connectedRegionIDs_;
//- Optional list of points representing a rough estimate of the centre
// for each underlying connected region. If these are not provided, the
// centre is calculated as the centre of all donors for a given
// connected region
List<point> regionCentrePoints_;
//- How many layers to move away from connected region donors to define
// acceptor (and holes)
label nLayers_;
//- Fringe hole cells
mutable labelList* fringeHolesPtr_;
//- Acceptor cells
mutable labelList* acceptorsPtr_;
//- Final donor/acceptor pairs for this region (fringe)
mutable donorAcceptorList* finalDonorAcceptorsPtr_;
//- Initialization helper
mutable bool isInitialized_;
// Private static data
//- Distance tolerance to determine propagation direction. Note:
// absolute value, default = 0. Might be useful is some strange cases
static const debug::tolerancesSwitch distTol_;
// Private Member Functions
//- Initialization
void init() const;
//- Calculate hole and acceptor addressing
void calcAddressing() const;
//- Clear addressing
void clearAddressing() const;
public:
//- Runtime type information
TypeName("donorBasedLayeredOverlap");
// Constructors
//- Construct from dictionary
donorBasedLayeredOverlapFringe
(
const fvMesh& mesh,
const oversetRegion& region,
const dictionary& dict
);
//- Disallow default bitwise copy construct
donorBasedLayeredOverlapFringe
(
const donorBasedLayeredOverlapFringe&
) = delete;
//- Disallow default bitwise assignment
void operator=(const donorBasedLayeredOverlapFringe&) = delete;
// Destructor
virtual ~donorBasedLayeredOverlapFringe();
// Member Functions
//- Update iteration. Note: invalidates parameter
virtual bool updateIteration
(
donorAcceptorList& donorAcceptorRegionData
) const;
//- Return list of deactivated (hole) cells
// Fringe hole cells are collected in addition to geometric hole
// cells, which fall outside of all donor regions
virtual const labelList& fringeHoles() const;
//- Return list of acceptor cells
virtual const labelList& candidateAcceptors() const;
//- Return list of final donor acceptor pairs. Note: caller may
// invalidate finalDonorAcceptorsPtr_ for optimisation purposes
virtual donorAcceptorList& finalDonorAcceptors() const;
//- Update the fringe
virtual void update() const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View file

@ -72,7 +72,7 @@ void Foam::faceCellsFringe::calcAddressing() const
( (
"void faceCellsFringe::calcAddressing() const" "void faceCellsFringe::calcAddressing() const"
) << "Fringe patch " << patchNames_[nameI] ) << "Fringe patch " << patchNames_[nameI]
<< " cannot be found" << " cannot be found."
<< abort(FatalError); << abort(FatalError);
} }

View file

@ -115,6 +115,12 @@ public:
// Member Functions // Member Functions
//- Access to patch names
const wordList& patchNames() const
{
return patchNames_;
}
//- Update iteration. Note: invalidates parameter //- Update iteration. Note: invalidates parameter
virtual bool updateIteration virtual bool updateIteration
( (

View file

@ -121,8 +121,9 @@ private:
//- Return overset type indicator field //- Return overset type indicator field
mutable volScalarField* oversetTypesPtr_; mutable volScalarField* oversetTypesPtr_;
//- Region ID: region index for each cell //- Region ID: region index for each cell as a volScalarField for
mutable labelList* regionIDPtr_; // visualization. VV, 15/Apr/2019
mutable volScalarField* regionIDPtr_;
// Overset discretisation support // Overset discretisation support
@ -286,7 +287,7 @@ public:
const volScalarField& oversetTypes() const; const volScalarField& oversetTypes() const;
//- Return region indicator //- Return region indicator
const labelList& regionID() const; const volScalarField& regionID() const;
// Overset discretisation support // Overset discretisation support

View file

@ -27,6 +27,7 @@ License
#include "surfaceFields.H" #include "surfaceFields.H"
#include "volFields.H" #include "volFields.H"
#include "polyPatchID.H" #include "polyPatchID.H"
#include "oversetFvPatchFields.H"
#include "demandDrivenData.H" #include "demandDrivenData.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -190,24 +191,52 @@ void Foam::oversetMesh::calcDomainMarkup() const
} }
} }
// Region ID // Region ID, initialized with -1 for sanity check later on
regionIDPtr_ = new labelList(mesh().nCells(), -1); regionIDPtr_ = new volScalarField
labelList& rID = *regionIDPtr_; (
IOobject
// Mark regions (
"regionIndex",
mesh().time().timeName(),
mesh(),
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
mesh(),
dimensionedScalar("minusOne", dimless, -1.0),
"zeroGradient"
);
volScalarField& regionID = *regionIDPtr_;
scalarField& regionIDIn = regionID.internalField();
// Mark regions (internal field)
forAll (regions_, regionI) forAll (regions_, regionI)
{ {
const labelList& curCells = regions_[regionI].zone(); const labelList& curCells = regions_[regionI].zone();
forAll (curCells, curCellI) forAll (curCells, curCellI)
{ {
rID[curCells[curCellI]] = regionI; regionIDIn[curCells[curCellI]] = regionI;
}
}
// Update boundary values, making sure that we skip the overset patch
volScalarField::GeometricBoundaryField& regionIDb =
regionID.boundaryField();
forAll (regionIDb, patchI)
{
// Get the patch field
fvPatchScalarField& ripf = regionIDb[patchI];
if (!isA<oversetFvPatchScalarField>(ripf))
{
ripf = ripf.patchInternalField();
} }
} }
// Check regions // Check regions
if (min(rID) < 0) if (min(regionID).value() < 0)
{ {
FatalErrorIn("void oversetMesh::calcDomainMarkup() const") FatalErrorIn("void oversetMesh::calcDomainMarkup() const")
<< "Found cells without region ID. Please check overset setup" << "Found cells without region ID. Please check overset setup"
@ -1166,7 +1195,7 @@ const Foam::volScalarField& Foam::oversetMesh::oversetTypes() const
} }
const Foam::labelList& Foam::oversetMesh::regionID() const const Foam::volScalarField& Foam::oversetMesh::regionID() const
{ {
if (!regionIDPtr_) if (!regionIDPtr_)
{ {

View file

@ -1730,6 +1730,20 @@ Foam::oversetRegion::~oversetRegion()
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
const Foam::oversetFringe& Foam::oversetRegion::fringe() const
{
if (fringePtr_.empty())
{
FatalErrorIn("const oversetFringe& oversetRegion::fringe() const")
<< "Fringe pointer not allocated. It should have been initialized"
<< " properly at construction. Something went wrong..."
<< abort(FatalError);
}
return fringePtr_();
}
const Foam::labelList& Foam::oversetRegion::donorRegions() const const Foam::labelList& Foam::oversetRegion::donorRegions() const
{ {
if (!donorRegionsPtr_) if (!donorRegionsPtr_)

View file

@ -327,6 +327,9 @@ public:
return zone(); return zone();
} }
//- Return overset fringe algorithm for this region
const oversetFringe& fringe() const;
//- Return list of donor region indices //- Return list of donor region indices
const labelList& donorRegions() const; const labelList& donorRegions() const;