BUG: misleading error message in finiteArea construct

- the default (uninitialised) value for edge connections of -1
  could be confused with a tagged finiteArea patch, which used
  (-patchid-1) encoding. This would lead to messages about erroneous
  processor-processor addressing, but is in fact an mismatched edge
  connection.

  Now tag the finiteArea patch as (-patchid-2) to avoid this ambiguity
  and correctly generate an "Undefined connection:" message instead.

  Properly flush the VTP writers before raising a FatalError
  to ensure that they are not prematurely truncated.

Open Point:

  The base problem of "Undefined connection:" is largely related to
  multiply-connected face edges (ie, from the underlying volume mesh).
  Not easily remedied in the finiteArea generation.

TUT: basic finiteArea setup on motorBike
This commit is contained in:
Mark Olesen 2023-04-24 10:21:51 +02:00
parent ee39e3d276
commit 7a5ecd70b8
7 changed files with 262 additions and 84 deletions

View File

@ -101,11 +101,11 @@ class faMesh
{
// Private (internal) classes/structures
//- A (proc, patchi, patchEdgei) tuple used internally for managing
//- patch/patch bookkeeping during construction.
// Finite-area patches are stored with negated indices, which makes
// them readily identifiable and always sort before normal patches.
// Note
//- A (proc, patchi, patchEdgei, meshFacei) tuple used internally
//- for managing patch/patch bookkeeping during construction.
// Finite-area patches are stored with negated indices (offset -2),
// which makes them readily identifiable and always sort before normal
// patches.
struct patchTuple
:
public FixedList<label, 4>
@ -150,17 +150,17 @@ class faMesh
meshFacei(-1);
}
//- Valid if proc and edge are non-negative
bool valid() const noexcept
//- Valid if proc and patch (or patch edge) are non-negative
bool valid() const
{
return (procNo() >= 0 && patchEdgei() >= 0);
return (procNo() >= 0 && patchi() != -1);
}
// Processor is the first sort index
label procNo() const { return (*this)[0]; }
void procNo(label val) { (*this)[0] = val; }
// PatchId (-ve for finiteArea patches) is the second sort index
// PatchId is the second sort index (finiteArea patches are < -1)
label patchi() const { return (*this)[1]; }
void patchi(label val) { (*this)[1] = val; }
@ -177,25 +177,25 @@ class faMesh
label realPatchi() const
{
const label id = patchi();
return (id < 0 ? -(id + 1) : id);
return (id < -1 ? -(id + 2) : id);
}
//- Set patchId as finiteArea
void faPatchi(label val)
{
patchi(-(val + 1));
patchi(-(val + 2));
}
//- Considered to be finiteArea if patchi < 0
//- Considered to be finiteArea if (patchi < -1)
bool is_finiteArea() const noexcept
{
return (patchi() < 0);
return (patchi() < -1);
}
//- Considered to be processor local
bool is_localProc() const noexcept
bool is_localProc() const
{
return (procNo() == Pstream::myProcNo());
return (procNo() == UPstream::myProcNo());
}
};

View File

@ -120,9 +120,7 @@ Foam::faPatchList Foam::faMesh::createPatchList
const dictionary& patchDict = dEntry.dict();
// Add entry
faPatchDefs.append(faPatchData());
auto& patchDef = faPatchDefs.last();
auto& patchDef = faPatchDefs.emplace_back();
patchDef.name_ = dEntry.keyword();
patchDef.type_ = patchDict.get<word>("type");
@ -156,9 +154,7 @@ Foam::faPatchList Foam::faMesh::createPatchList
// Additional empty placeholder patch?
if (!emptyPatchName.empty())
{
faPatchDefs.append(faPatchData());
auto& patchDef = faPatchDefs.last();
auto& patchDef = faPatchDefs.emplace_back();
patchDef.name_ = emptyPatchName;
patchDef.type_ = "empty";
}
@ -168,9 +164,7 @@ Foam::faPatchList Foam::faMesh::createPatchList
// Placeholder for any undefined edges
const label undefPatchIndex = faPatchDefs.size();
{
faPatchDefs.append(faPatchData());
auto& patchDef = faPatchDefs.last();
auto& patchDef = faPatchDefs.emplace_back();
patchDef.name_ = "undefined";
patchDef.type_ = "patch";
@ -207,7 +201,6 @@ Foam::faPatchList Foam::faMesh::createPatchList
Map<labelHashSet> procConnections;
labelHashSet patchDefsUsed;
label nBadEdges(0);
labelHashSet badEdges(2*bndEdgeConnections.size());
forAll(bndEdgeConnections, connecti)
@ -218,17 +211,26 @@ Foam::faPatchList Foam::faMesh::createPatchList
edge patchPair;
if (a.is_finiteArea())
if (!a.valid() || !b.valid())
{
// Skip checking pairs where either is not valid
continue;
}
else if (a.is_finiteArea())
{
if (b.is_finiteArea())
{
// A processor-processor connection
// Expecting an inter-processor connection
if (a.procNo() == b.procNo())
{
// An intra-processor connection (should not be possible)
FatalErrorInFunction
<< "Processor-processor addressing error:" << nl
<< "Both connections have the same processor: "
<< a.procNo() << nl
<< "Connecting patches "
<< a.realPatchi() << " and " << b.realPatchi() << nl
<< abort(FatalError);
}
else if (a.is_localProc())
@ -323,22 +325,38 @@ Foam::faPatchList Foam::faMesh::createPatchList
<< "(patch:" << a.realPatchi()
<< " face:" << a.meshFacei()
<< ") and (patch:" << b.realPatchi()
<< " face:" << b.meshFacei() << ") connects: "
<< pbm[a.realPatchi()].name() << " to "
<< pbm[b.realPatchi()].name() << nl;
<< " face:" << b.meshFacei() << ") patch:"
<<
(
a.realPatchi() >= 0
? pbm[a.realPatchi()].name()
: word::null
)
<< " and patch:"
<<
(
b.realPatchi() >= 0
? pbm[b.realPatchi()].name()
: word::null
)
<< nl;
}
}
}
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) != 0)
if (returnReduceOr(badEdges.size()))
{
// Report directly as Info, not InfoInFunction
// since it can also be an expected result when
// nWarnUndefinedPatch == 0
Info<< "Had " << nBadEdges << '/'
Info<< nl
<< "Had "
<< returnReduce(badEdges.size(), sumOp<label>()) << '/'
<< returnReduce(patch().nBoundaryEdges(), sumOp<label>())
<< " undefined edge connections, added to defaultPatch: "
<< faPatchDefs[undefPatchIndex].name_ << nl;
<< faPatchDefs[undefPatchIndex].name_ << nl << nl
<< "==> Could indicate a non-manifold patch geometry" << nl
<< nl;
if (nWarnUndefinedPatch)
{
@ -378,10 +396,9 @@ Foam::faPatchList Foam::faMesh::createPatchList
procToDefLookup.insert(otherProci, patchDefi);
// Add entry
faPatchDefs.append(faPatchData());
auto& patchDef = faPatchDefs.last();
auto& patchDef = faPatchDefs.emplace_back();
patchDef.assign_coupled(Pstream::myProcNo(), otherProci);
patchDef.assign_coupled(UPstream::myProcNo(), otherProci);
}
}

View File

@ -30,6 +30,7 @@ License
#include "globalMeshData.H"
#include "indirectPrimitivePatch.H"
#include "edgeHashes.H"
#include "syncTools.H"
#include "foamVtkLineWriter.H"
#include "foamVtkIndPatchWriter.H"
@ -88,7 +89,6 @@ Foam::faMesh::getBoundaryEdgeConnections() const
// Map edges (mesh numbering) back to a boundary index
EdgeMap<label> edgeToBoundaryIndex(2*nBoundaryEdges);
label nBadEdges(0);
labelHashSet badEdges(2*nBoundaryEdges);
{
@ -102,7 +102,7 @@ Foam::faMesh::getBoundaryEdgeConnections() const
void clear()
{
Foam::edge::clear();
Foam::edge::clear(); // ie, (-1, -1)
patchEdgei_ = -1;
meshFacei_ = -1;
}
@ -114,7 +114,6 @@ Foam::faMesh::getBoundaryEdgeConnections() const
<< "Determining required boundary edge connections, "
<< "resolving locally attached boundary edges." << endl;
// Pass 1:
// - setup lookup (edge -> bnd index)
// - add owner patch for each boundary edge
@ -148,7 +147,7 @@ Foam::faMesh::getBoundaryEdgeConnections() const
{
auto& tuple = bndEdgeConnections[bndEdgei].first();
tuple.procNo(Pstream::myProcNo());
tuple.procNo(UPstream::myProcNo());
tuple.faPatchi(patchId); // Tag as finiteArea patch
tuple.patchEdgei(patchEdgei);
tuple.meshFacei(meshFacei);
@ -165,7 +164,7 @@ Foam::faMesh::getBoundaryEdgeConnections() const
}
}
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) != 0)
if (returnReduceOr(badEdges.size()))
{
edgeList dumpEdges(patch().edges(), badEdges.sortedToc());
@ -186,9 +185,15 @@ Foam::faMesh::getBoundaryEdgeConnections() const
writer.beginCellData();
writer.writeProcIDs();
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
writer.close(); // Flush writer before raising FatalError
FatalErrorInFunction
<< "Boundary edges not singly connected: "
<< nBadEdges << '/' << nBoundaryEdges << nl;
<< returnReduce(badEdges.size(), sumOp<label>()) << '/'
<< nBoundaryEdges << nl;
printPatchEdges
(
@ -197,9 +202,6 @@ Foam::faMesh::getBoundaryEdgeConnections() const
badEdges.sortedToc()
);
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
FatalError << abort(FatalError);
}
badEdges.clear();
@ -263,10 +265,12 @@ Foam::faMesh::getBoundaryEdgeConnections() const
}
}
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) != 0)
if (returnReduceOr(badEdges.size()))
{
FatalErrorInFunction
<< "Had " << nBadEdges
<< "Had "
<< returnReduce(badEdges.size(), sumOp<label>()) << '/'
<< nBoundaryEdges
<< " boundary edges with missing or multiple edge connections"
<< abort(FatalError);
}
@ -275,22 +279,36 @@ Foam::faMesh::getBoundaryEdgeConnections() const
badEdges.clear();
for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
{
// Primary bookkeeping
auto& tuple = bndEdgeConnections[bndEdgei].second();
// Local bookkeeping
const auto& pairing = patchPairings[bndEdgei];
const label nbrPatchi = pairing.second();
const label nbrPatchEdgei = pairing.patchEdgei_;
const label nbrMeshFacei = pairing.meshFacei_;
if (nbrMeshFacei >= 0)
if (nbrMeshFacei >= 0) // Additional safety
{
// Add into primary bookkeeping
auto& tuple = bndEdgeConnections[bndEdgei].second();
tuple.procNo(Pstream::myProcNo());
tuple.patchi(nbrPatchi);
tuple.patchEdgei(nbrPatchEdgei);
tuple.meshFacei(nbrMeshFacei);
if (nbrPatchi >= 0)
{
// Local connection
tuple.procNo(UPstream::myProcNo());
tuple.patchi(nbrPatchi);
tuple.patchEdgei(nbrPatchEdgei);
tuple.meshFacei(nbrMeshFacei);
}
else
{
// No local connection.
// Is likely to be a processor connection
tuple.procNo(UPstream::myProcNo());
tuple.patchi(-1);
tuple.patchEdgei(-1);
tuple.meshFacei(-1);
}
}
else if (!Pstream::parRun())
else if (!UPstream::parRun())
{
badEdges.insert(nInternalEdges + bndEdgei);
}
@ -301,12 +319,11 @@ Foam::faMesh::getBoundaryEdgeConnections() const
// ~~~~~~
// Serial - can return already
// ~~~~~~
if (!Pstream::parRun())
if (!UPstream::parRun())
{
// Verbose report of missing edges - in serial
nBadEdges = badEdges.size();
if (nBadEdges)
if (returnReduceOr(badEdges.size()))
{
edgeList dumpEdges(patch().edges(), badEdges.sortedToc());
@ -327,9 +344,15 @@ Foam::faMesh::getBoundaryEdgeConnections() const
writer.beginCellData();
writer.writeProcIDs();
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
writer.close(); // Flush writer before raising FatalError
FatalErrorInFunction
<< "Boundary edges with missing/invalid neighbours: "
<< nBadEdges << '/' << nBoundaryEdges << nl;
<< returnReduce(badEdges.size(), sumOp<label>()) << '/'
<< nBoundaryEdges << nl;
printPatchEdges
(
@ -338,9 +361,6 @@ Foam::faMesh::getBoundaryEdgeConnections() const
badEdges.sortedToc()
);
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
FatalError << abort(FatalError);
}
@ -430,7 +450,7 @@ Foam::faMesh::getBoundaryEdgeConnections() const
auto& gathered = gatheredConnections[cppEdgei];
gathered.setCapacity(2);
gathered.resize(1);
auto& tuple = gathered.last();
auto& tuple = gathered.back();
tuple = bndEdgeConnections[bndEdgei].first();
}
@ -487,9 +507,9 @@ Foam::faMesh::getBoundaryEdgeConnections() const
auto& gathered = gatheredConnections[cppEdgei];
gathered.setCapacity(2);
gathered.resize(1);
auto& tuple = gathered.last();
auto& tuple = gathered.back();
tuple.procNo(Pstream::myProcNo());
tuple.procNo(UPstream::myProcNo());
tuple.patchi(patchi);
tuple.patchEdgei(patchEdgei);
tuple.meshFacei(meshFacei);
@ -502,10 +522,11 @@ Foam::faMesh::getBoundaryEdgeConnections() const
}
}
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) != 0)
if (returnReduceOr(badEdges.size()))
{
FatalErrorInFunction
<< "Had " << nBadEdges << " coupled boundary edges"
<< "Had " << returnReduce(badEdges.size(), sumOp<label>())
<< " coupled boundary edges"
<< " with missing or multiple edge connections"
<< abort(FatalError);
}
@ -623,7 +644,7 @@ Foam::faMesh::getBoundaryEdgeConnections() const
}
// Verbose report of missing edges
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) != 0)
if (returnReduceOr(badEdges.size()))
{
edgeList dumpEdges(patch().edges(), badEdges.sortedToc());
@ -644,9 +665,15 @@ Foam::faMesh::getBoundaryEdgeConnections() const
writer.beginCellData();
writer.writeProcIDs();
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
writer.close(); // Flush writer before raising FatalError
FatalErrorInFunction
<< "Boundary edges with missing/invalid neighbours: "
<< nBadEdges << '/' << nBoundaryEdges << nl;
<< returnReduce(badEdges.size(), sumOp<label>()) << '/'
<< nBoundaryEdges << nl;
printPatchEdges
(
@ -655,9 +682,6 @@ Foam::faMesh::getBoundaryEdgeConnections() const
badEdges.sortedToc()
);
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
FatalError << abort(FatalError);
}
@ -723,7 +747,7 @@ void Foam::faMesh::setBoundaryConnections
}
}
label nInvalid = 0;
label nInvalid(0);
for (const auto& connection : bndConnect)
{
if (connection.first() < 0 || connection.second() < 0)
@ -732,15 +756,49 @@ void Foam::faMesh::setBoundaryConnections
}
}
if (Pstream::parRun())
if (returnReduceOr(nInvalid))
{
reduce(nInvalid, sumOp<label>());
}
labelHashSet badEdges(2*nInvalid);
forAll(bndConnect, bndEdgei)
{
if
(
bndConnect[bndEdgei].first() < 0
|| bndConnect[bndEdgei].second() < 0
)
{
badEdges.insert(nInternalEdges + bndEdgei);
}
}
edgeList dumpEdges(patch().edges(), badEdges.sortedToc());
vtk::lineWriter writer
(
patch().localPoints(),
dumpEdges,
fileName
(
mesh().time().globalPath()
/ ("faMesh-construct.invalidMatches")
)
);
writer.writeGeometry();
// CellData
writer.beginCellData();
writer.writeProcIDs();
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
writer.close(); // Flush writer before raising FatalError
if (nInvalid)
{
FatalErrorInFunction
<< "Did not properly match " << nInvalid
<< "Did not properly match "
<< returnReduce(nInvalid, sumOp<label>())
<< " boundary edges" << nl
<< abort(FatalError);
}
@ -759,7 +817,7 @@ Foam::labelList Foam::faMesh::boundaryProcs() const
{
const auto& connections = this->boundaryConnections();
labelHashSet procsUsed(2*Pstream::nProcs());
labelHashSet procsUsed(2*UPstream::nProcs());
for (const labelPair& tuple : connections)
{
@ -767,7 +825,7 @@ Foam::labelList Foam::faMesh::boundaryProcs() const
}
procsUsed.erase(-1); // placeholder value
procsUsed.erase(Pstream::myProcNo());
procsUsed.erase(UPstream::myProcNo());
return procsUsed.sortedToc();
}
@ -777,14 +835,14 @@ Foam::List<Foam::labelPair> Foam::faMesh::boundaryProcSizes() const
{
const auto& connections = this->boundaryConnections();
Map<label> procCount(2*Pstream::nProcs());
Map<label> procCount(2*UPstream::nProcs());
for (const labelPair& tuple : connections)
{
++procCount(tuple.first());
}
procCount.erase(-1); // placeholder value
procCount.erase(Pstream::myProcNo());
procCount.erase(UPstream::myProcNo());
// Flatten as list
List<labelPair> output(procCount.size());

View File

@ -0,0 +1,22 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2306 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object decomposeParDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
numberOfSubdomains 3;
method random;
// ************************************************************************* //

View File

@ -0,0 +1,30 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2306 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object faMeshDefinition;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
polyMeshPatches (motorBikeGroup);
boundary
{
bottom
{
type patch;
// ownerPolyPatch motorBikeGroup;
neighbourPolyPatch lowerWall;
}
}
// ************************************************************************** //

View File

@ -0,0 +1,32 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2306 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object faSchemes;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
ddtSchemes {}
gradSchemes {}
divSchemes {}
laplacianSchemes {}
interpolationSchemes {}
lnGradSchemes {}
fluxRequired {}
// ************************************************************************* //

View File

@ -0,0 +1,19 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2306 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object faSolution;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
solvers {}
// ************************************************************************* //