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:
Mark Olesen
2023-05-03 16:01:59 +02:00
parent 1bef57d018
commit 783934ccad
8 changed files with 619 additions and 116 deletions

View File

@ -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();
}
}
// ************************************************************************* //

View File

@ -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

View File

@ -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"
);
}

View File

@ -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);