$extrastylesheet
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