ENH: add global topology check in checkMesh and makeFaMesh (#2771)
- detect when boundary patches are multiply connected across edges STYLE: initialize some faMesh values
This commit is contained in:
parent
1bef57d018
commit
783934ccad
@ -0,0 +1,164 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2023 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
|
||||
|
||||
Description
|
||||
Check topology of poly patches used for finite-area.
|
||||
|
||||
Input
|
||||
mesh (polyMesh)
|
||||
meshDefDict (system/faMeshDefintion)
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
// Preliminary checks
|
||||
{
|
||||
typedef typename uindirectPrimitivePatch::surfaceTopo TopoType;
|
||||
|
||||
const polyBoundaryMesh& pbm = mesh.boundaryMesh();
|
||||
|
||||
wordRes polyPatchNames;
|
||||
meshDefDict.readIfPresent("polyMeshPatches", polyPatchNames);
|
||||
|
||||
const labelList patchIDs
|
||||
(
|
||||
pbm.patchSet
|
||||
(
|
||||
polyPatchNames,
|
||||
false, // warnNotFound
|
||||
true // useGroups
|
||||
).sortedToc()
|
||||
);
|
||||
|
||||
label nFaceLabels = 0;
|
||||
for (const label patchi : patchIDs)
|
||||
{
|
||||
nFaceLabels += pbm[patchi].size();
|
||||
}
|
||||
|
||||
labelList faceLabels(nFaceLabels);
|
||||
|
||||
nFaceLabels = 0;
|
||||
for (const label patchi : patchIDs)
|
||||
{
|
||||
for (const label facei : pbm[patchi].range())
|
||||
{
|
||||
faceLabels[nFaceLabels] = facei;
|
||||
++nFaceLabels;
|
||||
}
|
||||
}
|
||||
|
||||
uindirectPrimitivePatch pp
|
||||
(
|
||||
UIndirectList<face>(mesh.faces(), faceLabels),
|
||||
mesh.points()
|
||||
);
|
||||
|
||||
// Non-manifold
|
||||
labelHashSet badEdges(pp.nEdges()/20);
|
||||
|
||||
labelHashSet* pointSetPtr = nullptr;
|
||||
labelHashSet* badEdgesPtr = &badEdges;
|
||||
|
||||
bool foundError = false;
|
||||
|
||||
if (returnReduceAnd(pp.empty()))
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
else if (UPstream::parRun())
|
||||
{
|
||||
const labelList meshEdges
|
||||
(
|
||||
pp.meshEdges(mesh.edges(), mesh.pointEdges())
|
||||
);
|
||||
|
||||
// Parallel - use mesh edges
|
||||
// - no check for point-pinch
|
||||
// - no check for consistent orientation (if that is posible to
|
||||
// check?)
|
||||
|
||||
// Count number of edge/face connections (globally)
|
||||
labelList nEdgeConnections(mesh.nEdges(), Zero);
|
||||
|
||||
const labelListList& edgeFaces = pp.edgeFaces();
|
||||
|
||||
forAll(edgeFaces, edgei)
|
||||
{
|
||||
nEdgeConnections[meshEdges[edgei]] = edgeFaces[edgei].size();
|
||||
}
|
||||
|
||||
// Synchronise across coupled edges
|
||||
syncTools::syncEdgeList
|
||||
(
|
||||
mesh,
|
||||
nEdgeConnections,
|
||||
plusEqOp<label>(),
|
||||
label(0) // null value
|
||||
);
|
||||
|
||||
label labelTyp = TopoType::MANIFOLD;
|
||||
forAll(meshEdges, edgei)
|
||||
{
|
||||
const label meshEdgei = meshEdges[edgei];
|
||||
const label numNbrs = nEdgeConnections[meshEdgei];
|
||||
if (numNbrs == 1)
|
||||
{
|
||||
//if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
|
||||
labelTyp = max(labelTyp, TopoType::OPEN);
|
||||
}
|
||||
else if (numNbrs == 0 || numNbrs > 2)
|
||||
{
|
||||
if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
|
||||
if (badEdgesPtr) badEdgesPtr->insert(edgei);
|
||||
labelTyp = max(labelTyp, TopoType::ILLEGAL);
|
||||
}
|
||||
}
|
||||
reduce(labelTyp, maxOp<label>());
|
||||
|
||||
foundError = (labelTyp == TopoType::ILLEGAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
const TopoType pTyp = pp.surfaceType(badEdgesPtr);
|
||||
|
||||
foundError = (pTyp == TopoType::ILLEGAL);
|
||||
}
|
||||
|
||||
if (foundError)
|
||||
{
|
||||
edgeList dumpEdges(pp.edges(), badEdges.sortedToc());
|
||||
|
||||
vtk::lineWriter writer
|
||||
(
|
||||
pp.localPoints(),
|
||||
dumpEdges,
|
||||
fileName
|
||||
(
|
||||
mesh.time().globalPath()
|
||||
/ ("faMesh-construct.illegalEdges")
|
||||
)
|
||||
);
|
||||
|
||||
writer.writeGeometry();
|
||||
|
||||
// CellData
|
||||
writer.beginCellData();
|
||||
writer.writeProcIDs();
|
||||
|
||||
Info<< "Wrote "
|
||||
<< returnReduce(dumpEdges.size(), sumOp<label>())
|
||||
<< " bad edges: " << writer.output().name() << nl;
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
// ************************************************************************* //
|
@ -16,10 +16,10 @@ Description
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
// Embed do-while to support early termination
|
||||
if (doDecompose && Pstream::parRun())
|
||||
if (doDecompose && UPstream::parRun())
|
||||
do
|
||||
{
|
||||
faMeshReconstructor reconstructor(aMesh, IOobjectOption::READ_IF_PRESENT);
|
||||
faMeshReconstructor reconstructor(aMesh, IOobjectOption::LAZY_READ);
|
||||
|
||||
if (!reconstructor.good())
|
||||
{
|
||||
@ -61,16 +61,16 @@ do
|
||||
auto oldHandler = fileHandler(fileOperation::NewUncollated());
|
||||
fileHandler().distributed(true);
|
||||
|
||||
if (Pstream::master())
|
||||
if (UPstream::master())
|
||||
{
|
||||
haveMeshOnProc.set(Pstream::myProcNo());
|
||||
haveMeshOnProc.set(UPstream::myProcNo());
|
||||
subsetter.reset(new faMeshSubset(serialMesh));
|
||||
|
||||
const bool oldParRun = Pstream::parRun(false);
|
||||
const bool oldParRun = UPstream::parRun(false);
|
||||
|
||||
objects = IOobjectList(serialMesh.time(), runTime.timeName());
|
||||
|
||||
Pstream::parRun(oldParRun);
|
||||
UPstream::parRun(oldParRun);
|
||||
}
|
||||
|
||||
// Restore old settings
|
||||
|
@ -21,12 +21,12 @@ Description
|
||||
|
||||
word outputName("finiteArea-edges.obj");
|
||||
|
||||
if (Pstream::parRun())
|
||||
if (UPstream::parRun())
|
||||
{
|
||||
outputName = word
|
||||
(
|
||||
"finiteArea-edges-proc"
|
||||
+ Foam::name(Pstream::myProcNo())
|
||||
+ Foam::name(UPstream::myProcNo())
|
||||
+ ".obj"
|
||||
);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016-2017 Wikki Ltd
|
||||
Copyright (C) 2021-2022 OpenCFD Ltd.
|
||||
Copyright (C) 2021-2023 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -51,7 +51,9 @@ Original Authors
|
||||
#include "faMeshReconstructor.H"
|
||||
#include "faMeshSubset.H"
|
||||
#include "PtrListOps.H"
|
||||
#include "foamVtkLineWriter.H"
|
||||
#include "foamVtkIndPatchWriter.H"
|
||||
#include "syncTools.H"
|
||||
#include "OBJstream.H"
|
||||
|
||||
using namespace Foam;
|
||||
@ -128,6 +130,8 @@ int main(int argc, char *argv[])
|
||||
meshDefDict.add("emptyPatch", patchName, true);
|
||||
}
|
||||
|
||||
// Preliminary checks
|
||||
#include "checkPatchTopology.H"
|
||||
|
||||
// Create
|
||||
faMesh aMesh(mesh, meshDefDict);
|
||||
|
@ -6,7 +6,7 @@
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2011-2016 OpenFOAM Foundation
|
||||
Copyright (C) 2017-2022 OpenCFD Ltd.
|
||||
Copyright (C) 2017-2023 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -36,6 +36,7 @@ License
|
||||
#include "IOmanip.H"
|
||||
#include "emptyPolyPatch.H"
|
||||
#include "processorPolyPatch.H"
|
||||
#include "foamVtkLineWriter.H"
|
||||
#include "vtkCoordSetWriter.H"
|
||||
#include "vtkSurfaceWriter.H"
|
||||
#include "checkTools.H"
|
||||
@ -44,24 +45,34 @@ License
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
// NEWER CODE
|
||||
// ~~~~~~~~~~
|
||||
// Return true on error
|
||||
template<class PatchType>
|
||||
void Foam::checkPatch
|
||||
bool Foam::checkPatch
|
||||
(
|
||||
const bool allGeometry,
|
||||
const word& name,
|
||||
const std::string& name,
|
||||
const polyMesh& mesh,
|
||||
const PatchType& pp,
|
||||
const labelList& meshFaces,
|
||||
const labelList& meshEdges,
|
||||
pointSet& points
|
||||
const labelUList& meshEdges,
|
||||
labelHashSet* pointSetPtr,
|
||||
labelHashSet* badEdgesPtr
|
||||
)
|
||||
{
|
||||
if (badEdgesPtr)
|
||||
{
|
||||
badEdgesPtr->clear();
|
||||
}
|
||||
|
||||
typedef typename PatchType::surfaceTopo TopoType;
|
||||
|
||||
bool foundError = false;
|
||||
|
||||
const label globalSize = returnReduce(pp.size(), sumOp<label>());
|
||||
|
||||
Info<< " "
|
||||
<< setw(20) << name
|
||||
<< setw(20) << name.c_str()
|
||||
<< setw(9) << globalSize
|
||||
<< setw(9) << returnReduce(pp.nPoints(), sumOp<label>());
|
||||
|
||||
@ -69,13 +80,169 @@ void Foam::checkPatch
|
||||
{
|
||||
Info<< setw(34) << "ok (empty)";
|
||||
}
|
||||
else if (Pstream::parRun())
|
||||
else if (UPstream::parRun())
|
||||
{
|
||||
// Parallel - use mesh edges
|
||||
// - no check for point-pinch
|
||||
// - no check for consistent orientation (if that is posible to
|
||||
// check?)
|
||||
|
||||
// Count number of edge/face connections (globally)
|
||||
labelList nEdgeConnections(mesh.nEdges(), Zero);
|
||||
|
||||
const labelListList& edgeFaces = pp.edgeFaces();
|
||||
|
||||
forAll(edgeFaces, edgei)
|
||||
{
|
||||
nEdgeConnections[meshEdges[edgei]] = edgeFaces[edgei].size();
|
||||
}
|
||||
|
||||
// Synchronise across coupled edges
|
||||
syncTools::syncEdgeList
|
||||
(
|
||||
mesh,
|
||||
nEdgeConnections,
|
||||
plusEqOp<label>(),
|
||||
label(0) // null value
|
||||
);
|
||||
|
||||
label labelTyp = TopoType::MANIFOLD;
|
||||
forAll(meshEdges, edgei)
|
||||
{
|
||||
const label meshEdgei = meshEdges[edgei];
|
||||
const label numNbrs = nEdgeConnections[meshEdgei];
|
||||
if (numNbrs == 1)
|
||||
{
|
||||
//if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
|
||||
labelTyp = max(labelTyp, TopoType::OPEN);
|
||||
}
|
||||
else if (numNbrs == 0 || numNbrs > 2)
|
||||
{
|
||||
if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
|
||||
if (badEdgesPtr) badEdgesPtr->insert(edgei);
|
||||
labelTyp = max(labelTyp, TopoType::ILLEGAL);
|
||||
}
|
||||
}
|
||||
reduce(labelTyp, maxOp<label>());
|
||||
|
||||
if (labelTyp == TopoType::MANIFOLD)
|
||||
{
|
||||
Info<< setw(34) << "ok (closed singly connected)";
|
||||
}
|
||||
else if (labelTyp == TopoType::OPEN)
|
||||
{
|
||||
Info<< setw(34) << "ok (non-closed singly connected)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< setw(34) << "multiply connected (shared edge)";
|
||||
}
|
||||
|
||||
foundError = (labelTyp == TopoType::ILLEGAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
const TopoType pTyp = pp.surfaceType(badEdgesPtr);
|
||||
|
||||
if (pTyp == TopoType::MANIFOLD)
|
||||
{
|
||||
if (pp.checkPointManifold(true, pointSetPtr))
|
||||
{
|
||||
Info<< setw(34) << "multiply connected (shared point)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< setw(34) << "ok (closed singly connected)";
|
||||
}
|
||||
|
||||
if (pointSetPtr)
|
||||
{
|
||||
// Add points on non-manifold edges to make set complete
|
||||
pp.checkTopology(false, pointSetPtr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pointSetPtr)
|
||||
{
|
||||
pp.checkTopology(false, pointSetPtr);
|
||||
}
|
||||
|
||||
if (pTyp == TopoType::OPEN)
|
||||
{
|
||||
Info<< setw(34) << "ok (non-closed singly connected)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< setw(34) << "multiply connected (shared edge)";
|
||||
}
|
||||
}
|
||||
|
||||
foundError = (pTyp == TopoType::ILLEGAL);
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
boundBox bb(pp.box());
|
||||
bb.reduce();
|
||||
|
||||
if (bb.good())
|
||||
{
|
||||
Info<< ' ' << bb;
|
||||
}
|
||||
}
|
||||
|
||||
return foundError;
|
||||
}
|
||||
|
||||
|
||||
// OLDER CODE
|
||||
// ~~~~~~~~~~
|
||||
// Return true on error
|
||||
template<class PatchType>
|
||||
bool Foam::checkPatch
|
||||
(
|
||||
const bool allGeometry,
|
||||
const std::string& name,
|
||||
const polyMesh& mesh,
|
||||
const PatchType& pp,
|
||||
const labelUList& meshFaces,
|
||||
const labelUList& meshEdges,
|
||||
labelHashSet* pointSetPtr,
|
||||
labelHashSet* badEdgesPtr
|
||||
)
|
||||
{
|
||||
if (badEdgesPtr)
|
||||
{
|
||||
badEdgesPtr->clear();
|
||||
}
|
||||
|
||||
typedef typename PatchType::surfaceTopo TopoType;
|
||||
|
||||
bool foundError = false;
|
||||
|
||||
const label globalSize = returnReduce(pp.size(), sumOp<label>());
|
||||
|
||||
Info<< " "
|
||||
<< setw(20) << name.c_str()
|
||||
<< setw(9) << globalSize
|
||||
<< setw(9) << returnReduce(pp.nPoints(), sumOp<label>());
|
||||
|
||||
if (globalSize == 0)
|
||||
{
|
||||
Info<< setw(34) << "ok (empty)";
|
||||
}
|
||||
else if (UPstream::parRun())
|
||||
{
|
||||
// Parallel - use mesh edges
|
||||
// - no check for point-pinch
|
||||
// - no check for consistent orientation (if that is posible to
|
||||
// check?)
|
||||
|
||||
// OLDER CODE
|
||||
// ~~~~~~~~~~
|
||||
// Synchronise connected faces using global face numbering
|
||||
//
|
||||
// (see addPatchCellLayer::globalEdgeFaces)
|
||||
// From mesh edge to global face labels. Non-empty sublists only for
|
||||
// pp edges.
|
||||
@ -88,12 +255,12 @@ void Foam::checkPatch
|
||||
|
||||
forAll(edgeFaces, edgei)
|
||||
{
|
||||
label meshEdgei = meshEdges[edgei];
|
||||
const label meshEdgei = meshEdges[edgei];
|
||||
const labelList& eFaces = edgeFaces[edgei];
|
||||
|
||||
// Store face and processor as unique tag.
|
||||
labelList& globalEFaces = globalEdgeFaces[meshEdgei];
|
||||
globalEFaces.setSize(eFaces.size());
|
||||
globalEFaces.resize(eFaces.size());
|
||||
forAll(eFaces, i)
|
||||
{
|
||||
globalEFaces[i] = globalFaces.toGlobal(meshFaces[eFaces[i]]);
|
||||
@ -104,29 +271,30 @@ void Foam::checkPatch
|
||||
// << endl;
|
||||
}
|
||||
|
||||
// Synchronise across coupled edges.
|
||||
// Synchronise across coupled edges
|
||||
syncTools::syncEdgeList
|
||||
(
|
||||
mesh,
|
||||
globalEdgeFaces,
|
||||
ListOps::uniqueEqOp<label>(),
|
||||
labelList() // null value
|
||||
labelList() // null value
|
||||
);
|
||||
|
||||
|
||||
label labelTyp = TopoType::MANIFOLD;
|
||||
forAll(meshEdges, edgei)
|
||||
{
|
||||
const label meshEdgei = meshEdges[edgei];
|
||||
const labelList& globalEFaces = globalEdgeFaces[meshEdgei];
|
||||
if (globalEFaces.size() == 1)
|
||||
const label numNbrs = globalEFaces.size();
|
||||
if (numNbrs == 1)
|
||||
{
|
||||
//points.insert(mesh.edges()[meshEdgei]);
|
||||
//if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
|
||||
labelTyp = max(labelTyp, TopoType::OPEN);
|
||||
}
|
||||
else if (globalEFaces.size() == 0 || globalEFaces.size() > 2)
|
||||
else if (numNbrs == 0 || numNbrs > 2)
|
||||
{
|
||||
points.insert(mesh.edges()[meshEdgei]);
|
||||
if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
|
||||
if (badEdgesPtr) badEdgesPtr->insert(edgei);
|
||||
labelTyp = max(labelTyp, TopoType::ILLEGAL);
|
||||
}
|
||||
}
|
||||
@ -138,61 +306,68 @@ void Foam::checkPatch
|
||||
}
|
||||
else if (labelTyp == TopoType::OPEN)
|
||||
{
|
||||
Info<< setw(34)
|
||||
<< "ok (non-closed singly connected)";
|
||||
Info<< setw(34) << "ok (non-closed singly connected)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< setw(34)
|
||||
<< "multiply connected (shared edge)";
|
||||
Info<< setw(34) << "multiply connected (shared edge)";
|
||||
}
|
||||
|
||||
foundError = (labelTyp == TopoType::ILLEGAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
TopoType pTyp = pp.surfaceType();
|
||||
const TopoType pTyp = pp.surfaceType(badEdgesPtr);
|
||||
|
||||
if (pTyp == TopoType::MANIFOLD)
|
||||
{
|
||||
if (pp.checkPointManifold(true, &points))
|
||||
if (pp.checkPointManifold(true, pointSetPtr))
|
||||
{
|
||||
Info<< setw(34)
|
||||
<< "multiply connected (shared point)";
|
||||
Info<< setw(34) << "multiply connected (shared point)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< setw(34) << "ok (closed singly connected)";
|
||||
}
|
||||
|
||||
// Add points on non-manifold edges to make set complete
|
||||
pp.checkTopology(false, &points);
|
||||
if (pointSetPtr)
|
||||
{
|
||||
// Add points on non-manifold edges to make set complete
|
||||
pp.checkTopology(false, pointSetPtr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pp.checkTopology(false, &points);
|
||||
if (pointSetPtr)
|
||||
{
|
||||
pp.checkTopology(false, pointSetPtr);
|
||||
}
|
||||
|
||||
if (pTyp == TopoType::OPEN)
|
||||
{
|
||||
Info<< setw(34)
|
||||
<< "ok (non-closed singly connected)";
|
||||
Info<< setw(34) << "ok (non-closed singly connected)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< setw(34)
|
||||
<< "multiply connected (shared edge)";
|
||||
Info<< setw(34) << "multiply connected (shared edge)";
|
||||
}
|
||||
}
|
||||
|
||||
foundError = (pTyp == TopoType::ILLEGAL);
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
const labelList& mp = pp.meshPoints();
|
||||
boundBox bb(pp.box());
|
||||
bb.reduce();
|
||||
|
||||
if (returnReduceOr(mp.size()))
|
||||
if (bb.good())
|
||||
{
|
||||
boundBox bb(pp.points(), mp, true); // reduce
|
||||
Info<< ' ' << bb;
|
||||
}
|
||||
}
|
||||
|
||||
return foundError;
|
||||
}
|
||||
|
||||
|
||||
@ -524,7 +699,7 @@ Foam::label Foam::checkTopology
|
||||
<< mesh.time().timeName()/"cellToRegion"
|
||||
<< endl;
|
||||
|
||||
labelIOList ctr
|
||||
IOListRef<label>
|
||||
(
|
||||
IOobject
|
||||
(
|
||||
@ -532,11 +707,11 @@ Foam::label Foam::checkTopology
|
||||
mesh.time().timeName(),
|
||||
mesh,
|
||||
IOobject::NO_READ,
|
||||
IOobject::NO_WRITE
|
||||
IOobject::NO_WRITE,
|
||||
IOobject::NO_REGISTER
|
||||
),
|
||||
rs
|
||||
);
|
||||
ctr.write();
|
||||
).write();
|
||||
|
||||
|
||||
// Points in multiple regions
|
||||
@ -651,7 +826,7 @@ Foam::label Foam::checkTopology
|
||||
}
|
||||
|
||||
// Non-manifold points
|
||||
pointSet points
|
||||
pointSet nonManifoldPoints
|
||||
(
|
||||
mesh,
|
||||
"nonManifoldPoints",
|
||||
@ -670,17 +845,15 @@ Foam::label Foam::checkTopology
|
||||
<< setw(20) << "Patch"
|
||||
<< setw(9) << "Faces"
|
||||
<< setw(9) << "Points"
|
||||
<< "Surface topology";
|
||||
<< " Surface topology";
|
||||
if (allGeometry)
|
||||
{
|
||||
Info<< " Bounding box";
|
||||
Info<< " Bounding box";
|
||||
}
|
||||
Info<< endl;
|
||||
|
||||
forAll(patches, patchi)
|
||||
for (const polyPatch& pp : patches)
|
||||
{
|
||||
const polyPatch& pp = patches[patchi];
|
||||
|
||||
if (!isA<processorPolyPatch>(pp))
|
||||
{
|
||||
checkPatch
|
||||
@ -689,14 +862,78 @@ Foam::label Foam::checkTopology
|
||||
pp.name(),
|
||||
mesh,
|
||||
pp,
|
||||
identity(pp.size(), pp.start()),
|
||||
// identity(pp.size(), pp.start()),
|
||||
pp.meshEdges(),
|
||||
points
|
||||
&nonManifoldPoints
|
||||
);
|
||||
Info<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
// All non-processor boundary patches
|
||||
{
|
||||
labelList faceLabels
|
||||
(
|
||||
identity
|
||||
(
|
||||
(
|
||||
patches.range(patches.nNonProcessor()-1).end_value()
|
||||
- patches.start()
|
||||
),
|
||||
patches.start()
|
||||
)
|
||||
);
|
||||
|
||||
uindirectPrimitivePatch pp
|
||||
(
|
||||
UIndirectList<face>(mesh.faces(), faceLabels),
|
||||
mesh.points()
|
||||
);
|
||||
|
||||
// Non-manifold
|
||||
labelHashSet badEdges(pp.nEdges()/20);
|
||||
|
||||
bool hadBadEdges = checkPatch
|
||||
(
|
||||
allGeometry,
|
||||
"## ALL BOUNDARIES ##",
|
||||
mesh,
|
||||
pp,
|
||||
// faceLabels,
|
||||
pp.meshEdges(mesh.edges(), mesh.pointEdges()),
|
||||
nullptr, // No point set
|
||||
&badEdges
|
||||
);
|
||||
Info<< nl << endl;
|
||||
|
||||
if (hadBadEdges)
|
||||
{
|
||||
edgeList dumpEdges(pp.edges(), badEdges.sortedToc());
|
||||
|
||||
vtk::lineWriter writer
|
||||
(
|
||||
pp.localPoints(),
|
||||
dumpEdges,
|
||||
fileName
|
||||
(
|
||||
mesh.time().globalPath()
|
||||
/ ("checkMesh-illegal-edges")
|
||||
)
|
||||
);
|
||||
|
||||
writer.writeGeometry();
|
||||
|
||||
// CellData
|
||||
writer.beginCellData();
|
||||
writer.writeProcIDs();
|
||||
|
||||
Info<< "Wrote "
|
||||
<< returnReduce(dumpEdges.size(), sumOp<label>())
|
||||
<< " bad edges: " << writer.output().name() << nl;
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
//Info.setf(ios_base::right);
|
||||
}
|
||||
|
||||
@ -729,9 +966,9 @@ Foam::label Foam::checkTopology
|
||||
fz.name(),
|
||||
mesh,
|
||||
fz(), // patch
|
||||
fz, // mesh face labels
|
||||
// fz, // mesh face labels
|
||||
fz.meshEdges(), // mesh edge labels
|
||||
points
|
||||
&nonManifoldPoints
|
||||
);
|
||||
Info<< endl;
|
||||
}
|
||||
@ -761,17 +998,20 @@ Foam::label Foam::checkTopology
|
||||
}
|
||||
}
|
||||
|
||||
const label nPoints = returnReduce(points.size(), sumOp<label>());
|
||||
|
||||
const label nPoints =
|
||||
returnReduce(nonManifoldPoints.size(), sumOp<label>());
|
||||
|
||||
if (nPoints)
|
||||
{
|
||||
Info<< " <<Writing " << nPoints
|
||||
<< " conflicting points to set " << points.name() << endl;
|
||||
points.instance() = mesh.pointsInstance();
|
||||
points.write();
|
||||
<< " conflicting points to set " << nonManifoldPoints.name()
|
||||
<< endl;
|
||||
nonManifoldPoints.instance() = mesh.pointsInstance();
|
||||
nonManifoldPoints.write();
|
||||
if (setWriter && setWriter->enabled())
|
||||
{
|
||||
mergeAndWrite(*setWriter, points);
|
||||
mergeAndWrite(*setWriter, nonManifoldPoints);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2023 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
|
||||
|
||||
Description
|
||||
Global functions for topology checking
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "labelList.H"
|
||||
#include "autoPtr.H"
|
||||
#include "ZoneMesh.H"
|
||||
@ -5,38 +22,62 @@
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
// Forward Declarations
|
||||
class polyMesh;
|
||||
class pointSet;
|
||||
class coordSetWriter;
|
||||
class surfaceWriter;
|
||||
|
||||
template<class PatchType>
|
||||
void checkPatch
|
||||
(
|
||||
const bool allGeometry,
|
||||
const word& name,
|
||||
const polyMesh& mesh,
|
||||
const PatchType& pp,
|
||||
const labelList& meshFaces,
|
||||
const labelList& meshEdges,
|
||||
pointSet& points
|
||||
);
|
||||
// Forward Declarations
|
||||
class polyMesh;
|
||||
class pointSet;
|
||||
class coordSetWriter;
|
||||
class surfaceWriter;
|
||||
|
||||
template<class Zone>
|
||||
label checkZones
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const ZoneMesh<Zone, polyMesh>& zones,
|
||||
topoSet& set
|
||||
);
|
||||
|
||||
label checkTopology
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const bool allTopology,
|
||||
const bool allGeometry,
|
||||
autoPtr<surfaceWriter>& surfWriter,
|
||||
autoPtr<coordSetWriter>& setWriter
|
||||
);
|
||||
}
|
||||
// Check patch topology.
|
||||
// In parallel, uses count of edge connections
|
||||
template<class PatchType>
|
||||
bool checkPatch
|
||||
(
|
||||
const bool allGeometry,
|
||||
const std::string& name,
|
||||
const polyMesh& mesh,
|
||||
const PatchType& pp,
|
||||
const labelUList& meshEdges,
|
||||
labelHashSet* pointSetPtr = nullptr,
|
||||
labelHashSet* badEdgesPtr = nullptr
|
||||
);
|
||||
|
||||
// OLDER CODE
|
||||
// ~~~~~~~~~~
|
||||
// Check patch topology.
|
||||
// In parallel, uses mesh face ids and global face numbering
|
||||
template<class PatchType>
|
||||
bool checkPatch
|
||||
(
|
||||
const bool allGeometry,
|
||||
const std::string& name,
|
||||
const polyMesh& mesh,
|
||||
const PatchType& pp,
|
||||
const labelUList& meshFaces,
|
||||
const labelUList& meshEdges,
|
||||
labelHashSet* pointSetPtr = nullptr,
|
||||
labelHashSet* badEdgesPtr = nullptr
|
||||
);
|
||||
|
||||
template<class Zone>
|
||||
label checkZones
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const ZoneMesh<Zone, polyMesh>& zones,
|
||||
topoSet& set
|
||||
);
|
||||
|
||||
label checkTopology
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const bool allTopology,
|
||||
const bool allGeometry,
|
||||
autoPtr<surfaceWriter>& surfWriter,
|
||||
autoPtr<coordSetWriter>& setWriter
|
||||
);
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// ************************************************************************* //
|
||||
|
@ -163,6 +163,11 @@ void Foam::faMesh::initPatch() const
|
||||
mesh().points()
|
||||
)
|
||||
);
|
||||
// Could set some basic primitive data here...
|
||||
// nEdges_ = patchPtr_->nEdges();
|
||||
// nInternalEdges_ = patchPtr_->nInternalEdges();
|
||||
// nFaces_ = patchPtr_->size();
|
||||
// nPoints_ = patchPtr_->nPoints();
|
||||
bndConnectPtr_.reset(nullptr);
|
||||
haloMapPtr_.reset(nullptr);
|
||||
haloFaceCentresPtr_.reset(nullptr);
|
||||
@ -512,7 +517,13 @@ Foam::faMesh::faMesh
|
||||
haloMapPtr_(nullptr),
|
||||
haloFaceCentresPtr_(nullptr),
|
||||
haloFaceNormalsPtr_(nullptr)
|
||||
{}
|
||||
{
|
||||
// Not yet much for primitive mesh data possible...
|
||||
nPoints_ = 0;
|
||||
nEdges_ = 0;
|
||||
nInternalEdges_ = 0;
|
||||
nFaces_ = faceLabels_.size();
|
||||
}
|
||||
|
||||
|
||||
Foam::faMesh::faMesh
|
||||
@ -593,7 +604,13 @@ Foam::faMesh::faMesh
|
||||
haloMapPtr_(nullptr),
|
||||
haloFaceCentresPtr_(nullptr),
|
||||
haloFaceNormalsPtr_(nullptr)
|
||||
{}
|
||||
{
|
||||
// Not yet much for primitive mesh data possible...
|
||||
nPoints_ = 0;
|
||||
nEdges_ = 0;
|
||||
nInternalEdges_ = 0;
|
||||
nFaces_ = faceLabels_.size();
|
||||
}
|
||||
|
||||
|
||||
Foam::faMesh::faMesh(const polyPatch& pp, const bool doInit)
|
||||
|
@ -445,9 +445,9 @@ Foam::faMesh::getBoundaryEdgeConnections() const
|
||||
// A boundary finiteEdge edge (known from this side)
|
||||
|
||||
auto& gathered = gatheredConnections[cppEdgei];
|
||||
gathered.setCapacity(2);
|
||||
gathered.resize(1);
|
||||
auto& tuple = gathered.back();
|
||||
gathered.setCapacity_nocopy(2);
|
||||
gathered.resize_nocopy(1);
|
||||
auto& tuple = gathered.front();
|
||||
|
||||
tuple = bndEdgeConnections[bndEdgei].first();
|
||||
}
|
||||
@ -502,9 +502,9 @@ Foam::faMesh::getBoundaryEdgeConnections() const
|
||||
const label meshFacei = patchFacei + pp.start();
|
||||
|
||||
auto& gathered = gatheredConnections[cppEdgei];
|
||||
gathered.setCapacity(2);
|
||||
gathered.resize(1);
|
||||
auto& tuple = gathered.back();
|
||||
gathered.setCapacity_nocopy(2);
|
||||
gathered.resize_nocopy(1);
|
||||
auto& tuple = gathered.front();
|
||||
|
||||
tuple.procNo(UPstream::myProcNo());
|
||||
tuple.patchi(patchi);
|
||||
@ -545,6 +545,7 @@ Foam::faMesh::getBoundaryEdgeConnections() const
|
||||
<< "Collating sync information" << endl;
|
||||
|
||||
// Pick out gathered connections and add into primary bookkeeping
|
||||
badEdges.clear();
|
||||
for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
|
||||
{
|
||||
const auto& gathered = gatheredConnections[cppEdgei];
|
||||
@ -552,25 +553,61 @@ Foam::faMesh::getBoundaryEdgeConnections() const
|
||||
const label bndEdgei =
|
||||
edgeToBoundaryIndex.lookup(cpp.meshEdge(cppEdgei), -1);
|
||||
|
||||
if
|
||||
(
|
||||
// A boundary finiteEdge edge (known from this side)
|
||||
bndEdgei != -1
|
||||
|
||||
// Gathered a one-to-one connection
|
||||
&& gathered.size() == 2
|
||||
)
|
||||
if (bndEdgei != -1)
|
||||
{
|
||||
const auto& a = gathered[0];
|
||||
const auto& b = gathered[1];
|
||||
|
||||
// Copy second side of connection
|
||||
// A boundary finiteEdge edge (known from this side)
|
||||
auto& connection = bndEdgeConnections[bndEdgei];
|
||||
|
||||
connection.second() = (connection.first() == b) ? a : b;
|
||||
if (gathered.size() == 2)
|
||||
{
|
||||
// Copy second side of connection
|
||||
const auto& a = gathered[0];
|
||||
const auto& b = gathered[1];
|
||||
|
||||
connection.second() = (connection.first() == b) ? a : b;
|
||||
}
|
||||
else if (gathered.size() > 2)
|
||||
{
|
||||
// Multiply connected!! - this needs to be addressed
|
||||
badEdges.insert(cppEdgei);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (returnReduceOr(badEdges.size()))
|
||||
{
|
||||
WarningInFunction
|
||||
<< nl << "Multiply connected edges detected" << endl;
|
||||
|
||||
// Print out edges as point pairs
|
||||
// These are globally synchronised - so only output on master
|
||||
constexpr label maxOutput = 10;
|
||||
|
||||
label nOutput = 0;
|
||||
|
||||
for (const label cppEdgei : badEdges.sortedToc())
|
||||
{
|
||||
const edge e(cpp.meshEdge(cppEdgei));
|
||||
|
||||
const auto& gathered = gatheredConnections[cppEdgei];
|
||||
|
||||
Info<< "connection: ";
|
||||
gathered.writeList(Info) << nl;
|
||||
|
||||
Info<<" edge : "
|
||||
<< cpp.points()[e.first()] << ' '
|
||||
<< cpp.points()[e.second()] << nl;
|
||||
|
||||
++nOutput;
|
||||
if (maxOutput > 0 && nOutput >= maxOutput)
|
||||
{
|
||||
Info<< " ... suppressing further output" << nl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check missing/invalid
|
||||
badEdges.clear();
|
||||
for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
|
||||
|
Loading…
Reference in New Issue
Block a user