RINGMesh  Version 5.0.0
A programming library for geological model meshes
geomodel_api.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 #include <array>
39 #include <iomanip>
40 #include <iostream>
41 
42 #include <geogram/basic/progress.h>
43 
45 
50 
52 
57 namespace
58 {
59  using namespace RINGMesh;
60 
61  template < index_t DIMENSION >
62  index_t count_geomodel_edges( const GeoModel< DIMENSION >& geomodel )
63  {
64  index_t result{ 0 };
65  for( const auto& line : geomodel.lines() )
66  {
67  result += line.nb_mesh_elements();
68  }
69  return result;
70  }
71 
72  template < index_t DIMENSION >
73  index_t count_geomodel_polygons( const GeoModel< DIMENSION >& geomodel )
74  {
75  index_t result{ 0 };
76  for( const auto& surface : geomodel.surfaces() )
77  {
78  result += surface.nb_mesh_elements();
79  }
80  return result;
81  }
82 
83  template < index_t DIMENSION >
84  index_t count_geomodel_cells( const GeoModel< DIMENSION >& geomodel );
85 
86  template <>
87  index_t count_geomodel_cells( const GeoModel2D& geomodel )
88  {
89  ringmesh_unused( geomodel );
90  return 0;
91  }
92 
93  template <>
94  index_t count_geomodel_cells( const GeoModel3D& geomodel )
95  {
96  index_t nb_cells{ 0 };
97  for( const auto& region : geomodel.regions() )
98  {
99  nb_cells += region.nb_mesh_elements();
100  }
101  return nb_cells;
102  }
103 
104  template < index_t DIMENSION >
105  std::tuple< double, double, double, double, double >
106  compute_region_volumes_per_cell_type(
107  const Region< DIMENSION >& region )
108  {
109  double tet_volume{ 0 };
110  double pyramid_volume{ 0 };
111  double prism_volume{ 0 };
112  double hex_volume{ 0 };
113  double poly_volume{ 0 };
114  for( auto c : range( region.nb_mesh_elements() ) )
115  {
116  index_t nb_vertices{ region.nb_mesh_element_vertices( c ) };
117  double volume{ region.mesh().cell_volume( c ) };
118  switch( nb_vertices )
119  {
120  case 4:
121  tet_volume += volume;
122  break;
123  case 5:
124  pyramid_volume += volume;
125  break;
126  case 6:
127  prism_volume += volume;
128  break;
129  case 8:
130  hex_volume += volume;
131  break;
132  default:
133  poly_volume += volume;
134  break;
135  }
136  }
137  return std::make_tuple(
138  tet_volume, pyramid_volume, prism_volume, hex_volume, poly_volume );
139  }
140 
141  template < index_t DIMENSION >
142  std::tuple< double, double, double, double, double, double >
143  compute_geomodel_volumes_per_cell_type(
144  const GeoModel< DIMENSION >& geomodel )
145  {
146  double tet_volume{ 0 };
147  double pyramid_volume{ 0 };
148  double prism_volume{ 0 };
149  ;
150  double hex_volume{ 0 };
151  double poly_volume{ 0 };
152 
153  for( const auto& region : geomodel.regions() )
154  {
155  std::tie( tet_volume, pyramid_volume, prism_volume, hex_volume,
156  poly_volume ) = compute_region_volumes_per_cell_type( region );
157  }
158  double total{ tet_volume + pyramid_volume + prism_volume + hex_volume
159  + poly_volume };
160  return std::make_tuple( total, tet_volume, pyramid_volume, prism_volume,
161  hex_volume, poly_volume );
162  }
163 
164  void print_one_cell_stat( const index_t& nb_cell,
165  const index_t& nb_cell_total,
166  const std::string& cell_type )
167  {
168  Logger::out( "GeoModel", "* ", nb_cell, " ", cell_type, " (",
169  nb_cell * 100 / nb_cell_total, "%)" );
170  }
171 
172  void print_one_cell_volume_stat( const double& cell_volume,
173  const double& cell_volume_total,
174  const std::string& cell_type )
175  {
176  Logger::out( "GeoModel", "* ", cell_type, " volume ", cell_volume, " (",
177  static_cast< index_t >(
178  cell_volume * 100 / cell_volume_total + 0.5 ),
179  "%)" );
180  }
181  template < index_t DIMENSION >
182  void print_geomodel_base_mesh_stats( const GeoModel< DIMENSION >& geomodel )
183  {
184  Logger::out( "GeoModel", "Model ", geomodel.name(), " is made of\n",
185  std::setw( 10 ), std::left, geomodel.mesh.vertices.nb(),
186  " vertices\n", std::setw( 10 ), std::left,
187  count_geomodel_edges( geomodel ), " edges" );
188 
189  index_t nb_triangles{ geomodel.mesh.polygons.nb_triangle() };
190  index_t nb_quads{ geomodel.mesh.polygons.nb_quad() };
191  index_t nb_unclassified_polygons{
192  geomodel.mesh.polygons.nb_unclassified_polygon()
193  };
194  index_t nb_polygons{ geomodel.mesh.polygons.nb_polygons() };
195  Logger::out(
196  "GeoModel", std::setw( 10 ), std::left, nb_polygons, " polygons" );
197  if( nb_triangles > 0 )
198  {
199  print_one_cell_stat( nb_triangles, nb_polygons, "triangles" );
200  }
201  if( nb_quads > 0 )
202  {
203  print_one_cell_stat( nb_quads, nb_polygons, "quads" );
204  }
205  if( nb_unclassified_polygons > 0 )
206  {
207  print_one_cell_stat( nb_unclassified_polygons, nb_polygons,
208  "unclassified polygons" );
209  }
210  }
211 }
212 
213 namespace RINGMesh
214 {
215  template < index_t DIMENSION >
217  const GeoModel< DIMENSION >& geomodel, const MeshEntityType& type )
218  {
219  Logger::out( "GeoModel", std::setw( 10 ), std::left,
220  geomodel.nb_mesh_entities( type ), " ", type );
221  }
222 
223  template < index_t DIMENSION >
225  const GeologicalEntityType& type )
226  {
227  if( geomodel.nb_geological_entities( type ) == 0 )
228  {
229  return;
230  }
231  Logger::out( "GeoModel", std::setw( 10 ), std::left,
232  geomodel.nb_geological_entities( type ), " ", type );
233  }
234 
235  template < index_t DIMENSION >
236  void print_geomodel( const GeoModel< DIMENSION >& geomodel )
237  {
238  Logger::out( "GeoModel", "Model ", geomodel.name(), " has\n",
239  std::setw( 10 ), std::left, geomodel.mesh.vertices.nb(),
240  " vertices\n", std::setw( 10 ), std::left,
241  count_geomodel_edges( geomodel ), " edges\n", std::setw( 10 ),
242  std::left, count_geomodel_polygons( geomodel ), " polygons" );
243  index_t nb_cells{ count_geomodel_cells( geomodel ) };
244  if( nb_cells != 0 )
245  {
246  Logger::out(
247  "GeoModel", std::setw( 10 ), std::left, nb_cells, " cells" );
248  }
249  Logger::out( "GeoModel" );
250 
251  const EntityTypeManager< DIMENSION >& manager =
252  geomodel.entity_type_manager();
253  const std::vector< MeshEntityType >& mesh_entity_types =
254  manager.mesh_entity_manager.mesh_entity_types();
255  for( const MeshEntityType& type : mesh_entity_types )
256  {
257  print_nb_mesh_entities( geomodel, type );
258  }
259  const std::vector< GeologicalEntityType >& geological_entity_types =
261  for( const GeologicalEntityType& type : geological_entity_types )
262  {
263  print_nb_geological_entities( geomodel, type );
264  }
265  }
266 
267  template <>
268  void RINGMESH_API print_geomodel_mesh_stats( const GeoModel2D& geomodel )
269  {
270  print_geomodel_base_mesh_stats( geomodel );
271  }
272 
273  template <>
274  void RINGMESH_API print_geomodel_mesh_stats( const GeoModel3D& geomodel )
275  {
276  print_geomodel_base_mesh_stats( geomodel );
277 
278  index_t nb_tet{ geomodel.mesh.cells.nb_tet() };
279  index_t nb_pyramids{ geomodel.mesh.cells.nb_pyramid() };
280  index_t nb_prisms{ geomodel.mesh.cells.nb_prism() };
281  index_t nb_hex{ geomodel.mesh.cells.nb_hex() };
282  index_t nb_poly{ geomodel.mesh.cells.nb_connector() };
283  index_t nb_cells{ geomodel.mesh.cells.nb_cells() };
284  Logger::out(
285  "GeoModel", std::setw( 10 ), std::left, nb_cells, " cells" );
286  if( nb_tet > 0 )
287  {
288  print_one_cell_stat( nb_tet, nb_cells, "tet" );
289  }
290  if( nb_pyramids > 0 )
291  {
292  print_one_cell_stat( nb_pyramids, nb_cells, "pyramids" );
293  }
294  if( nb_prisms > 0 )
295  {
296  print_one_cell_stat( nb_prisms, nb_cells, "prisms" );
297  }
298  if( nb_hex > 0 )
299  {
300  print_one_cell_stat( nb_hex, nb_cells, "hex" );
301  }
302  if( nb_poly > 0 )
303  {
304  print_one_cell_stat( nb_poly, nb_cells, "polyhedra" );
305  }
306  Logger::out( "GeoModel" );
307  }
308 
309  void print_geomodel_mesh_cell_volumes( const GeoModel3D& geomodel )
310  {
311  double volume{ 0 };
312  double tet_volume{ 0 };
313  double pyramid_volume{ 0 };
314  double prism_volume{ 0 };
315  double hex_volume{ 0 };
316  double poly_volume{ 0 };
317  std::tie( volume, tet_volume, pyramid_volume, prism_volume, hex_volume,
318  poly_volume ) = compute_geomodel_volumes_per_cell_type( geomodel );
319  Logger::out( "GeoModel", "Model ", geomodel.name(), " has a volume of ",
320  volume );
321  if( tet_volume > 0 )
322  {
323  print_one_cell_volume_stat( tet_volume, volume, "tet" );
324  }
325  if( pyramid_volume > 0 )
326  {
327  print_one_cell_volume_stat( pyramid_volume, volume, "pyramid" );
328  }
329  if( prism_volume > 0 )
330  {
331  print_one_cell_volume_stat( prism_volume, volume, "prism" );
332  }
333  if( hex_volume > 0 )
334  {
335  print_one_cell_volume_stat( hex_volume, volume, "hex" );
336  }
337  if( poly_volume > 0 )
338  {
339  print_one_cell_volume_stat( poly_volume, volume, "polyhedron" );
340  }
341  Logger::out( "GeoModel" );
342  }
343 
344  template < index_t DIMENSION >
346  const GeoModel< DIMENSION >& geomodel,
347  const MeshEntityType& gmme_type,
348  const std::string& name )
349  {
350  index_t mesh_entity_id{ NO_ID };
351  for( auto elt_i : range( geomodel.nb_mesh_entities( gmme_type ) ) )
352  {
353  const GeoModelMeshEntity< DIMENSION >& cur_gme =
354  geomodel.mesh_entity( gmme_type, elt_i );
355  if( cur_gme.name() == name )
356  {
357  if( mesh_entity_id != NO_ID )
358  {
359  throw RINGMeshException( "GeoModel",
360  " At least two GeoModelMeshEntity have the same name "
361  "in the GeoModel: ",
362  name );
363  }
364  mesh_entity_id = cur_gme.index();
365  }
366  }
367  if( mesh_entity_id == NO_ID )
368  {
369  throw RINGMeshException(
370  "GeoModel", name, " does not match with any actual "
371  "GeoModelEntity name in the GeoModel" );
372  }
373  return mesh_entity_id;
374  }
375 
376  template < index_t DIMENSION >
378  const RINGMesh::GeoModel< DIMENSION >& geomodel,
379  const RINGMesh::GeologicalEntityType& gmge_type,
380  const std::string& name )
381  {
382  index_t geological_entity_id{ NO_ID };
383  for( auto& cur_gme : geomodel.geol_entities( gmge_type ) )
384  {
385  if( cur_gme.name() == name )
386  {
387  if( geological_entity_id != NO_ID )
388  {
389  throw RINGMeshException( "GeoModel",
390  "At least two GeoModelGeologicalEntity have the same "
391  "name in the GeoModel : ",
392  name );
393  }
394  geological_entity_id = cur_gme.index();
395  }
396  }
397  if( geological_entity_id == NO_ID )
398  {
399  throw RINGMeshException(
400  "GeoModel", name, " does not match with any actual "
401  "GeoModelEntity name in the GeoModel" );
402  }
403  return geological_entity_id;
404  }
405 
406  /*******************************************************************************/
407 
408  template < index_t DIMENSION >
410  const vecn< DIMENSION >& translation_vector )
411  {
412  for( auto v : range( geomodel.mesh.vertices.nb() ) )
413  {
414  // Coordinates are not directly modified to
415  // update the matching vertices in geomodel entities
416  const vecn< DIMENSION >& p = geomodel.mesh.vertices.vertex( v );
417  geomodel.mesh.vertices.update_point( v, p + translation_vector );
418  }
419  }
420 
421  void rotate( GeoModel3D& geomodel,
422  const vec3& origin,
423  const vec3& axis,
424  double theta,
425  bool degrees )
426  {
427  if( length( axis ) < geomodel.epsilon() )
428  {
429  Logger::err( "GeoModel",
430  "Rotation around an epsilon length axis is impossible" );
431  return;
432  }
433  GEO::Matrix< 4, double > rot_mat{ rotation_matrix_about_arbitrary_axis(
434  origin, axis, theta, degrees ) };
435 
436  for( auto v : range( geomodel.mesh.vertices.nb() ) )
437  {
438  const vec3& p = geomodel.mesh.vertices.vertex( v );
439  std::array< double, 4 > old{ { p[0], p[1], p[2], 1. } };
440  std::array< double, 4 > new_p{ { 0, 0, 0, 1. } };
441  GEO::mult( rot_mat, old.data(), new_p.data() );
442  ringmesh_assert( std::fabs( new_p[3] - 1. ) < global_epsilon );
443 
444  geomodel.mesh.vertices.update_point( v, vec3{ new_p.data() } );
445  }
446  }
447 
448 #ifdef RINGMESH_WITH_TETGEN
449 
450  void tetrahedralize( GeoModel3D& geomodel,
451  const std::string& method,
452  index_t region_id,
453  bool add_steiner_points )
454  {
455  /* @todo Review: Maybe rethink these functions
456  * to have a function that can mesh a region of a geomodel
457  * taking only one vector of points [JP]
458  */
459  std::vector< std::vector< vec3 > > internal_vertices(
460  geomodel.nb_regions() );
461  tetrahedralize( geomodel, method, region_id, add_steiner_points,
462  internal_vertices );
463  }
464 
465  void tetrahedralize( GeoModel3D& geomodel,
466  const std::string& method,
467  index_t region_id,
468  bool add_steiner_points,
469  const std::vector< std::vector< vec3 > >& internal_vertices )
470  {
471  if( region_id == NO_ID )
472  {
473  Logger::out( "Info", "Using ", method );
474  GEO::ProgressTask progress( "Compute", geomodel.nb_regions() );
475  for( auto i : range( geomodel.nb_regions() ) )
476  {
477  tetrahedralize( geomodel, method, i, add_steiner_points,
478  internal_vertices );
479  progress.next();
480  }
481  }
482  else
483  {
484  std::unique_ptr< TetraGen > tetragen{ TetraGen::create(
485  geomodel, region_id, method ) };
486  tetragen->set_boundaries(
487  geomodel.region( region_id ), geomodel.wells() );
488  tetragen->set_internal_points( internal_vertices[region_id] );
489  bool status{ Logger::instance()->is_quiet() };
490  Logger::instance()->set_quiet( true );
491  tetragen->tetrahedralize( add_steiner_points );
492  Logger::instance()->set_quiet( status );
493  }
494 
495  // The GeoModelMesh should be updated, just erase everything
496  // and it will be re-computed during its next access.
497  geomodel.mesh.vertices.clear();
498  }
499 #endif
500 
501  template void RINGMESH_API print_geomodel( const GeoModel2D& );
502  template index_t RINGMESH_API find_mesh_entity_id_from_name(
503  const GeoModel2D&, const MeshEntityType&, const std::string& );
504  template index_t RINGMESH_API find_geological_entity_id_from_name(
505  const RINGMesh::GeoModel2D&,
507  const std::string& );
508  template void RINGMESH_API translate( GeoModel2D&, const vec2& );
509 
510  template void RINGMESH_API print_geomodel( const GeoModel3D& );
511  template index_t RINGMESH_API find_mesh_entity_id_from_name(
512  const GeoModel3D&, const MeshEntityType&, const std::string& );
513  template index_t RINGMESH_API find_geological_entity_id_from_name(
514  const RINGMesh::GeoModel3D&,
516  const std::string& );
517  template void RINGMESH_API translate( GeoModel3D&, const vec3& );
518 
519 } // namespace RINGMesh
const VolumeMesh< DIMENSION > & mesh() const
Get the low level mesh data structure.
GeoModelMesh< DIMENSION > mesh
Definition: geomodel.h:241
void print_nb_geological_entities(const GeoModel< DIMENSION > &geomodel, const GeologicalEntityType &type)
Abstract base class for GeoModelMeshEntity.
GEO::vecng< DIMENSION, double > vecn
Definition: types.h:74
index_t find_mesh_entity_id_from_name(const GeoModel< DIMENSION > &geomodel, const MeshEntityType &gmme_type, const std::string &name)
GeologicalTypeManager geological_entity_manager
const EntityTypeManager< DIMENSION > & entity_type_manager() const
Gets the EntityTypeManager associated to the GeoModel.
Definition: geomodel.h:119
The GeologicalEntityType described the type of the Geological entities User can defined there own Geo...
Definition: entity_type.h:137
virtual const GeoModelMeshEntity< DIMENSION > & mesh_entity(const gmme_id &id) const
Generic access to a meshed entity.
Definition: geomodel.cpp:131
vecn< 3 > vec3
Definition: types.h:76
void RINGMESH_API print_geomodel_mesh_cell_volumes(const GeoModel3D &geomodel)
void ringmesh_unused(const T &)
Definition: common.h:105
void RINGMESH_API rotate(GeoModel3D &geomodel, const vec3 &origin, const vec3 &axis, double angle, bool degrees=false)
Rotate the boundary geomodel.
void translate(GeoModel< DIMENSION > &geomodel, const vecn< DIMENSION > &translation_vector)
Translates the boundary geomodel by a vector.
void print_nb_mesh_entities(const GeoModel< DIMENSION > &geomodel, const MeshEntityType &type)
void print_geomodel(const GeoModel< DIMENSION > &geomodel)
Print in the console the geomodel statistics.
MeshEntityTypeManager< DIMENSION > mesh_entity_manager
const std::string & name() const
Gets the name of the GeoModel.
Definition: geomodel.h:111
static std::unique_ptr< TetraGen > create(GeoModel3D &M, index_t region_id, const std::string &algo_name)
Definition: tetra_gen.cpp:431
vecn< 2 > vec2
Definition: types.h:78
geol_entity_range< DIMENSION > geol_entities(const GeologicalEntityType &geol_type) const
Definition: geomodel.h:341
GEO::Matrix< 4, double > RINGMESH_API rotation_matrix_about_arbitrary_axis(const vec3 &origin, const vec3 &axis, double theta, bool degrees)
Builds a rotational matrix about an arbitrary axis.
Definition: geometry.cpp:202
index_t nb_mesh_elements() const final
index_t nb_mesh_element_vertices(index_t cell_index) const final
static void err(const std::string &feature, const Args &... args)
Definition: logger.h:68
index_t find_geological_entity_id_from_name(const RINGMesh::GeoModel< DIMENSION > &geomodel, const RINGMesh::GeologicalEntityType &gmge_type, const std::string &name)
const std::vector< GeologicalEntityType > & geological_entity_types() const
const std::string & name() const
static void out(const std::string &feature, const Args &... args)
Definition: logger.h:61
static GEO::Logger * instance()
Definition: logger.h:81
Global entity manager which could be associated to a geomodel to give access to different manager to ...
line_range< DIMENSION > lines() const
Definition: geomodel.h:333
#define ringmesh_assert(x)
A GeoModelEntity of type REGION.
The MeshEntityType described the type of the meshed entities There are 4 MeshEntityTypes correspondin...
Definition: entity_type.h:117
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
void print_geomodel_mesh_stats(const GeoModel< DIMENSION > &geomodel)
surface_range< DIMENSION > surfaces() const
Definition: geomodel.h:337
virtual index_t nb_mesh_entities(const MeshEntityType &type) const
Returns the number of mesh entities of the given type.
Definition: geomodel.cpp:108
index_t nb_geological_entities(const GeologicalEntityType &type) const
Returns the number of geological entities of the given type.
Definition: geomodel.h:136