56 #ifdef RINGMESH_WITH_TETGEN 57 class RINGMESH_API TetraGen_TetGen final :
public TetraGen
60 TetraGen_TetGen( GeoModel3D& geomodel, index_t region_id )
61 : TetraGen( geomodel, region_id )
65 bool do_tetrahedralize(
bool refine )
final 68 builder_.geometry.create_region_builder( output_region_ );
69 tetrahedralize_mesh_tetgen(
70 *mesh3D_builder.get(), tetmesh_constraint_, refine, 1.0 );
78 void start_redirect( fpos_t& pos, FILE* out,
int& fd )
80 #ifndef RINGMESH_DEBUG 84 fd = _dup( fileno( out ) );
85 freopen(
"nul",
"w", out );
87 fd = dup( fileno( out ) );
88 FILE* f = freopen(
"/dev/null",
"w", out );
94 void stop_redirect( fpos_t& pos, FILE* out,
int& fd )
96 #ifndef RINGMESH_DEBUG 102 _dup2( fd, fileno( out ) );
104 dup2( fd, fileno( out ) );
108 fsetpos( out, &pos );
112 class RINGMESH_API TetraGen_MG_Tetra final :
public TetraGen
115 TetraGen_MG_Tetra( GeoModel3D& geomodel, index_t region_id )
116 : TetraGen( geomodel, region_id )
120 virtual ~TetraGen_MG_Tetra()
124 start_redirect( pos, stdout, fd );
127 start_redirect( pos_err, stderr, fd_err );
129 tetra_regain_mesh( tms_, mesh_output_ );
130 tetra_session_delete( tms_ );
131 mesh_delete( mesh_input_ );
132 context_delete( context_ );
134 stop_redirect( pos, stdout, fd );
135 stop_redirect( pos_err, stderr, fd_err );
138 bool do_tetrahedralize(
bool refine )
final 142 start_redirect( pos, stdout, fd );
145 start_redirect( pos_err, stderr, fd_err );
147 initialize_mgtetra_variables();
149 set_mesh_in_mgtetra();
151 set_meshing_parameters();
153 generate_mesh( refine );
154 initialize_ringmesh_storage();
155 write_vertices_in_ringmesh_data_structure();
156 write_tet_in_ringmesh_data_structure();
158 stop_redirect( pos, stdout, fd );
159 stop_redirect( pos_err, stderr, fd_err );
164 static status_t my_message_cb( message_t* msg,
void* user_data )
170 message_get_description( msg, &desc );
171 message_get_number( msg, &e );
177 else if( e == MESHGEMS_TETRA_CODE( -5110 ) )
179 message_get_integer_data( msg, 1, 4, ibuff );
181 "two surface edges are intersecting : ", ibuff[0],
" ",
182 ibuff[1],
" intersects ", ibuff[2],
" ", ibuff[3] );
184 else if( e == MESHGEMS_TETRA_CODE( -5120 ) )
186 message_get_integer_data( msg, 1, 5, ibuff );
188 "surface edge intersects a surface face : ", ibuff[0],
" ",
189 ibuff[1],
" intersects ", ibuff[2],
" ", ibuff[3],
" ",
192 else if( e == MESHGEMS_TETRA_CODE( -5150 ) )
194 message_get_integer_data( msg, 1, 4, ibuff );
196 "boundary point inside a surface face : ", ibuff[0],
" in ",
197 ibuff[1],
" ", ibuff[2],
" ", ibuff[3] );
199 else if( e == MESHGEMS_TETRA_CODE( 5200 ) )
201 message_get_integer_data( msg, 1, 3, ibuff );
202 Logger::err(
"TetraGen",
"duplicated face : ", ibuff[0],
" ",
203 ibuff[1],
" ", ibuff[2],
" ", ibuff[3] );
205 else if( e == MESHGEMS_TETRA_CODE( -5621 ) )
207 message_get_integer_data( msg, 1, 4, ibuff );
208 message_get_real_data( msg, 1, 1, rbuff );
209 Logger::err(
"TetraGen",
"degenerated face : face ", ibuff[0],
210 " (", ibuff[1],
", ", ibuff[2],
", ", ibuff[3],
211 ") with small inradius = ", rbuff[0] );
213 else if( e == MESHGEMS_TETRA_CODE( -5820 ) )
215 message_get_integer_data( msg, 1, 2, ibuff );
216 Logger::err(
"TetraGen",
"edge bounding a hole : ", ibuff[0],
219 else if( e == MESHGEMS_TETRA_CODE( 8423 ) )
221 message_get_integer_data( msg, 1, 3, ibuff );
223 "constrained face cannot be enforced : ", ibuff[0],
" ",
224 ibuff[1],
" ", ibuff[2] );
226 else if( e == MESHGEMS_TETRA_CODE( 8441 ) )
228 message_get_integer_data( msg, 1, 2, ibuff );
230 "constrained edge cannot be enforced : ", ibuff[0],
" ",
235 Logger::err(
"TetraGen",
"Error message not directly handle" );
236 Logger::err(
"TetraGen",
"Error(", e,
") : ", desc );
242 context_t* context_{
nullptr };
243 mesh_t* mesh_input_{
nullptr };
244 mesh_t* mesh_output_{
nullptr };
245 tetra_session_t* tms_{
nullptr };
246 index_t starting_index_{ 1 };
249 void initialize_ringmesh_storage()
251 tetra_get_mesh( tms_, &mesh_output_ );
252 signed_index_t nb_points = 0;
253 mesh_get_vertex_count( mesh_output_, &nb_points );
254 signed_index_t nb_tets = 0;
255 mesh_get_tetrahedron_count( mesh_output_, &nb_tets );
256 initialize_storage( nb_points, nb_tets );
259 void initialize_mgtetra_variables()
261 context_ = context_new();
262 mesh_input_ = mesh_new_in_memory( context_ );
265 void set_mesh_in_mgtetra()
272 bool generate_mesh(
bool refine )
274 if( !create_boundary_mesh() )
280 return refine_mesh();
287 mesh_set_vertex_count(
288 mesh_input_, tetmesh_constraint_.vertices.nb() );
289 for(
auto p : range( tetmesh_constraint_.vertices.nb() ) )
291 mesh_set_vertex_coordinates( mesh_input_, p + starting_index_,
292 tetmesh_constraint_.vertices.point_ptr( p ) );
298 mesh_set_edge_count( mesh_input_, tetmesh_constraint_.edges.nb() );
299 for(
auto e : range( tetmesh_constraint_.edges.nb() ) )
301 meshgems_integer edge_indices[2];
303 tetmesh_constraint_.edges.vertex( e, 0 ) + starting_index_;
305 tetmesh_constraint_.edges.vertex( e, 1 ) + starting_index_;
306 mesh_set_edge_vertices(
307 mesh_input_, e + starting_index_, edge_indices );
313 mesh_set_triangle_count(
314 mesh_input_, tetmesh_constraint_.facets.nb() );
315 for(
auto t : range( tetmesh_constraint_.facets.nb() ) )
317 meshgems_integer triangle_indices[3];
318 triangle_indices[0] =
319 tetmesh_constraint_.facets.vertex( t, 0 ) + starting_index_;
320 triangle_indices[1] =
321 tetmesh_constraint_.facets.vertex( t, 1 ) + starting_index_;
322 triangle_indices[2] =
323 tetmesh_constraint_.facets.vertex( t, 2 ) + starting_index_;
324 mesh_set_triangle_vertices(
325 mesh_input_, t + starting_index_, triangle_indices );
329 void set_meshing_parameters()
331 tms_ = tetra_session_new( context_ );
332 tetra_set_surface_mesh( tms_, mesh_input_ );
333 tetra_set_param( tms_,
"verbose",
"4" );
334 tetra_set_param( tms_,
"components",
"all" );
335 tetra_set_param( tms_,
"optimisation_level",
"standard" );
336 tetra_set_param( tms_,
"gradation",
"1.1" );
337 tetra_set_param( tms_,
"pthreads_mode",
"aggressive" );
338 tetra_set_param( tms_,
"max_number_of_threads",
"8" );
339 tetra_set_param( tms_,
"max_error_count",
"5" );
342 bool create_boundary_mesh()
344 status_t ret = tetra_mesh_boundary( tms_ );
345 if( ret != STATUS_OK )
348 "Encountered a problem while meshing boundary..." );
356 status_t ret = tetra_insert_volume_vertices( tms_ );
357 if( ret != STATUS_OK )
360 "Encountered a problem while meshing inside..." );
363 ret = tetra_optimise_volume_regular( tms_ );
364 if( ret != STATUS_OK )
367 "Encountered a problem while meshing inside..." );
373 void write_vertices_in_ringmesh_data_structure() final
375 parallel_for( region_->nb_vertices(), [
this]( index_t v ) {
377 mesh_get_vertex_coordinates(
378 mesh_output_, v + starting_index_, point );
379 set_point( v, point );
383 void write_tet_in_ringmesh_data_structure() final
385 parallel_for( region_->nb_mesh_elements(), [
this]( index_t t ) {
387 mesh_get_tetrahedron_vertices(
388 mesh_output_, t + starting_index_, tet );
390 for(
auto v : range( 4 ) )
392 tet[v] -= starting_index_;
396 builder_->geometry.compute_region_adjacencies( output_region_ );
398 void set_point( index_t index,
const double* point )
401 vec3 vertex( point );
402 builder_->geometry.set_mesh_entity_vertex(
407 void set_tetra( index_t tetra_index,
int* vertex_indices )
409 std::vector< index_t > corners( 4 );
410 for(
auto v : range( 4 ) )
412 index_t vertex_id =
static_cast< index_t
>( vertex_indices[v] );
413 corners[v] = vertex_id;
415 builder_->geometry.set_region_element_geometry(
416 output_region_, tetra_index, corners );
419 void initialize_storage( index_t nb_points, index_t nb_tets )
422 builder_->geometry.delete_mesh_entity_mesh( region_id );
423 builder_->geometry.create_mesh_entity_vertices(
424 region_id, nb_points );
425 builder_->geometry.create_region_cells(
426 output_region_, GEO::MESH_TET, nb_tets );
432 GeoModel3D& M, index_t region_id,
const std::string& algo_name )
437 #ifdef RINGMESH_WITH_TETGEN 439 "TetraGen",
"Could not create TetraGen mesher: ", algo_name );
440 Logger::warn(
"TetraGen",
"Falling back to TetGen mode" );
441 mesher.reset(
new TetraGen_TetGen( M, region_id ) );
443 Logger::err(
"I/O",
"Currently supported mesher are: " );
449 "TetraGen",
"Could not create TetraGen mesher: ", algo_name );
456 const Region3D& region,
const WellGroup3D* wells )
459 index_t nb_surfaces = region_->nb_boundaries();
460 std::vector< const GeoModelMeshEntity3D* > unique_surfaces;
461 unique_surfaces.reserve( nb_surfaces );
462 std::vector< index_t > surface_id;
463 surface_id.reserve( nb_surfaces );
464 index_t nb_surface_vertices{ 0 }, nb_polygons{ 0 };
465 for(
auto s :
range( nb_surfaces ) )
467 const Surface3D& surface = region_->boundary( s );
468 if(
contains( surface_id, surface.index() ) )
472 nb_surface_vertices += surface.nb_vertices();
473 nb_polygons += surface.nb_mesh_elements();
474 surface_id.push_back( surface.index() );
475 unique_surfaces.push_back( &surface );
478 std::vector< vec3 > region_surfaces_and_wells_vertices;
479 std::vector< std::vector< Edge3D > > well_edges;
480 index_t nb_region_vertices{ region.nb_vertices() };
481 index_t nb_well_vertices{ 0 };
482 if( wells !=
nullptr )
484 for(
const auto& edges : well_edges )
486 nb_well_vertices += 2 * edges.size();
489 region_surfaces_and_wells_vertices.reserve(
490 nb_surface_vertices + nb_region_vertices + nb_well_vertices );
493 for(
const GeoModelMeshEntity3D*& surface : unique_surfaces )
495 for(
auto v :
range( surface->nb_vertices() ) )
497 region_surfaces_and_wells_vertices.push_back(
498 surface->vertex( v ) );
503 for(
auto v :
range( nb_region_vertices ) )
505 region_surfaces_and_wells_vertices.push_back( region.vertex( v ) );
509 if( wells !=
nullptr )
511 wells->get_region_edges( region.index(), well_edges );
512 for(
const auto& edges : well_edges )
514 for(
const auto& edge : edges )
516 region_surfaces_and_wells_vertices.push_back(
518 region_surfaces_and_wells_vertices.push_back(
524 NNSearch3D nn_search( region_surfaces_and_wells_vertices );
525 std::vector< index_t > unique_indices;
526 std::vector< vec3 > unique_points;
527 std::tie( std::ignore, unique_indices, unique_points ) =
528 nn_search.get_colocated_index_mapping_and_unique_points(
529 region.geomodel().epsilon() );
531 index_t starting_index = tetmesh_constraint_.vertices.create_vertices(
532 unique_points.size() );
534 tetmesh_constraint_.vertices.point_ptr( starting_index ),
535 unique_points.data()->data(),
536 3 *
sizeof(
double ) * unique_points.size() );
537 if( !well_edges.empty() )
539 index_t nb_well_edges{ 0 };
540 for(
const auto& edges : well_edges )
542 nb_well_edges += edges.size();
544 tetmesh_constraint_.edges.create_edges( nb_well_edges );
545 GEO::Attribute< index_t > edge_region(
546 tetmesh_constraint_.edges.attributes(),
548 index_t cur_vertex_id{ nb_surface_vertices };
549 index_t cur_edge{ 0 };
550 for(
auto w :
range( well_edges.size() ) )
552 for(
auto e :
range( well_edges[w].size() ) )
555 tetmesh_constraint_.edges.set_vertex( cur_edge, 0,
556 starting_index + unique_indices[cur_vertex_id++] );
557 tetmesh_constraint_.edges.set_vertex( cur_edge, 1,
558 starting_index + unique_indices[cur_vertex_id++] );
559 edge_region[cur_edge++] = w;
564 index_t offset_vertices{ 0 };
565 index_t offset_polygons{ 0 };
566 tetmesh_constraint_.facets.create_triangles( nb_polygons );
567 GEO::Attribute< index_t > surface_region(
568 tetmesh_constraint_.facets.attributes(),
570 for(
const GeoModelMeshEntity3D*& surface : unique_surfaces )
572 for(
auto t :
range( surface->nb_mesh_elements() ) )
575 for(
auto v :
range( 3 ) )
577 tetmesh_constraint_.facets.set_vertex( offset_polygons + t,
581 + surface->mesh_element_vertex_index(
584 surface_region[offset_polygons + t] = surface->index();
586 offset_vertices += surface->nb_vertices();
587 offset_polygons += surface->nb_mesh_elements();
589 tetmesh_constraint_.facets.connect();
599 tetmesh_constraint_.vertices.create_vertices( points.size() );
600 GEO::Memory::copy( tetmesh_constraint_.vertices.point_ptr( start ),
601 points.front().data(), points.size() * 3 *
sizeof(
double ) );
606 bool result = do_tetrahedralize( refine );
609 builder_.geometry.clear_geomodel_mesh();
615 #ifdef RINGMESH_WITH_TETGEN 616 TetraGenFactory::register_creator< TetraGen_TetGen >(
"TetGen" );
620 TetraGenFactory::register_creator< TetraGen_MG_Tetra >(
"MG_Tetra" );
static std::vector< Key > list_creators()
static std::unique_ptr< BaseClass > create(const Key &key, const Args &... args)
void set_boundaries(const Region3D ®ion, const WellGroup3D *wells=nullptr)
void ringmesh_unused(const T &)
static void warn(const std::string &feature, const Args &... args)
static std::unique_ptr< TetraGen > create(GeoModel3D &M, index_t region_id, const std::string &algo_name)
static void err(const std::string &feature, const Args &... args)
static void out(const std::string &feature, const Args &... args)
void set_internal_points(const std::vector< vec3 > &points)
#define ringmesh_assert(x)
bool contains(const container &in, const T &value)
Classes to build GeoModel from various inputs.
static MeshEntityType type_name_static()
bool tetrahedralize(bool refine=true)
Send the set of points/edges/triangles to MGTetra or TetGen.
void parallel_for(index_t size, const ACTION &action)