Finished writing first version of donorBasedLayeredOverlap fringe

Note: did not attempt to compile yet, let alone test it
This commit is contained in:
Vuko Vukcevic 2019-04-10 18:17:51 +02:00
parent 31019383d2
commit 6a5117ed51
2 changed files with 565 additions and 131 deletions

View file

@ -27,6 +27,7 @@ License
#include "faceCellsFringe.H" #include "faceCellsFringe.H"
#include "oversetRegion.H" #include "oversetRegion.H"
#include "addToRunTimeSelectionTable.H" #include "addToRunTimeSelectionTable.H"
#include "syncTools.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -41,11 +42,22 @@ namespace Foam
); );
} }
const Foam::debug::tolerancesSwitch
Foam::donorBasedLayeredOverlapFringe::distTol_
(
"donorBasedLayeredOverlapDistanceTolerance"
0.0
);
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::donorBasedLayeredOverlapFringe::calcAddressing() const void Foam::donorBasedLayeredOverlapFringe::calcAddressing() const
{ {
if (acceptorsPtr_) // Make sure that either acceptorsPtr is unnalocated or if it is allocated,
// that it is empty
if (acceptorsPtr_ && !acceptorsPtr_->empty())
{ {
FatalErrorIn FatalErrorIn
( (
@ -54,52 +66,438 @@ void Foam::donorBasedLayeredOverlapFringe::calcAddressing() const
<< abort(FatalError); << abort(FatalError);
} }
// Get reference to region cell zone // Get list of all overset regions
const cellZone& rcz = region().zone(); const PtrList<oversetRegion>& allRegions =
this->region().overset().regions();
// Make a hash set to collect acceptor points // Loop through all connected regions and check whether the fringe overlap
// Note: 2 patches connecting at the corner may create a duplicate, // has been found for all of them
// which is filtered on insertion bool allFringesReady = true;
labelHashSet acceptorSet; forAll (connectedRegionIDs_, crI)
// Find patches and mark cells
forAll (patchNames_, nameI)
{ {
const polyPatchID curFringePatch // Get ID of this region
( const label& regionID = connectedRegionIDs_[crI];
patchNames_[nameI],
mesh().boundaryMesh()
);
if (!curFringePatch.active()) // 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))
{ {
FatalErrorIn WarningIn
( (
"void donorBasedLayeredOverlapFringe::calcAddressing() const" "void Foam::donorBasedLayeredOverlapFringe::"
) << "Fringe patch " << patchNames_[nameI] "updateIteration(donorAcceptorList&) const"
<< " cannot be found" ) << "donorBasedLayeredOverlap fringe is designed to work"
<< abort(FatalError); << " with faceCells fringe as a connected region fringe."
<< nl
<< "Connected overset region " << region.name()
<< " has " << fringe.type() " fringe type. "
<< nl
<< "Proceed with care!"
<< endl;
} }
const unallocLabelList& curFaceCells = // Update flag collecting whether all connected regions found the
mesh().boundaryMesh()[curFringePatch.index()].donorBasedLayeredOverlap(); // overlap
allFringesReady &= fringe.foundSuitableOverlap();
forAll (curFaceCells, fcI)
{
// Check if cell is in region zone
if (rcz.whichCell(curFaceCells[fcI]) > -1)
{
// Found acceptor
acceptorSet.insert(curFaceCells[fcI]);
}
}
} }
// Collect acceptors // Sets containing all acceptors and all holes for all connected regions
acceptorsPtr_ = new labelList(acceptorSet.sortedToc()); labelHashSet allAcceptors(0.02*mesh.nCells());
labelHashSet allFringeHoles(0.02*mesh.nCells());
// Holes are empty for this fringe if (allFringesReady)
fringeHolesPtr_ = new labelList(); {
// 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();
// Hash set containing donors
labelHashSet donors(6*crDonorAcceptorPairs.size());
// Initialize centre of the donors of this connected region in order
// to search in a given direction
vector centrePoint(vector::zero);
// Loop through all donor/acceptors
forAll (crDonorAcceptorPairs, daI)
{
// Get this donor/acceptor pair
const donorAcceptor& daPair = crDonorAcceptorPairs[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
donorAcceptor::DynamicLabelList& extDonors =
daPair.extendedDonorCells();
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
// 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 the data
reduce(centrePoint, sumOp<vector>());
nUniqueDonors = returnReduce(donors.size(), sumOp<label>());
// Calculate the final centre point by finding the arithmetic mean
centrePoint /= nUniqueDonors;
}
// 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 polyMesh& mesh = this->mesh();
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();
// 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 label& cell = 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
(
faceCentreToRegionCentreDist
- donorCentreToRegionCentreDist
< distTol_
)
{
// 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 last iteration
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 containing fringe holes, initialized with
// acceptors in order to avoid having special conditions in the loop
// below for the first pass
labelHashSet fringeHoles(10*acceptors.size());
// Collect holes until there are no holes to collect
label nAddedHoles;
do
{
// Reset number of newly added holes
nAddedHoles = 0;
// Face markup for propagation
boolList propagateFace(mesh.nFaces(), false);
// Loop through all acceptors and mark faces that are pointing
// towards the centre point and have eligible neighbour (not an
// acceptor or donor)
forAllConstIter (labelHashSet, fringeHoles, iter)
{
// Get the cell index and the cell
const label& cellI = iter.key();
const label& cell = 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;
}
}
}
// 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(0);
holesPtr_ = new labelList(0);
}
} }
@ -132,6 +530,25 @@ Foam::donorBasedLayeredOverlapFringe::donorBasedLayeredOverlapFringe
acceptorsPtr_(nullptr), acceptorsPtr_(nullptr),
finalDonorAcceptorsPtr_(nullptr) finalDonorAcceptorsPtr_(nullptr)
{ {
// 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);
}
// Read names of connected regions // Read names of connected regions
const wordList connectedRegionNames(dict.lookup("connectedRegions")); const wordList connectedRegionNames(dict.lookup("connectedRegions"));
@ -150,10 +567,10 @@ Foam::donorBasedLayeredOverlapFringe::donorBasedLayeredOverlapFringe
} }
// Loop through all regions and check whether the overlap has been found // Loop through all regions and check whether the overlap has been found
forAll (connectedRegion_, crI) forAll (connectedRegionNames, crI)
{ {
// Get name of this connected region // Get name of this connected region
const word& crName = connectedRegion_[crI]; const word& crName = connectedRegionNames[crI];
// Find this region in the list of all regions // Find this region in the list of all regions
const label regionID = findIndex(allRegions, crName); const label regionID = findIndex(allRegions, crName);
@ -176,13 +593,51 @@ Foam::donorBasedLayeredOverlapFringe::donorBasedLayeredOverlapFringe
// Collect the region index in the list // Collect the region index in the list
connectedRegionIDs_[crI] = regionID; 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
(
"donorBasedLayeredOverlapFringe::"
"donorBasedLayeredOverlapFringe\n"
"(\n"
" const fvMesh& mesh,\n"
" const oversetRegion& region,\n"
" const dictionary& dict\n"
")"
) << "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 (regionID != this->region().index())
{
FatalErrorIn
(
"donorBasedLayeredOverlapFringe::"
"donorBasedLayeredOverlapFringe\n"
"(\n"
" const fvMesh& mesh,\n"
" const oversetRegion& region,\n"
" const dictionary& dict\n"
")"
) << "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 // Sanity check: number of (optionally) specified centre points must be
// equal to the number of connected regions // equal to the number of connected regions
if if
( (
regionCentrePoints_.size() !regionCentrePoints_.empty()
&& (regionCentrePoints_.size() != connectedRegionIDs_.size()) && (regionCentrePoints_.size() != connectedRegionIDs_.size())
) )
{ {
@ -207,6 +662,33 @@ Foam::donorBasedLayeredOverlapFringe::donorBasedLayeredOverlapFringe
<< " make sure to specify centre points for all connected regions." << " make sure to specify centre points for all connected regions."
<< abort(FatalError); << abort(FatalError);
} }
// Sanity check: if the user has specified centre points, dynamic mesh
// simulations may be problematic. Issue a warning
if
(
!regionCentrePoints_.empty()
&& isA<dynamicFvMesh>(this->mesh())
)
{
WarningIn
(
"donorBasedLayeredOverlapFringe::"
"donorBasedLayeredOverlapFringe\n"
"(\n"
" const fvMesh& mesh,\n"
" const oversetRegion& region,\n"
" const dictionary& dict\n"
")"
) << "You have specified regionCentrePoints for a dynamic mesh"
<< " simulation."
<< nl
<< "Make sure that the centre points always remain in/near the"
<< " centre of donors in connected regions!"
<< nl
<< "Proceed with care!"
<< endl;
}
} }
@ -232,98 +714,23 @@ bool Foam::donorBasedLayeredOverlapFringe::updateIteration
{ {
FatalErrorIn FatalErrorIn
( (
"donorBasedLayeredOverlapFringe::updateIteration(donorAcceptorList&" "donorBasedLayeredOverlapFringe::"
"updateIteration(donorAcceptorList&) const"
) << "finalDonorAcceptorPtr_ already allocated. Something went " ) << "finalDonorAcceptorPtr_ already allocated. Something went "
<< "wrong with the iteration procedure (flag was not updated)." << "wrong with the iteration procedure (flag was not updated)."
<< nl << "This should not happen for donorBasedLayeredOverlapFringe." << nl << "This should not happen for donorBasedLayeredOverlapFringe."
<< abort(FatalError); << abort(FatalError);
} }
// Get list of all overset regions // Allocate the list by reusing the argument list
const PtrList<oversetRegion>& allRegions = finalDonorAcceptorsPtr_ = new donorAcceptorList
this->region().overset().regions(); (
donorAcceptorRegionData,
true
);
// Loop through all connected regions and check whether the fringe overlap // Set the flag to true and return
// has been found for all of them updateSuitableOverlapFlag(true);
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::"
"calcAddressing() 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();
}
if (allFringesReady)
{
// 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();
// Sanity check the validity of donor
// Mark donors on my processor from this connected region
// Calculate the centre for this connected region if it isn't
// specified
// Mark all future acceptors by moving towards the centre using
// face-neighbour walk nLayers times
// Now that the acceptors are marked, mark all remaining holes
}
// Allocate the list by reusing the argument list
finalDonorAcceptorsPtr_ = new donorAcceptorList
(
donorAcceptorRegionData,
true
);
// Set the flag to true and return
updateSuitableOverlapFlag(true);
return foundSuitableOverlap();
} // else connected fringes are not ready yet, foundSuitableOverlap flag is
// alraedy false, so there's nothing to do
return foundSuitablaOverlap(); return foundSuitablaOverlap();
} }
@ -335,6 +742,16 @@ const Foam::labelList& Foam::donorBasedLayeredOverlapFringe::fringeHoles() const
{ {
calcAddressing(); calcAddressing();
} }
else if (fringeHolesPtr_->empty())
{
// Fringe holes pointer is empty, delete both acceptorPtr_ and
// fringeHolesPtr_ and calculate addressing. Since this fringe strategy
// depends on other fringes, we need to have a special control in the
// iterative algorithm that will start only when all the others are done
// See calcAddressing() for details
deleteDemandDrivenData(fringeHolesPtr_);
deleteDemandDrivenData(acceptorsPtr_);
}
return *fringeHolesPtr_; return *fringeHolesPtr_;
} }
@ -346,6 +763,16 @@ const Foam::labelList& Foam::donorBasedLayeredOverlapFringe::candidateAcceptors(
{ {
calcAddressing(); calcAddressing();
} }
else if (acceptorsPtr_->empty())
{
// Acceptors pointer is empty, delete both acceptorPtr_ and
// fringeHolesPtr_ and calculate addressing. Since this fringe strategy
// depends on other fringes, we need to have a special control in the
// iterative algorithm that will start only when all the others are done
// See calcAddressing() for details
deleteDemandDrivenData(fringeHolesPtr_);
deleteDemandDrivenData(acceptorsPtr_);
}
return *acceptorsPtr_; return *acceptorsPtr_;
} }

View file

@ -29,13 +29,13 @@ Description
until the final donor/acceptor assembly has been performed for all regions 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 whose donor region is this region. Then, all donors are collected and the
acceptors are cells neighbouring the donors nLayers towards the interior. acceptors are cells neighbouring the donors nLayers towards the interior.
Interior is defined either by a user-specified point or as a centre of Interior is defined either by user-specified points for each region or as a
volume of the donor cells. centre of volume of the donor cells in particular region.
This fringe algorithm is intended to be used along with the faceCells fringe 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 on the other side, where the cell sizes are not significantly different from
each other (e.g. 10:1 cell ratio will probably be problematic to correctly each other (e.g. 10:1 cell ratio, where the finer cells are found on the
set-up). background mesh will probably be problematic to correctly set-up).
Author Author
Vuko Vukcevic, Wikki Ltd. All rights reserved. Vuko Vukcevic, Wikki Ltd. All rights reserved.
@ -72,8 +72,8 @@ class donorBasedLayeredOverlapFringe
//- Optional list of points representing a rough estimate of the centre //- Optional list of points representing a rough estimate of the centre
// for each underlying connected region. If these are not provided, the // for each underlying connected region. If these are not provided, the
// centre is calculated as the centre of volume of all donors for a // centre is calculated as the centre of all donors for a given
// given connected region // connected region
pointList regionCentrePoints_; pointList regionCentrePoints_;
//- How many layers to move away from connected region donors to define //- How many layers to move away from connected region donors to define
@ -90,6 +90,13 @@ class donorBasedLayeredOverlapFringe
mutable donorAcceptorList* finalDonorAcceptorsPtr_; mutable donorAcceptorList* finalDonorAcceptorsPtr_;
// 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 // Private Member Functions
//- Disallow default bitwise copy construct //- Disallow default bitwise copy construct