Skip to content

Commit ee0cca4

Browse files
authored
Merge pull request #4408 from roystgnr/more_hull_integrity_checks
Add more integrity checks for tetrahedralization
2 parents c889858 + 555dfd0 commit ee0cca4

5 files changed

Lines changed: 381 additions & 41 deletions

File tree

include/mesh/mesh_tet_interface.h

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,23 @@ class MeshTetInterface
9292
*/
9393
virtual void triangulate () = 0;
9494

95+
/**
96+
* Sets a verbosity level, defaulting to 0 (print nothing), to be
97+
* set as high as 100 (print everything).
98+
*
99+
* For verbosity >= 50, print all detected surface mesh integrity
100+
* issues as they're found. Subclasses may add other output at
101+
* other verbosity levels.
102+
*/
103+
void set_verbosity(unsigned int v);
104+
95105
protected:
96106

107+
/**
108+
* verbosity setting
109+
*/
110+
unsigned int _verbosity;
111+
97112
/**
98113
* Remove volume elements from the given mesh, after converting
99114
* their outer boundary faces to surface elements.
@@ -103,30 +118,52 @@ class MeshTetInterface
103118
*/
104119
static BoundingBox volume_to_surface_mesh (UnstructuredMesh & mesh);
105120

121+
/**
122+
* Enumeration of possible surface mesh integrity issues.
123+
*/
124+
enum SurfaceIntegrity {
125+
NON_TRI3 = 1, // a non-TRI3 element is found
126+
MISSING_NEIGHBOR = 2, // an element with a nullptr-neighbor is found
127+
EMPTY_MESH = 3, // the mesh is empty
128+
MISSING_BACKLINK = 4, // an element neighbor isn't linked back to it
129+
BAD_NEIGHBOR_NODES = 5, // an element neighbor isn't linked to expected nodes
130+
NON_ORIENTED = 6, // an element neighbor has inconsistent orientation
131+
BAD_NEIGHBOR_LINKS = 7, // an element neighbor has other inconsistent links
132+
DEGENERATE_ELEMENT = 8, // an element has zero area
133+
DEGENERATE_MESH = 9 // the mesh clearly bounds zero volume
134+
};
135+
106136
/**
107137
* This function checks the integrity of the current set of
108138
* elements in the Mesh to see if they comprise a topological
109139
* 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
140+
* valid boundary for a tetrahedralized volume.
114141
*
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
142+
* Named \p check_hull_integrity() for backward compatibility, but
143+
* now accepts non-convex manifolds.
144+
*
145+
* \returns a set of \p enums describing problems
146+
* found, or an empty set if no problems are found.
147+
*/
148+
[[nodiscard]] std::set<SurfaceIntegrity> check_hull_integrity() const;
149+
150+
/**
151+
* This function checks the integrity of the current set of
152+
* elements in the Mesh, and corrects what it can.
153+
*
154+
* \returns A set of SurfaceIntegrity codes from \p
155+
* check_hull_integrity() if there are problems it can't fix, or an
156+
* empty set otherwise.
120157
*/
121-
[[nodiscard]] unsigned int check_hull_integrity();
158+
[[nodiscard]] std::set<SurfaceIntegrity> improve_hull_integrity();
122159

123160
/**
124161
* This function prints an informative message and throws an
125162
* exception based on the output of the check_hull_integrity()
126163
* function. It is a separate function so that you can check hull
127164
* integrity without exiting or catching an exception if desired.
128165
*/
129-
void process_hull_integrity_result(unsigned int result);
166+
void process_hull_integrity_result(const std::set<SurfaceIntegrity> & result) const;
130167

131168
/**
132169
* Delete original convex hull elements from the Mesh
@@ -163,6 +200,17 @@ class MeshTetInterface
163200
std::unique_ptr<std::vector<std::unique_ptr<UnstructuredMesh>>> _holes;
164201
};
165202

203+
204+
// ------------------------------------------------------------
205+
// MeshTetInterface class member functions
206+
inline
207+
void
208+
MeshTetInterface::set_verbosity(unsigned int v)
209+
{
210+
this->_verbosity = v;
211+
}
212+
213+
166214
} // namespace libMesh
167215

168216
#endif // LIBMESH_MESH_TET_INTERFACE_H

src/mesh/mesh_netgen_interface.C

Lines changed: 22 additions & 5 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.
@@ -123,13 +128,16 @@ void NetGenMeshInterface::triangulate ()
123128
// Receive the mesh data rank 0 will send later, then fix it up
124129
// together
125130
MeshCommunication().broadcast(this->_mesh);
131+
132+
// If we got an empty mesh here then our tetrahedralization
133+
// failed.
134+
libmesh_error_msg_if (!this->_mesh.n_elem(),
135+
"NetGen failed to generate any tetrahedra");
136+
126137
this->_mesh.prepare_for_use();
127138
return;
128139
}
129140

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

135143
// Override any default parameters we might need to, to avoid
@@ -324,8 +332,17 @@ void NetGenMeshInterface::triangulate ()
324332

325333
const int n_elem = Ng_GetNE(ngmesh);
326334

327-
libmesh_error_msg_if (n_elem <= 0,
328-
"NetGen failed to generate any tetrahedra");
335+
// If Netgen fails us, we're likely to get n_elem <= 0. This is a
336+
// common enough failure from bad setups that I want to make sure
337+
// it's thrown in parallel so as to not desynchronize any unit tests
338+
// that trigger it. So we'll broadcast the empty mesh to indicate
339+
// the problem and enable throwing exceptions in parallel.
340+
if (n_elem <= 0)
341+
{
342+
this->_mesh.clear();
343+
MeshCommunication().broadcast(this->_mesh);
344+
libmesh_error_msg ("NetGen failed to generate any tetrahedra");
345+
}
329346

330347
const dof_id_type n_points = Ng_GetNP(ngmesh);
331348
const dof_id_type old_nodes = this->_mesh.n_nodes();

0 commit comments

Comments
 (0)