libMesh
mesh_tetgen_interface.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2019 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 #include "libmesh/libmesh_config.h"
19 #ifdef LIBMESH_HAVE_TETGEN
20 
21 
22 // C++ includes
23 #include <sstream>
24 
25 // Local includes
26 #include "libmesh/cell_tet4.h"
27 #include "libmesh/face_tri3.h"
28 #include "libmesh/unstructured_mesh.h"
29 #include "libmesh/mesh_tetgen_interface.h"
30 #include "libmesh/utility.h" // binary_find
31 #include "libmesh/mesh_tetgen_wrapper.h"
32 
33 namespace libMesh
34 {
35 
36 //----------------------------------------------------------------------
37 // TetGenMeshInterface class members
39  _mesh(mesh),
40  _serializer(_mesh),
41  _switches("Q")
42 {
43 }
44 
45 void TetGenMeshInterface::set_switches(const std::string & switches)
46 {
47  // set the tetgen switch manually:
48  // p = tetrahedralizes a piecewise linear complex (see definition in user manual)
49  // Q = quiet, no terminal output
50  // q = specify a minimum radius/edge ratio
51  // a = tetrahedron volume constraint
52  // V = verbose output
53  // for full list of options and their meaning: see the tetgen manual
54  // (http://wias-berlin.de/software/tetgen/1.5/doc/manual/manual005.html)
55  _switches = switches;
56 }
57 
58 
60 {
61  // class tetgen_wrapper allows library access on a basic level:
62  TetGenWrapper tetgen_wrapper;
63 
64  // fill input structure with point set data:
65  this->fill_pointlist(tetgen_wrapper);
66 
67  // Run TetGen triangulation method:
68  // Q = quiet, no terminal output
69  // V = verbose, more terminal output
70  // Note: if no switch is used, the input must be a list of 3D points
71  // (.node file) and the Delaunay tetrahedralization of this point set
72  // will be generated.
73 
74  // Can we apply quality and volume constraints in
75  // triangulate_pointset()?. On at least one test problem,
76  // specifying any quality or volume constraints here causes tetgen
77  // to segfault down in the insphere method: a nullptr is passed
78  // to the routine.
79  std::ostringstream oss;
80  oss << _switches;
81  // oss << "V"; // verbose operation
82  //oss << "q" << std::fixed << 2.0; // quality constraint
83  //oss << "a" << std::fixed << 100.; // volume constraint
84  tetgen_wrapper.set_switches(oss.str());
85 
86  // Run tetgen
87  tetgen_wrapper.run_tetgen();
88 
89  // save elements to mesh structure, nodes will not be changed:
90  const unsigned int num_elements = tetgen_wrapper.get_numberoftetrahedra();
91 
92  // Vector that temporarily holds the node labels defining element.
93  unsigned int node_labels[4];
94 
95  for (unsigned int i=0; i<num_elements; ++i)
96  {
97  Elem * elem = new Tet4;
98 
99  // Get the nodes associated with this element
100  for (auto j : elem->node_index_range())
101  node_labels[j] = tetgen_wrapper.get_element_node(i,j);
102 
103  // Associate the nodes with this element
104  this->assign_nodes_to_elem(node_labels, elem);
105 
106  // Finally, add this element to the mesh.
107  this->_mesh.add_elem(elem);
108  }
109 }
110 
111 
112 
114 {
115  // class tetgen_wrapper allows library access on a basic level
116  TetGenWrapper tetgen_wrapper;
117 
118  // Copy Mesh's node points into TetGen data structure
119  this->fill_pointlist(tetgen_wrapper);
120 
121  // Run TetGen triangulation method:
122  // Q = quiet, no terminal output
123  // Note: if no switch is used, the input must be a list of 3D points
124  // (.node file) and the Delaunay tetrahedralization of this point set
125  // will be generated. In this particular function, we are throwing
126  // away the tetrahedra generated by TetGen, and keeping only the
127  // convex hull...
128  tetgen_wrapper.set_switches(_switches);
129  tetgen_wrapper.run_tetgen();
130  unsigned int num_elements = tetgen_wrapper.get_numberoftrifaces();
131 
132  // Delete *all* old elements. Yes, we legally delete elements while
133  // iterating over them because no entries from the underlying container
134  // are actually erased.
135  for (auto & elem : this->_mesh.element_ptr_range())
136  this->_mesh.delete_elem (elem);
137 
138  // We just removed any boundary info associated with element faces
139  // or edges, so let's update the boundary id caches.
141 
142  // Add the 2D elements which comprise the convex hull back to the mesh.
143  // Vector that temporarily holds the node labels defining element.
144  unsigned int node_labels[3];
145 
146  for (unsigned int i=0; i<num_elements; ++i)
147  {
148  Elem * elem = new Tri3;
149 
150  // Get node labels associated with this element
151  for (auto j : elem->node_index_range())
152  node_labels[j] = tetgen_wrapper.get_triface_node(i,j);
153 
154  this->assign_nodes_to_elem(node_labels, elem);
155 
156  // Finally, add this element to the mesh.
157  this->_mesh.add_elem(elem);
158  }
159 }
160 
161 
162 
163 
164 
166  double volume_constraint)
167 {
168  // start triangulation method with empty holes list:
169  std::vector<Point> noholes;
170  triangulate_conformingDelaunayMesh_carvehole(noholes, quality_constraint, volume_constraint);
171 }
172 
173 
174 
176  double quality_constraint,
177  double volume_constraint)
178 {
179  // Before calling this function, the Mesh must contain a convex hull
180  // of TRI3 elements which define the boundary.
181  unsigned hull_integrity_check = check_hull_integrity();
182 
183  // Possibly die if hull integrity check failed
184  this->process_hull_integrity_result(hull_integrity_check);
185 
186  // class tetgen_wrapper allows library access on a basic level
187  TetGenWrapper tetgen_wrapper;
188 
189  // Copy Mesh's node points into TetGen data structure
190  this->fill_pointlist(tetgen_wrapper);
191 
192  // >>> fill input structure "tetgenio" with facet data:
193  int facet_num = this->_mesh.n_elem();
194 
195  // allocate memory in "tetgenio" structure:
196  tetgen_wrapper.allocate_facetlist
197  (facet_num, cast_int<int>(holes.size()));
198 
199 
200  // Set up tetgen data structures with existing facet information
201  // from the convex hull.
202  {
203  int insertnum = 0;
204  for (auto & elem : this->_mesh.element_ptr_range())
205  {
206  tetgen_wrapper.allocate_facet_polygonlist(insertnum, 1);
207  tetgen_wrapper.allocate_polygon_vertexlist(insertnum, 0, 3);
208 
209  for (auto j : elem->node_index_range())
210  {
211  // We need to get the sequential index of elem->node_ptr(j), but
212  // it should already be stored in _sequential_to_libmesh_node_map...
213  unsigned libmesh_node_id = elem->node_id(j);
214 
215  // The libmesh node IDs may not be sequential, but can we assume
216  // they are at least in order??? We will do so here.
217  std::vector<unsigned>::iterator node_iter =
220  libmesh_node_id);
221 
222  // Check to see if not found: this could also indicate the sequential
223  // node map is not sorted...
224  if (node_iter == _sequential_to_libmesh_node_map.end())
225  libmesh_error_msg("Global node " << libmesh_node_id << " not found in sequential node map!");
226 
227  int sequential_index = cast_int<int>
229  node_iter));
230 
231  // Debugging:
232  // libMesh::out << "libmesh_node_id=" << libmesh_node_id
233  // << ", sequential_index=" << sequential_index
234  // << std::endl;
235 
236  tetgen_wrapper.set_vertex(insertnum, // facet number
237  0, // polygon (always 0)
238  j, // local vertex index in tetgen input
239  sequential_index);
240  }
241 
242  // Go to next facet in polygonlist
243  insertnum++;
244  }
245  }
246 
247 
248 
249  // fill hole list (if there are holes):
250  if (holes.size() > 0)
251  {
252  unsigned hole_index = 0;
253  for (Point ihole : holes)
254  tetgen_wrapper.set_hole(hole_index++,
255  REAL(ihole(0)),
256  REAL(ihole(1)),
257  REAL(ihole(2)));
258  }
259 
260 
261  // Run TetGen triangulation method
262 
263  // Assemble switches: we append the user's switches (if any) to 'p',
264  // which tetrahedralizes a piecewise linear complex (see definition
265  // in user manual)
266  std::ostringstream oss;
267  oss << "p";
268  oss << _switches;
269 
270  if (quality_constraint != 0)
271  oss << "q" << std::fixed << quality_constraint;
272 
273  if (volume_constraint != 0)
274  oss << "a" << std::fixed << volume_constraint;
275 
276  std::string params = oss.str();
277 
278  tetgen_wrapper.set_switches(params); // TetGen switches: Piecewise linear complex, Quiet mode
279  tetgen_wrapper.run_tetgen();
280 
281  // => nodes:
282  unsigned int old_nodesnum = this->_mesh.n_nodes();
283  REAL x=0., y=0., z=0.;
284  const unsigned int num_nodes = tetgen_wrapper.get_numberofpoints();
285 
286  // Debugging:
287  // libMesh::out << "Original mesh had " << old_nodesnum << " nodes." << std::endl;
288  // libMesh::out << "Reserving space for " << num_nodes << " total nodes." << std::endl;
289 
290  // Reserve space for additional nodes in the node map
291  _sequential_to_libmesh_node_map.reserve(num_nodes);
292 
293  // Add additional nodes to the Mesh.
294  // Original code had i<=num_nodes here (Note: the indexing is:
295  // foo[3*i], [3*i+1], [3*i+2]) But according to the TetGen docs, "In
296  // all cases, the first item in any array is stored starting at
297  // index [0]."
298  for (unsigned int i=old_nodesnum; i<num_nodes; i++)
299  {
300  // Fill in x, y, z values
301  tetgen_wrapper.get_output_node(i, x,y,z);
302 
303  // Catch the node returned by add_point()... this will tell us the ID
304  // assigned by the Mesh.
305  Node * new_node = this->_mesh.add_point ( Point(x,y,z) );
306 
307  // Store this new ID in our sequential-to-libmesh node mapping array
308  _sequential_to_libmesh_node_map.push_back( new_node->id() );
309  }
310 
311  // Debugging:
312  // std::copy(_sequential_to_libmesh_node_map.begin(),
313  // _sequential_to_libmesh_node_map.end(),
314  // std::ostream_iterator<unsigned>(std::cout, " "));
315  // std::cout << std::endl;
316 
317 
318  // => tetrahedra:
319  const unsigned int num_elements = tetgen_wrapper.get_numberoftetrahedra();
320 
321  // Vector that temporarily holds the node labels defining element connectivity.
322  unsigned int node_labels[4];
323 
324  for (unsigned int i=0; i<num_elements; i++)
325  {
326  // TetGen only supports Tet4 elements.
327  Elem * elem = new Tet4;
328 
329  // Fill up the the node_labels vector
330  for (auto j : elem->node_index_range())
331  node_labels[j] = tetgen_wrapper.get_element_node(i,j);
332 
333  // Associate nodes with this element
334  this->assign_nodes_to_elem(node_labels, elem);
335 
336  // Finally, add this element to the mesh
337  this->_mesh.add_elem(elem);
338  }
339 
340  // Delete original convex hull elements. Is there ever a case where
341  // we should not do this?
342  this->delete_2D_hull_elements();
343 }
344 
345 
346 
347 
348 
350 {
351  // fill input structure with point set data:
352  wrapper.allocate_pointlist( this->_mesh.n_nodes() );
353 
354  // Make enough space to store a mapping between the implied sequential
355  // node numbering used in tetgen and libmesh's (possibly) non-sequential
356  // numbering scheme.
359 
360  {
361  unsigned index = 0;
362  for (auto & node : this->_mesh.node_ptr_range())
363  {
364  _sequential_to_libmesh_node_map[index] = node->id();
365  wrapper.set_node(index++,
366  REAL((*node)(0)),
367  REAL((*node)(1)),
368  REAL((*node)(2)));
369  }
370  }
371 }
372 
373 
374 
375 
376 
377 void TetGenMeshInterface::assign_nodes_to_elem(unsigned * node_labels, Elem * elem)
378 {
379  for (auto j : elem->node_index_range())
380  {
381  // Get the mapped node index to ask the Mesh for
382  unsigned mapped_node_id = _sequential_to_libmesh_node_map[ node_labels[j] ];
383 
384  Node * current_node = this->_mesh.node_ptr( mapped_node_id );
385 
386  elem->set_node(j) = current_node;
387  }
388 }
389 
390 
391 
392 
393 
395 {
396  // Check for easy return: if the Mesh is empty (i.e. if
397  // somebody called triangulate_conformingDelaunayMesh on
398  // a Mesh with no elements, then hull integrity check must
399  // fail...
400  if (_mesh.n_elem() == 0)
401  return 3;
402 
403  for (auto & elem : this->_mesh.element_ptr_range())
404  {
405  // Check for proper element type
406  if (elem->type() != TRI3)
407  {
408  //libmesh_error_msg("ERROR: Some of the elements in the original mesh were not TRI3!");
409  return 1;
410  }
411 
412  for (auto neigh : elem->neighbor_ptr_range())
413  {
414  if (neigh == nullptr)
415  {
416  // libmesh_error_msg("ERROR: Non-convex hull, cannot be tetrahedralized.");
417  return 2;
418  }
419  }
420  }
421 
422  // If we made it here, return success!
423  return 0;
424 }
425 
426 
427 
428 
429 
431 {
432  if (result != 0)
433  {
434  libMesh::err << "Error! Conforming Delaunay mesh tetrahedralization requires a convex hull." << std::endl;
435 
436  if (result==1)
437  {
438  libMesh::err << "Non-TRI3 elements were found in the input Mesh. ";
439  libMesh::err << "A constrained Delaunay triangulation requires a convex hull of TRI3 elements." << std::endl;
440  }
441 
442  libmesh_error_msg("Consider calling TetGenMeshInterface::pointset_convexhull() followed by Mesh::find_neighbors() first.");
443  }
444 }
445 
446 
447 
448 
450 {
451  for (auto & elem : this->_mesh.element_ptr_range())
452  {
453  // Check for proper element type. Yes, we legally delete elements while
454  // iterating over them because no entries from the underlying container
455  // are actually erased.
456  if (elem->type() == TRI3)
457  _mesh.delete_elem(elem);
458  }
459 
460  // We just removed any boundary info associated with hull element
461  // edges, so let's update the boundary id caches.
463 }
464 
465 
466 
467 } // namespace libMesh
468 
469 
470 #endif // #ifdef LIBMESH_HAVE_TETGEN
libMesh::TetGenWrapper::get_numberofpoints
int get_numberofpoints()
Definition: mesh_tetgen_wrapper.C:107
libMesh::MeshBase::get_boundary_info
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:132
libMesh::TetGenWrapper
The TetGenWrapper provides an interface for basic access to TetGen data structures and methods.
Definition: mesh_tetgen_wrapper.h:45
libMesh::MeshBase::delete_elem
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
libMesh::TetGenMeshInterface::TetGenMeshInterface
TetGenMeshInterface(UnstructuredMesh &mesh)
Constructor.
Definition: mesh_tetgen_interface.C:38
libMesh::MeshBase::n_elem
virtual dof_id_type n_elem() const =0
libMesh
The libMesh namespace provides an interface to certain functionality in the library.
Definition: factoryfunction.C:55
libMesh::TetGenMeshInterface::fill_pointlist
void fill_pointlist(TetGenWrapper &wrapper)
This function copies nodes from the _mesh into TetGen's pointlist.
Definition: mesh_tetgen_interface.C:349
libMesh::Elem::node_index_range
IntRange< unsigned short > node_index_range() const
Definition: elem.h:2170
libMesh::TetGenMeshInterface::check_hull_integrity
unsigned check_hull_integrity()
This function checks the integrity of the current set of elements in the Mesh to see if they comprise...
Definition: mesh_tetgen_interface.C:394
libMesh::TetGenWrapper::get_element_node
int get_element_node(unsigned i, unsigned j)
Definition: mesh_tetgen_wrapper.C:114
mesh
MeshBase & mesh
Definition: mesh_communication.C:1257
libMesh::MeshBase::node_ptr
virtual const Node * node_ptr(const dof_id_type i) const =0
libMesh::TetGenMeshInterface::triangulate_conformingDelaunayMesh_carvehole
void triangulate_conformingDelaunayMesh_carvehole(const std::vector< Point > &holes, double quality_constraint=0., double volume_constraint=0.)
Method invokes TetGen library to compute a Delaunay tetrahedralization from the nodes point set.
Definition: mesh_tetgen_interface.C:175
libMesh::TetGenMeshInterface::_mesh
UnstructuredMesh & _mesh
Local reference to the mesh we are working with.
Definition: mesh_tetgen_interface.h:151
libMesh::TetGenWrapper::allocate_polygon_vertexlist
void allocate_polygon_vertexlist(unsigned i, unsigned j, int numofvertices)
Allocates memory, sets number of vertices for polygon j, facet i in the TetGen input.
Definition: mesh_tetgen_wrapper.C:307
libMesh::TetGenWrapper::set_hole
void set_hole(unsigned i, REAL x, REAL y, REAL z)
Sets coordinates of hole i in the TetGen input.
Definition: mesh_tetgen_wrapper.C:58
libMesh::TetGenMeshInterface::_sequential_to_libmesh_node_map
std::vector< unsigned > _sequential_to_libmesh_node_map
We should not assume libmesh nodes are numbered sequentially...
Definition: mesh_tetgen_interface.h:160
libMesh::MeshBase::element_ptr_range
virtual SimpleRange< element_iterator > element_ptr_range()=0
libMesh::TetGenWrapper::allocate_pointlist
void allocate_pointlist(int numofpoints)
Allocates memory, sets number of nodes in the TetGen input.
Definition: mesh_tetgen_wrapper.C:136
libMesh::TetGenWrapper::set_vertex
void set_vertex(unsigned i, unsigned j, unsigned k, int nodeindex)
Sets index of ith facet, jth polygon, kth vertex in the TetGen input.
Definition: mesh_tetgen_wrapper.C:326
libMesh::TetGenMeshInterface::process_hull_integrity_result
void process_hull_integrity_result(unsigned result)
This function prints an informative message and crashes based on the output of the check_hull_integri...
Definition: mesh_tetgen_interface.C:430
libMesh::MeshBase::node_ptr_range
virtual SimpleRange< node_iterator > node_ptr_range()=0
libMesh::TetGenMeshInterface::_switches
std::string _switches
Parameter controlling the behaviour of tetgen.
Definition: mesh_tetgen_interface.h:171
libMesh::TetGenWrapper::set_node
void set_node(unsigned i, REAL x, REAL y, REAL z)
Sets coordinates of point i in the TetGen input.
Definition: mesh_tetgen_wrapper.C:48
libMesh::TetGenWrapper::get_numberoftetrahedra
int get_numberoftetrahedra()
Definition: mesh_tetgen_wrapper.C:93
libMesh::REAL
Real REAL
Definition: mesh_triangle_wrapper.h:44
libMesh::TetGenMeshInterface::delete_2D_hull_elements
void delete_2D_hull_elements()
Delete original convex hull elements from the Mesh after performing a Delaunay tetrahedralization.
Definition: mesh_tetgen_interface.C:449
libMesh::Point
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:38
libMesh::TRI3
Definition: enum_elem_type.h:39
libMesh::Utility::binary_find
ForwardIterator binary_find(ForwardIterator first, ForwardIterator last, const T &value)
The STL provides std::binary_search() which returns true or false depending on whether the searched-f...
Definition: utility.h:183
libMesh::Node
A Node is like a Point, but with more information.
Definition: node.h:52
libMesh::Tet4
The Tet4 is an element in 3D composed of 4 nodes.
Definition: cell_tet4.h:53
libMesh::TetGenWrapper::get_output_node
void get_output_node(unsigned i, REAL &x, REAL &y, REAL &z)
Definition: mesh_tetgen_wrapper.C:76
libMesh::BoundaryInfo::regenerate_id_sets
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
Definition: boundary_info.C:159
libMesh::Elem::set_node
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:2059
libMesh::TetGenMeshInterface::assign_nodes_to_elem
void assign_nodes_to_elem(unsigned *node_labels, Elem *elem)
Assigns the node IDs contained in the 'node_labels' array to 'elem'.
Definition: mesh_tetgen_interface.C:377
libMesh::UnstructuredMesh
The UnstructuredMesh class is derived from the MeshBase class.
Definition: unstructured_mesh.h:48
libMesh::MeshBase::n_nodes
virtual dof_id_type n_nodes() const =0
libMesh::TetGenWrapper::set_switches
void set_switches(const std::string &s)
Method to set TetGen commandline switches -p Tetrahedralizes a piecewise linear complex (....
Definition: mesh_tetgen_wrapper.C:155
distance
Real distance(const Point &p)
Definition: subdomains_ex3.C:50
libMesh::TetGenWrapper::get_triface_node
int get_triface_node(unsigned i, unsigned j)
Definition: mesh_tetgen_wrapper.C:121
libMesh::TetGenWrapper::get_numberoftrifaces
int get_numberoftrifaces()
Definition: mesh_tetgen_wrapper.C:100
libMesh::TetGenMeshInterface::triangulate_conformingDelaunayMesh
void triangulate_conformingDelaunayMesh(double quality_constraint=0., double volume_constraint=0.)
Method invokes TetGen library to compute a Delaunay tetrahedralization from the nodes point set.
Definition: mesh_tetgen_interface.C:165
libMesh::MeshBase::add_elem
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
libMesh::DofObject::id
dof_id_type id() const
Definition: dof_object.h:767
libMesh::Tri3
The Tri3 is an element in 2D composed of 3 nodes.
Definition: face_tri3.h:56
libMesh::TetGenMeshInterface::triangulate_pointset
void triangulate_pointset()
Method invokes TetGen library to compute a Delaunay tetrahedralization from the nodes point set.
Definition: mesh_tetgen_interface.C:59
libMesh::TetGenMeshInterface::set_switches
void set_switches(const std::string &)
Method to set switches to tetgen, allowing for different behaviours.
Definition: mesh_tetgen_interface.C:45
libMesh::Elem
This is the base class from which all geometric element types are derived.
Definition: elem.h:100
libMesh::MeshBase::add_point
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
libMesh::err
OStreamProxy err
libMesh::TetGenWrapper::allocate_facet_polygonlist
void allocate_facet_polygonlist(unsigned i, int numofpolygons)
Allocates memory, sets number of polygons for facet i in the TetGen input.
Definition: mesh_tetgen_wrapper.C:277
libMesh::TetGenWrapper::allocate_facetlist
void allocate_facetlist(int numoffacets, int numofholes)
Allocates memory, sets number of facets, holes in the TetGen input.
Definition: mesh_tetgen_wrapper.C:208
libMesh::TetGenWrapper::run_tetgen
void run_tetgen()
Starts the triangulation.
Definition: mesh_tetgen_wrapper.C:176
libMesh::TetGenMeshInterface::pointset_convexhull
void pointset_convexhull()
Method invokes TetGen library to compute a Delaunay tetrahedralization from the nodes point set.
Definition: mesh_tetgen_interface.C:113