RINGMesh  Version 5.0.0
A programming library for geological model meshes
tetra_gen.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012-2017, Association Scientifique pour la Geologie et ses
3  * Applications (ASGA). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of ASGA nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ASGA BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * http://www.ring-team.org
28  *
29  * RING Project
30  * Ecole Nationale Superieure de Geologie - GeoRessources
31  * 2 Rue du Doyen Marcel Roubault - TSA 70605
32  * 54518 VANDOEUVRE-LES-NANCY
33  * FRANCE
34  */
35 
37 
38 #ifdef WIN32
39 #include <io.h>
40 #endif
41 
44 
45 #include <ringmesh/mesh/well.h>
46 
48 
54 namespace RINGMesh
55 {
56 #ifdef RINGMESH_WITH_TETGEN
57  class RINGMESH_API TetraGen_TetGen final : public TetraGen
58  {
59  public:
60  TetraGen_TetGen( GeoModel3D& geomodel, index_t region_id )
61  : TetraGen( geomodel, region_id )
62  {
63  }
64 
65  bool do_tetrahedralize( bool refine ) final
66  {
67  auto mesh3D_builder =
68  builder_.geometry.create_region_builder( output_region_ );
69  tetrahedralize_mesh_tetgen(
70  *mesh3D_builder.get(), tetmesh_constraint_, refine, 1.0 );
71  return true;
72  }
73  };
74 #endif
75 
76 #ifdef USE_MG_TETRA
77 
78  void start_redirect( fpos_t& pos, FILE* out, int& fd )
79  {
80 #ifndef RINGMESH_DEBUG
81  // Save position of current standard output
82  fgetpos( out, &pos );
83 #ifdef WIN32
84  fd = _dup( fileno( out ) );
85  freopen( "nul", "w", out );
86 #else
87  fd = dup( fileno( out ) );
88  FILE* f = freopen( "/dev/null", "w", out );
89  ringmesh_unused( f );
90 #endif
91 #endif
92  }
93 
94  void stop_redirect( fpos_t& pos, FILE* out, int& fd )
95  {
96 #ifndef RINGMESH_DEBUG
97  // Flush stdout so any buffered messages are delivered
98  fflush( out );
99 // Close file and restore standard output to stdout - which should be the
100 // terminal
101 #ifdef WIN32
102  _dup2( fd, fileno( out ) );
103 #else
104  dup2( fd, fileno( out ) );
105 #endif
106  close( fd );
107  clearerr( out );
108  fsetpos( out, &pos );
109 #endif
110  }
111 
112  class RINGMESH_API TetraGen_MG_Tetra final : public TetraGen
113  {
114  public:
115  TetraGen_MG_Tetra( GeoModel3D& geomodel, index_t region_id )
116  : TetraGen( geomodel, region_id )
117  {
118  }
119 
120  virtual ~TetraGen_MG_Tetra()
121  {
122  fpos_t pos;
123  int fd = 0;
124  start_redirect( pos, stdout, fd );
125  fpos_t pos_err;
126  int fd_err = 0;
127  start_redirect( pos_err, stderr, fd_err );
128 
129  tetra_regain_mesh( tms_, mesh_output_ );
130  tetra_session_delete( tms_ );
131  mesh_delete( mesh_input_ );
132  context_delete( context_ );
133 
134  stop_redirect( pos, stdout, fd );
135  stop_redirect( pos_err, stderr, fd_err );
136  }
137 
138  bool do_tetrahedralize( bool refine ) final
139  {
140  fpos_t pos;
141  int fd = 0;
142  start_redirect( pos, stdout, fd );
143  fpos_t pos_err;
144  int fd_err = 0;
145  start_redirect( pos_err, stderr, fd_err );
146 
147  initialize_mgtetra_variables();
148 
149  set_mesh_in_mgtetra();
150 
151  set_meshing_parameters();
152 
153  generate_mesh( refine );
154  initialize_ringmesh_storage();
155  write_vertices_in_ringmesh_data_structure();
156  write_tet_in_ringmesh_data_structure();
157 
158  stop_redirect( pos, stdout, fd );
159  stop_redirect( pos_err, stderr, fd_err );
160 
161  return true;
162  }
163 
164  static status_t my_message_cb( message_t* msg, void* user_data )
165  {
166  char* desc;
167  integer e, ibuff[6];
168  real rbuff[3];
169 
170  message_get_description( msg, &desc );
171  message_get_number( msg, &e );
172 
173  if( e == 0 )
174  {
175  Logger::err( "TetraGen", desc );
176  }
177  else if( e == MESHGEMS_TETRA_CODE( -5110 ) )
178  {
179  message_get_integer_data( msg, 1, 4, ibuff );
180  Logger::err( "TetraGen",
181  "two surface edges are intersecting : ", ibuff[0], " ",
182  ibuff[1], " intersects ", ibuff[2], " ", ibuff[3] );
183  }
184  else if( e == MESHGEMS_TETRA_CODE( -5120 ) )
185  {
186  message_get_integer_data( msg, 1, 5, ibuff );
187  Logger::err( "TetraGen",
188  "surface edge intersects a surface face : ", ibuff[0], " ",
189  ibuff[1], " intersects ", ibuff[2], " ", ibuff[3], " ",
190  ibuff[4] );
191  }
192  else if( e == MESHGEMS_TETRA_CODE( -5150 ) )
193  {
194  message_get_integer_data( msg, 1, 4, ibuff );
195  Logger::err( "TetraGen",
196  "boundary point inside a surface face : ", ibuff[0], " in ",
197  ibuff[1], " ", ibuff[2], " ", ibuff[3] );
198  }
199  else if( e == MESHGEMS_TETRA_CODE( 5200 ) )
200  {
201  message_get_integer_data( msg, 1, 3, ibuff );
202  Logger::err( "TetraGen", "duplicated face : ", ibuff[0], " ",
203  ibuff[1], " ", ibuff[2], " ", ibuff[3] );
204  }
205  else if( e == MESHGEMS_TETRA_CODE( -5621 ) )
206  {
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] );
212  }
213  else if( e == MESHGEMS_TETRA_CODE( -5820 ) )
214  {
215  message_get_integer_data( msg, 1, 2, ibuff );
216  Logger::err( "TetraGen", "edge bounding a hole : ", ibuff[0],
217  " ", ibuff[1] );
218  }
219  else if( e == MESHGEMS_TETRA_CODE( 8423 ) )
220  {
221  message_get_integer_data( msg, 1, 3, ibuff );
222  Logger::err( "TetraGen",
223  "constrained face cannot be enforced : ", ibuff[0], " ",
224  ibuff[1], " ", ibuff[2] );
225  }
226  else if( e == MESHGEMS_TETRA_CODE( 8441 ) )
227  {
228  message_get_integer_data( msg, 1, 2, ibuff );
229  Logger::err( "TetraGen",
230  "constrained edge cannot be enforced : ", ibuff[0], " ",
231  ibuff[1] );
232  }
233  else
234  {
235  Logger::err( "TetraGen", "Error message not directly handle" );
236  Logger::err( "TetraGen", "Error(", e, ") : ", desc );
237  }
238  return STATUS_OK;
239  }
240 
241  private:
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 };
247 
248  private:
249  void initialize_ringmesh_storage()
250  {
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 );
257  }
258 
259  void initialize_mgtetra_variables()
260  {
261  context_ = context_new();
262  mesh_input_ = mesh_new_in_memory( context_ );
263  }
264 
265  void set_mesh_in_mgtetra()
266  {
267  set_vertices();
268  set_edges();
269  set_triangles();
270  }
271 
272  bool generate_mesh( bool refine )
273  {
274  if( !create_boundary_mesh() )
275  {
276  return false;
277  }
278  if( refine )
279  {
280  return refine_mesh();
281  }
282  return true;
283  }
284 
285  void set_vertices()
286  {
287  mesh_set_vertex_count(
288  mesh_input_, tetmesh_constraint_.vertices.nb() );
289  for( auto p : range( tetmesh_constraint_.vertices.nb() ) )
290  {
291  mesh_set_vertex_coordinates( mesh_input_, p + starting_index_,
292  tetmesh_constraint_.vertices.point_ptr( p ) );
293  }
294  }
295 
296  void set_edges()
297  {
298  mesh_set_edge_count( mesh_input_, tetmesh_constraint_.edges.nb() );
299  for( auto e : range( tetmesh_constraint_.edges.nb() ) )
300  {
301  meshgems_integer edge_indices[2];
302  edge_indices[0] =
303  tetmesh_constraint_.edges.vertex( e, 0 ) + starting_index_;
304  edge_indices[1] =
305  tetmesh_constraint_.edges.vertex( e, 1 ) + starting_index_;
306  mesh_set_edge_vertices(
307  mesh_input_, e + starting_index_, edge_indices );
308  }
309  }
310 
311  void set_triangles()
312  {
313  mesh_set_triangle_count(
314  mesh_input_, tetmesh_constraint_.facets.nb() );
315  for( auto t : range( tetmesh_constraint_.facets.nb() ) )
316  {
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 );
326  }
327  }
328 
329  void set_meshing_parameters()
330  {
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" );
340  }
341 
342  bool create_boundary_mesh()
343  {
344  status_t ret = tetra_mesh_boundary( tms_ );
345  if( ret != STATUS_OK )
346  {
347  Logger::err( "TetraGen",
348  "Encountered a problem while meshing boundary..." );
349  return false;
350  }
351  return true;
352  }
353 
354  bool refine_mesh()
355  {
356  status_t ret = tetra_insert_volume_vertices( tms_ );
357  if( ret != STATUS_OK )
358  {
359  Logger::err( "TetraGen",
360  "Encountered a problem while meshing inside..." );
361  return false;
362  }
363  ret = tetra_optimise_volume_regular( tms_ );
364  if( ret != STATUS_OK )
365  {
366  Logger::err( "TetraGen",
367  "Encountered a problem while meshing inside..." );
368  return false;
369  }
370  return true;
371  }
372 
373  void write_vertices_in_ringmesh_data_structure() final
374  {
375  parallel_for( region_->nb_vertices(), [this]( index_t v ) {
376  double point[3];
377  mesh_get_vertex_coordinates(
378  mesh_output_, v + starting_index_, point );
379  set_point( v, point );
380  } );
381  }
382 
383  void write_tet_in_ringmesh_data_structure() final
384  {
385  parallel_for( region_->nb_mesh_elements(), [this]( index_t t ) {
386  int tet[4];
387  mesh_get_tetrahedron_vertices(
388  mesh_output_, t + starting_index_, tet );
389  // Because MG Tetra count the vertices starting with 1
390  for( auto v : range( 4 ) )
391  {
392  tet[v] -= starting_index_;
393  }
394  set_tetra( t, tet );
395  } );
396  builder_->geometry.compute_region_adjacencies( output_region_ );
397  }
398  void set_point( index_t index, const double* point )
399  {
400  bool update = false;
401  vec3 vertex( point );
402  builder_->geometry.set_mesh_entity_vertex(
403  gmme_id( Region::type_name_static(), output_region_ ), index,
404  vertex, update );
405  }
406 
407  void set_tetra( index_t tetra_index, int* vertex_indices )
408  {
409  std::vector< index_t > corners( 4 );
410  for( auto v : range( 4 ) )
411  {
412  index_t vertex_id = static_cast< index_t >( vertex_indices[v] );
413  corners[v] = vertex_id;
414  }
415  builder_->geometry.set_region_element_geometry(
416  output_region_, tetra_index, corners );
417  }
418 
419  void initialize_storage( index_t nb_points, index_t nb_tets )
420  {
421  gmme_id region_id( Region::type_name_static(), output_region_ );
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 );
427  }
428  };
429 #endif
430 
431  std::unique_ptr< TetraGen > TetraGen::create(
432  GeoModel3D& M, index_t region_id, const std::string& algo_name )
433  {
434  auto mesher = TetraGenFactory::create( algo_name, M, region_id );
435  if( !mesher )
436  {
437 #ifdef RINGMESH_WITH_TETGEN
438  Logger::warn(
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 ) );
442 #else
443  Logger::err( "I/O", "Currently supported mesher are: " );
444  for( const std::string& name : TetraGenFactory::list_creators() )
445  {
446  Logger::out( "I/O", " ", name );
447  }
448  throw RINGMeshException(
449  "TetraGen", "Could not create TetraGen mesher: ", algo_name );
450 #endif
451  }
452  return mesher;
453  }
454 
456  const Region3D& region, const WellGroup3D* wells )
457  {
458  region_ = &region;
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 ) )
466  {
467  const Surface3D& surface = region_->boundary( s );
468  if( contains( surface_id, surface.index() ) )
469  {
470  continue;
471  }
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 );
476  }
477 
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 )
483  {
484  for( const auto& edges : well_edges )
485  {
486  nb_well_vertices += 2 * edges.size();
487  }
488  }
489  region_surfaces_and_wells_vertices.reserve(
490  nb_surface_vertices + nb_region_vertices + nb_well_vertices );
491 
492  // Add the surfaces vertices
493  for( const GeoModelMeshEntity3D*& surface : unique_surfaces )
494  {
495  for( auto v : range( surface->nb_vertices() ) )
496  {
497  region_surfaces_and_wells_vertices.push_back(
498  surface->vertex( v ) );
499  }
500  }
501 
502  // Add the region vertices
503  for( auto v : range( nb_region_vertices ) )
504  {
505  region_surfaces_and_wells_vertices.push_back( region.vertex( v ) );
506  }
507 
508  // Add the well vertices
509  if( wells != nullptr )
510  {
511  wells->get_region_edges( region.index(), well_edges );
512  for( const auto& edges : well_edges )
513  {
514  for( const auto& edge : edges )
515  {
516  region_surfaces_and_wells_vertices.push_back(
517  edge.vertex( 0 ) );
518  region_surfaces_and_wells_vertices.push_back(
519  edge.vertex( 1 ) );
520  }
521  }
522  }
523 
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() );
530 
531  index_t starting_index = tetmesh_constraint_.vertices.create_vertices(
532  unique_points.size() );
533  GEO::Memory::copy(
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() )
538  {
539  index_t nb_well_edges{ 0 };
540  for( const auto& edges : well_edges )
541  {
542  nb_well_edges += edges.size();
543  }
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() ) )
551  {
552  for( auto e : range( well_edges[w].size() ) )
553  {
554  ringmesh_unused( e );
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;
560  }
561  }
562  }
563 
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 )
571  {
572  for( auto t : range( surface->nb_mesh_elements() ) )
573  {
574  ringmesh_assert( surface->nb_mesh_element_vertices( t ) == 3 );
575  for( auto v : range( 3 ) )
576  {
577  tetmesh_constraint_.facets.set_vertex( offset_polygons + t,
578  v, starting_index
579  + unique_indices
580  [offset_vertices
581  + surface->mesh_element_vertex_index(
582  ElementLocalVertex( t, v ) )] );
583  }
584  surface_region[offset_polygons + t] = surface->index();
585  }
586  offset_vertices += surface->nb_vertices();
587  offset_polygons += surface->nb_mesh_elements();
588  }
589  tetmesh_constraint_.facets.connect();
590  }
591 
592  void TetraGen::set_internal_points( const std::vector< vec3 >& points )
593  {
594  if( points.empty() )
595  {
596  return;
597  }
598  index_t start =
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 ) );
602  }
603 
604  bool TetraGen::tetrahedralize( bool refine )
605  {
606  bool result = do_tetrahedralize( refine );
607  if( result )
608  {
609  builder_.geometry.clear_geomodel_mesh();
610  }
611  return result;
612  }
614  {
615 #ifdef RINGMESH_WITH_TETGEN
616  TetraGenFactory::register_creator< TetraGen_TetGen >( "TetGen" );
617 #endif
618 
619 #ifdef USE_MG_TETRA
620  TetraGenFactory::register_creator< TetraGen_MG_Tetra >( "MG_Tetra" );
621 #endif
622  }
623 } // namespace RINGMesh
static std::vector< Key > list_creators()
Definition: factory.h:97
static std::unique_ptr< BaseClass > create(const Key &key, const Args &... args)
Definition: factory.h:85
void set_boundaries(const Region3D &region, const WellGroup3D *wells=nullptr)
Definition: tetra_gen.cpp:455
vecn< 3 > vec3
Definition: types.h:76
void ringmesh_unused(const T &)
Definition: common.h:105
static void warn(const std::string &feature, const Args &... args)
Definition: logger.h:75
static std::unique_ptr< TetraGen > create(GeoModel3D &M, index_t region_id, const std::string &algo_name)
Definition: tetra_gen.cpp:431
static void initialize()
Definition: tetra_gen.cpp:613
static void err(const std::string &feature, const Args &... args)
Definition: logger.h:68
static void out(const std::string &feature, const Args &... args)
Definition: logger.h:61
void set_internal_points(const std::vector< vec3 > &points)
Definition: tetra_gen.cpp:592
#define ringmesh_assert(x)
bool contains(const container &in, const T &value)
Definition: algorithm.h:87
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
static MeshEntityType type_name_static()
bool tetrahedralize(bool refine=true)
Send the set of points/edges/triangles to MGTetra or TetGen.
Definition: tetra_gen.cpp:604
void parallel_for(index_t size, const ACTION &action)
Definition: common.h:244