$extrastylesheet
mesh_tetgen_interface.C
Go to the documentation of this file.
00001 // The libMesh Finite Element Library.
00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
00003 
00004 // This library is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public
00006 // License as published by the Free Software Foundation; either
00007 // version 2.1 of the License, or (at your option) any later version.
00008 
00009 // This library is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // Lesser General Public License for more details.
00013 
00014 // You should have received a copy of the GNU Lesser General Public
00015 // License along with this library; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 #include "libmesh/libmesh_config.h"
00019 #ifdef LIBMESH_HAVE_TETGEN
00020 
00021 
00022 // C++ includes
00023 #include <sstream>
00024 
00025 // Local includes
00026 #include "libmesh/cell_tet4.h"
00027 #include "libmesh/face_tri3.h"
00028 #include "libmesh/unstructured_mesh.h"
00029 #include "libmesh/mesh_tetgen_interface.h"
00030 #include "libmesh/utility.h" // binary_find
00031 #include "libmesh/mesh_tetgen_wrapper.h"
00032 
00033 namespace libMesh
00034 {
00035 
00036 //----------------------------------------------------------------------
00037 // TetGenMeshInterface class members
00038 TetGenMeshInterface::TetGenMeshInterface (UnstructuredMesh& mesh) :
00039   _mesh(mesh),
00040   _serializer(_mesh)
00041 {
00042 }
00043 
00044 
00045 
00046 void TetGenMeshInterface::triangulate_pointset ()
00047 {
00048   // class tetgen_wrapper allows library access on a basic level:
00049   TetGenWrapper tetgen_wrapper;
00050 
00051   // fill input structure with point set data:
00052   this->fill_pointlist(tetgen_wrapper);
00053 
00054   // Run TetGen triangulation method:
00055   // Q = quiet, no terminal output
00056   // V = verbose, more terminal output
00057   // Note: if no switch is used, the input must be a list of 3D points
00058   // (.node file) and the Delaunay tetrahedralization of this point set
00059   // will be generated.
00060 
00061   // Can we apply quality and volume constraints in
00062   // triangulate_pointset()?.  On at least one test problem,
00063   // specifying any quality or volume constraints here causes tetgen
00064   // to segfault down in the insphere method: a NULL pointer is passed
00065   // to the routine.
00066   std::ostringstream oss;
00067   oss << "Q"; // quiet operation
00068   // oss << "V"; // verbose operation
00069   //oss  << "q" << std::fixed << 2.0;  // quality constraint
00070   //oss  << "a" << std::fixed << 100.; // volume constraint
00071   tetgen_wrapper.set_switches(oss.str());
00072 
00073   // Run tetgen
00074   tetgen_wrapper.run_tetgen();
00075 
00076   // save elements to mesh structure, nodes will not be changed:
00077   const unsigned int num_elements   = tetgen_wrapper.get_numberoftetrahedra();
00078 
00079   // Vector that temporarily holds the node labels defining element.
00080   unsigned int node_labels[4];
00081 
00082   for (unsigned int i=0; i<num_elements; ++i)
00083     {
00084       Elem* elem = new Tet4;
00085 
00086       // Get the nodes associated with this element
00087       for (unsigned int j=0; j<elem->n_nodes(); ++j)
00088         node_labels[j] = tetgen_wrapper.get_element_node(i,j);
00089 
00090       // Associate the nodes with this element
00091       this->assign_nodes_to_elem(node_labels, elem);
00092 
00093       // Finally, add this element to the mesh.
00094       this->_mesh.add_elem(elem);
00095     }
00096 }
00097 
00098 
00099 
00100 void TetGenMeshInterface::pointset_convexhull ()
00101 {
00102   // class tetgen_wrapper allows library access on a basic level
00103   TetGenWrapper tetgen_wrapper;
00104 
00105   // Copy Mesh's node points into TetGen data structure
00106   this->fill_pointlist(tetgen_wrapper);
00107 
00108   // Run TetGen triangulation method:
00109   // Q = quiet, no terminal output
00110   // Note: if no switch is used, the input must be a list of 3D points
00111   // (.node file) and the Delaunay tetrahedralization of this point set
00112   // will be generated.  In this particular function, we are throwing
00113   // away the tetrahedra generated by TetGen, and keeping only the
00114   // convex hull...
00115   tetgen_wrapper.set_switches("Q");
00116   tetgen_wrapper.run_tetgen();
00117   unsigned int num_elements   = tetgen_wrapper.get_numberoftrifaces();
00118 
00119   // Delete *all* old elements.  Yes, we legally delete elements while
00120   // iterating over them because no entries from the underlying container
00121   // are actually erased.
00122   {
00123     MeshBase::element_iterator       it  = this->_mesh.elements_begin();
00124     const MeshBase::element_iterator end = this->_mesh.elements_end();
00125     for ( ; it != end; ++it)
00126       this->_mesh.delete_elem (*it);
00127   }
00128 
00129 
00130   // Add the 2D elements which comprise the convex hull back to the mesh.
00131   // Vector that temporarily holds the node labels defining element.
00132   unsigned int node_labels[3];
00133 
00134   for (unsigned int i=0; i<num_elements; ++i)
00135     {
00136       Elem* elem = new Tri3;
00137 
00138       // Get node labels associated with this element
00139       for (unsigned int j=0; j<elem->n_nodes(); ++j)
00140         node_labels[j] = tetgen_wrapper.get_triface_node(i,j);
00141 
00142       this->assign_nodes_to_elem(node_labels, elem);
00143 
00144       // Finally, add this element to the mesh.
00145       this->_mesh.add_elem(elem);
00146     }
00147 }
00148 
00149 
00150 
00151 
00152 
00153 void TetGenMeshInterface::triangulate_conformingDelaunayMesh (double quality_constraint,
00154                                                               double volume_constraint)
00155 {
00156   // start triangulation method with empty holes list:
00157   std::vector<Point> noholes;
00158   triangulate_conformingDelaunayMesh_carvehole(noholes, quality_constraint, volume_constraint);
00159 }
00160 
00161 
00162 
00163 void TetGenMeshInterface::triangulate_conformingDelaunayMesh_carvehole  (const std::vector<Point>& holes,
00164                                                                          double quality_constraint,
00165                                                                          double volume_constraint)
00166 {
00167   // Before calling this function, the Mesh must contain a convex hull
00168   // of TRI3 elements which define the boundary.
00169   unsigned hull_integrity_check = check_hull_integrity();
00170 
00171   // Possibly die if hull integrity check failed
00172   this->process_hull_integrity_result(hull_integrity_check);
00173 
00174   // class tetgen_wrapper allows library access on a basic level
00175   TetGenWrapper tetgen_wrapper;
00176 
00177   // Copy Mesh's node points into TetGen data structure
00178   this->fill_pointlist(tetgen_wrapper);
00179 
00180   // >>> fill input structure "tetgenio" with facet data:
00181   int facet_num = this->_mesh.n_elem();
00182 
00183   // allocate memory in "tetgenio" structure:
00184   tetgen_wrapper.allocate_facetlist
00185     (facet_num, cast_int<int>(holes.size()));
00186 
00187 
00188   // Set up tetgen data structures with existing facet information
00189   // from the convex hull.
00190   {
00191     int insertnum = 0;
00192     MeshBase::element_iterator it        = this->_mesh.elements_begin();
00193     const MeshBase::element_iterator end = this->_mesh.elements_end();
00194     for (; it != end ; ++it)
00195       {
00196         tetgen_wrapper.allocate_facet_polygonlist(insertnum, 1);
00197         tetgen_wrapper.allocate_polygon_vertexlist(insertnum, 0, 3);
00198 
00199         Elem* elem = *it;
00200 
00201         for (unsigned int j=0; j<elem->n_nodes(); ++j)
00202           {
00203             // We need to get the sequential index of elem->get_node(j), but
00204             // it should already be stored in _sequential_to_libmesh_node_map...
00205             unsigned libmesh_node_id = elem->node(j);
00206 
00207             // The libmesh node IDs may not be sequential, but can we assume
00208             // they are at least in order???  We will do so here.
00209             std::vector<unsigned>::iterator node_iter =
00210               Utility::binary_find(_sequential_to_libmesh_node_map.begin(),
00211                                    _sequential_to_libmesh_node_map.end(),
00212                                    libmesh_node_id);
00213 
00214             // Check to see if not found: this could also indicate the sequential
00215             // node map is not sorted...
00216             if (node_iter == _sequential_to_libmesh_node_map.end())
00217               libmesh_error_msg("Global node " << libmesh_node_id << " not found in sequential node map!");
00218 
00219             int sequential_index = cast_int<int>
00220               (std::distance(_sequential_to_libmesh_node_map.begin(),
00221                              node_iter));
00222 
00223             // Debugging:
00224             //    libMesh::out << "libmesh_node_id=" << libmesh_node_id
00225             //         << ", sequential_index=" << sequential_index
00226             //         << std::endl;
00227 
00228             tetgen_wrapper.set_vertex(insertnum, // facet number
00229                                       0,         // polygon (always 0)
00230                                       j,         // local vertex index in tetgen input
00231                                       sequential_index);
00232           }
00233 
00234         // Go to next facet in polygonlist
00235         insertnum++;
00236       }
00237   }
00238 
00239 
00240 
00241   // fill hole list (if there are holes):
00242   if (holes.size() > 0)
00243     {
00244       std::vector<Point>::const_iterator ihole;
00245       unsigned hole_index = 0;
00246       for (ihole=holes.begin(); ihole!=holes.end(); ++ihole)
00247         tetgen_wrapper.set_hole(hole_index++, (*ihole)(0), (*ihole)(1), (*ihole)(2));
00248     }
00249 
00250 
00251   // Run TetGen triangulation method:
00252   // p = tetrahedralizes a piecewise linear complex (see definition in user manual)
00253   // Q = quiet, no terminal output
00254   // q = specify a minimum radius/edge ratio
00255   // a = tetrahedron volume constraint
00256 
00257   // assemble switches:
00258   std::ostringstream oss; // string holding switches
00259   oss << "pQ";
00260 
00261   if (quality_constraint != 0)
00262     oss << "q" << std::fixed << quality_constraint;
00263 
00264   if (volume_constraint != 0)
00265     oss << "a" << std::fixed << volume_constraint;
00266 
00267   std::string params = oss.str();
00268 
00269   tetgen_wrapper.set_switches(params); // TetGen switches: Piecewise linear complex, Quiet mode
00270   tetgen_wrapper.run_tetgen();
00271 
00272   // => nodes:
00273   unsigned int old_nodesnum = this->_mesh.n_nodes();
00274   REAL x=0., y=0., z=0.;
00275   const unsigned int num_nodes = tetgen_wrapper.get_numberofpoints();
00276 
00277   // Debugging:
00278   // libMesh::out << "Original mesh had " << old_nodesnum << " nodes." << std::endl;
00279   // libMesh::out << "Reserving space for " << num_nodes << " total nodes." << std::endl;
00280 
00281   // Reserve space for additional nodes in the node map
00282   _sequential_to_libmesh_node_map.reserve(num_nodes);
00283 
00284   // Add additional nodes to the Mesh.
00285   // Original code had i<=num_nodes here (Note: the indexing is:
00286   // foo[3*i], [3*i+1], [3*i+2]) But according to the TetGen docs, "In
00287   // all cases, the first item in any array is stored starting at
00288   // index [0]."
00289   for (unsigned int i=old_nodesnum; i<num_nodes; i++)
00290     {
00291       // Fill in x, y, z values
00292       tetgen_wrapper.get_output_node(i, x,y,z);
00293 
00294       // Catch the node returned by add_point()... this will tell us the ID
00295       // assigned by the Mesh.
00296       Node* new_node = this->_mesh.add_point ( Point(x,y,z) );
00297 
00298       // Store this new ID in our sequential-to-libmesh node mapping array
00299       _sequential_to_libmesh_node_map.push_back( new_node->id() );
00300     }
00301 
00302   // Debugging:
00303   //  std::copy(_sequential_to_libmesh_node_map.begin(),
00304   //    _sequential_to_libmesh_node_map.end(),
00305   //    std::ostream_iterator<unsigned>(std::cout, " "));
00306   //  std::cout << std::endl;
00307 
00308 
00309   // => tetrahedra:
00310   const unsigned int num_elements = tetgen_wrapper.get_numberoftetrahedra();
00311 
00312   // Vector that temporarily holds the node labels defining element connectivity.
00313   unsigned int node_labels[4];
00314 
00315   for (unsigned int i=0; i<num_elements; i++)
00316     {
00317       // TetGen only supports Tet4 elements.
00318       Elem* elem = new Tet4;
00319 
00320       // Fill up the the node_labels vector
00321       for (unsigned int j=0; j<elem->n_nodes(); j++)
00322         node_labels[j] = tetgen_wrapper.get_element_node(i,j);
00323 
00324       // Associate nodes with this element
00325       this->assign_nodes_to_elem(node_labels, elem);
00326 
00327       // Finally, add this element to the mesh
00328       this->_mesh.add_elem(elem);
00329     }
00330 
00331   // Delete original convex hull elements.  Is there ever a case where
00332   // we should not do this?
00333   this->delete_2D_hull_elements();
00334 }
00335 
00336 
00337 
00338 
00339 
00340 void TetGenMeshInterface::fill_pointlist(TetGenWrapper& wrapper)
00341 {
00342   // fill input structure with point set data:
00343   wrapper.allocate_pointlist( this->_mesh.n_nodes() );
00344 
00345   // Make enough space to store a mapping between the implied sequential
00346   // node numbering used in tetgen and libmesh's (possibly) non-sequential
00347   // numbering scheme.
00348   _sequential_to_libmesh_node_map.clear();
00349   _sequential_to_libmesh_node_map.resize( this->_mesh.n_nodes() );
00350 
00351   {
00352     unsigned index = 0;
00353     MeshBase::node_iterator it  = this->_mesh.nodes_begin();
00354     const MeshBase::node_iterator end = this->_mesh.nodes_end();
00355     for ( ; it != end; ++it)
00356       {
00357         _sequential_to_libmesh_node_map[index] = (*it)->id();
00358         wrapper.set_node(index++, (**it)(0), (**it)(1), (**it)(2));
00359       }
00360   }
00361 }
00362 
00363 
00364 
00365 
00366 
00367 void TetGenMeshInterface::assign_nodes_to_elem(unsigned* node_labels, Elem* elem)
00368 {
00369   for (unsigned int j=0; j<elem->n_nodes(); ++j)
00370     {
00371       // Get the mapped node index to ask the Mesh for
00372       unsigned mapped_node_id = _sequential_to_libmesh_node_map[ node_labels[j] ];
00373 
00374       // Parallel mesh can return NULL pointers, this is bad...
00375       Node* current_node = this->_mesh.node_ptr( mapped_node_id );
00376 
00377       if (current_node == NULL)
00378         libmesh_error_msg("Error! Mesh returned NULL node pointer!");
00379 
00380       elem->set_node(j) = current_node;
00381     }
00382 }
00383 
00384 
00385 
00386 
00387 
00388 unsigned TetGenMeshInterface::check_hull_integrity()
00389 {
00390   // Check for easy return: if the Mesh is empty (i.e. if
00391   // somebody called triangulate_conformingDelaunayMesh on
00392   // a Mesh with no elements, then hull integrity check must
00393   // fail...
00394   if (_mesh.n_elem() == 0)
00395     return 3;
00396 
00397   MeshBase::element_iterator it        = this->_mesh.elements_begin();
00398   const MeshBase::element_iterator end = this->_mesh.elements_end();
00399 
00400   for (; it != end ; ++it)
00401     {
00402       Elem* elem = *it;
00403 
00404       // Check for proper element type
00405       if (elem->type() != TRI3)
00406         {
00407           //libmesh_error_msg("ERROR: Some of the elements in the original mesh were not TRI3!");
00408           return 1;
00409         }
00410 
00411       for (unsigned int i=0; i<elem->n_neighbors(); ++i)
00412         {
00413           if (elem->neighbor(i) == NULL)
00414             {
00415               // libmesh_error_msg("ERROR: Non-convex hull, cannot be tetrahedralized.");
00416               return 2;
00417             }
00418         }
00419     }
00420 
00421   // If we made it here, return success!
00422   return 0;
00423 }
00424 
00425 
00426 
00427 
00428 
00429 void TetGenMeshInterface::process_hull_integrity_result(unsigned result)
00430 {
00431   if (result != 0)
00432     {
00433       libMesh::err << "Error! Conforming Delaunay mesh tetrahedralization requires a convex hull." << std::endl;
00434 
00435       if (result==1)
00436         {
00437           libMesh::err << "Non-TRI3 elements were found in the input Mesh.  ";
00438           libMesh::err << "A constrained Delaunay triangulation requires a convex hull of TRI3 elements." << std::endl;
00439         }
00440 
00441       libmesh_error_msg("Consider calling TetGenMeshInterface::pointset_convexhull() followed by Mesh::find_neighbors() first.");
00442     }
00443 }
00444 
00445 
00446 
00447 
00448 void TetGenMeshInterface::delete_2D_hull_elements()
00449 {
00450   MeshBase::element_iterator it        = this->_mesh.elements_begin();
00451   const MeshBase::element_iterator end = this->_mesh.elements_end();
00452 
00453   for (; it != end ; ++it)
00454     {
00455       Elem* elem = *it;
00456 
00457       // Check for proper element type. Yes, we legally delete elements while
00458       // iterating over them because no entries from the underlying container
00459       // are actually erased.
00460       if (elem->type() == TRI3)
00461         _mesh.delete_elem(elem);
00462     }
00463 }
00464 
00465 
00466 
00467 } // namespace libMesh
00468 
00469 
00470 #endif // #ifdef LIBMESH_HAVE_TETGEN