/*---------------------------------------------------------------------------*\
========= |
\\ / 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) 2018-2022 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 "polyBoundaryMesh.H"
#include "polyMesh.H"
#include "primitiveMesh.H"
#include "processorPolyPatch.H"
#include "PstreamBuffers.H"
#include "lduSchedule.H"
#include "globalMeshData.H"
#include "stringListOps.H"
#include "DynamicList.H"
#include "PtrListOps.H"
#include "edgeHashes.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(polyBoundaryMesh, 0);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
bool Foam::polyBoundaryMesh::hasGroupIDs() const
{
if (groupIDsPtr_)
{
// Use existing cache
return !groupIDsPtr_->empty();
}
const polyPatchList& patches = *this;
for (const polyPatch& p : patches)
{
if (!p.inGroups().empty())
{
return true;
}
}
return false;
}
void Foam::polyBoundaryMesh::calcGroupIDs() const
{
if (groupIDsPtr_)
{
return; // Or FatalError
}
groupIDsPtr_.reset(new HashTable(16));
auto& groupLookup = *groupIDsPtr_;
const polyPatchList& patches = *this;
forAll(patches, patchi)
{
const wordList& groups = patches[patchi].inGroups();
for (const word& groupName : groups)
{
groupLookup(groupName).append(patchi);
}
}
// Remove groups that clash with patch names
forAll(patches, patchi)
{
if (groupLookup.erase(patches[patchi].name()))
{
WarningInFunction
<< "Removed group '" << patches[patchi].name()
<< "' which clashes with patch " << patchi
<< " of the same name."
<< endl;
}
}
}
bool Foam::polyBoundaryMesh::readContents(const bool allowReadIfPresent)
{
if
(
this->isReadRequired()
|| (allowReadIfPresent && this->isReadOptional() && this->headerOk())
)
{
// Warn for MUST_READ_IF_MODIFIED
warnNoRereading();
polyPatchList& patches = *this;
// Read polyPatchList
Istream& is = readStream(typeName);
// Read patches as entries
PtrList patchEntries(is);
patches.resize(patchEntries.size());
// Transcribe
forAll(patches, patchi)
{
patches.set
(
patchi,
polyPatch::New
(
patchEntries[patchi].keyword(),
patchEntries[patchi].dict(),
patchi,
*this
)
);
}
is.check(FUNCTION_NAME);
close();
return true;
}
return false;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::polyBoundaryMesh::polyBoundaryMesh
(
const IOobject& io,
const polyMesh& mesh
)
:
polyPatchList(),
regIOobject(io),
mesh_(mesh)
{
readContents(false); // READ_IF_PRESENT allowed: False
}
Foam::polyBoundaryMesh::polyBoundaryMesh
(
const IOobject& io,
const polyMesh& pm,
const label size
)
:
polyPatchList(size),
regIOobject(io),
mesh_(pm)
{}
Foam::polyBoundaryMesh::polyBoundaryMesh
(
const IOobject& io,
const polyMesh& pm,
const polyPatchList& ppl
)
:
polyPatchList(),
regIOobject(io),
mesh_(pm)
{
if (!readContents(true)) // READ_IF_PRESENT allowed: True
{
polyPatchList& patches = *this;
patches.resize(ppl.size());
forAll(patches, patchi)
{
patches.set(patchi, ppl[patchi].clone(*this));
}
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
void Foam::polyBoundaryMesh::clearGeom()
{
polyPatchList& patches = *this;
for (polyPatch& p : patches)
{
p.clearGeom();
}
}
void Foam::polyBoundaryMesh::clearAddressing()
{
neighbourEdgesPtr_.clear();
patchIDPtr_.clear();
groupIDsPtr_.clear();
polyPatchList& patches = *this;
for (polyPatch& p : patches)
{
p.clearAddressing();
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::polyBoundaryMesh::calcGeometry()
{
PstreamBuffers pBufs(Pstream::defaultCommsType);
if
(
pBufs.commsType() == Pstream::commsTypes::blocking
|| pBufs.commsType() == Pstream::commsTypes::nonBlocking
)
{
forAll(*this, patchi)
{
operator[](patchi).initGeometry(pBufs);
}
pBufs.finishedSends();
forAll(*this, patchi)
{
operator[](patchi).calcGeometry(pBufs);
}
}
else if (pBufs.commsType() == Pstream::commsTypes::scheduled)
{
const lduSchedule& patchSchedule = mesh().globalData().patchSchedule();
// Dummy.
pBufs.finishedSends();
for (const auto& schedEval : patchSchedule)
{
const label patchi = schedEval.patch;
if (schedEval.init)
{
operator[](patchi).initGeometry(pBufs);
}
else
{
operator[](patchi).calcGeometry(pBufs);
}
}
}
}
Foam::UPtrList
Foam::polyBoundaryMesh::faceCells() const
{
const polyPatchList& patches = *this;
UPtrList list(patches.size());
forAll(patches, patchi)
{
list.set(patchi, &patches[patchi].faceCells());
}
return list;
}
const Foam::List&
Foam::polyBoundaryMesh::neighbourEdges() const
{
if (Pstream::parRun())
{
WarningInFunction
<< "Neighbour edge addressing not correct across parallel"
<< " boundaries." << endl;
}
if (!neighbourEdgesPtr_)
{
neighbourEdgesPtr_.reset(new List(size()));
auto& neighbourEdges = *neighbourEdgesPtr_;
// Initialize.
label nEdgePairs = 0;
forAll(*this, patchi)
{
const polyPatch& pp = operator[](patchi);
neighbourEdges[patchi].setSize(pp.nEdges() - pp.nInternalEdges());
for (labelPair& edgeInfo : neighbourEdges[patchi])
{
edgeInfo[0] = -1;
edgeInfo[1] = -1;
}
nEdgePairs += pp.nEdges() - pp.nInternalEdges();
}
// From mesh edge (expressed as a point pair so as not to construct
// point addressing) to patch + relative edge index.
EdgeMap pointsToEdge(nEdgePairs);
forAll(*this, patchi)
{
const polyPatch& pp = operator[](patchi);
const edgeList& edges = pp.edges();
for
(
label edgei = pp.nInternalEdges();
edgei < edges.size();
edgei++
)
{
// Edge in patch local points
const edge& e = edges[edgei];
// Edge in mesh points.
edge meshEdge(pp.meshPoints()[e[0]], pp.meshPoints()[e[1]]);
auto fnd = pointsToEdge.find(meshEdge);
if (!fnd.found())
{
// First occurrence of mesh edge. Store patch and my
// local index.
pointsToEdge.insert
(
meshEdge,
labelPair
(
patchi,
edgei - pp.nInternalEdges()
)
);
}
else
{
// Second occurrence. Store.
const labelPair& edgeInfo = fnd.val();
neighbourEdges[patchi][edgei - pp.nInternalEdges()] =
edgeInfo;
neighbourEdges[edgeInfo[0]][edgeInfo[1]]
= labelPair(patchi, edgei - pp.nInternalEdges());
// Found all two occurrences of this edge so remove from
// hash to save space. Note that this will give lots of
// problems if the polyBoundaryMesh is multiply connected.
pointsToEdge.erase(meshEdge);
}
}
}
if (pointsToEdge.size())
{
FatalErrorInFunction
<< "Not all boundary edges of patches match up." << nl
<< "Is the outside of your mesh multiply connected?"
<< abort(FatalError);
}
forAll(*this, patchi)
{
const polyPatch& pp = operator[](patchi);
const labelPairList& nbrEdges = neighbourEdges[patchi];
forAll(nbrEdges, i)
{
const labelPair& edgeInfo = nbrEdges[i];
if (edgeInfo[0] == -1 || edgeInfo[1] == -1)
{
const label edgei = pp.nInternalEdges() + i;
const edge& e = pp.edges()[edgei];
FatalErrorInFunction
<< "Not all boundary edges of patches match up." << nl
<< "Edge " << edgei << " on patch " << pp.name()
<< " end points " << pp.localPoints()[e[0]] << ' '
<< pp.localPoints()[e[1]] << " is not matched to an"
<< " edge on any other patch." << nl
<< "Is the outside of your mesh multiply connected?"
<< abort(FatalError);
}
}
}
}
return *neighbourEdgesPtr_;
}
const Foam::labelList& Foam::polyBoundaryMesh::patchID() const
{
if (!patchIDPtr_)
{
patchIDPtr_.reset(new labelList(mesh_.nBoundaryFaces()));
labelList& list = *patchIDPtr_;
const polyPatchList& patches = *this;
forAll(patches, patchi)
{
SubList