Skip to content

Commit 7a07148

Browse files
committed
Use a set of enums for tet hull integrity
An integer error code was kind of lazy of us, and using a set lets us cleanly fix errors rather than just report them.
1 parent c4ac194 commit 7a07148

4 files changed

Lines changed: 119 additions & 45 deletions

File tree

include/mesh/mesh_tet_interface.h

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,30 +103,40 @@ class MeshTetInterface
103103
*/
104104
static BoundingBox volume_to_surface_mesh (UnstructuredMesh & mesh);
105105

106+
/**
107+
* Enumeration of possible surface mesh integrity issues.
108+
*/
109+
enum SurfaceIntegrity {
110+
NON_TRI3 = 1, // a non-TRI3 element is found
111+
MISSING_NEIGHBOR = 2, // an element with a nullptr-neighbor is found
112+
EMPTY_MESH = 3, // the mesh is empty
113+
MISSING_BACKLINK = 4, // an element neighbor isn't linked back to it
114+
BAD_NEIGHBOR_NODES = 5, // an element neighbor isn't linked to expected nodes
115+
NON_ORIENTED = 6, // an element neighbor has inconsistent orientation
116+
BAD_NEIGHBOR_LINKS = 7 // an element neighbor has other inconsistent links
117+
};
118+
106119
/**
107120
* This function checks the integrity of the current set of
108121
* elements in the Mesh to see if they comprise a topological
109122
* manifold that (if it's also geometrically valid) would define
110-
* valid hull for a tetrahedralized volume.
111-
* That is:
112-
* - If they are all TRI3 elements
113-
* - They all have non-nullptr neighbors
123+
* valid boundary for a tetrahedralized volume.
124+
*
125+
* Named \p check_hull_integrity() for backward compatibility, but
126+
* now accepts non-convex manifolds.
114127
*
115-
* \returns
116-
* - 0 if the mesh forms a topologically valid hull
117-
* - 1 if a non-TRI3 element is found
118-
* - 2 if an element with a nullptr-neighbor is found
119-
* - 3 if the mesh is empty
128+
* \returns a set of \p enums describing problems
129+
* found, or an empty set if no problems are found.
120130
*/
121-
[[nodiscard]] unsigned int check_hull_integrity();
131+
[[nodiscard]] std::set<SurfaceIntegrity> check_hull_integrity() const;
122132

123133
/**
124134
* This function prints an informative message and throws an
125135
* exception based on the output of the check_hull_integrity()
126136
* function. It is a separate function so that you can check hull
127137
* integrity without exiting or catching an exception if desired.
128138
*/
129-
void process_hull_integrity_result(unsigned int result);
139+
void process_hull_integrity_result(const std::set<SurfaceIntegrity> & result) const;
130140

131141
/**
132142
* Delete original convex hull elements from the Mesh

src/mesh/mesh_netgen_interface.C

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ void NetGenMeshInterface::triangulate ()
109109
/* serial_only_needed_on_proc_0 */ true);
110110
}
111111

112+
// This should probably only be done on rank 0, but the API is
113+
// designed with the hope that we'll parallelize it eventually
114+
auto integrity = this->improve_hull_integrity();
115+
this->process_hull_integrity_result(integrity);
116+
112117
// If we're not rank 0, we're just going to wait for rank 0 to call
113118
// Netgen, then receive its data afterward, we're not going to hope
114119
// that Netgen does the exact same thing on every processor.
@@ -127,9 +132,6 @@ void NetGenMeshInterface::triangulate ()
127132
return;
128133
}
129134

130-
auto integrity = this->check_hull_integrity();
131-
this->process_hull_integrity_result(integrity);
132-
133135
Ng_Meshing_Parameters params;
134136

135137
// Override any default parameters we might need to, to avoid

src/mesh/mesh_tet_interface.C

Lines changed: 92 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -335,65 +335,127 @@ BoundingBox MeshTetInterface::volume_to_surface_mesh(UnstructuredMesh & mesh)
335335
}
336336

337337

338-
unsigned int MeshTetInterface::check_hull_integrity()
338+
std::set<MeshTetInterface::SurfaceIntegrity> MeshTetInterface::check_hull_integrity() const
339339
{
340340
// Check for easy return: if the Mesh is empty (i.e. if
341341
// somebody called triangulate_conformingDelaunayMesh on
342342
// a Mesh with no elements, then hull integrity check must
343343
// fail...
344344
if (_mesh.n_elem() == 0)
345-
return 3;
345+
return {EMPTY_MESH};
346+
347+
std::set<MeshTetInterface::SurfaceIntegrity> returnval;
346348

347349
for (auto & elem : this->_mesh.element_ptr_range())
348350
{
349351
// Check for proper element type
350352
if (elem->type() != TRI3)
351-
{
352-
//libmesh_error_msg("ERROR: Some of the elements in the original mesh were not TRI3!");
353-
return 1;
354-
}
353+
returnval.insert(NON_TRI3);
355354

356-
for (auto neigh : elem->neighbor_ptr_range())
355+
for (auto s : elem->side_index_range())
357356
{
357+
const Elem * const neigh = elem->neighbor_ptr(s);
358+
358359
if (neigh == nullptr)
359360
{
360-
// libmesh_error_msg("ERROR: Non-convex hull, cannot be tetrahedralized.");
361-
return 2;
361+
returnval.insert(MISSING_NEIGHBOR);
362+
continue;
363+
}
364+
365+
// Make sure our neighbor points back to us
366+
const unsigned int nn = neigh->which_neighbor_am_i(elem);
367+
368+
if (nn >= 3)
369+
{
370+
returnval.insert(MISSING_BACKLINK);
371+
continue;
372+
}
373+
374+
// Our neighbor should have the same the edge nodes we do on
375+
// the neighboring edgei
376+
const Node * const n1 = elem->node_ptr(s);
377+
const Node * const n2 = elem->node_ptr((s+1)%3);
378+
379+
const unsigned int i1 = neigh->local_node(n1->id());
380+
const unsigned int i2 = neigh->local_node(n2->id());
381+
if (i1 >= 3 || i2 >= 3)
382+
{
383+
returnval.insert(BAD_NEIGHBOR_NODES);
384+
continue;
385+
}
386+
387+
// It should have those edge nodes in the opposite order
388+
// (because they have the same orientation we do)
389+
if ((i2 + 1)%3 != i1)
390+
{
391+
returnval.insert(NON_ORIENTED);
392+
continue;
393+
}
394+
395+
// And it should have those edge nodes in the expected
396+
// places relative to its neighbor link
397+
if (i2 != nn)
398+
{
399+
returnval.insert(BAD_NEIGHBOR_LINKS);
400+
continue;
362401
}
363402
}
364403
}
365404

366-
// If we made it here, return success!
367-
return 0;
405+
// Return anything and everything we found
406+
return returnval;
368407
}
369408

370409

371410

372-
void MeshTetInterface::process_hull_integrity_result(unsigned result)
373-
{
374-
std::ostringstream err_msg;
375411

376-
if (result != 0)
377-
{
378-
err_msg << "Error! Conforming Delaunay mesh tetrahedralization requires a convex hull." << std::endl;
379412

380-
if (result==1)
381-
{
382-
err_msg << "Non-TRI3 elements were found in the input Mesh. ";
383-
err_msg << "A constrained Delaunay tetrahedralization requires a convex hull of TRI3 elements." << std::endl;
384-
}
413+
void MeshTetInterface::process_hull_integrity_result
414+
(const std::set<MeshTetInterface::SurfaceIntegrity> & result) const
415+
{
416+
std::ostringstream err_msg;
385417

386-
if (result==2)
387-
{
388-
err_msg << "At least one triangle without three neighbors was found in the input Mesh. ";
389-
err_msg << "A constrained Delaunay tetrahedralization must be a triangular manifold without boundary." << std::endl;
390-
}
418+
if (result.empty()) // success
419+
return;
391420

392-
if (result==3)
393-
err_msg << "The input Mesh was empty!" << std::endl;
421+
err_msg << "Error! Conforming Delaunay mesh tetrahedralization requires a convex hull." << std::endl;
394422

395-
libmesh_error_msg(err_msg.str());
423+
if (result.count(NON_TRI3))
424+
{
425+
err_msg << "At least one non-Tri3 element was found in the input boundary mesh. ";
426+
err_msg << "Our constrained Delaunay tetrahedralization boundary must be a triangulation of Tri3 elements." << std::endl;
427+
}
428+
if (result.count(MISSING_NEIGHBOR))
429+
{
430+
err_msg << "At least one triangle without three neighbors was found in the input boundary mesh. ";
431+
err_msg << "A constrained Delaunay tetrahedralization boundary must be a triangular manifold without boundary." << std::endl;
432+
}
433+
if (result.count(EMPTY_MESH))
434+
{
435+
err_msg << "The input boundary mesh was empty!" << std::endl;
436+
err_msg << "Our constrained Delaunay tetrahedralization boundary must be a triangulation of Tri3 elements." << std::endl;
437+
}
438+
if (result.count(MISSING_BACKLINK))
439+
{
440+
err_msg << "At least one triangle neighbor without a return neighbor link was found in the input boundary mesh. ";
441+
err_msg << "A constrained Delaunay tetrahedralization boundary must be a conforming and non-adaptively-refined mesh." << std::endl;
442+
}
443+
if (result.count(BAD_NEIGHBOR_NODES))
444+
{
445+
err_msg << "At least one triangle neighbor without expected node links was found in the input boundary mesh. ";
446+
err_msg << "A constrained Delaunay tetrahedralization boundary must be a conforming and non-adaptively-refined mesh." << std::endl;
396447
}
448+
if (result.count(NON_ORIENTED))
449+
{
450+
err_msg << "At least one triangle neighbor with an inconsistent orientation was found in the input boundary mesh. ";
451+
err_msg << "A constrained Delaunay tetrahedralization boundary must be an oriented Tri3 mesh." << std::endl;
452+
}
453+
if (result.count(BAD_NEIGHBOR_LINKS))
454+
{
455+
err_msg << "At least one triangle neighbor with inconsistent node and neighbor links was found in the input boundary mesh." << std::endl;
456+
}
457+
458+
libmesh_error_msg(err_msg.str());
397459
}
398460

399461

src/mesh/mesh_tetgen_interface.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ void TetGenMeshInterface::triangulate_conformingDelaunayMesh_carvehole (const s
202202
{
203203
// Before calling this function, the Mesh must contain a convex hull
204204
// of TRI3 elements which define the boundary.
205-
unsigned hull_integrity_check = check_hull_integrity();
205+
auto hull_integrity_check = this->improve_hull_integrity();
206206

207207
// Possibly die if hull integrity check failed
208208
this->process_hull_integrity_result(hull_integrity_check);

0 commit comments

Comments
 (0)