GEOSX
ComputationalGeometry.hpp
Go to the documentation of this file.
1 /*
2  * ------------------------------------------------------------------------------------------------------------
3  * SPDX-License-Identifier: LGPL-2.1-only
4  *
5  * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC
6  * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University
7  * Copyright (c) 2018-2020 Total, S.A
8  * Copyright (c) 2019- GEOSX Contributors
9  * All rights reserved
10  *
11  * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
12  * ------------------------------------------------------------------------------------------------------------
13  */
14 
19 #ifndef GEOSX_MESHUTILITIES_COMPUTATIONALGEOMETRY_HPP_
20 #define GEOSX_MESHUTILITIES_COMPUTATIONALGEOMETRY_HPP_
21 
22 #include "common/DataTypes.hpp"
23 #include "common/DataLayouts.hpp"
24 #include "LvArray/src/output.hpp"
26 
27 namespace geosx
28 {
29 namespace computationalGeometry
30 {
31 
33 constexpr real64 machinePrecision = std::numeric_limits< real64 >::epsilon();
34 
48 template< typename LINEDIR_TYPE,
49  typename POINT_TYPE,
50  typename NORMAL_TYPE,
51  typename ORIGIN_TYPE,
52  typename INTPOINT_TYPE >
53 void LinePlaneIntersection( LINEDIR_TYPE const & lineDir,
54  POINT_TYPE const & linePoint,
55  NORMAL_TYPE const & planeNormal,
56  ORIGIN_TYPE const & planeOrigin,
57  INTPOINT_TYPE & intersectionPoint )
58 {
59  /* Find intersection line plane
60  * line equation: p - (d*lineDir + linePoing) = 0;
61  * plane equation: ( p - planeOrigin) * planeNormal = 0;
62  * d = (planeOrigin - linePoint) * planeNormal / (lineDir * planeNormal )
63  * pInt = d*lineDir+linePoint;
64  */
65  real64 dummy[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( planeOrigin );
66  LvArray::tensorOps::subtract< 3 >( dummy, linePoint );
67  real64 const d = LvArray::tensorOps::AiBi< 3 >( dummy, planeNormal ) /
68  LvArray::tensorOps::AiBi< 3 >( lineDir, planeNormal );
69 
70  LvArray::tensorOps::copy< 3 >( intersectionPoint, linePoint );
71  LvArray::tensorOps::scaledAdd< 3 >( intersectionPoint, lineDir, d );
72 }
73 
74 
81 template< typename NORMAL_TYPE >
83  NORMAL_TYPE const & normal )
84 {
85  localIndex const numPoints = points.size( 0 );
86 
87  array2d< real64 > orderedPoints( numPoints, 3 );
88 
89  std::vector< int > indices( numPoints );
90  std::vector< real64 > angle( numPoints );
91 
92  // compute centroid of the set of points
93  real64 centroid[3];
94  LvArray::tensorOps::fill< 3 >( centroid, 0 );
95  for( localIndex a = 0; a < numPoints; ++a )
96  {
97  LvArray::tensorOps::add< 3 >( centroid, points[ a ] );
98  indices[ a ] = a;
99  }
100 
101  LvArray::tensorOps::scale< 3 >( centroid, 1.0 / numPoints );
102 
103  real64 v0[3] = LVARRAY_TENSOROPS_INIT_LOCAL_3( centroid );
104  LvArray::tensorOps::subtract< 3 >( v0, points[ 0 ] );
105  LvArray::tensorOps::normalize< 3 >( v0 );
106 
107  // compute angles
108  angle[ 0 ] = 0;
109  for( localIndex a = 1; a < numPoints; ++a )
110  {
111  real64 v[3] = LVARRAY_TENSOROPS_INIT_LOCAL_3( centroid );
112  LvArray::tensorOps::subtract< 3 >( v, points[ a ] );
113  real64 const dot = LvArray::tensorOps::AiBi< 3 >( v, v0 );
114 
115  real64 crossProduct[ 3 ];
116  LvArray::tensorOps::crossProduct( crossProduct, v, v0 );
117  real64 const det = LvArray::tensorOps::AiBi< 3 >( normal, crossProduct );
118 
119  angle[ a ] = std::atan2( det, dot );
120  }
121 
122  // sort the indices
123  std::sort( indices.begin(), indices.end(), [&]( int i, int j ) { return angle[ i ] < angle[ j ]; } );
124 
125  // copy the points in the reorderedPoints array.
126  for( localIndex a=0; a < numPoints; a++ )
127  {
128  // fill in with ordered
129  LvArray::tensorOps::copy< 3 >( orderedPoints[ a ], points[ indices[ a ] ] );
130  }
131 
132  for( localIndex a = 0; a < numPoints; a++ )
133  {
134  LvArray::tensorOps::copy< 3 >( points[a], orderedPoints[a] );
135  }
136 }
137 
145 template< typename NORMAL_TYPE >
147  NORMAL_TYPE const && normal )
148 {
149  real64 surfaceArea = 0.0;
150 
151  array2d< real64 > orderedPoints( points.size( 0 ), 3 );
152 
153  for( localIndex a = 0; a < points.size( 0 ); a++ )
154  {
155  LvArray::tensorOps::copy< 3 >( orderedPoints[a], points[a] );
156  }
157 
158  orderPointsCCW( orderedPoints, normal );
159 
160  for( localIndex a = 0; a < points.size( 0 ) - 2; ++a )
161  {
162  real64 v1[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( orderedPoints[ a + 1 ] );
163  real64 v2[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( orderedPoints[ a + 2 ] );
164 
165  LvArray::tensorOps::subtract< 3 >( v1, orderedPoints[ 0 ] );
166  LvArray::tensorOps::subtract< 3 >( v2, orderedPoints[ 0 ] );
167 
168  real64 triangleNormal[ 3 ];
169  LvArray::tensorOps::crossProduct( triangleNormal, v1, v2 );
170  surfaceArea += LvArray::tensorOps::l2Norm< 3 >( triangleNormal );
171  }
172 
173  return surfaceArea * 0.5;
174 }
175 
190 template< typename CENTER_TYPE, typename NORMAL_TYPE >
195  CENTER_TYPE && center,
196  NORMAL_TYPE && normal,
197  real64 const areaTolerance = 0.0 )
198 {
199  real64 area = 0.0;
200  LvArray::tensorOps::fill< 3 >( center, 0 );
201  LvArray::tensorOps::fill< 3 >( normal, 0 );
202 
203  GEOSX_ERROR_IF_LT( pointsIndices.size(), 2 );
204  for( localIndex a=0; a<(pointsIndices.size()-2); ++a )
205  {
206  real64 v1[ 3 ], v2[ 3 ], vc[ 3 ];
207 
208  LvArray::tensorOps::copy< 3 >( v1, points[ pointsIndices[ a + 1 ] ] );
209  LvArray::tensorOps::copy< 3 >( v2, points[ pointsIndices[ a + 2 ] ] );
210 
211  LvArray::tensorOps::copy< 3 >( vc, points[ pointsIndices[ 0 ] ] );
212  LvArray::tensorOps::add< 3 >( vc, v1 );
213  LvArray::tensorOps::add< 3 >( vc, v2 );
214 
215  LvArray::tensorOps::subtract< 3 >( v1, points[ pointsIndices[ 0 ] ] );
216  LvArray::tensorOps::subtract< 3 >( v2, points[ pointsIndices[ 0 ] ] );
217 
218  real64 triangleNormal[ 3 ];
219  LvArray::tensorOps::crossProduct( triangleNormal, v1, v2 );
220  real64 const triangleArea = LvArray::tensorOps::l2Norm< 3 >( triangleNormal );
221 
222  LvArray::tensorOps::add< 3 >( normal, triangleNormal );
223 
224  area += triangleArea;
225  LvArray::tensorOps::scaledAdd< 3 >( center, vc, triangleArea );
226  }
227  if( area > areaTolerance )
228  {
229  LvArray::tensorOps::scale< 3 >( center, 1.0 / ( area * 3.0 ) );
230  LvArray::tensorOps::normalize< 3 >( normal );
231  area *= 0.5;
232  }
233  else if( area < -areaTolerance )
234  {
235  for( localIndex a=0; a<pointsIndices.size(); ++a )
236  {
237  GEOSX_LOG_RANK( "Points: " << points[ pointsIndices[ a ] ] << " " << pointsIndices[ a ] );
238  }
239  GEOSX_ERROR( "Negative area found : " << area );
240  }
241  else
242  {
243  return 0.0;
244  }
245 
246  return area;
247 }
248 
254 template< typename NORMAL_TYPE >
256 void FixNormalOrientation_3D( NORMAL_TYPE && normal )
257 {
258  real64 const orientationTolerance = 10 * machinePrecision;
259 
260  // Orient local normal in global sense.
261  // First check: align with z direction
262  if( normal[ 2 ] <= -orientationTolerance )
263  {
264  LvArray::tensorOps::scale< 3 >( normal, -1.0 );
265  }
266  else if( std::fabs( normal[ 2 ] ) < orientationTolerance )
267  {
268  // If needed, second check: align with y direction
269  if( normal[ 1 ] <= -orientationTolerance )
270  {
271  LvArray::tensorOps::scale< 3 >( normal, -1.0 );
272  }
273  else if( fabs( normal[ 1 ] ) < orientationTolerance )
274  {
275  // If needed, third check: align with x direction
276  if( normal[ 0 ] <= -orientationTolerance )
277  {
278  LvArray::tensorOps::scale< 3 >( normal, -1.0 );
279  }
280  }
281  }
282 }
283 
291 template< typename NORMAL_TYPE, typename MATRIX_TYPE >
293 void RotationMatrix_3D( NORMAL_TYPE const & normal,
294  MATRIX_TYPE && rotationMatrix )
295 {
296  real64 m1[ 3 ] = { normal[ 2 ], 0.0, -normal[ 0 ] };
297  real64 m2[ 3 ] = { 0.0, normal[ 2 ], -normal[ 1 ] };
298  real64 const norm_m1 = LvArray::tensorOps::l2Norm< 3 >( m1 );
299  real64 const norm_m2 = LvArray::tensorOps::l2Norm< 3 >( m2 );
300 
301  // If present, looks for a vector with 0 norm
302  // Fix the uncertain case of norm_m1 very close to norm_m2
303  if( norm_m1+1.e+2*machinePrecision > norm_m2 )
304  {
305  LvArray::tensorOps::crossProduct( m2, normal, m1 );
306  LvArray::tensorOps::normalize< 3 >( m2 );
307  LvArray::tensorOps::normalize< 3 >( m1 );
308  }
309  else
310  {
311  LvArray::tensorOps::crossProduct( m1, normal, m2 );
312  LvArray::tensorOps::scale< 3 >( m1, -1 );
313  LvArray::tensorOps::normalize< 3 >( m1 );
314  LvArray::tensorOps::normalize< 3 >( m2 );
315  }
316 
317  // Save everything in the standard form (3x3 rotation matrix)
318  rotationMatrix( 0, 0 ) = normal[ 0 ];
319  rotationMatrix( 1, 0 ) = normal[ 1 ];
320  rotationMatrix( 2, 0 ) = normal[ 2 ];
321  rotationMatrix( 0, 1 ) = m1[ 0 ];
322  rotationMatrix( 1, 1 ) = m1[ 1 ];
323  rotationMatrix( 2, 1 ) = m1[ 2 ];
324  rotationMatrix( 0, 2 ) = m2[ 0 ];
325  rotationMatrix( 1, 2 ) = m2[ 1 ];
326  rotationMatrix( 2, 2 ) = m2[ 2 ];
327 
328  GEOSX_ERROR_IF( fabs( LvArray::tensorOps::determinant< 3 >( rotationMatrix ) - 1.0 ) > 1.e+1 * machinePrecision,
329  "Rotation matrix with determinant different from +1.0" );
330 }
331 
338 template< typename T >
341 int sign( T const val )
342 {
343  return (T( 0 ) < val) - (val < T( 0 ));
344 }
345 
360 template< typename POINT_TYPE >
363  array1d< array1d< localIndex > > const & faceNodeIndicies,
364  POINT_TYPE const & point,
365  real64 const areaTolerance = 0.0 )
366 {
367  localIndex const numFaces = faceNodeIndicies.size( 0 );
368  R1Tensor faceCenter, faceNormal;
369  int prev_sign = 0;
370 
371  for( localIndex kf = 0; kf < numFaces; ++kf )
372  {
373  Centroid_3DPolygon( faceNodeIndicies[kf], nodeCoordinates, faceCenter, faceNormal, areaTolerance );
374 
375  LvArray::tensorOps::subtract< 3 >( faceCenter, point );
376  int const s = sign( LvArray::tensorOps::AiBi< 3 >( faceNormal, faceCenter ) );
377 
378  // all dot products should be non-negative (for outward normals) or non-positive (for inward normals)
379  if( prev_sign * s < 0 )
380  {
381  return false;
382  }
383  prev_sign = s;
384  }
385 
386  return true;
387 }
388 
398 template< typename VEC_TYPE >
400 void GetBoundingBox( localIndex const elemIndex,
403  VEC_TYPE && boxDims )
404 {
405  // This holds the min coordinates of the set in each direction
408  LvArray::NumericLimits< real64 >::max };
409 
410  // boxDims is used to hold the max coordinates.
411  LvArray::tensorOps::fill< 3 >( boxDims, LvArray::NumericLimits< real64 >::min );
412 
413  // loop over all the vertices of the element to get the min and max coords
414  for( localIndex a = 0; a < pointIndices.size( 1 ); ++a )
415  {
416  localIndex const id = pointIndices( elemIndex, a );
417  for( localIndex d = 0; d < 3; ++d )
418  {
419  minCoords[ d ] = fmin( minCoords[ d ], pointCoordinates( id, d ) );
420  boxDims[ d ] = fmax( boxDims[ d ], pointCoordinates( id, d ) );
421  }
422  }
423 
424  LvArray::tensorOps::subtract< 3 >( boxDims, minCoords );
425 }
426 
433 inline
434 real64 HexVolume( real64 const X[][3] )
435 {
436  real64 X7_X1[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[7] );
437  LvArray::tensorOps::subtract< 3 >( X7_X1, X[1] );
438 
439  real64 X6_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[6] );
440  LvArray::tensorOps::subtract< 3 >( X6_X0, X[0] );
441 
442  real64 X7_X2[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[7] );
443  LvArray::tensorOps::subtract< 3 >( X7_X2, X[2] );
444 
445  real64 X3_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[3] );
446  LvArray::tensorOps::subtract< 3 >( X3_X0, X[0] );
447 
448  real64 X5_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[5] );
449  LvArray::tensorOps::subtract< 3 >( X5_X0, X[0] );
450 
451  real64 X7_X4[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[7] );
452  LvArray::tensorOps::subtract< 3 >( X7_X4, X[4] );
453 
454  real64 X7_X1plusX6_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X7_X1 );
455  LvArray::tensorOps::add< 3 >( X7_X1plusX6_X0, X6_X0 );
456 
457  real64 X7_X2plusX5_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X7_X2 );
458  LvArray::tensorOps::add< 3 >( X7_X2plusX5_X0, X5_X0 );
459 
460  real64 X7_X4plusX3_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X7_X4 );
461  LvArray::tensorOps::add< 3 >( X7_X4plusX3_X0, X3_X0 );
462 
463  real64 X7_X2crossX3_X0[3];
464  LvArray::tensorOps::crossProduct( X7_X2crossX3_X0, X7_X2, X3_X0 );
465 
466  real64 X7_X2plusX5_X0crossX7_X4[3];
467  LvArray::tensorOps::crossProduct( X7_X2plusX5_X0crossX7_X4, X7_X2plusX5_X0, X7_X4 );
468 
469  real64 X5_X0crossX7_X4plusX3_X0[3];
470  LvArray::tensorOps::crossProduct( X5_X0crossX7_X4plusX3_X0, X5_X0, X7_X4plusX3_X0 );
471 
472  return 1.0/12.0 * ( LvArray::tensorOps::AiBi< 3 >( X7_X1plusX6_X0, X7_X2crossX3_X0 ) +
473  LvArray::tensorOps::AiBi< 3 >( X6_X0, X7_X2plusX5_X0crossX7_X4 ) +
474  LvArray::tensorOps::AiBi< 3 >( X7_X1, X5_X0crossX7_X4plusX3_X0 ) );
475 }
476 
483 inline
484 real64 TetVolume( real64 const X[][3] )
485 {
486  real64 X1_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[1] );
487  LvArray::tensorOps::subtract< 3 >( X1_X0, X[0] );
488 
489  real64 X2_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[2] );
490  LvArray::tensorOps::subtract< 3 >( X2_X0, X[0] );
491 
492  real64 X3_X0[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( X[3] );
493  LvArray::tensorOps::subtract< 3 >( X3_X0, X[0] );
494 
495  real64 X2_X0crossX3_X0[ 3 ];
496  LvArray::tensorOps::crossProduct( X2_X0crossX3_X0, X2_X0, X3_X0 );
497 
498  return std::fabs( LvArray::tensorOps::AiBi< 3 >( X1_X0, X2_X0crossX3_X0 ) / 6.0 );
499 }
500 
507 inline
508 real64 WedgeVolume( real64 const X[][3] )
509 {
510  real64 const tet1[4][3] = { LVARRAY_TENSOROPS_INIT_LOCAL_3( X[0] ),
514 
515  real64 const tet2[4][3] = { LVARRAY_TENSOROPS_INIT_LOCAL_3( X[0] ),
519 
520  real64 const tet3[4][3] = { LVARRAY_TENSOROPS_INIT_LOCAL_3( X[0] ),
524 
525  return TetVolume( tet1 ) + TetVolume( tet2 ) + TetVolume( tet3 );
526 }
527 
534 inline
535 real64 PyramidVolume( real64 const X[][3] )
536 {
537  real64 const tet1[4][3] = { LVARRAY_TENSOROPS_INIT_LOCAL_3( X[0] ),
541 
542  real64 const tet2[4][3] = { LVARRAY_TENSOROPS_INIT_LOCAL_3( X[0] ),
546 
547  return TetVolume( tet1 ) + TetVolume( tet2 );
548 }
549 
550 } // namespace computationalGeometry
551 } // namespace geosx
552 
553 #endif /* GEOSX_MESHUTILITIES_COMPUTATIONALGEOMETRY_HPP_ */
#define GEOSX_HOST_DEVICE
Marks a host-device function.
Definition: GeosxMacros.hpp:48
Contains functions for outputting array objects.
#define LVARRAY_TENSOROPS_INIT_LOCAL_3(EXP)
Create an initializer list of length 3 from EXP.
This class serves to provide a sliced multidimensional interface to the family of LvArray classes...
Definition: ArraySlice.hpp:89
void orderPointsCCW(arrayView2d< real64 > const &points, NORMAL_TYPE const &normal)
Reorder a set of points counter-clockwise.
INDEX_TYPE size() const noexcept
Definition: ArrayView.hpp:361
GEOSX_HOST_DEVICE void FixNormalOrientation_3D(NORMAL_TYPE &&normal)
Change the orientation of the input vector to be consistent in a global sense.
This class serves to provide a "view" of a multidimensional array.
Definition: ArrayView.hpp:67
GEOSX_HOST_DEVICE real64 WedgeVolume(real64 const X[][3])
Compute the volume of a wedge.
double real64
64-bit floating point type.
Definition: DataTypes.hpp:136
GEOSX_HOST_DEVICE void RotationMatrix_3D(NORMAL_TYPE const &normal, MATRIX_TYPE &&rotationMatrix)
Calculate the rotation matrix for a face in the 3D space.
GEOSX_HOST_DEVICE GEOSX_FORCE_INLINE int sign(T const val)
Return the sign of a given value as an integer.
constexpr INDEX_TYPE size() const noexcept
Definition: ArraySlice.hpp:171
#define GEOSX_ERROR(msg)
Raise a hard error and terminate the program.
Definition: Logger.hpp:110
A wrapper for the std::numeric_limits< T > member functions, this allows their values to be used on d...
Definition: limits.hpp:32
#define GEOSX_FORCE_INLINE
Marks a function or lambda for inlining.
Definition: GeosxMacros.hpp:50
std::ptrdiff_t localIndex
Local index type (for indexing objects within an MPI partition).
Definition: DataTypes.hpp:125
#define GEOSX_LOG_RANK(msg)
Log a message to the rank output stream.
Definition: Logger.hpp:87
constexpr void crossProduct(DST_VECTOR &&LVARRAY_RESTRICT_REF dstVector, VECTOR_A const &LVARRAY_RESTRICT_REF vectorA, VECTOR_B const &LVARRAY_RESTRICT_REF vectorB)
Compute the cross product of vectorA and vectorB and put it in dstVector.
GEOSX_HOST_DEVICE GEOSX_FORCE_INLINE real64 Centroid_3DPolygon(arraySlice1d< localIndex const > const pointsIndices, arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const &points, CENTER_TYPE &&center, NORMAL_TYPE &&normal, real64 const areaTolerance=0.0)
Calculate the centroid of a convex 3D polygon as well as the normal and the rotation matrix...
real64 ComputeSurfaceArea(arrayView2d< real64 const > const &points, NORMAL_TYPE const &&normal)
Calculate the area of a polygon given the set of points in ccw order defining it. ...
GEOSX_HOST_DEVICE bool IsPointInsidePolyhedron(arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const &nodeCoordinates, array1d< array1d< localIndex > > const &faceNodeIndicies, POINT_TYPE const &point, real64 const areaTolerance=0.0)
Check if a point is inside a convex polyhedron (3D polygon)
#define GEOSX_ERROR_IF(EXP, msg)
Conditionally raise a hard error and terminate the program.
Definition: Logger.hpp:103
GEOSX_HOST_DEVICE real64 TetVolume(real64 const X[][3])
Compute the volume of an tetrahedron.
GEOSX_HOST_DEVICE real64 PyramidVolume(real64 const X[][3])
Compute the volume of a pyramid.
void LinePlaneIntersection(LINEDIR_TYPE const &lineDir, POINT_TYPE const &linePoint, NORMAL_TYPE const &planeNormal, ORIGIN_TYPE const &planeOrigin, INTPOINT_TYPE &intersectionPoint)
Calculate the intersection between a line and a plane.
This class provides a fixed dimensional resizeable array interface in addition to an interface simila...
Definition: Array.hpp:55
GEOSX_HOST_DEVICE real64 HexVolume(real64 const X[][3])
Compute the volume of an hexahedron.
#define GEOSX_ERROR_IF_LT(lhs, rhs)
Raise a hard error if one value compares less than the other.
Definition: Logger.hpp:224
GEOSX_HOST_DEVICE void GetBoundingBox(localIndex const elemIndex, arrayView2d< localIndex const, cells::NODE_MAP_USD > const &pointIndices, arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const &pointCoordinates, VEC_TYPE &&boxDims)
Compute the dimensions of the bounding box containing the element defined here by the coordinates of ...
constexpr real64 machinePrecision
Machine epsilon for double-precision calculations.
float atan2(float const y, float const x)
Definition: math.hpp:286