GEOS
ComputationalGeometry.hpp
Go to the documentation of this file.
1 /*
2  * ------------------------------------------------------------------------------------------------------------
3  * SPDX-License-Identifier: LGPL-2.1-only
4  *
5  * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
6  * Copyright (c) 2018-2024 TotalEnergies
7  * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
8  * Copyright (c) 2023-2024 Chevron
9  * Copyright (c) 2019- GEOS/GEOSX Contributors
10  * All rights reserved
11  *
12  * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
13  * ------------------------------------------------------------------------------------------------------------
14  */
15 
20 #ifndef GEOS_MESH_UTILITIES_COMPUTATIONALGEOMETRY_HPP_
21 #define GEOS_MESH_UTILITIES_COMPUTATIONALGEOMETRY_HPP_
22 
23 #include "common/DataTypes.hpp"
24 #include "common/DataLayouts.hpp"
29 #include "LvArray/src/output.hpp"
30 #include "LvArray/src/tensorOps.hpp"
31 
32 namespace geos
33 {
34 namespace computationalGeometry
35 {
36 
38 constexpr real64 machinePrecision = LvArray::NumericLimits< real64 >::epsilon;
39 
53 template< typename LINEDIR_TYPE,
54  typename POINT_TYPE,
55  typename NORMAL_TYPE,
56  typename ORIGIN_TYPE,
57  typename INTPOINT_TYPE >
58 void LinePlaneIntersection( LINEDIR_TYPE const & lineDir,
59  POINT_TYPE const & linePoint,
60  NORMAL_TYPE const & planeNormal,
61  ORIGIN_TYPE const & planeOrigin,
62  INTPOINT_TYPE & intersectionPoint )
63 {
64  /* Find intersection line plane
65  * line equation: p - (d*lineDir + linePoing) = 0;
66  * plane equation: ( p - planeOrigin) * planeNormal = 0;
67  * d = (planeOrigin - linePoint) * planeNormal / (lineDir * planeNormal )
68  * pInt = d*lineDir+linePoint;
69  */
70  real64 dummy[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( planeOrigin );
71  LvArray::tensorOps::subtract< 3 >( dummy, linePoint );
72  real64 const d = LvArray::tensorOps::AiBi< 3 >( dummy, planeNormal ) /
73  LvArray::tensorOps::AiBi< 3 >( lineDir, planeNormal );
74 
75  LvArray::tensorOps::copy< 3 >( intersectionPoint, linePoint );
76  LvArray::tensorOps::scaledAdd< 3 >( intersectionPoint, lineDir, d );
77 }
78 
86 template< typename NORMAL_TYPE >
88  NORMAL_TYPE const & normal )
89 {
90  localIndex const numPoints = points.size( 0 );
91 
92  array2d< real64 > orderedPoints( numPoints, 3 );
93 
94  array1d< int > indices( numPoints );
95  array1d< real64 > angle( numPoints );
96 
97  // compute centroid of the set of points
98  real64 centroid[3];
99  LvArray::tensorOps::fill< 3 >( centroid, 0 );
100  for( localIndex a = 0; a < numPoints; ++a )
101  {
102  LvArray::tensorOps::add< 3 >( centroid, points[ a ] );
103  indices[ a ] = a;
104  }
105 
106  LvArray::tensorOps::scale< 3 >( centroid, 1.0 / numPoints );
107 
108  real64 v0[3] = LVARRAY_TENSOROPS_INIT_LOCAL_3( centroid );
109  LvArray::tensorOps::subtract< 3 >( v0, points[ 0 ] );
110  LvArray::tensorOps::normalize< 3 >( v0 );
111 
112  // compute angles
113  angle[ 0 ] = 0;
114  for( localIndex a = 1; a < numPoints; ++a )
115  {
116  real64 v[3] = LVARRAY_TENSOROPS_INIT_LOCAL_3( centroid );
117  LvArray::tensorOps::subtract< 3 >( v, points[ a ] );
118  real64 const dot = LvArray::tensorOps::AiBi< 3 >( v, v0 );
119 
120  real64 crossProduct[ 3 ];
121  LvArray::tensorOps::crossProduct( crossProduct, v, v0 );
122  real64 const det = LvArray::tensorOps::AiBi< 3 >( normal, crossProduct );
123 
124  angle[ a ] = std::atan2( det, dot );
125  }
126 
127  // sort the indices
128  std::sort( indices.begin(), indices.end(), [&]( int i, int j ) { return angle[ i ] < angle[ j ]; } );
129 
130  // copy the points in the reorderedPoints array.
131  for( localIndex a=0; a < numPoints; a++ )
132  {
133  // fill in with ordered
134  LvArray::tensorOps::copy< 3 >( orderedPoints[ a ], points[ indices[ a ] ] );
135  }
136 
137  for( localIndex a = 0; a < numPoints; a++ )
138  {
139  LvArray::tensorOps::copy< 3 >( points[a], orderedPoints[a] );
140  }
141 
142  return indices;
143 }
144 
152 template< typename NORMAL_TYPE >
154  NORMAL_TYPE const && normal )
155 {
156  real64 surfaceArea = 0.0;
157 
158  array2d< real64 > orderedPoints( points.size( 0 ), 3 );
159 
160  for( localIndex a = 0; a < points.size( 0 ); a++ )
161  {
162  LvArray::tensorOps::copy< 3 >( orderedPoints[a], points[a] );
163  }
164 
165  orderPointsCCW( orderedPoints, normal );
166 
167  for( localIndex a = 0; a < points.size( 0 ) - 2; ++a )
168  {
169  real64 v1[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( orderedPoints[ a + 1 ] );
170  real64 v2[ 3 ] = LVARRAY_TENSOROPS_INIT_LOCAL_3( orderedPoints[ a + 2 ] );
171 
172  LvArray::tensorOps::subtract< 3 >( v1, orderedPoints[ 0 ] );
173  LvArray::tensorOps::subtract< 3 >( v2, orderedPoints[ 0 ] );
174 
175  real64 triangleNormal[ 3 ];
176  LvArray::tensorOps::crossProduct( triangleNormal, v1, v2 );
177  surfaceArea += LvArray::tensorOps::l2Norm< 3 >( triangleNormal );
178  }
179 
180  return surfaceArea * 0.5;
181 }
182 
191 template< localIndex DIMENSION, typename POINT_COORDS_TYPE >
194 real64 computeDiameter( POINT_COORDS_TYPE points,
195  localIndex const & numPoints )
196 {
197  real64 diameter = 0;
198  for( localIndex numPoint = 0; numPoint < numPoints; ++numPoint )
199  {
200  for( localIndex numOthPoint = 0; numOthPoint < numPoint; ++numOthPoint )
201  {
202  real64 candidateDiameter = 0.0;
203  for( localIndex i = 0; i < DIMENSION; ++i )
204  {
205  real64 coordDiff = points[numPoint][i] - points[numOthPoint][i];
206  candidateDiameter += coordDiff * coordDiff;
207  }
208  if( diameter < candidateDiameter )
209  {
210  diameter = candidateDiameter;
211  }
212  }
213  }
214  return LvArray::math::sqrt< real64 >( diameter );
215 }
216 
233 template< typename CENTER_TYPE, typename NORMAL_TYPE >
238  CENTER_TYPE && center,
239  NORMAL_TYPE && normal,
240  real64 const areaTolerance = 0.0 )
241 {
242  real64 area = 0.0;
243  LvArray::tensorOps::fill< 3 >( center, 0 );
244  LvArray::tensorOps::fill< 3 >( normal, 0 );
245 
246  localIndex const numberOfPoints = pointsIndices.size();
247 
248  GEOS_ERROR_IF_LT( numberOfPoints, 2 );
249 
250  real64 current[ 3 ], next[ 3 ], origin[ 3 ], crossProduct[ 3 ];
251 
252  // first moments of the fan triangles, accumulated in one pass; the projection on the
253  // normal and the shift of the fan anchor to the vertex average are applied after the loop
254  real64 pairSum[ 3 ], edgeDiff[ 3 ];
255  real64 crossMoment[ 3 ][ 3 ] = {{ 0 }};
256  real64 diffMoment[ 3 ][ 3 ] = {{ 0 }};
257 
258  LvArray::tensorOps::copy< 3 >( next, points[ pointsIndices[ numberOfPoints - 1 ] ] );
259  LvArray::tensorOps::copy< 3 >( origin, points[ pointsIndices[ 0 ]] );
260 
261  for( localIndex a=0; a<numberOfPoints; )
262  {
263  LvArray::tensorOps::copy< 3 >( current, points[ pointsIndices[ a++ ]] );
264  LvArray::tensorOps::scaledAdd< 3 >( current, origin, -1. );
265  LvArray::tensorOps::copy< 3 >( next, points[ pointsIndices[ a % numberOfPoints ] ] );
266  LvArray::tensorOps::scaledAdd< 3 >( next, origin, -1. );
267 
268  LvArray::tensorOps::crossProduct( crossProduct, current, next );
269 
270  LvArray::tensorOps::add< 3 >( normal, crossProduct );
271  LvArray::tensorOps::add< 3 >( center, next );
272 
273  // crossMoment += (v_a + v_b) (x) (v_a x v_b), diffMoment += (v_a + v_b) (x) (v_a - v_b)
274  LvArray::tensorOps::copy< 3 >( pairSum, current );
275  LvArray::tensorOps::add< 3 >( pairSum, next );
276  LvArray::tensorOps::copy< 3 >( edgeDiff, current );
277  LvArray::tensorOps::subtract< 3 >( edgeDiff, next );
278  LvArray::tensorOps::Rij_add_AiBj< 3, 3 >( crossMoment, pairSum, crossProduct );
279  LvArray::tensorOps::Rij_add_AiBj< 3, 3 >( diffMoment, pairSum, edgeDiff );
280  }
281 
282  area = LvArray::tensorOps::l2Norm< 3 >( normal );
283  LvArray::tensorOps::scale< 3 >( center, 1.0 / numberOfPoints );
284 
285  if( area > areaTolerance )
286  {
287  LvArray::tensorOps::normalize< 3 >( normal );
288  area *= 0.5;
289 
290  // area centroid (exact for planar polygons), fan anchored at the vertex average G = origin + d:
291  // center = origin + d/3 + ( crossMoment . n + diffMoment . (n x d) ) / (6 area)
292  real64 nxd[ 3 ], firstMoment[ 3 ] = { 0.0 };
293  LvArray::tensorOps::crossProduct( nxd, normal, center );
294  LvArray::tensorOps::Ri_add_AijBj< 3, 3 >( firstMoment, crossMoment, normal );
295  LvArray::tensorOps::Ri_add_AijBj< 3, 3 >( firstMoment, diffMoment, nxd );
296 
297  LvArray::tensorOps::scale< 3 >( center, 1.0 / 3.0 );
298  LvArray::tensorOps::scaledAdd< 3 >( center, firstMoment, 1.0 / ( 6.0 * area ) );
299  LvArray::tensorOps::scaledAdd< 3 >( center, origin, 1. );
300  }
301  else if( area < -areaTolerance )
302  {
303  for( localIndex a=0; a<numberOfPoints; ++a )
304  {
305  GEOS_LOG_RANK( "Points: " << points[ pointsIndices[ a ] ] << " " << pointsIndices[ a ] );
306  }
307 #if defined(GEOS_DEVICE_COMPILE)
308  GEOS_ERROR( "Negative area found" );
309 #else
310  GEOS_ERROR( GEOS_FMT( "Negative area found : {}", area ) );
311 #endif
312  }
313  else
314  {
315  for( localIndex a=0; a<numberOfPoints; ++a )
316  {
317  GEOS_LOG_RANK( "Points: " << points[ pointsIndices[ a ] ] << " " << pointsIndices[ a ] );
318  }
319 #if defined(GEOS_DEVICE_COMPILE)
320  GEOS_ERROR( "Null area found" );
321 #else
322  GEOS_ERROR( GEOS_FMT( "Null area found : {}", area ) );
323 #endif
324 
325  return 0.0;
326  }
327 
328  return area;
329 }
330 
336 template< typename NORMAL_TYPE >
338 void FixNormalOrientation_3D( NORMAL_TYPE && normal )
339 {
340  real64 const orientationTolerance = 10 * machinePrecision;
341 
342  // Orient local normal in global sense.
343  // First check: align with z direction
344  if( normal[ 2 ] <= -orientationTolerance )
345  {
346  LvArray::tensorOps::scale< 3 >( normal, -1.0 );
347  }
348  else if( std::fabs( normal[ 2 ] ) < orientationTolerance )
349  {
350  // If needed, second check: align with y direction
351  if( normal[ 1 ] <= -orientationTolerance )
352  {
353  LvArray::tensorOps::scale< 3 >( normal, -1.0 );
354  }
355  else if( fabs( normal[ 1 ] ) < orientationTolerance )
356  {
357  // If needed, third check: align with x direction
358  if( normal[ 0 ] <= -orientationTolerance )
359  {
360  LvArray::tensorOps::scale< 3 >( normal, -1.0 );
361  }
362  }
363  }
364 }
365 
373 template< typename NORMAL_TYPE, typename MATRIX_TYPE >
375 void RotationMatrix_3D( NORMAL_TYPE const & normal,
376  MATRIX_TYPE && rotationMatrix )
377 {
378  real64 m1[ 3 ] = { normal[ 2 ], 0.0, -normal[ 0 ] };
379  real64 m2[ 3 ] = { 0.0, normal[ 2 ], -normal[ 1 ] };
380  real64 const norm_m1 = LvArray::tensorOps::l2Norm< 3 >( m1 );
381  real64 const norm_m2 = LvArray::tensorOps::l2Norm< 3 >( m2 );
382 
383  // If present, looks for a vector with 0 norm
384  // Fix the uncertain case of norm_m1 very close to norm_m2
385  if( norm_m1+1.e+2*machinePrecision > norm_m2 )
386  {
387  LvArray::tensorOps::crossProduct( m2, normal, m1 );
388  LvArray::tensorOps::normalize< 3 >( m2 );
389  LvArray::tensorOps::normalize< 3 >( m1 );
390  }
391  else
392  {
393  LvArray::tensorOps::crossProduct( m1, normal, m2 );
394  LvArray::tensorOps::scale< 3 >( m1, -1 );
395  LvArray::tensorOps::normalize< 3 >( m1 );
396  LvArray::tensorOps::normalize< 3 >( m2 );
397  }
398 
399  // Save everything in the standard form (3x3 rotation matrix)
400  rotationMatrix[ 0 ][ 0 ] = normal[ 0 ];
401  rotationMatrix[ 1 ][ 0 ] = normal[ 1 ];
402  rotationMatrix[ 2 ][ 0 ] = normal[ 2 ];
403  rotationMatrix[ 0 ][ 1 ] = m1[ 0 ];
404  rotationMatrix[ 1 ][ 1 ] = m1[ 1 ];
405  rotationMatrix[ 2 ][ 1 ] = m1[ 2 ];
406  rotationMatrix[ 0 ][ 2 ] = m2[ 0 ];
407  rotationMatrix[ 1 ][ 2 ] = m2[ 1 ];
408  rotationMatrix[ 2 ][ 2 ] = m2[ 2 ];
409 
410  GEOS_ERROR_IF( fabs( LvArray::tensorOps::determinant< 3 >( rotationMatrix ) - 1.0 ) > 1.e+1 * machinePrecision,
411  "Rotation matrix with determinant different from +1.0" );
412 }
413 
420 template< typename T >
423 int sign( T const val )
424 {
425  return (T( 0 ) < val) - (val < T( 0 ));
426 }
427 
441 template< typename POINT_TYPE >
444  arraySlice1d< localIndex const > const & faceIndices,
445  ArrayOfArraysView< localIndex const > const & facesToNodes,
446  POINT_TYPE const & elemCenter,
447  POINT_TYPE const & point,
448  real64 const areaTolerance = 0.0 )
449 {
450  localIndex const numFaces = faceIndices.size();
451  R1Tensor faceCenter, faceNormal, cellToFaceVec;
452 
453  for( localIndex kf = 0; kf < numFaces; ++kf )
454  {
455  // compute the face normal at this face
456  localIndex const faceIndex = faceIndices[kf];
457  centroid_3DPolygon( facesToNodes[faceIndex], nodeCoordinates, faceCenter, faceNormal, areaTolerance );
458 
459  // make sure that the normal is outward pointing
460  LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter );
461  LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter );
462  if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 )
463  {
464  LvArray::tensorOps::scale< 3 >( faceNormal, -1 );
465  }
466 
467  // compute the vector face center to query point
468  LvArray::tensorOps::subtract< 3 >( faceCenter, point );
469  int const s = sign( LvArray::tensorOps::AiBi< 3 >( faceNormal, faceCenter ) );
470 
471  // all dot products should be non-negative (we enforce outward normals)
472  if( s < 0 )
473  {
474  return false;
475  }
476  }
477  return true;
478 }
479 
490 template< typename POLYGON_TYPE, typename POINT_TYPE >
491 bool isPointInPolygon2d( POLYGON_TYPE const & polygon,
492  integer n,
493  POINT_TYPE const & point,
494  real64 const tol = 1e-10 )
495 {
496  integer count = 0;
497 
498  for( integer i = 0; i < n; ++i )
499  {
500  auto const & p1 = polygon[i];
501  auto const & p2 = polygon[(i + 1) % n];
502 
503  real64 y1 = p1[1], y2 = p2[1];
504  real64 x1 = p1[0], x2 = p2[0];
505  real64 py = point[1], px = point[0];
506 
507  // quick reject in y with tolerance
508  if( py + tol < std::min( y1, y2 ) || py - tol > std::max( y1, y2 ) )
509  continue;
510 
511  // check if point is (approximately) on the segment
512  // parametric t for projection on segment in y (if segment vertical-ish use x)
513  if( std::abs( (x2 - x1) * (py - y1) - (px - x1) * (y2 - y1) ) < tol *
514  ( std::hypot( x2 - x1, y2 - y1 ) + 1.0 ) )
515  {
516  // ensure px is between x1,x2 and py between y1,y2 (with tol)
517  if( px + tol >= std::min( x1, x2 ) && px - tol <= std::max( x1, x2 ) &&
518  py + tol >= std::min( y1, y2 ) && py - tol <= std::max( y1, y2 ) )
519  return true; // on boundary -> consider inside
520  }
521 
522  // ignore nearly-horizontal edges for intersection counting
523  if( std::abs( y2 - y1 ) < tol )
524  continue;
525 
526  // compute x coordinate of intersection of horizontal line py with segment p1-p2
527  real64 xIntersect = x1 + (py - y1) * (x2 - x1) / (y2 - y1);
528 
529  // count crossing where intersection is strictly to the right of point (robust with tol)
530  if( px < xIntersect - tol )
531  ++count;
532  }
533 
534  return (count % 2) == 1;
535 }
536 
547 template< typename POLYGON_TYPE, typename POINT_TYPE >
548 bool isPointInPolygon3d( POLYGON_TYPE const & polygon,
549  integer const n,
550  POINT_TYPE const & point,
551  real64 const tol = 1e-10 )
552 {
553  // Check if the point lies in the plane of the polygon
554  auto const & p0 = polygon[0];
555  POINT_TYPE normal = {0, 0, 0};
556  for( integer i = 1; i < n - 1; i++ )
557  {
558  auto const & p1 = polygon[i];
559  auto const & p2 = polygon[i + 1];
560  normal[0] += (p1[1] - p0[1]) * (p2[2] - p0[2]) - (p1[2] - p0[2]) * (p2[1] - p0[1]);
561  normal[1] += (p1[2] - p0[2]) * (p2[0] - p0[0]) - (p1[0] - p0[0]) * (p2[2] - p0[2]);
562  normal[2] += (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p1[1] - p0[1]) * (p2[0] - p0[0]);
563  }
564 
565  real64 const dist = normal[0] * point[0] + normal[1] * point[1] + normal[2] * point[2] -(normal[0] * p0[0] + normal[1] * p0[1] + normal[2] * p0[2]);
566 
567  if( std::abs( dist ) > tol )
568  {
569  return false;
570  }
571 
572  // Determine the dominant component of the normal vector
573  int dominantIndex = 0;
574  if( std::abs( normal[1] ) > std::abs( normal[0] ))
575  {
576  dominantIndex = 1;
577  }
578  if( std::abs( normal[2] ) > std::abs( normal[dominantIndex] ))
579  {
580  dominantIndex = 2;
581  }
582 
583  // Project the polygon and the point onto a 2D plane
584  POLYGON_TYPE projectedPolygon( n );
585  POINT_TYPE projectedPoint;
586  if( dominantIndex == 0 ) // X is dominant, project onto YZ plane
587  {
588  for( int i = 0; i < n; i++ )
589  {
590  projectedPolygon[i][0] = polygon[i][1];
591  projectedPolygon[i][1] = polygon[i][2];
592  }
593  projectedPoint[0] = point[1];
594  projectedPoint[1] = point[2];
595  }
596  else if( dominantIndex == 1 ) // Y is dominant, project onto XZ plane
597  {
598  for( int i = 0; i < n; i++ )
599  {
600  projectedPolygon[i][0] = polygon[i][0];
601  projectedPolygon[i][1] = polygon[i][2];
602  }
603  projectedPoint[0] = point[0];
604  projectedPoint[1] = point[2];
605  }
606  else // Z is dominant, project onto XY plane
607  {
608  for( int i = 0; i < n; i++ )
609  {
610  projectedPolygon[i][0] = polygon[i][0];
611  projectedPolygon[i][1] = polygon[i][1];
612  }
613  projectedPoint[0] = point[0];
614  projectedPoint[1] = point[1];
615  }
616 
617  return isPointInPolygon2d( projectedPolygon, n, projectedPoint );
618 }
619 
632 template< typename COORD_TYPE, typename POINT_TYPE >
634 int lexicographicalCompareVertex( POINT_TYPE const ax, POINT_TYPE const ay, POINT_TYPE const az,
635  COORD_TYPE const bx, COORD_TYPE const by, COORD_TYPE const bz )
636 {
637  if( ax < bx )
638  return -1;
639  else if( ax > bx )
640  return 1;
641  if( ay < by )
642  return -1;
643  else if( ay > by )
644  return 1;
645  if( az < bz )
646  return -1;
647  else if( az > bz )
648  return 1;
649  return 0;
650 }
651 
667 template< typename COORD_TYPE, typename POINT_TYPE >
669 int lexicographicalCompareEdge( POINT_TYPE const ax, POINT_TYPE const ay, POINT_TYPE const az,
670  COORD_TYPE const e1x, COORD_TYPE const e1y, COORD_TYPE const e1z,
671  COORD_TYPE const e2x, COORD_TYPE const e2y, COORD_TYPE const e2z )
672 {
673  return lexicographicalCompareVertex( ( e1y - ay ) * ( e2x - ax ),
674  ( e1z - az ) * ( e2x - ax ),
675  ( e1z - az ) * ( e2y - ay ),
676  ( e1x - ax ) * ( e2y - ay ),
677  ( e1x - ax ) * ( e2z - az ),
678  ( e1y - ay ) * ( e2z - az ) );
679 }
680 
699 template< typename COORD_TYPE, typename POINT_TYPE >
701 int lexicographicalCompareTriangle( POINT_TYPE const ax, POINT_TYPE const ay, POINT_TYPE const az,
702  COORD_TYPE const t1x, COORD_TYPE const t1y, COORD_TYPE const t1z,
703  COORD_TYPE const t2x, COORD_TYPE const t2y, COORD_TYPE const t2z,
704  COORD_TYPE const t3x, COORD_TYPE const t3y, COORD_TYPE const t3z )
705 {
706  COORD_TYPE v1x = t1x - ax;
707  COORD_TYPE v1y = t1y - ay;
708  COORD_TYPE v1z = t1z - az;
709  COORD_TYPE v2x = t2x - ax;
710  COORD_TYPE v2y = t2y - ay;
711  COORD_TYPE v2z = t2z - az;
712  COORD_TYPE v3x = t3x - ax;
713  COORD_TYPE v3y = t3y - ay;
714  COORD_TYPE v3z = t3z - az;
715  COORD_TYPE sign = ( v1x * v2y - v1y * v2x ) * v3z +
716  ( v2x * v3y - v2y * v3x ) * v1z +
717  ( v3x * v1y - v3y * v1x ) * v2z;
718  if( sign > 0 )
719  return 1;
720  else if( sign < 0 )
721  return -1;
722  return 0;
723 }
731 template< typename ... LIST_TYPE >
734  arrayView1d< globalIndex const > const & elementGlobalIndex )
735 {
736  localIndex minElement = -1;
737  globalIndex minElementGID = LvArray::NumericLimits< globalIndex >::max;
738  for( int i = 0; i < nodeElements.size(); i++ )
739  {
740  localIndex e = nodeElements( i );
741  if( elementGlobalIndex[ e ] < minElementGID )
742  {
743  minElementGID = elementGlobalIndex[ e ];
744  minElement = e;
745  }
746  }
747  return minElement;
748 }
749 
758 template< typename ... LIST_TYPE >
761  arraySlice1d< localIndex const > const & nodeElements2,
762  arrayView1d< globalIndex const > const & elementGlobalIndex )
763 {
764  localIndex minElement = -1;
765  globalIndex minElementGID = LvArray::NumericLimits< globalIndex >::max;
766  for( int i = 0; i < nodeElements1.size(); i++ )
767  {
768  localIndex e1 = nodeElements1( i );
769  for( int j = 0; j < nodeElements2.size(); j++ )
770  {
771  localIndex e2 = nodeElements2( j );
772  if( e1 == e2 )
773  {
774  if( elementGlobalIndex[ e1 ] < minElementGID )
775  {
776  minElementGID = elementGlobalIndex[ e1 ];
777  minElement = e1;
778  }
779  }
780  }
781  }
782  return minElement;
783 }
784 
794 template< typename ... LIST_TYPE >
797  arraySlice1d< localIndex const > const & nodeElements2,
798  arraySlice1d< localIndex const > const & nodeElements3,
799  arrayView1d< globalIndex const > const & elementGlobalIndex )
800 {
801  localIndex minElement = -1;
802  globalIndex minElementGID = LvArray::NumericLimits< globalIndex >::max;
803  for( int i = 0; i < nodeElements1.size(); i++ )
804  {
805  localIndex e1 = nodeElements1( i );
806  for( int j = 0; j < nodeElements2.size(); j++ )
807  {
808  localIndex e2 = nodeElements2( j );
809  for( int k = 0; k < nodeElements3.size(); k++ )
810  {
811  localIndex e3 = nodeElements3( k );
812  if( e1 == e2 && e2 == e3 )
813  {
814  if( elementGlobalIndex[ e1 ] < minElementGID )
815  {
816  minElementGID = elementGlobalIndex[ e1 ];
817  minElement = e1;
818  }
819  }
820  }
821  }
822  }
823  return minElement;
824 }
825 
840 template< typename COORD_TYPE, typename POINT_TYPE >
844  arrayView2d< localIndex const > const & elementsToFaces,
845  ArrayOfArraysView< localIndex const > const & facesToNodes,
846  ArrayOfArraysView< localIndex const > const & nodesToElements,
847  arrayView1d< globalIndex const > const & nodeLocalToGlobal,
848  arrayView1d< globalIndex const > const & elementLocalToGlobal,
849  POINT_TYPE const & elemCenter,
850  POINT_TYPE const & point )
851 {
852  arraySlice1d< localIndex const > const & faceIndices = elementsToFaces[ element ];
853  localIndex const numFaces = faceIndices.size();
854  int omega = 0;
855  for( localIndex kf = 0; kf < numFaces; ++kf )
856  {
857  // triangulate the face. The triangulation must be done in a consistent way across ranks.
858  // This can be achieved by always picking the vertex with the lowest global index as root.
859  localIndex const faceIndex = faceIndices[kf];
860  globalIndex minGlobalId = LvArray::NumericLimits< globalIndex >::max;
861  localIndex minVertex = -1;
862  localIndex numFaceVertices = facesToNodes[faceIndex].size();
863  for( localIndex v = 0; v < numFaceVertices; v++ )
864  {
865  localIndex vIndex = facesToNodes( faceIndex, v );
866  globalIndex globalId = nodeLocalToGlobal[ vIndex ];
867  if( globalId < minGlobalId )
868  {
869  minGlobalId = globalId;
870  minVertex = vIndex;
871  }
872  }
873  // triangulate the face using the minimum-id vertex as root
874  localIndex vi[ 3 ] = { minVertex, -1, -1 };
875  for( localIndex v = 0; v < numFaceVertices; v++ )
876  {
877  vi[ 1 ] = facesToNodes( faceIndex, v );
878  vi[ 2 ] = facesToNodes( faceIndex, (v + 1) % numFaceVertices );
879  if( vi[ 1 ] != minVertex && vi[ 2 ] != minVertex )
880  {
881  // To make the algorithm independent of rank, always take the two additional vertices in increasing global ID
882  if( nodeLocalToGlobal[ vi[ 1 ] ] > nodeLocalToGlobal[ vi[ 2 ] ] )
883  {
884  localIndex temp = vi[ 1 ];
885  vi[ 1 ] = vi[ 2 ];
886  vi[ 2 ] = temp;
887  }
888  COORD_TYPE v1x = nodeCoordinates( vi[ 0 ], 0 );
889  COORD_TYPE v1y = nodeCoordinates( vi[ 0 ], 1 );
890  COORD_TYPE v1z = nodeCoordinates( vi[ 0 ], 2 );
891  COORD_TYPE v2x = nodeCoordinates( vi[ 1 ], 0 );
892  COORD_TYPE v2y = nodeCoordinates( vi[ 1 ], 1 );
893  COORD_TYPE v2z = nodeCoordinates( vi[ 1 ], 2 );
894  COORD_TYPE v3x = nodeCoordinates( vi[ 2 ], 0 );
895  COORD_TYPE v3y = nodeCoordinates( vi[ 2 ], 1 );
896  COORD_TYPE v3z = nodeCoordinates( vi[ 2 ], 2 );
897  // check the orientation of this triangle
898  R1Tensor vv1 = { v2x - v1x, v2y - v1y, v2z - v1z };
899  R1Tensor vv2 = { v3x - v1x, v3y - v1y, v3z - v1z };
900  R1Tensor dist = { elemCenter[ 0 ] - ( v1x + v2x + v3x )/3.0,
901  elemCenter[ 1 ] - ( v1y + v2y + v3y )/3.0,
902  elemCenter[ 2 ] - ( v1z + v2z + v3z )/3.0 };
903  R1Tensor norm = { };
904  LvArray::tensorOps::crossProduct( norm, vv1, vv2 );
905  // check if face is oriented coherently, and change sign otherwise
906  int sign = LvArray::tensorOps::AiBi< 3 >( norm, dist ) > 0 ? -1 : +1;
907  // Compute the winding number contributed by this triangle
908  int cmp1 = lexicographicalCompareVertex( point[ 0 ], point[ 1 ], point[ 2 ], v1x, v1y, v1z );
909  if( cmp1 == 0 )
910  {
911  return findVertexRefElement( nodesToElements[ vi[ 0 ] ], elementLocalToGlobal ) == element;
912  }
913  int cmp2 = lexicographicalCompareVertex( point[ 0 ], point[ 1 ], point[ 2 ], v2x, v2y, v2z );
914  if( cmp2 == 0 )
915  {
916  return findVertexRefElement( nodesToElements[ vi[ 1 ] ], elementLocalToGlobal ) == element;
917  }
918  int cmp3 = lexicographicalCompareVertex( point[ 0 ], point[ 1 ], point[ 2 ], v3x, v3y, v3z );
919  if( cmp3 == 0 )
920  {
921  return findVertexRefElement( nodesToElements[ vi[ 2 ] ], elementLocalToGlobal ) == element;
922  }
923  int facecmp = 0;
924  int edgecmp = 0;
925  if( cmp1 != cmp2 )
926  {
927  edgecmp = lexicographicalCompareEdge( point[ 0 ], point[ 1 ], point[ 2 ],
928  v1x, v1y, v1z,
929  v2x, v2y, v2z );
930  if( edgecmp == 0 )
931  {
932  return findEdgeRefElement( nodesToElements[ vi[ 0 ] ], nodesToElements[ vi[ 1 ] ], elementLocalToGlobal ) == element;
933  }
934  facecmp += sign * edgecmp;
935  }
936  if( cmp2 != cmp3 )
937  {
938  edgecmp = lexicographicalCompareEdge( point[ 0 ], point[ 1 ], point[ 2 ],
939  v2x, v2y, v2z,
940  v3x, v3y, v3z );
941  if( edgecmp == 0 )
942  {
943  return findEdgeRefElement( nodesToElements[ vi[ 1 ] ], nodesToElements[ vi[ 2 ] ], elementLocalToGlobal ) == element;
944  }
945  facecmp += sign * edgecmp;
946  }
947  if( cmp3 != cmp1 )
948  {
949  edgecmp = lexicographicalCompareEdge( point[ 0 ], point[ 1 ], point[ 2 ],
950  v3x, v3y, v3z,
951  v1x, v1y, v1z );
952  if( edgecmp == 0 )
953  {
954  return findEdgeRefElement( nodesToElements[ vi[ 0 ] ], nodesToElements[ vi[ 2 ] ], elementLocalToGlobal ) == element;
955  }
956  facecmp += sign * edgecmp;
957  }
958  // if all edges are on the same side, this triangle does not contribute to the winding number
959  if( facecmp == 0 )
960  continue;
961  facecmp = lexicographicalCompareTriangle( point[ 0 ], point[ 1 ], point[ 2 ],
962  v1x, v1y, v1z,
963  v2x, v2y, v2z,
964  v3x, v3y, v3z );
965 
966  if( facecmp == 0 )
967  {
968  return findTriangleRefElement( nodesToElements[ vi[ 0 ] ], nodesToElements[ vi[ 1 ] ], nodesToElements[ vi[ 2 ] ], elementLocalToGlobal ) == element;
969  }
970  omega += sign * facecmp;
971  }
972  }
973  }
974 
975  return omega;
976 }
977 
1001 template< typename COORD_TYPE, typename POINT_TYPE >
1005  arrayView2d< localIndex const > const & elementsToFaces,
1006  ArrayOfArraysView< localIndex const > const & facesToNodes,
1007  ArrayOfArraysView< localIndex const > const & nodesToElements,
1008  arrayView1d< globalIndex const > const & nodeLocalToGlobal,
1009  arrayView1d< globalIndex const > const & elementLocalToGlobal,
1010  POINT_TYPE const & elemCenter,
1011  POINT_TYPE const & point )
1012 {
1013  return computeWindingNumber( element, nodeCoordinates, elementsToFaces, facesToNodes, nodesToElements, nodeLocalToGlobal, elementLocalToGlobal, elemCenter, point ) > 0;
1014 }
1015 
1025 template< typename NODE_MAP_TYPE, typename VEC_TYPE >
1027 void getBoundingBox( localIndex const elemIndex,
1028  NODE_MAP_TYPE const & pointIndices,
1030  VEC_TYPE && boxDims )
1031 {
1032  // This holds the min coordinates of the set in each direction
1033  R1Tensor minCoords = { LvArray::NumericLimits< real64 >::max,
1034  LvArray::NumericLimits< real64 >::max,
1035  LvArray::NumericLimits< real64 >::max };
1036 
1037  // boxDims is used to hold the max coordinates.
1038  LvArray::tensorOps::fill< 3 >( boxDims, LvArray::NumericLimits< real64 >::lowest );
1039 
1040  // loop over all the vertices of the element to get the min and max coords
1041  for( localIndex a = 0; a < pointIndices[elemIndex].size(); ++a )
1042  {
1043  localIndex const id = pointIndices( elemIndex, a );
1044  for( localIndex d = 0; d < 3; ++d )
1045  {
1046  minCoords[ d ] = fmin( minCoords[ d ], pointCoordinates( id, d ) );
1047  boxDims[ d ] = fmax( boxDims[ d ], pointCoordinates( id, d ) );
1048  }
1049  }
1050 
1051  LvArray::tensorOps::subtract< 3 >( boxDims, minCoords );
1052 }
1053 
1060 template< typename FE_TYPE >
1061 GEOS_HOST_DEVICE inline
1062 real64 elementVolume( real64 const (&X)[FE_TYPE::numNodes][3] )
1063 {
1064  real64 result{};
1065  for( localIndex q=0; q<FE_TYPE::numQuadraturePoints; ++q )
1066  {
1067  result = result + FE_TYPE::transformedQuadratureWeight( q, X );
1068  }
1069  return result;
1070 }
1071 
1078 inline
1079 real64 hexahedronVolume( real64 const (&X)[8][3] )
1080 {
1081  return elementVolume< finiteElement::H1_Hexahedron_Lagrange1_GaussLegendre2 >( X );
1082 }
1083 
1090 inline
1091 real64 tetrahedronVolume( real64 const (&X)[4][3] )
1092 {
1093  return elementVolume< finiteElement::H1_Tetrahedron_Lagrange1_Gauss1 >( X );
1094 }
1095 
1102 inline
1103 real64 wedgeVolume( real64 const (&X)[6][3] )
1104 {
1105  return elementVolume< finiteElement::H1_Wedge_Lagrange1_Gauss6 >( X );
1106 }
1107 
1114 inline
1115 real64 pyramidVolume( real64 const (&X)[5][3] )
1116 {
1117  return elementVolume< finiteElement::H1_Pyramid_Lagrange1_Gauss5 >( X );
1118 }
1119 
1130 template< integer N >
1132 inline
1133 real64 prismVolume( real64 const (&X)[2*N][3] )
1134 {
1135  static_assert( N > 4,
1136  "Function prismVolume can be called for a prism with N-sided polygon base where N > 5." );
1137 
1138  real64 result{};
1139 
1140  // Compute the barycenters of the prism bases
1141  real64 XGBot[3]{};
1142  real64 XGTop[3]{};
1143  for( integer a = 0; a < N; ++a )
1144  {
1145  LvArray::tensorOps::add< 3 >( XGBot, X[a] );
1146  }
1147  for( integer a = N; a < 2 * N; ++a )
1148  {
1149  LvArray::tensorOps::add< 3 >( XGTop, X[a] );
1150  }
1151  LvArray::tensorOps::scale< 3 >( XGBot, 1.0 / N );
1152  LvArray::tensorOps::scale< 3 >( XGTop, 1.0 / N );
1153 
1154  real64 XWedge[6][3];
1155  for( int a = 0; a < N - 1; ++a )
1156  {
1157 
1158  LvArray::tensorOps::copy< 3 >( XWedge[0], X[a] );
1159  LvArray::tensorOps::copy< 3 >( XWedge[1], X[a+N] );
1160  LvArray::tensorOps::copy< 3 >( XWedge[2], X[a+1] );
1161  LvArray::tensorOps::copy< 3 >( XWedge[3], X[a+1+N] );
1162  LvArray::tensorOps::copy< 3 >( XWedge[4], XGBot );
1163  LvArray::tensorOps::copy< 3 >( XWedge[5], XGTop );
1164  result = result + computationalGeometry::elementVolume< finiteElement::H1_Wedge_Lagrange1_Gauss6 >( XWedge );
1165  }
1166  LvArray::tensorOps::copy< 3 >( XWedge[0], X[N-1] );
1167  LvArray::tensorOps::copy< 3 >( XWedge[1], X[2*N-1] );
1168  LvArray::tensorOps::copy< 3 >( XWedge[2], X[0] );
1169  LvArray::tensorOps::copy< 3 >( XWedge[3], X[N] );
1170  LvArray::tensorOps::copy< 3 >( XWedge[4], XGBot );
1171  LvArray::tensorOps::copy< 3 >( XWedge[5], XGTop );
1172  result = result + computationalGeometry::elementVolume< finiteElement::H1_Wedge_Lagrange1_Gauss6 >( XWedge );
1173  return result;
1174 }
1175 
1176 } /* namespace computationalGeometry */
1177 } /* namespace geos */
1178 
1179 #endif /* GEOS_MESH_UTILITIES_COMPUTATIONALGEOMETRY_HPP_ */
GEOS_HOST_DEVICE int lexicographicalCompareTriangle(POINT_TYPE const ax, POINT_TYPE const ay, POINT_TYPE const az, COORD_TYPE const t1x, COORD_TYPE const t1y, COORD_TYPE const t1z, COORD_TYPE const t2x, COORD_TYPE const t2y, COORD_TYPE const t2z, COORD_TYPE const t3x, COORD_TYPE const t3y, COORD_TYPE const t3z)
Method to perform lexicographic comparison of a node and a triangle based on coordinates.
GEOS_HOST_DEVICE void getBoundingBox(localIndex const elemIndex, NODE_MAP_TYPE 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 ...
GEOS_HOST_DEVICE bool isPointInsideConvexPolyhedronRobust(localIndex element, arrayView2d< COORD_TYPE const, nodes::REFERENCE_POSITION_USD > const &nodeCoordinates, arrayView2d< localIndex const > const &elementsToFaces, ArrayOfArraysView< localIndex const > const &facesToNodes, ArrayOfArraysView< localIndex const > const &nodesToElements, arrayView1d< globalIndex const > const &nodeLocalToGlobal, arrayView1d< globalIndex const > const &elementLocalToGlobal, POINT_TYPE const &elemCenter, POINT_TYPE const &point)
Check if a point is inside a convex polyhedron (3D polygon), using a robust method to avoid ambiguity...
GEOS_HOST_DEVICE void FixNormalOrientation_3D(NORMAL_TYPE &&normal)
Change the orientation of the input vector to be consistent in a global sense.
bool isPointInPolygon3d(POLYGON_TYPE const &polygon, integer const n, POINT_TYPE const &point, real64 const tol=1e-10)
Check if a point is inside a polygon (3D version)
GEOS_HOST_DEVICE real64 prismVolume(real64 const (&X)[2 *N][3])
Compute the volume of a prism with N-sided polygon base.
GEOS_HOST_DEVICE bool isPointInsidePolyhedron(arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const &nodeCoordinates, arraySlice1d< localIndex const > const &faceIndices, ArrayOfArraysView< localIndex const > const &facesToNodes, POINT_TYPE const &elemCenter, POINT_TYPE const &point, real64 const areaTolerance=0.0)
Check if a point is inside a convex polyhedron (3D polygon)
GEOS_HOST_DEVICE GEOS_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.
GEOS_HOST_DEVICE void RotationMatrix_3D(NORMAL_TYPE const &normal, MATRIX_TYPE &&rotationMatrix)
Calculate the rotation matrix for a face in the 3D space.
GEOS_HOST_DEVICE bool computeWindingNumber(localIndex element, arrayView2d< COORD_TYPE const, nodes::REFERENCE_POSITION_USD > const &nodeCoordinates, arrayView2d< localIndex const > const &elementsToFaces, ArrayOfArraysView< localIndex const > const &facesToNodes, ArrayOfArraysView< localIndex const > const &nodesToElements, arrayView1d< globalIndex const > const &nodeLocalToGlobal, arrayView1d< globalIndex const > const &elementLocalToGlobal, POINT_TYPE const &elemCenter, POINT_TYPE const &point)
Computes the winding number of a point with respect to a mesh element.
GEOS_HOST_DEVICE int findVertexRefElement(arraySlice1d< localIndex const > const &nodeElements, arrayView1d< globalIndex const > const &elementGlobalIndex)
Method to find the reference element touching a vertex. The element with the lowest global ID is chos...
bool isPointInPolygon2d(POLYGON_TYPE const &polygon, integer n, POINT_TYPE const &point, real64 const tol=1e-10)
Check if a point is inside a polygon (2D version)
constexpr real64 machinePrecision
Machine epsilon for double-precision calculations.
GEOS_HOST_DEVICE real64 elementVolume(real64 const (&X)[FE_TYPE::numNodes][3])
Compute the volume of an element (tetrahedron, pyramid, wedge, hexahedron)
GEOS_HOST_DEVICE int findTriangleRefElement(arraySlice1d< localIndex const > const &nodeElements1, arraySlice1d< localIndex const > const &nodeElements2, arraySlice1d< localIndex const > const &nodeElements3, arrayView1d< globalIndex const > const &elementGlobalIndex)
Method to find the reference element for a triangle. The element with the lowest global ID is chosen ...
GEOS_HOST_DEVICE real64 hexahedronVolume(real64 const (&X)[8][3])
Compute the volume of an hexahedron.
array1d< int > orderPointsCCW(arrayView2d< real64 > const &points, NORMAL_TYPE const &normal)
Reorder a set of points counter-clockwise.
GEOS_HOST_DEVICE int lexicographicalCompareVertex(POINT_TYPE const ax, POINT_TYPE const ay, POINT_TYPE const az, COORD_TYPE const bx, COORD_TYPE const by, COORD_TYPE const bz)
Method to perform lexicographic comparison of two nodes based on coordinates.
GEOS_HOST_DEVICE real64 tetrahedronVolume(real64 const (&X)[4][3])
Compute the volume of an tetrahedron.
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.
GEOS_HOST_DEVICE int findEdgeRefElement(arraySlice1d< localIndex const > const &nodeElements1, arraySlice1d< localIndex const > const &nodeElements2, arrayView1d< globalIndex const > const &elementGlobalIndex)
Method to find the reference element for an edge. The element with the lowest global ID is chosen fro...
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.
GEOS_HOST_DEVICE real64 wedgeVolume(real64 const (&X)[6][3])
Compute the volume of a wedge.
GEOS_HOST_DEVICE real64 pyramidVolume(real64 const (&X)[5][3])
Compute the volume of a pyramid.
GEOS_HOST_DEVICE GEOS_FORCE_INLINE real64 computeDiameter(POINT_COORDS_TYPE points, localIndex const &numPoints)
Calculate the diameter of a set of points in a given dimension.
GEOS_HOST_DEVICE GEOS_FORCE_INLINE int sign(T const val)
Return the sign of a given value as an integer.
GEOS_HOST_DEVICE int lexicographicalCompareEdge(POINT_TYPE const ax, POINT_TYPE const ay, POINT_TYPE const az, COORD_TYPE const e1x, COORD_TYPE const e1y, COORD_TYPE const e1z, COORD_TYPE const e2x, COORD_TYPE const e2y, COORD_TYPE const e2z)
Method to perform lexicographic comparison of a node and an edge based on coordinates.
#define GEOS_HOST_DEVICE
Marks a host-device function.
Definition: GeosxMacros.hpp:49
#define GEOS_FORCE_INLINE
Marks a function or lambda for inlining.
Definition: GeosxMacros.hpp:51
#define GEOS_ERROR(...)
Raise a hard error and terminate the program.
Definition: Logger.hpp:225
#define GEOS_LOG_RANK(msg)
Log a message to the rank output stream.
Definition: Logger.hpp:124
#define GEOS_ERROR_IF_LT(lhs, rhs)
Raise a hard error if one value compares less than the other.
Definition: Logger.hpp:527
#define GEOS_ERROR_IF(COND,...)
Conditionally raise a hard error and terminate the program.
Definition: Logger.hpp:216
ArrayView< T, 1 > arrayView1d
Alias for 1D array view.
Definition: DataTypes.hpp:179
Array< T, 2, PERMUTATION > array2d
Alias for 2D array.
Definition: DataTypes.hpp:191
GEOS_GLOBALINDEX_TYPE globalIndex
Global index type (for indexing objects across MPI partitions).
Definition: DataTypes.hpp:87
LvArray::ArrayOfArraysView< T, INDEX_TYPE const, CONST_SIZES, LvArray::ChaiBuffer > ArrayOfArraysView
View of array of variable-sized arrays. See LvArray::ArrayOfArraysView for details.
Definition: DataTypes.hpp:285
double real64
64-bit floating point type.
Definition: DataTypes.hpp:98
GEOS_LOCALINDEX_TYPE localIndex
Local index type (for indexing objects within an MPI partition).
Definition: DataTypes.hpp:84
ArraySlice< T, 1, USD > arraySlice1d
Alias for 1D array slice.
Definition: DataTypes.hpp:183
ArrayView< T, 2, USD > arrayView2d
Alias for 2D array view.
Definition: DataTypes.hpp:195
int integer
Signed integer type.
Definition: DataTypes.hpp:81
Array< T, 1 > array1d
Alias for 1D array.
Definition: DataTypes.hpp:175