Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
IntxUtils.cpp
Go to the documentation of this file.
1 /*
2  * IntxUtils.cpp
3  *
4  * Created on: Oct 3, 2012
5  */
6 #if defined( _MSC_VER ) || defined( WIN32 ) /* windows */
7 #define _USE_MATH_DEFINES // For M_PI
8 #endif
9 
10 #include <cmath>
11 #include <cassert>
12 #include <iostream>
13 #include <iomanip>
14 
16 
17 #include "moab/MergeMesh.hpp"
18 #include "moab/ReadUtilIface.hpp"
19 #include "MBTagConventions.hpp"
20 
21 #define CHECKNEGATIVEAREA
22 #ifdef CHECKNEGATIVEAREA
23 #include <iomanip>
24 #endif
25 #include <queue>
26 #include <map>
27 
28 #ifdef MOAB_HAVE_TEMPESTREMAP
29 #include "GridElements.h"
30 #endif
31 
32 #ifdef MOAB_HAVE_EIGEN3
33 #define EIGEN_NO_DEBUG
34 #define EIGEN_MAX_CPP_VER 11
35 #include "Eigen/Dense"
36 #endif
37 
38 namespace moab
39 {
40 /**
41  * This code defines several utility functions for computing edge intersections and performing geometric operations.
42  *
43  * - `borderPointsOfXinY2`: Computes the border points of a set of points `X` inside another set of points `Y`.
44  * - `SortAndRemoveDoubles2`: Sorts a set of points `P` according to their angles and removes duplicate points.
45  * - `EdgeIntersections2`: Computes the intersections between the edges of two sets of points `blue` and `red`.
46  * - `EdgeIntxRllCs`: Computes the intersections between the edges of a set of points `blue` and a set of points `red` on a specific plane.
47  *
48  * The code also defines some helper structs and functions used by these utility functions.
49  */
50 
51 #define CORRTAGNAME "__correspondent"
52 #define MAXEDGES 10
53 
54 /**
55  * Computes the border points of X in Y2.
56  *
57  * @param X The array of points representing X.
58  * @param nX The number of points in X.
59  * @param Y The array of points representing Y.
60  * @param nY The number of points in Y.
61  * @param P The array to store the border points of X in Y2.
62  * @param side The array to store the side information for each point in X.
63  * @param epsilon_area The epsilon value for area comparison.
64  * @return The number of extra points found.
65  */
66 int IntxUtils::borderPointsOfXinY2( double* X, int nX, double* Y, int nY, double* P, int* side, double epsilon_area )
67 {
68  // 2 triangles, 3 corners, is the corner of X in Y?
69  // Y must have a positive area
70  /*
71  */
72  int extraPoint = 0;
73  for( int i = 0; i < nX; i++ )
74  {
75  // compute double the area of all nY triangles formed by a side of Y and a corner of X; if
76  // one is negative, stop (negative means it is outside; X and Y are all oriented such that
77  // they are positive oriented;
78  // if one area is negative, it means it is outside the convex region, for sure)
79  double* A = X + 2 * i;
80 
81  int inside = 1;
82  for( int j = 0; j < nY; j++ )
83  {
84  double* B = Y + 2 * j;
85  int j1 = ( j + 1 ) % nY;
86  double* C = Y + 2 * j1; // no copy of data
87 
88  double area2 = ( B[0] - A[0] ) * ( C[1] - A[1] ) - ( C[0] - A[0] ) * ( B[1] - A[1] );
89  if( area2 < -epsilon_area )
90  {
91  inside = 0;
92  break;
93  }
94  }
95  if( inside )
96  {
97  side[i] = 1; // so vertex i of X is inside the convex region formed by Y
98  // so side has nX dimension (first array)
99  P[extraPoint * 2] = A[0];
100  P[extraPoint * 2 + 1] = A[1];
101  extraPoint++;
102  }
103  }
104  return extraPoint;
105 }
106 
107 // used to order according to angle, so it can remove double easily
109 {
110  double angle;
111  int index;
112 };
113 
115 {
116  return lhs.angle < rhs.angle;
117 }
118 
119 /**
120  * Sorts and removes duplicate points in the given array.
121  *
122  * Note: nP might be modified too, we will remove duplicates if found
123  *
124  * @param P The array of points to be sorted and checked for duplicates.
125  * @param nP The number of points in P.
126  * @param epsilon_1 The epsilon value for distance comparison.
127  * @return 0 if successful.
128  */
129 int IntxUtils::SortAndRemoveDoubles2( double* P, int& nP, double epsilon_1 )
130 {
131  if( nP < 2 ) return 0; // nothing to do
132 
133  // center of gravity for the points
134  double c[2] = { 0., 0. };
135  int k = 0;
136  for( k = 0; k < nP; k++ )
137  {
138  c[0] += P[2 * k];
139  c[1] += P[2 * k + 1];
140  }
141  c[0] /= nP;
142  c[1] /= nP;
143 
144  // how many? we dimensioned P at MAXEDGES*10; so we imply we could have at most 5*MAXEDGES
145  // intersection points
146  struct angleAndIndex pairAngleIndex[5 * MAXEDGES];
147 
148  for( k = 0; k < nP; k++ )
149  {
150  double x = P[2 * k] - c[0], y = P[2 * k + 1] - c[1];
151  if( x != 0. || y != 0. )
152  {
153  pairAngleIndex[k].angle = atan2( y, x );
154  }
155  else
156  {
157  pairAngleIndex[k].angle = 0;
158  // it would mean that the cells are touching at a vertex
159  }
160  pairAngleIndex[k].index = k;
161  }
162 
163  // this should be faster than the bubble sort we had before
164  std::sort( pairAngleIndex, pairAngleIndex + nP, angleCompare );
165  // copy now to a new double array
166  double PCopy[10 * MAXEDGES]; // the same dimension as P; very conservative, but faster than
167  // reallocate for a vector
168  for( k = 0; k < nP; k++ ) // index will show where it should go now;
169  {
170  int ck = pairAngleIndex[k].index;
171  PCopy[2 * k] = P[2 * ck];
172  PCopy[2 * k + 1] = P[2 * ck + 1];
173  }
174  // now copy from PCopy over original P location
175  std::copy( PCopy, PCopy + 2 * nP, P );
176 
177  // eliminate duplicates, finally
178 
179  int i = 0, j = 1; // the next one; j may advance faster than i
180  // check the unit
181  // double epsilon_1 = 1.e-5; // these are cm; 2 points are the same if the distance is less
182  // than 1.e-5 cm
183  while( j < nP )
184  {
185  double d2 = dist2( &P[2 * i], &P[2 * j] );
186  if( d2 > epsilon_1 )
187  {
188  i++;
189  P[2 * i] = P[2 * j];
190  P[2 * i + 1] = P[2 * j + 1];
191  }
192  j++;
193  }
194  // test also the last point with the first one (index 0)
195  // the first one could be at -PI; last one could be at +PI, according to atan2 span
196 
197  double d2 = dist2( P, &P[2 * i] ); // check the first and last points (ordered from -pi to +pi)
198  if( d2 > epsilon_1 )
199  {
200  nP = i + 1;
201  }
202  else
203  nP = i; // effectively delete the last point (that would have been the same with first)
204  if( nP == 0 ) nP = 1; // we should be left with at least one point we already tested if nP is 0 originally
205  return 0;
206 }
207 
208 /**
209  * Computes the edge intersections of two elements.
210  *
211  * @param blue The array of points representing the blue element.
212  * @param nsBlue The number of points in the blue element.
213  * @param red The array of points representing the red element.
214  * @param nsRed The number of points in the red element.
215  * @param markb The array to mark the intersecting edges of the blue element.
216  * @param markr The array to mark the intersecting edges of the red element.
217  * @param points The array to store the intersection points.
218  * @param nPoints The number of intersection points found.
219  * @return The error code.
220  */
222  int nsBlue,
223  double* red,
224  int nsRed,
225  int* markb,
226  int* markr,
227  double* points,
228  int& nPoints )
229 {
230  /* EDGEINTERSECTIONS computes edge intersections of two elements
231  [P,n]=EdgeIntersections(X,Y) computes for the two given elements * red
232  and blue ( stored column wise )
233  (point coordinates are stored column-wise, in counter clock
234  order) the points P where their edges intersect. In addition,
235  in n the indices of which neighbors of red are also intersecting
236  with blue are given.
237  */
238 
239  // points is an array with enough slots (24 * 2 doubles)
240  nPoints = 0;
241  for( int i = 0; i < MAXEDGES; i++ )
242  {
243  markb[i] = markr[i] = 0;
244  }
245 
246  for( int i = 0; i < nsBlue; i++ )
247  {
248  for( int j = 0; j < nsRed; j++ )
249  {
250  double b[2];
251  double a[2][2]; // 2*2
252  int iPlus1 = ( i + 1 ) % nsBlue;
253  int jPlus1 = ( j + 1 ) % nsRed;
254  for( int k = 0; k < 2; k++ )
255  {
256  b[k] = red[2 * j + k] - blue[2 * i + k];
257  // row k of a: a(k, 0), a(k, 1)
258  a[k][0] = blue[2 * iPlus1 + k] - blue[2 * i + k];
259  a[k][1] = red[2 * j + k] - red[2 * jPlus1 + k];
260  }
261  double delta = a[0][0] * a[1][1] - a[0][1] * a[1][0];
262  if( fabs( delta ) > 1.e-14 ) // this is close to machine epsilon
263  {
264  // not parallel
265  double alfa = ( b[0] * a[1][1] - a[0][1] * b[1] ) / delta;
266  double beta = ( -b[0] * a[1][0] + b[1] * a[0][0] ) / delta;
267  if( 0 <= alfa && alfa <= 1. && 0 <= beta && beta <= 1. )
268  {
269  // the intersection is good
270  for( int k = 0; k < 2; k++ )
271  {
272  points[2 * nPoints + k] = blue[2 * i + k] + alfa * ( blue[2 * iPlus1 + k] - blue[2 * i + k] );
273  }
274  markb[i] = 1; // so neighbor number i of blue will be considered too.
275  markr[j] = 1; // this will be used in advancing red around blue quad
276  nPoints++;
277  }
278  }
279  // the case delta ~ 0. will be considered by the interior points logic
280  }
281  }
282  return MB_SUCCESS;
283 }
284 
285 /**
286  * Computes the edge intersections between a RLL and CS quad.
287  *
288  * Note: Special function.
289  *
290  * @param blue The array of points representing the blue element.
291  * @param bluec The array of Cartesian coordinates for the blue element.
292  * @param blueEdgeType The array of edge types for the blue element.
293  * @param nsBlue The number of points in the blue element.
294  * @param red The array of points representing the red element.
295  * @param redc The array of Cartesian coordinates for the red element.
296  * @param nsRed The number of points in the red element.
297  * @param markb The array to mark the intersecting edges of the blue element.
298  * @param markr The array to mark the intersecting edges of the red element.
299  * @param plane The plane of intersection.
300  * @param R The radius of the sphere.
301  * @param points The array to store the intersection points.
302  * @param nPoints The number of intersection points found.
303  * @return The error code.
304  */
306  CartVect* bluec,
307  int* blueEdgeType,
308  int nsBlue,
309  double* red,
310  CartVect* redc,
311  int nsRed,
312  int* markb,
313  int* markr,
314  int plane,
315  double R,
316  double* points,
317  int& nPoints )
318 {
319  // if blue edge type is 1, intersect in 3d then project to 2d by gnomonic projection
320  // everything else the same (except if there are 2 points resulting, which is rare)
321  for( int i = 0; i < 4; i++ )
322  { // always at most 4 , so maybe don't bother
323  markb[i] = markr[i] = 0;
324  }
325 
326  for( int i = 0; i < nsBlue; i++ )
327  {
328  int iPlus1 = ( i + 1 ) % nsBlue;
329  if( blueEdgeType[i] == 0 ) // old style, just 2d
330  {
331  for( int j = 0; j < nsRed; j++ )
332  {
333  double b[2];
334  double a[2][2]; // 2*2
335 
336  int jPlus1 = ( j + 1 ) % nsRed;
337  for( int k = 0; k < 2; k++ )
338  {
339  b[k] = red[2 * j + k] - blue[2 * i + k];
340  // row k of a: a(k, 0), a(k, 1)
341  a[k][0] = blue[2 * iPlus1 + k] - blue[2 * i + k];
342  a[k][1] = red[2 * j + k] - red[2 * jPlus1 + k];
343  }
344  double delta = a[0][0] * a[1][1] - a[0][1] * a[1][0];
345  if( fabs( delta ) > 1.e-14 )
346  {
347  // not parallel
348  double alfa = ( b[0] * a[1][1] - a[0][1] * b[1] ) / delta;
349  double beta = ( -b[0] * a[1][0] + b[1] * a[0][0] ) / delta;
350  if( 0 <= alfa && alfa <= 1. && 0 <= beta && beta <= 1. )
351  {
352  // the intersection is good
353  for( int k = 0; k < 2; k++ )
354  {
355  points[2 * nPoints + k] =
356  blue[2 * i + k] + alfa * ( blue[2 * iPlus1 + k] - blue[2 * i + k] );
357  }
358  markb[i] = 1; // so neighbor number i of blue will be considered too.
359  markr[j] = 1; // this will be used in advancing red around blue quad
360  nPoints++;
361  }
362  } // if the edges are too "parallel", skip them
363  }
364  }
365  else // edge type is 1, so use 3d intersection
366  {
367  CartVect& C = bluec[i];
368  CartVect& D = bluec[iPlus1];
369  for( int j = 0; j < nsRed; j++ )
370  {
371  int jPlus1 = ( j + 1 ) % nsRed; // nsRed is just 4, forget about it, usually
372  CartVect& A = redc[j];
373  CartVect& B = redc[jPlus1];
374  int np = 0;
375  double E[9];
376  intersect_great_circle_arc_with_clat_arc( A.array(), B.array(), C.array(), D.array(), R, E, np );
377  if( np == 0 ) continue;
378  if( np >= 2 )
379  {
380  std::cout << "intersection with 2 points :" << A << B << C << D << "\n";
381  }
382  for( int k = 0; k < np; k++ )
383  {
384  gnomonic_projection( CartVect( E + k * 3 ), R, plane, points[2 * nPoints],
385  points[2 * nPoints + 1] );
386  nPoints++;
387  }
388  markb[i] = 1; // so neighbor number i of blue will be considered too.
389  markr[j] = 1; // this will be used in advancing red around blue quad
390  }
391  }
392  }
393  return MB_SUCCESS;
394 }
395 
396 // vec utils related to gnomonic projection on a sphere
397 
398 // vec utils
399 
400 /*
401  *
402  * position on a sphere of radius R
403  * if plane specified, use it; if not, return the plane, and the point in the plane
404  * there are 6 planes, numbered 1 to 6
405  * plane 1: x=R, plane 2: y=R, 3: x=-R, 4: y=-R, 5: z=-R, 6: z=R
406  *
407  * projection on the plane will preserve the orientation, such that a triangle, quad pointing
408  * outside the sphere will have a positive orientation in the projection plane
409  */
410 void IntxUtils::decide_gnomonic_plane( const CartVect& pos, int& plane )
411 {
412  // decide plane, based on max x, y, z
413  if( fabs( pos[0] ) < fabs( pos[1] ) )
414  {
415  if( fabs( pos[2] ) < fabs( pos[1] ) )
416  {
417  // pos[1] is biggest
418  if( pos[1] > 0 )
419  plane = 2;
420  else
421  plane = 4;
422  }
423  else
424  {
425  // pos[2] is biggest
426  if( pos[2] < 0 )
427  plane = 5;
428  else
429  plane = 6;
430  }
431  }
432  else
433  {
434  if( fabs( pos[2] ) < fabs( pos[0] ) )
435  {
436  // pos[0] is the greatest
437  if( pos[0] > 0 )
438  plane = 1;
439  else
440  plane = 3;
441  }
442  else
443  {
444  // pos[2] is biggest
445  if( pos[2] < 0 )
446  plane = 5;
447  else
448  plane = 6;
449  }
450  }
451  return;
452 }
453 
455 {
456 
457  double d = P.length();
458  if( d == 0.0 )
459  {
460  MB_CHK_SET_ERR( MB_FAILURE, "point P is at the origin" );
461  }
462  double x = P[0];
463  double y = P[1];
464  double z = P[2];
465  // easy cases
466  if( x == 0.0 && y == 0.0 )
467  {
468  if( z > 0. )
469  {
470  u = CartVect( 1., 0., 0. );
471  v = CartVect( 0., 1., 0. ); // gnomonic plane 6
472  }
473  else
474  {
475  u = CartVect( 0., 1., 0. );
476  v = CartVect( 1., 0., 0. ); // gnomonic plane 5
477  }
478  return MB_SUCCESS;
479  }
480  if( x == 0.0 && z == 0.0 )
481  {
482  if( y > 0. )
483  {
484  u = CartVect( -1., 0., 0. );
485  v = CartVect( 0., 0., 1. ); // gnomonic plane 2
486  }
487  else
488  {
489  u = CartVect( 0., 0., 1. );
490  v = CartVect( -1., 0., 0. ); // gnomonic plane 4
491  }
492  return MB_SUCCESS;
493  }
494  if( z == 0.0 && y == 0.0 )
495  {
496  if( x > 0. )
497  {
498  u = CartVect( 0., 1., 0. );
499  v = CartVect( 0., 0., 1. ); // gnomonic plane 1
500  }
501  else
502  {
503  u = CartVect( 0., 0., 1. );
504  v = CartVect( 0., 1., 0. ); // gnomonic plane 3
505  }
506  return MB_SUCCESS;
507  }
508  int plane;
510  if( 1 == plane ) // towards x > 0
511  {
512  u = CartVect( 1., 0., 0. ) * P;
513  }
514 
515  if( 2 == plane ) // towards y > 0
516  {
517  u = CartVect( 0., 1., 0. ) * P;
518  }
519  if( 3 == plane ) // towards x < 0
520  {
521  u = CartVect( -1., 0., 0. ) * P;
522  }
523  if( 4 == plane ) // towards y < 0
524  {
525  u = CartVect( 0., -1., 0. ) * P;
526  }
527  if( 5 == plane ) // towards z < 0
528  {
529  u = CartVect( 0., 0., -1. ) * P;
530  }
531  if( 6 == plane ) // towards z > 0
532  {
533  u = CartVect( 0., 0., 1. ) * P;
534  }
535  v = P * u;
536  u.normalize();
537  v.normalize();
538 
539  return MB_SUCCESS;
540 }
541 
543  const CartVect axis[3],
544  double& c1,
545  double& c2 )
546 {
547  double ang = angle( pos, axis[0] );
548  if( ang > 1.57 ) // pi/2 do not project if very close to hemisphere
549  return MB_FAILURE;
550  // solve the equation in plane, (alfa * pos - axis[0]) % axis[0] = 0.0
551  double alpha = axis[0] % axis[0] / ( pos % axis[0] ); // we know this denominator is greater than 0
552  CartVect planeVect = alpha * pos - axis[0]; // axis[0] is P
553  c1 = planeVect % axis[1];
554  c2 = planeVect % axis[2];
555  return MB_SUCCESS;
556 }
557 
558 // point on a sphere is projected on one of six planes, decided earlier
559 ErrorCode IntxUtils::gnomonic_projection( const CartVect& pos, double R, int plane, double& c1, double& c2 )
560 {
561  double alfa = 1.; // the new point will be on line alfa*pos
562 
563  switch( plane )
564  {
565  case 1: {
566  // the plane with x = R; x>y, x>z
567  // c1->y, c2->z
568  alfa = R / pos[0];
569  c1 = alfa * pos[1];
570  c2 = alfa * pos[2];
571  break;
572  }
573  case 2: {
574  // y = R -> zx
575  alfa = R / pos[1];
576  c1 = alfa * pos[2];
577  c2 = alfa * pos[0];
578  break;
579  }
580  case 3: {
581  // x=-R, -> yz
582  alfa = -R / pos[0];
583  c1 = -alfa * pos[1]; // the sign is to preserve orientation
584  c2 = alfa * pos[2];
585  break;
586  }
587  case 4: {
588  // y = -R
589  alfa = -R / pos[1];
590  c1 = -alfa * pos[2]; // the sign is to preserve orientation
591  c2 = alfa * pos[0];
592  break;
593  }
594  case 5: {
595  // z = -R
596  alfa = -R / pos[2];
597  c1 = -alfa * pos[0]; // the sign is to preserve orientation
598  c2 = alfa * pos[1];
599  break;
600  }
601  case 6: {
602  alfa = R / pos[2];
603  c1 = alfa * pos[0];
604  c2 = alfa * pos[1];
605  break;
606  }
607  default:
608  return MB_FAILURE; // error
609  }
610 
611  return MB_SUCCESS; // no error
612 }
613 
614 // given the position on plane (one out of 6), find out the position on sphere
616  const double& c2,
617  double R,
618  int plane,
619  CartVect& pos )
620 {
621 
622  // the new point will be on line beta*pos
623  double len = sqrt( c1 * c1 + c2 * c2 + R * R );
624  double beta = R / len; // it is less than 1, in general
625 
626  switch( plane )
627  {
628  case 1: {
629  // the plane with x = R; x>y, x>z
630  // c1->y, c2->z
631  pos[0] = beta * R;
632  pos[1] = c1 * beta;
633  pos[2] = c2 * beta;
634  break;
635  }
636  case 2: {
637  // y = R -> zx
638  pos[1] = R * beta;
639  pos[2] = c1 * beta;
640  pos[0] = c2 * beta;
641  break;
642  }
643  case 3: {
644  // x=-R, -> yz
645  pos[0] = -R * beta;
646  pos[1] = -c1 * beta; // the sign is to preserve orientation
647  pos[2] = c2 * beta;
648  break;
649  }
650  case 4: {
651  // y = -R
652  pos[1] = -R * beta;
653  pos[2] = -c1 * beta; // the sign is to preserve orientation
654  pos[0] = c2 * beta;
655  break;
656  }
657  case 5: {
658  // z = -R
659  pos[2] = -R * beta;
660  pos[0] = -c1 * beta; // the sign is to preserve orientation
661  pos[1] = c2 * beta;
662  break;
663  }
664  case 6: {
665  pos[2] = R * beta;
666  pos[0] = c1 * beta;
667  pos[1] = c2 * beta;
668  break;
669  }
670  default:
671  return MB_FAILURE; // error
672  }
673 
674  return MB_SUCCESS; // no error
675 }
676 
677 void IntxUtils::gnomonic_unroll( double& c1, double& c2, double R, int plane )
678 {
679  double tmp;
680  switch( plane )
681  {
682  case 1:
683  break; // nothing
684  case 2: // rotate + 90
685  tmp = c1;
686  c1 = -c2;
687  c2 = tmp;
688  c1 = c1 + 2 * R;
689  break;
690  case 3:
691  c1 = c1 + 4 * R;
692  break;
693  case 4: // rotate with -90 x-> -y; y -> x
694 
695  tmp = c1;
696  c1 = c2;
697  c2 = -tmp;
698  c1 = c1 - 2 * R;
699  break;
700  case 5: // South Pole
701  // rotate 180 then move to (-2, -2)
702  c1 = -c1 - 2. * R;
703  c2 = -c2 - 2. * R;
704  break;
705  ;
706  case 6: // North Pole
707  c1 = c1 - 2. * R;
708  c2 = c2 + 2. * R;
709  break;
710  }
711  return;
712 }
713 
714 // given a mesh on a hemisphere, and a point P that defines the hemisphere, project the mesh
715 // on a plane tangent at P (gnomonic plane at P)
717  EntityHandle inSet,
718  CartVect P,
719  EntityHandle& outSet )
720 {
721  std::string parTagName( "PARALLEL_PARTITION" );
722  Tag part_tag;
723  Tag gidTag = mb->globalId_tag();
724  Tag targetParentTag, sourceParentTag;
725  mb->tag_get_handle( "TargetParent", targetParentTag );
726  mb->tag_get_handle( "SourceParent", sourceParentTag );
727  bool intxMesh = false;
728  if( targetParentTag != nullptr && sourceParentTag != nullptr )
729  intxMesh = true; // interested in source and target parent tags then
730  Range partSets;
731  ErrorCode rval = mb->tag_get_handle( parTagName.c_str(), part_tag );
732  if( MB_SUCCESS == rval && part_tag != 0 )
733  {
734  rval =
735  mb->get_entities_by_type_and_tag( inSet, MBENTITYSET, &part_tag, nullptr, 1, partSets, Interface::UNION );MB_CHK_ERR( rval );
736  }
737  rval = ScaleToRadius( mb, inSet, 1.0 );MB_CHK_ERR( rval );
738  // Get all entities of dimension 2
739  Range inputRange; // get
740  rval = mb->get_entities_by_dimension( inSet, 1, inputRange );MB_CHK_ERR( rval );
741  rval = mb->get_entities_by_dimension( inSet, 2, inputRange );MB_CHK_ERR( rval );
742 
743  std::map< EntityHandle, int > partsAssign;
744  std::map< int, EntityHandle > newPartSets;
745  if( !partSets.empty() )
746  {
747  // get all cells, and assign parts
748  for( Range::iterator setIt = partSets.begin(); setIt != partSets.end(); ++setIt )
749  {
750  EntityHandle pSet = *setIt;
751  Range ents;
752  rval = mb->get_entities_by_handle( pSet, ents );MB_CHK_ERR( rval );
753  int val;
754  rval = mb->tag_get_data( part_tag, &pSet, 1, &val );MB_CHK_ERR( rval );
755  // create a new set with the same part id tag, in the outSet
756  EntityHandle newPartSet;
757  rval = mb->create_meshset( MESHSET_SET, newPartSet );MB_CHK_ERR( rval );
758  rval = mb->tag_set_data( part_tag, &newPartSet, 1, &val );MB_CHK_ERR( rval );
759  newPartSets[val] = newPartSet;
760  rval = mb->add_entities( outSet, &newPartSet, 1 );MB_CHK_ERR( rval );
761  for( Range::iterator it = ents.begin(); it != ents.end(); ++it )
762  {
763  partsAssign[*it] = val;
764  }
765  }
766  }
767 
768  // decide gnomonic plane
769  CartVect axis[3];
770  axis[0] = P;
771  IntxUtils::gnomonic_projection_plane_at_point( axis[0], axis[1], axis[2] );
772  // project all vertices, and then create new cells
773 
774  Range verts;
775  rval = mb->get_connectivity( inputRange, verts );MB_CHK_ERR( rval );
776  std::map< EntityHandle, EntityHandle > corr;
777  for( Range::iterator vt = verts.begin(); vt != verts.end(); ++vt )
778  {
779  CartVect vect;
780  EntityHandle v = *vt;
781  rval = mb->get_coords( &v, 1, vect.array() );MB_CHK_ERR( rval );
782  double c[3];
783  c[2] = 0.;
784  IntxUtils::gnomonic_projection_generalized( vect, axis, c[0], c[1] );
785 
787  rval = mb->create_vertex( c, vertex );MB_CHK_ERR( rval );
788  int vID;
789  if( !intxMesh )
790  {
791  rval = mb->tag_get_data( gidTag, &v, 1, &vID );MB_CHK_SET_ERR( rval, "can't get id tag on vertex" );
792  // new vertex will get old ID
793  rval = mb->tag_set_data( gidTag, &vertex, 1, &vID );MB_CHK_SET_ERR( rval, "can't get id tag on vertex" );
794  }
795  corr[v] = vertex; // for new connectivity
796  }
797  EntityHandle new_conn[20]; // max edges in 2d ?
798  for( Range::iterator eit = inputRange.begin(); eit != inputRange.end(); ++eit )
799  {
800  EntityHandle eh = *eit;
801  const EntityHandle* conn = nullptr;
802  int num_nodes;
803  rval = mb->get_connectivity( eh, conn, num_nodes );MB_CHK_ERR( rval );
804  // build a new vertex array
805  for( int j = 0; j < num_nodes; j++ )
806  new_conn[j] = corr[conn[j]];
807  EntityType type = mb->type_from_handle( eh );
808  EntityHandle newCell;
809  rval = mb->create_element( type, new_conn, num_nodes, newCell );MB_CHK_ERR( rval );
810  rval = mb->add_entities( outSet, &newCell, 1 );MB_CHK_ERR( rval );
811  int eID;
812  if( !intxMesh )
813  {
814  rval = mb->tag_get_data( gidTag, &eh, 1, &eID );MB_CHK_SET_ERR( rval, "can't get id tag on entity handle" );
815  // new vertex will get old ID
816  rval = mb->tag_set_data( gidTag, &newCell, 1, &eID );MB_CHK_SET_ERR( rval, "can't set id tag on new cell" );
817  }
818  else
819  {
820  // look for parent tags if intx mesh targetParentTag , sourceParentTag
821  if( type >= moab::MBPOLYGON )
822  {
823  rval = mb->tag_get_data( targetParentTag, &eh, 1, &eID );MB_CHK_SET_ERR( rval, "can't get parent tag on entity handle" );
824  rval = mb->tag_set_data( targetParentTag, &newCell, 1, &eID );MB_CHK_SET_ERR( rval, "can't set parent tag on entity handle" );
825  rval = mb->tag_get_data( sourceParentTag, &eh, 1, &eID );MB_CHK_SET_ERR( rval, "can't get parent tag on entity handle" );
826  rval = mb->tag_set_data( sourceParentTag, &newCell, 1, &eID );MB_CHK_SET_ERR( rval, "can't set parent tag on entity handle" );
827  }
828  }
829  std::map< EntityHandle, int >::iterator mit = partsAssign.find( eh );
830  if( mit != partsAssign.end() )
831  {
832  int val = mit->second;
833  rval = mb->add_entities( newPartSets[val], &newCell, 1 );MB_CHK_ERR( rval );
834  }
835  }
836  return MB_SUCCESS;
837 }
838 
839 // given a mesh on the sphere, project all centers in 6 gnomonic planes, or project mesh too
841  EntityHandle inSet,
842  double R,
843  bool centers_only,
844  EntityHandle& outSet )
845 {
846  std::string parTagName( "PARALLEL_PARTITION" );
847  Tag part_tag;
848  Tag gidTag = mb->globalId_tag();
849  Tag targetParentTag, sourceParentTag;
850  mb->tag_get_handle( "TargetParent", targetParentTag );
851  mb->tag_get_handle( "SourceParent", sourceParentTag );
852  bool intxMesh = false;
853  if( targetParentTag != nullptr && sourceParentTag != nullptr )
854  intxMesh = true; // interested in source and target parent tags then
855  Range partSets;
856  ErrorCode rval = mb->tag_get_handle( parTagName.c_str(), part_tag );
857  if( MB_SUCCESS == rval && part_tag != 0 )
858  {
859  MB_CHK_ERR(
860  mb->get_entities_by_type_and_tag( inSet, MBENTITYSET, &part_tag, nullptr, 1, partSets, Interface::UNION ) );
861  }
862  MB_CHK_ERR( ScaleToRadius( mb, inSet, 1.0 ) );
863  // Get all entities of dimension 2
864  Range inputRange; // get
865  MB_CHK_ERR( mb->get_entities_by_dimension( inSet, 1, inputRange ) );
866  MB_CHK_ERR( mb->get_entities_by_dimension( inSet, 2, inputRange ) );
867 
868  std::map< EntityHandle, int > partsAssign;
869  std::map< int, EntityHandle > newPartSets;
870  if( !partSets.empty() )
871  {
872  // get all cells, and assign parts
873  for( Range::iterator setIt = partSets.begin(); setIt != partSets.end(); ++setIt )
874  {
875  EntityHandle pSet = *setIt;
876  Range ents;
877  MB_CHK_ERR( mb->get_entities_by_handle( pSet, ents ) );
878  int val;
879  MB_CHK_ERR( mb->tag_get_data( part_tag, &pSet, 1, &val ) );
880  // create a new set with the same part id tag, in the outSet
881  EntityHandle newPartSet;
882  MB_CHK_ERR( mb->create_meshset( MESHSET_SET, newPartSet ) );
883  MB_CHK_ERR( mb->tag_set_data( part_tag, &newPartSet, 1, &val ) );
884  newPartSets[val] = newPartSet;
885  MB_CHK_ERR( mb->add_entities( outSet, &newPartSet, 1 ) );
886  for( Range::iterator it = ents.begin(); it != ents.end(); ++it )
887  {
888  partsAssign[*it] = val;
889  }
890  }
891  }
892 
893  if( centers_only )
894  {
895  for( Range::iterator it = inputRange.begin(); it != inputRange.end(); ++it )
896  {
898  EntityHandle cell = *it;
899  MB_CHK_ERR( mb->get_coords( &cell, 1, center.array() ) );
900  int globalID = 0;
901  if( !intxMesh )
902  {
903  MB_CHK_SET_ERR( mb->tag_get_data( gidTag, &cell, 1, &globalID ), "can't get id tag on cell" );
904  }
905 
906  int plane;
907  decide_gnomonic_plane( center, plane );
908  double c[3];
909  c[2] = 0.;
910  gnomonic_projection( center, R, plane, c[0], c[1] );
911 
912  gnomonic_unroll( c[0], c[1], R, plane );
913 
915  MB_CHK_ERR( mb->create_vertex( c, vertex ) );
916 
917  if( !intxMesh )
918  {
919  MB_CHK_SET_ERR( mb->tag_set_data( gidTag, &vertex, 1, &globalID ), "can't set id tag on center" );
920  }
921  MB_CHK_ERR( mb->add_entities( outSet, &vertex, 1 ) );
922  }
923  }
924  else
925  {
926  // distribute the cells to 6 planes, based on the center
927  Range subranges[6];
928  for( Range::iterator it = inputRange.begin(); it != inputRange.end(); ++it )
929  {
931  EntityHandle cell = *it;
932  MB_CHK_ERR( mb->get_coords( &cell, 1, center.array() ) );
933  int plane;
934  decide_gnomonic_plane( center, plane );
935  subranges[plane - 1].insert( cell ); // includes edges if they exist
936  }
937  for( int i = 1; i <= 6; i++ )
938  {
939  Range verts;
940  MB_CHK_ERR( mb->get_connectivity( subranges[i - 1], verts ) );
941  std::map< EntityHandle, EntityHandle > corr;
942  for( Range::iterator vt = verts.begin(); vt != verts.end(); ++vt )
943  {
944  CartVect vect;
945  EntityHandle v = *vt;
946  MB_CHK_ERR( mb->get_coords( &v, 1, vect.array() ) );
947  double c[3];
948  c[2] = 0.;
949  gnomonic_projection( vect, R, i, c[0], c[1] );
950  gnomonic_unroll( c[0], c[1], R, i );
952  MB_CHK_ERR( mb->create_vertex( c, vertex ) );
953 
954  int vID;
955  if( !intxMesh )
956  {
957  MB_CHK_SET_ERR( mb->tag_get_data( gidTag, &v, 1, &vID ), "can't get id tag on vertex" );
958  // new vertex will get old ID
959  MB_CHK_SET_ERR( mb->tag_set_data( gidTag, &vertex, 1, &vID ), "can't get id tag on vertex" );
960  }
961  corr[v] = vertex; // for new connectivity
962  }
963  EntityHandle new_conn[20]; // max edges in 2d ?
964  for( Range::iterator eit = subranges[i - 1].begin(); eit != subranges[i - 1].end(); ++eit )
965  {
966  EntityHandle eh = *eit;
967  const EntityHandle* conn = nullptr;
968  int num_nodes;
969  MB_CHK_ERR( mb->get_connectivity( eh, conn, num_nodes ) );
970 
971  // build a new vertex array
972  for( int j = 0; j < num_nodes; j++ )
973  new_conn[j] = corr[conn[j]];
974 
975  EntityType type = mb->type_from_handle( eh );
976  EntityHandle newCell;
977  MB_CHK_ERR( mb->create_element( type, new_conn, num_nodes, newCell ) );
978  MB_CHK_ERR( mb->add_entities( outSet, &newCell, 1 ) );
979 
980  int eID;
981  if( !intxMesh )
982  {
983  MB_CHK_SET_ERR( mb->tag_get_data( gidTag, &eh, 1, &eID ), "can't get id tag on entity handle" );
984  // new vertex will get old ID
985  MB_CHK_SET_ERR( mb->tag_set_data( gidTag, &newCell, 1, &eID ), "can't set id tag on new cell" );
986  }
987  else
988  {
989  // look for parent tags if intx mesh targetParentTag , sourceParentTag
990  if( type >= moab::MBPOLYGON )
991  {
992  MB_CHK_SET_ERR( mb->tag_get_data( targetParentTag, &eh, 1, &eID ),
993  "can't get parent tag on entity handle" );
994  MB_CHK_SET_ERR( mb->tag_set_data( targetParentTag, &newCell, 1, &eID ),
995  "can't set parent tag on entity handle" );
996  MB_CHK_SET_ERR( mb->tag_get_data( sourceParentTag, &eh, 1, &eID ),
997  "can't get parent tag on entity handle" );
998  MB_CHK_SET_ERR( mb->tag_set_data( sourceParentTag, &newCell, 1, &eID ),
999  "can't set parent tag on entity handle" );
1000  }
1001  }
1002 
1003  std::map< EntityHandle, int >::iterator mit = partsAssign.find( eh );
1004  if( mit != partsAssign.end() )
1005  {
1006  int val = mit->second;
1007  MB_CHK_ERR( mb->add_entities( newPartSets[val], &newCell, 1 ) );
1008  }
1009  }
1010  }
1011  }
1012 
1013  return MB_SUCCESS;
1014 }
1015 
1016 void IntxUtils::transform_coordinates( double* avg_position, int projection_type )
1017 {
1018  if( projection_type == 1 )
1019  {
1020  double R =
1021  avg_position[0] * avg_position[0] + avg_position[1] * avg_position[1] + avg_position[2] * avg_position[2];
1022  R = sqrt( R );
1023  double lat = asin( avg_position[2] / R );
1024  double lon = atan2( avg_position[1], avg_position[0] );
1025  avg_position[0] = lon;
1026  avg_position[1] = lat;
1027  avg_position[2] = R;
1028  }
1029  else if( projection_type == 2 ) // gnomonic projection
1030  {
1031  CartVect pos( avg_position );
1032  int gplane;
1033  IntxUtils::decide_gnomonic_plane( pos, gplane );
1034 
1035  IntxUtils::gnomonic_projection( pos, 1.0, gplane, avg_position[0], avg_position[1] );
1036  avg_position[2] = 0;
1037  IntxUtils::gnomonic_unroll( avg_position[0], avg_position[1], 1.0, gplane );
1038  }
1039 }
1040 
1041 /*
1042  *
1043  use physical_constants, only : dd_pi
1044  type(cartesian3D_t), intent(in) :: cart
1045  type(spherical_polar_t) :: sphere
1046 
1047  sphere%r=distance(cart)
1048  sphere%lat=ASIN(cart%z/sphere%r)
1049  sphere%lon=0
1050 
1051  ! ==========================================================
1052  ! enforce three facts:
1053  !
1054  ! 1) lon at poles is defined to be zero
1055  !
1056  ! 2) Grid points must be separated by about .01 Meter (on earth)
1057  ! from pole to be considered "not the pole".
1058  !
1059  ! 3) range of lon is { 0<= lon < 2*pi }
1060  !
1061  ! ==========================================================
1062 
1063  if (distance(cart) >= DIST_THRESHOLD) then
1064  sphere%lon=ATAN2(cart%y,cart%x)
1065  if (sphere%lon<0) then
1066  sphere%lon=sphere%lon+2*DD_PI
1067  end if
1068  end if
1069 
1070  end function cart_to_spherical
1071  */
1073 {
1074  SphereCoords res;
1075  res.R = cart3d.length();
1076  if( res.R < 0 )
1077  {
1078  res.lon = res.lat = 0.;
1079  return res;
1080  }
1081  res.lat = asin( cart3d[2] / res.R );
1082  res.lon = atan2( cart3d[1], cart3d[0] );
1083  if( res.lon < 0 ) res.lon += 2 * M_PI; // M_PI is defined in math.h? it seems to be true, although
1084  // there are some defines it depends on :(
1085  // #if defined __USE_BSD || defined __USE_XOPEN ???
1086 
1087  return res;
1088 }
1089 
1090 /*
1091  * ! ===================================================================
1092  ! spherical_to_cart:
1093  ! converts spherical polar {lon,lat} to 3D cartesian {x,y,z}
1094  ! on unit sphere
1095  ! ===================================================================
1096 
1097  function spherical_to_cart(sphere) result (cart)
1098 
1099  type(spherical_polar_t), intent(in) :: sphere
1100  type(cartesian3D_t) :: cart
1101 
1102  cart%x=sphere%r*COS(sphere%lat)*COS(sphere%lon)
1103  cart%y=sphere%r*COS(sphere%lat)*SIN(sphere%lon)
1104  cart%z=sphere%r*SIN(sphere%lat)
1105 
1106  end function spherical_to_cart
1107  */
1109 {
1110  CartVect res;
1111  res[0] = sc.R * cos( sc.lat ) * cos( sc.lon ); // x coordinate
1112  res[1] = sc.R * cos( sc.lat ) * sin( sc.lon ); // y
1113  res[2] = sc.R * sin( sc.lat ); // z
1114  return res;
1115 }
1116 
1118 {
1119  Range nodes;
1120  ErrorCode rval = mb->get_entities_by_type( set, MBVERTEX, nodes, true ); // recursive
1121  if( rval != moab::MB_SUCCESS ) return rval;
1122 
1123  // one by one, get the node and project it on the sphere, with a radius given
1124  // the center of the sphere is at 0,0,0
1125  for( Range::iterator nit = nodes.begin(); nit != nodes.end(); ++nit )
1126  {
1127  EntityHandle nd = *nit;
1128  CartVect pos;
1129  rval = mb->get_coords( &nd, 1, (double*)&( pos[0] ) );
1130  if( rval != moab::MB_SUCCESS ) return rval;
1131  double len = pos.length();
1132  if( len == 0. ) return MB_FAILURE;
1133  pos = R / len * pos;
1134  rval = mb->set_coords( &nd, 1, (double*)&( pos[0] ) );
1135  if( rval != moab::MB_SUCCESS ) return rval;
1136  }
1137  return MB_SUCCESS;
1138 }
1139 
1140 // assume they are one the same sphere
1141 double IntxAreaUtils::spherical_angle( const double* A, const double* B, const double* C, double Radius )
1142 {
1143  // the angle by definition is between the planes OAB and OBC
1144  CartVect a( A );
1145  CartVect b( B );
1146  CartVect c( C );
1147  double err1 = a.length_squared() - Radius * Radius;
1148  if( fabs( err1 ) > 0.0001 )
1149  {
1150  std::cout << " error in input " << a << " radius: " << Radius << " error:" << err1 << "\n";
1151  }
1152  CartVect normalOAB = a * b;
1153  CartVect normalOCB = c * b;
1154  return angle( normalOAB, normalOCB );
1155 }
1156 
1157 // could be bigger than M_PI;
1158 // angle at B could be bigger than M_PI, if the orientation is such that ABC points toward the
1159 // interior
1160 double IntxUtils::oriented_spherical_angle( const double* A, const double* B, const double* C )
1161 {
1162  // assume the same radius, sphere at origin
1163  CartVect a( A ), b( B ), c( C );
1164  CartVect normalOAB = a * b;
1165  CartVect normalOCB = c * b;
1166  CartVect orient = ( c - b ) * ( a - b );
1167  double ang = angle( normalOAB, normalOCB ); // this is between 0 and M_PI
1168  if( ang != ang )
1169  {
1170  // signal of a nan
1171  std::cout << a << " " << b << " " << c << "\n";
1172  std::cout << ang << "\n";
1173  }
1174  if( orient % b < 0 ) return ( 2 * M_PI - ang ); // the other angle, supplement
1175 
1176  return ang;
1177 }
1178 
1179 double IntxAreaUtils::area_spherical_triangle( const double* A, const double* B, const double* C, double Radius )
1180 {
1181  switch( m_eAreaMethod )
1182  {
1183  case Girard:
1184  return area_spherical_triangle_girard( A, B, C, Radius );
1185 #ifdef MOAB_HAVE_TEMPESTREMAP
1186  case GaussQuadrature:
1187  return area_spherical_triangle_GQ( A, B, C );
1188 #endif
1189  case lHuiller:
1190  default:
1191  return area_spherical_triangle_lHuiller( A, B, C, Radius );
1192  }
1193 }
1194 
1195 double IntxAreaUtils::area_spherical_polygon( const double* A, int N, double Radius, int* sign )
1196 {
1197  switch( m_eAreaMethod )
1198  {
1199  case Girard:
1200  return area_spherical_polygon_girard( A, N, Radius );
1201 #ifdef MOAB_HAVE_TEMPESTREMAP
1202  case GaussQuadrature:
1203  return area_spherical_polygon_GQ( A, N ) * Radius * Radius; //area_spherical_polygon_GQ normalizes
1204 #endif
1205  case lHuiller:
1206  default:
1207  return area_spherical_polygon_lHuiller( A, N, Radius, sign );
1208  }
1209 }
1210 
1211 double IntxAreaUtils::area_spherical_triangle_girard( const double* A, const double* B, const double* C, double Radius )
1212 {
1213  double correction = spherical_angle( A, B, C, Radius ) + spherical_angle( B, C, A, Radius ) +
1214  spherical_angle( C, A, B, Radius ) - M_PI;
1215  double area = Radius * Radius * correction;
1216  // now, is it negative or positive? is it pointing toward the center or outward?
1217  CartVect a( A ), b( B ), c( C );
1218  CartVect abc = ( b - a ) * ( c - a );
1219  if( abc % a > 0 ) // dot product positive, means ABC points out
1220  return area;
1221  else
1222  return -area;
1223 }
1224 
1225 double IntxAreaUtils::area_spherical_polygon_girard( const double* A, int N, double Radius )
1226 {
1227  // this should work for non-convex polygons too
1228  // assume that the A, A+3, ..., A+3*(N-1) are the coordinates
1229  //
1230  if( N <= 2 ) return 0.;
1231  double sum_angles = 0.;
1232  for( int i = 0; i < N; i++ )
1233  {
1234  int i1 = ( i + 1 ) % N;
1235  int i2 = ( i + 2 ) % N;
1236  sum_angles += IntxUtils::oriented_spherical_angle( A + 3 * i, A + 3 * i1, A + 3 * i2 );
1237  }
1238  double correction = sum_angles - ( N - 2 ) * M_PI;
1239  return Radius * Radius * correction;
1240 }
1241 
1242 double IntxAreaUtils::area_spherical_polygon_lHuiller( const double* A, int N, double Radius, int* sign )
1243 {
1244  // This should work for non-convex polygons too
1245  // In the input vector A, assume that the A, A+3, ..., A+3*(N-1) are the coordinates
1246  // We also assume that the orientation is positive;
1247  // If negative orientation, the area will be negative
1248  if( N <= 2 ) return 0.;
1249 
1250  int lsign = 1; // assume positive orientain
1251  double area = 0.;
1252  for( int i = 1; i < N - 1; i++ )
1253  {
1254  int i1 = i + 1;
1255  double areaTriangle = area_spherical_triangle_lHuiller( A, A + 3 * i, A + 3 * i1, Radius );
1256  if( areaTriangle < 0 ) lsign = -1; // signal that we have at least one triangle with negative orientation ;
1257  // possible nonconvex polygon
1258  area += areaTriangle;
1259  }
1260  if( sign ) *sign = lsign;
1261 
1262  return area;
1263 }
1264 
1265 #ifdef MOAB_HAVE_TEMPESTREMAP
1266 
1267 double IntxAreaUtils::area_spherical_polygon_GQ( const double* A, int N )
1268 {
1269  // this should work for non-convex polygons too
1270  // In the input vector A, assume that the A, A+3, ..., A+3*(N-1) are the coordinates
1271  // We also assume that the orientation is positive;
1272  // If negative orientation, the area can be negative
1273  if( N <= 2 ) return 0.;
1274 
1275  // assume positive orientation
1276  double area = 0.;
1277  for( int i = 1; i < N - 1; i++ )
1278  {
1279  area += area_spherical_triangle_GQ( A, A + 3 * i, A + 3 * ( i + 1 ) );
1280  }
1281  return area;
1282 }
1283 
1284 template < typename Derived >
1285 Eigen::Array< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > shift(
1286  const Eigen::ArrayBase< Derived >& array,
1287  int positions )
1288 {
1289  Eigen::Array< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > result = array;
1290  if( positions > 0 )
1291  {
1292  result.segment( positions, array.size() - positions ) = array.head( array.size() - positions );
1293  result.head( positions ).setZero();
1294  }
1295  else if( positions < 0 )
1296  {
1297  result.head( array.size() + positions ) = array.tail( array.size() + positions );
1298  result.tail( -positions ).setZero();
1299  }
1300  return result;
1301 }
1302 
1303 double IntxAreaUtils::area_spherical_triangle_GQ( const double* inode1, const double* inode2, const double* inode3 )
1304 {
1305 #if defined( MOAB_HAVE_EIGEN3 )
1306  typedef Eigen::Map< const Eigen::Vector3d > V3d;
1307  const V3d node1( inode1 );
1308  const V3d node2( inode2 );
1309  const V3d node3( inode3 );
1310  const int nOrder = 6;
1311 
1312  // If we change the quadrature order, use the call: GaussQuadrature::GetPoints(nOrder, 0.0, 1.0, dG, dW);
1313  const double dG[6] = { 0.03376524289842397, 0.1693953067668678, 0.3806904069584016,
1314  0.6193095930415985, 0.8306046932331322, 0.966234757101576 };
1315  const double dW[6] = { 0.08566224618958521, 0.1803807865240693, 0.2339569672863455,
1316  0.2339569672863455, 0.1803807865240693, 0.08566224618958521 };
1317 
1318  double dFaceArea = 0.0;
1319  Eigen::Vector3d dF, dF2, dDaF, dDbF, dDaG, dDbG;
1320  double nodeCross[3];
1321 
1322  // Calculate area at quadrature node and sum it up
1323  for( int p = 0; p < nOrder; p++ )
1324  {
1325  for( int q = 0; q < nOrder; q++ )
1326  {
1327 
1328  const double dA = dG[p];
1329  const double dB = dG[q];
1330 
1331  // V3d dF = (1.0 - dB) * (((1.0 - dA) * node1) + (dA * node2)) + (dB * node3);
1332  dF = ( ( ( 1.0 - dB ) * ( 1.0 - dA ) ) * node1 ) + ( ( ( 1.0 - dB ) * dA ) * node2 ) + ( dB * node3 );
1333  dF2 = dF.array().square();
1334 
1335  dDaF = ( node1 - node2 );
1336 
1337  // dDbF = -( 1.0 - dA ) * node1 - dA * node2 + node3;
1338  dDbF = ( node3 - node1 ) + dA * dDaF;
1339  dDaF *= ( dB - 1.0 );
1340 
1341  const double dDenomTerm = std::pow( dF.norm(), -3.0 );
1342 
1343  // Eigen::Vector3d temp1 = dF2;
1344  // temp1( 2 ) += dF2( 0 ); // temp1 = [dF2(0), dF2(1), dF2(0)+dF2(2)]
1345  // Eigen::Vector3d temp2 = dDaF.cwiseProduct( dF ); // temp2 = [dDaF(0)*dF(0), dDaF(1)*dF(1), dDaF(2)*dF(2)]
1346  // dDaG = dDaF.cwiseProduct( temp1.segment( 1, 2 ) ) - temp2.segment( 1, 2 );
1347  // Eigen::Vector3d temp3 = dDbF.cwiseProduct( dF ); // temp3 = [dDbF(0)*dF(0), dDbF(1)*dF(1), dDbF(2)*dF(2)]
1348  // dDbG = dDbF.cwiseProduct( temp1.segment( 1, 2 ) ) - temp3.segment( 1, 2 );
1349 
1350  // Eigen::Vector3d dF2_shifted = dF2 + Eigen::Vector3d( dF2( 1 ), dF2( 2 ), dF2( 0 ) );
1351  // Eigen::Vector3d dDaF_shifted = Eigen::Vector3d( dDaF( 1 ), dDaF( 2 ), dDaF( 0 ) ).cwiseProduct( dF );
1352 
1353  // dDaG = dDaF.cwiseProduct( dF2_shifted ) - dF.cwiseProduct( dDaF_shifted );
1354 
1355  // Eigen::Vector3d dDbF_shifted = Eigen::Vector3d( dDbF( 1 ), dDbF( 2 ), dDbF( 0 ) ).cwiseProduct( dF );
1356 
1357  // dDbG = dDbF.cwiseProduct( dF2_shifted ) - dF.cwiseProduct( dDbF_shifted );
1358 
1359  dDaG( 0 ) = dDaF( 0 ) * ( dF2( 1 ) + dF2( 2 ) ) - dF( 0 ) * ( dDaF( 1 ) * dF( 1 ) + dDaF( 2 ) * dF( 2 ) );
1360  dDaG( 1 ) = dDaF( 1 ) * ( dF2( 0 ) + dF2( 2 ) ) - dF( 1 ) * ( dDaF( 0 ) * dF( 0 ) + dDaF( 2 ) * dF( 2 ) );
1361  dDaG( 2 ) = dDaF( 2 ) * ( dF2( 0 ) + dF2( 1 ) ) - dF( 2 ) * ( dDaF( 0 ) * dF( 0 ) + dDaF( 1 ) * dF( 1 ) );
1362 
1363  // dDbG = dDbF.cwiseProduct( ( dF2.array().shift( -1 ) + dF2.array().shift( -2 ) ) ) + dF.cwiseProduct( dDbF );
1364  dDbG( 0 ) = dDbF( 0 ) * ( dF2( 1 ) + dF2( 2 ) ) - dF( 0 ) * ( dDbF( 1 ) * dF( 1 ) + dDbF( 2 ) * dF( 2 ) );
1365  dDbG( 1 ) = dDbF( 1 ) * ( dF2( 0 ) + dF2( 2 ) ) - dF( 1 ) * ( dDbF( 0 ) * dF( 0 ) + dDbF( 2 ) * dF( 2 ) );
1366  dDbG( 2 ) = dDbF( 2 ) * ( dF2( 0 ) + dF2( 1 ) ) - dF( 2 ) * ( dDbF( 0 ) * dF( 0 ) + dDbF( 1 ) * dF( 1 ) );
1367 
1368  // Scale the vectors by the denominator
1369  dDaG *= dDenomTerm;
1370  dDbG *= dDenomTerm;
1371 
1372  // Cross product gives local Jacobian: dGaG x dDbG
1373  nodeCross[0] = dDaG( 1 ) * dDbG( 2 ) - dDaG( 2 ) * dDbG( 1 );
1374  nodeCross[1] = dDaG( 2 ) * dDbG( 0 ) - dDaG( 0 ) * dDbG( 2 );
1375  nodeCross[2] = dDaG( 0 ) * dDbG( 1 ) - dDaG( 1 ) * dDbG( 0 );
1376 
1377  const double dJacobian =
1378  std::sqrt( nodeCross[0] * nodeCross[0] + nodeCross[1] * nodeCross[1] + nodeCross[2] * nodeCross[2] );
1379 
1380  // dFaceArea += 2.0 * dW[p] * dW[q] * (1.0 - dG[q]) * dJacobian;
1381  dFaceArea += dW[p] * dW[q] * dJacobian;
1382  }
1383  }
1384 
1385  return dFaceArea;
1386 #else
1387  /* compute the area by using Gauss-Quadratures; use TR interfaces directly */
1388  Face face( 3 );
1389  NodeVector nodes( 3 );
1390  nodes[0] = Node( inode1[0], inode1[1], inode1[2] );
1391  nodes[1] = Node( inode2[0], inode2[1], inode2[2] );
1392  nodes[2] = Node( inode3[0], inode3[1], inode3[2] );
1393  face.SetNode( 0, 0 );
1394  face.SetNode( 1, 1 );
1395  face.SetNode( 2, 2 );
1396  return CalculateFaceArea( face, nodes );
1397 #endif
1398 }
1399 
1400 #endif
1401 
1402 /*
1403  * l'Huiller's formula for spherical triangle
1404  * http://williams.best.vwh.net/avform.htm
1405  * a, b, c are arc measures in radians, too
1406  * A, B, C are angles on the sphere, for which we already have formula
1407  * c
1408  * A -------B
1409  * \ |
1410  * \ |
1411  * \b |a
1412  * \ |
1413  * \ |
1414  * \ |
1415  * \C|
1416  * \|
1417  *
1418  * (The angle at B is not necessarily a right angle)
1419  *
1420  * sin(a) sin(b) sin(c)
1421  * ----- = ------ = ------
1422  * sin(A) sin(B) sin(C)
1423  *
1424  * In terms of the sides (this is excess, as before, but numerically stable)
1425  *
1426  * E = 4*atan(sqrt(tan(s/2)*tan((s-a)/2)*tan((s-b)/2)*tan((s-c)/2)))
1427  */
1429  const double* ptB,
1430  const double* ptC,
1431  double Radius )
1432 {
1433 
1434  // now, a is angle BOC, O is origin
1435  CartVect vA( ptA ), vB( ptB ), vC( ptC );
1436  double a = angle_robust( vB, vC );
1437  double b = angle_robust( vC, vA );
1438  double c = angle_robust( vA, vB );
1439  int sign = 1;
1440  // if( fabs( ( vA * vB ) % vC ) < 1e-17 ) sign = -1;
1441  if( ( vA * vB ) % vC < 0 ) sign = -1;
1442  double s = ( a + b + c ) / 2;
1443  double a1 = ( s - a ) / 2;
1444  double b1 = ( s - b ) / 2;
1445  double c1 = ( s - c ) / 2;
1446 #ifdef MOAB_HAVE_TEMPESTREMAP
1447  if( fabs( a1 ) < 1.e-14 || fabs( b1 ) < 1.e-14 || fabs( c1 ) < 1.e-14 )
1448  {
1449  double area = area_spherical_triangle_GQ( ptA, ptB, ptC ) * sign;
1450 #ifdef VERBOSE
1451  std::cout << " very obtuse angle, use TR to compute area "
1452  << " a1:" << a1 << " b1:" << b1 << " c1:" << c1 << "\n";
1453  std::cout << " area with TR: " << area << "\n";
1454 #endif
1455  return area;
1456  }
1457 #endif
1458  double tmp = tan( s / 2 ) * tan( a1 ) * tan( b1 ) * tan( c1 );
1459  if( tmp < 0. ) tmp = 0.;
1460 
1461  double E = 4 * atan( sqrt( tmp ) );
1462  if( E != E ) std::cout << " NaN at spherical triangle area \n";
1463 
1464  double area = sign * E * Radius * Radius;
1465 
1466 #ifdef CHECKNEGATIVEAREA
1467  if( area < 0 )
1468  {
1469  std::cout << "negative area: " << area << "\n";
1470  std::cout << std::setprecision( 15 );
1471  std::cout << "vA: " << vA << "\n";
1472  std::cout << "vB: " << vB << "\n";
1473  std::cout << "vC: " << vC << "\n";
1474  std::cout << "sign: " << sign << "\n";
1475  std::cout << " a: " << a << "\n";
1476  std::cout << " b: " << b << "\n";
1477  std::cout << " c: " << c << "\n";
1478  }
1479 #endif
1480 
1481  return area;
1482 }
1483 
1485 {
1486  // Get all entities of dimension 2
1487  Range inputRange;
1488  ErrorCode rval = mb->get_entities_by_dimension( set, 2, inputRange );MB_CHK_ERR_RET_VAL( rval, -1.0 );
1489 
1490  // Filter by elements that are owned by current process
1491  std::vector< int > ownerinfo( inputRange.size(), -1 );
1492  Tag intxOwnerTag;
1493  rval = mb->tag_get_handle( "ORIG_PROC", intxOwnerTag );
1494  if( MB_SUCCESS == rval )
1495  {
1496  rval = mb->tag_get_data( intxOwnerTag, inputRange, &ownerinfo[0] );MB_CHK_ERR_RET_VAL( rval, -1.0 );
1497  }
1498 
1499  // compare total area with 4*M_PI * R^2
1500  int ie = 0;
1501  double total_area = 0.;
1502  for( Range::iterator eit = inputRange.begin(); eit != inputRange.end(); ++eit )
1503  {
1504 
1505  // All zero/positive owner data represents ghosted elems
1506  if( ownerinfo[ie++] >= 0 ) continue;
1507 
1508  EntityHandle eh = *eit;
1509  const double elem_area = this->area_spherical_element( mb, eh, R );
1510 
1511  // check whether the area of the spherical element is positive.
1512  if( elem_area <= 0 )
1513  {
1514  std::cout << "Area of element " << mb->id_from_handle( eh ) << " is = " << elem_area << "\n";
1515  mb->list_entity( eh );
1516  }
1517  assert( elem_area > 0 );
1518 
1519  // sum up the contribution
1520  total_area += elem_area;
1521  }
1522 
1523  // return total mesh area
1524  return total_area;
1525 }
1526 
1528 {
1529  // get the nodes, then the coordinates
1530  const EntityHandle* verts;
1531  int nsides;
1532  ErrorCode rval = mb->get_connectivity( elem, verts, nsides );MB_CHK_ERR_RET_VAL( rval, -1.0 );
1533 
1534  // account for possible padded polygons
1535  while( verts[nsides - 2] == verts[nsides - 1] && nsides > 3 )
1536  nsides--;
1537 
1538  // get coordinates
1539  std::vector< double > coords( 3 * nsides );
1540  rval = mb->get_coords( verts, nsides, &coords[0] );MB_CHK_ERR_RET_VAL( rval, -1.0 );
1541 
1542  // compute and return the area of the polygonal element
1543  return area_spherical_polygon( &coords[0], nsides, R );
1544 }
1545 
1547 {
1548  SphereCoords sph1 = cart_to_spherical( p1 );
1549  SphereCoords sph2 = cart_to_spherical( p2 );
1550  // radius should be the same
1551  return sph1.R *
1552  acos( sin( sph1.lon ) * sin( sph2.lon ) + cos( sph1.lat ) * cos( sph2.lat ) * cos( sph2.lon - sph2.lon ) );
1553 }
1554 
1555 //
1556 /**
1557  * @brief Enforces convexity for a given set of polygons.
1558  *
1559  * This function checks each polygon in the input set and computes the angles of each vertex.
1560  * If a reflex angle is found, the polygon is broken into triangles and added back to the set.
1561  * This process continues until all polygons in the set are convex.
1562  *
1563  * @param mb The interface to the MOAB instance.
1564  * @param lset The handle of the input set containing the polygons.
1565  * @param my_rank The rank of the local process.
1566  * @return The error code indicating the success or failure of the operation.
1567  */
1569 {
1570  Range inputRange;
1571  MB_CHK_ERR( mb->get_entities_by_dimension( lset, 2, inputRange ) );
1572 
1573  Tag corrTag = nullptr;
1574  EntityHandle dumH = 0;
1575  // no need to check return error
1576  mb->tag_get_handle( CORRTAGNAME, 1, MB_TYPE_HANDLE, corrTag, MB_TAG_DENSE, &dumH );
1577 
1578  Tag gidTag = mb->globalId_tag();
1579 
1580  std::vector< double > coords;
1581  coords.resize( 3 * MAXEDGES ); // at most 10 vertices per polygon
1582  // we should create a queue with new polygons that need processing for reflex angles
1583  // (obtuse)
1584  std::queue< EntityHandle > newPolys;
1585  int brokenPolys = 0;
1586  Range::iterator eit = inputRange.begin();
1587  while( eit != inputRange.end() || !newPolys.empty() )
1588  {
1589  EntityHandle eh;
1590  if( eit != inputRange.end() )
1591  {
1592  eh = *eit;
1593  ++eit;
1594  }
1595  else
1596  {
1597  eh = newPolys.front();
1598  newPolys.pop();
1599  }
1600  // get the nodes, then the coordinates
1601  const EntityHandle* verts;
1602  int num_nodes;
1603  MB_CHK_ERR( mb->get_connectivity( eh, verts, num_nodes ) );
1604  int nsides = num_nodes;
1605  // account for possible padded polygons
1606  while( verts[nsides - 2] == verts[nsides - 1] && nsides > 3 )
1607  nsides--;
1608  EntityHandle corrHandle = 0;
1609  if( corrTag )
1610  {
1611  MB_CHK_ERR( mb->tag_get_data( corrTag, &eh, 1, &corrHandle ) );
1612  }
1613  int gid = 0;
1614  MB_CHK_ERR( mb->tag_get_data( gidTag, &eh, 1, &gid ) );
1615  coords.resize( 3 * nsides );
1616  if( nsides < 4 ) continue; // if already triangles, don't bother
1617  // get coordinates
1618  MB_CHK_ERR( mb->get_coords( verts, nsides, &coords[0] ) );
1619  // compute each angle
1620  bool alreadyBroken = false;
1621 
1622  for( int i = 0; i < nsides; i++ )
1623  {
1624  double* A = &coords[3 * i];
1625  double* B = &coords[3 * ( ( i + 1 ) % nsides )];
1626  double* C = &coords[3 * ( ( i + 2 ) % nsides )];
1627  double angle = IntxUtils::oriented_spherical_angle( A, B, C );
1628  if( angle - M_PI > 0. ) // even almost reflex is bad; break it!
1629  {
1630  if( alreadyBroken )
1631  {
1632  mb->list_entities( &eh, 1 );
1633  mb->list_entities( verts, nsides );
1634  double* D = &coords[3 * ( ( i + 3 ) % nsides )];
1635  std::cout << "ABC: " << angle << " \n";
1636  std::cout << "BCD: " << IntxUtils::oriented_spherical_angle( B, C, D ) << " \n";
1637  std::cout << "CDA: " << IntxUtils::oriented_spherical_angle( C, D, A ) << " \n";
1638  std::cout << "DAB: " << IntxUtils::oriented_spherical_angle( D, A, B ) << " \n";
1639  std::cout << " this cell has at least 2 angles > 180, it has serious issues\n";
1640 
1641  return MB_FAILURE;
1642  }
1643  // the bad angle is at i+1;
1644  // create 1 triangle and one polygon; add the polygon to the input range, so
1645  // it will be processed too
1646  // also, add both to the set :) and remove the original polygon from the set
1647  // break the next triangle, even though not optimal
1648  // so create the triangle i+1, i+2, i+3; remove i+2 from original list
1649  // even though not optimal in general, it is good enough.
1650  EntityHandle conn3[3] = { verts[( i + 1 ) % nsides], verts[( i + 2 ) % nsides],
1651  verts[( i + 3 ) % nsides] };
1652  // create a polygon with num_nodes-1 vertices, and connectivity
1653  // verts[i+1], verts[i+3], (all except i+2)
1654  std::vector< EntityHandle > conn( nsides - 1 );
1655  for( int j = 1; j < nsides; j++ )
1656  {
1657  conn[j - 1] = verts[( i + j + 2 ) % nsides];
1658  }
1659  EntityHandle newElement;
1660  MB_CHK_ERR( mb->create_element( MBTRI, conn3, 3, newElement ) );
1661 
1662  MB_CHK_ERR( mb->add_entities( lset, &newElement, 1 ) );
1663  if( corrTag )
1664  {
1665  MB_CHK_ERR( mb->tag_set_data( corrTag, &newElement, 1, &corrHandle ) );
1666  }
1667  MB_CHK_ERR( mb->tag_set_data( gidTag, &newElement, 1, &gid ) );
1668  if( nsides == 4 )
1669  {
1670  // create another triangle
1671  MB_CHK_ERR( mb->create_element( MBTRI, &conn[0], 3, newElement ) );
1672  }
1673  else
1674  {
1675  // create another polygon, and add it to the inputRange
1676  MB_CHK_ERR( mb->create_element( MBPOLYGON, &conn[0], nsides - 1, newElement ) );
1677  newPolys.push( newElement ); // because it has less number of edges, the
1678  // reverse should work to find it.
1679  }
1680  MB_CHK_ERR( mb->add_entities( lset, &newElement, 1 ) );
1681  if( corrTag )
1682  {
1683  MB_CHK_ERR( mb->tag_set_data( corrTag, &newElement, 1, &corrHandle ) );
1684  }
1685  MB_CHK_ERR( mb->tag_set_data( gidTag, &newElement, 1, &gid ) );
1686  MB_CHK_ERR( mb->remove_entities( lset, &eh, 1 ) );
1687  brokenPolys++;
1688  alreadyBroken = true; // get out of the loop, element is broken
1689  }
1690  }
1691  }
1692  if( brokenPolys > 0 )
1693  {
1694  std::cout << "on local process " << my_rank << ", " << brokenPolys
1695  << " concave polygons were decomposed in convex ones \n";
1696 #ifdef VERBOSE
1697  std::stringstream fff;
1698  fff << "file_set" << mb->id_from_handle( lset ) << "rk_" << my_rank << ".h5m";
1699  MB_CHK_ERR( mb->write_file( fff.str().c_str(), 0, 0, &lset, 1 ) );
1700  std::cout << "wrote new file set: " << fff.str() << "\n";
1701 #endif
1702  }
1703  return MB_SUCCESS;
1704 }
1705 
1706 // looking at quad connectivity, collapse to triangle if 2 nodes equal
1707 // then delete the old quad
1709 {
1710  Range quads;
1711  MB_CHK_ERR( mb->get_entities_by_type( set, MBQUAD, quads ) );
1712 
1713  Tag gid = mb->globalId_tag();
1714  for( Range::iterator qit = quads.begin(); qit != quads.end(); ++qit )
1715  {
1716  EntityHandle quad = *qit;
1717  const EntityHandle* conn4 = nullptr;
1718  int num_nodes = 0;
1719  MB_CHK_ERR( mb->get_connectivity( quad, conn4, num_nodes ) );
1720  for( int i = 0; i < num_nodes; i++ )
1721  {
1722  int next_node_index = ( i + 1 ) % num_nodes;
1723  if( conn4[i] == conn4[next_node_index] )
1724  {
1725  // form a triangle and delete the quad
1726  // first get the global id, to set it on triangle later
1727  int global_id = 0;
1728  MB_CHK_ERR( mb->tag_get_data( gid, &quad, 1, &global_id ) );
1729  int i2 = ( i + 2 ) % num_nodes;
1730  int i3 = ( i + 3 ) % num_nodes;
1731  EntityHandle conn3[3] = { conn4[i], conn4[i2], conn4[i3] };
1732  EntityHandle tri;
1733  MB_CHK_ERR( mb->create_element( MBTRI, conn3, 3, tri ) );
1734  MB_CHK_ERR( mb->add_entities( set, &tri, 1 ) );
1735  MB_CHK_ERR( mb->remove_entities( set, &quad, 1 ) );
1736  MB_CHK_ERR( mb->delete_entities( &quad, 1 ) );
1737  MB_CHK_ERR( mb->tag_set_data( gid, &tri, 1, &global_id ) );
1738  }
1739  }
1740  }
1741  return MB_SUCCESS;
1742 }
1743 
1745 {
1746  Range cells2d;
1747  MB_CHK_ERR( mb->get_entities_by_dimension( set, 2, cells2d ) );
1748  for( Range::iterator qit = cells2d.begin(); qit != cells2d.end(); ++qit )
1749  {
1750  EntityHandle cell = *qit;
1751  const EntityHandle* conn = nullptr;
1752  int num_nodes = 0;
1753  MB_CHK_ERR( mb->get_connectivity( cell, conn, num_nodes ) );
1754  if( num_nodes < 3 ) return MB_FAILURE;
1755 
1756  double coords[9];
1757  MB_CHK_ERR( mb->get_coords( conn, 3, coords ) );
1758 
1759  double area;
1760  if( R > 0 )
1761  area = area_spherical_triangle_lHuiller( coords, coords + 3, coords + 6, R );
1762  else
1763  area = IntxUtils::area2D( coords, coords + 3, coords + 6 );
1764  if( area < 0 )
1765  {
1766  // compute all area, do not revert if total area is positive
1767  std::vector< double > coords2( 3 * num_nodes );
1768  // get coordinates
1769  MB_CHK_ERR( mb->get_coords( conn, num_nodes, &coords2[0] ) );
1770  double totArea = area_spherical_polygon_lHuiller( &coords2[0], num_nodes, R );
1771  if( totArea < 0 )
1772  {
1773  std::vector< EntityHandle > newconn( num_nodes );
1774  for( int i = 0; i < num_nodes; i++ )
1775  {
1776  newconn[num_nodes - 1 - i] = conn[i];
1777  }
1778  MB_CHK_ERR( mb->set_connectivity( cell, &newconn[0], num_nodes ) );
1779  }
1780  else
1781  {
1782  std::cout << " nonconvex problem first area:" << area << " total area: " << totArea << std::endl;
1783  }
1784  }
1785  }
1786  return MB_SUCCESS;
1787 }
1788 
1789 // distance along a great circle on a sphere of radius 1
1790 // page 4
1791 double IntxUtils::distance_on_sphere( double la1, double te1, double la2, double te2 )
1792 {
1793  return acos( sin( te1 ) * sin( te2 ) + cos( te1 ) * cos( te2 ) * cos( la1 - la2 ) );
1794 }
1795 
1796 /*
1797  * given 2 great circle arcs, AB and CD, compute the unique intersection point, if it exists
1798  * in between
1799  */
1800 ErrorCode IntxUtils::intersect_great_circle_arcs( double* A, double* B, double* C, double* D, double R, double* E )
1801 {
1802  // first verify A, B, C, D are on the same sphere
1803  double R2 = R * R;
1804  const double Tolerance = 1.e-12 * R2;
1805 
1806  CartVect a( A ), b( B ), c( C ), d( D );
1807 
1808  if( fabs( a.length_squared() - R2 ) + fabs( b.length_squared() - R2 ) + fabs( c.length_squared() - R2 ) +
1809  fabs( d.length_squared() - R2 ) >
1810  10 * Tolerance )
1811  return MB_FAILURE;
1812 
1813  CartVect n1 = a * b;
1814  if( n1.length_squared() < Tolerance ) return MB_FAILURE;
1815 
1816  CartVect n2 = c * d;
1817  if( n2.length_squared() < Tolerance ) return MB_FAILURE;
1818  CartVect n3 = n1 * n2;
1819  n3.normalize();
1820 
1821  n3 = R * n3;
1822  // the intersection is either n3 or -n3
1823  CartVect n4 = a * n3, n5 = n3 * b;
1824  if( n1 % n4 >= -Tolerance && n1 % n5 >= -Tolerance )
1825  {
1826  // n3 is good for ab, see if it is good for cd
1827  n4 = c * n3;
1828  n5 = n3 * d;
1829  if( n2 % n4 >= -Tolerance && n2 % n5 >= -Tolerance )
1830  {
1831  E[0] = n3[0];
1832  E[1] = n3[1];
1833  E[2] = n3[2];
1834  }
1835  else
1836  return MB_FAILURE;
1837  }
1838  else
1839  {
1840  // try -n3
1841  n3 = -n3;
1842  n4 = a * n3, n5 = n3 * b;
1843  if( n1 % n4 >= -Tolerance && n1 % n5 >= -Tolerance )
1844  {
1845  // n3 is good for ab, see if it is good for cd
1846  n4 = c * n3;
1847  n5 = n3 * d;
1848  if( n2 % n4 >= -Tolerance && n2 % n5 >= -Tolerance )
1849  {
1850  E[0] = n3[0];
1851  E[1] = n3[1];
1852  E[2] = n3[2];
1853  }
1854  else
1855  return MB_FAILURE;
1856  }
1857  else
1858  return MB_FAILURE;
1859  }
1860 
1861  return MB_SUCCESS;
1862 }
1863 
1864 // verify that result is in between a and b on a great circle arc, and between c and d on a constant
1865 // latitude arc
1866 static bool verify( CartVect a, CartVect b, CartVect c, CartVect d, double x, double y, double z )
1867 {
1868  // to check, the point has to be between a and b on a great arc, and between c and d on a const
1869  // lat circle
1870  CartVect s( x, y, z );
1871  CartVect n1 = a * b;
1872  CartVect n2 = a * s;
1873  CartVect n3 = s * b;
1874  if( n1 % n2 < 0 || n1 % n3 < 0 ) return false;
1875 
1876  // do the same for c, d, s, in plane z=0
1877  c[2] = d[2] = s[2] = 0.; // bring everything in the same plane, z=0;
1878 
1879  n1 = c * d;
1880  n2 = c * s;
1881  n3 = s * d;
1882  if( n1 % n2 < 0 || n1 % n3 < 0 ) return false;
1883 
1884  return true;
1885 }
1886 
1888  double* B,
1889  double* C,
1890  double* D,
1891  double R,
1892  double* E,
1893  int& np )
1894 {
1895  const double distTol = R * 1.e-6;
1896  const double Tolerance = R * R * 1.e-12; // radius should be 1, usually
1897  np = 0; // number of points in intersection
1898  CartVect a( A ), b( B ), c( C ), d( D );
1899  // check input first
1900  double R2 = R * R;
1901  if( fabs( a.length_squared() - R2 ) + fabs( b.length_squared() - R2 ) + fabs( c.length_squared() - R2 ) +
1902  fabs( d.length_squared() - R2 ) >
1903  10 * Tolerance )
1904  return MB_FAILURE;
1905 
1906  if( ( a - b ).length_squared() < Tolerance ) return MB_FAILURE;
1907  if( ( c - d ).length_squared() < Tolerance ) // edges are too short
1908  return MB_FAILURE;
1909 
1910  // CD is the const latitude arc
1911  if( fabs( C[2] - D[2] ) > distTol ) // cd is not on the same z (constant latitude)
1912  return MB_FAILURE;
1913 
1914  if( fabs( R - C[2] ) < distTol || fabs( R + C[2] ) < distTol ) return MB_FAILURE; // too close to the poles
1915 
1916  // find the points on the circle P(teta) = (r*sin(teta), r*cos(teta), C[2]) that are on the
1917  // great circle arc AB normal to the AB circle:
1918  CartVect n1 = a * b; // the normal to the great circle arc (circle)
1919  // solve the system of equations:
1920  /*
1921  * n1%(x, y, z) = 0 // on the great circle
1922  * z = C[2];
1923  * x^2+y^2+z^2 = R^2
1924  */
1925  double z = C[2];
1926  if( fabs( n1[0] ) + fabs( n1[1] ) < 2 * Tolerance )
1927  {
1928  // it is the Equator; check if the const lat edge is Equator too
1929  if( fabs( C[2] ) > distTol )
1930  {
1931  return MB_FAILURE; // no intx, too far from Eq
1932  }
1933  else
1934  {
1935  // all points are on the equator
1936  //
1937  CartVect cd = c * d;
1938  // by convention, c<d, positive is from c to d
1939  // is a or b between c , d?
1940  CartVect ca = c * a;
1941  CartVect ad = a * d;
1942  CartVect cb = c * b;
1943  CartVect bd = b * d;
1944  bool agtc = ( ca % cd >= -Tolerance ); // a>c?
1945  bool dgta = ( ad % cd >= -Tolerance ); // d>a?
1946  bool bgtc = ( cb % cd >= -Tolerance ); // b>c?
1947  bool dgtb = ( bd % cd >= -Tolerance ); // d>b?
1948  if( agtc )
1949  {
1950  if( dgta )
1951  {
1952  // a is for sure a point
1953  E[0] = a[0];
1954  E[1] = a[1];
1955  E[2] = a[2];
1956  np++;
1957  if( bgtc )
1958  {
1959  if( dgtb )
1960  {
1961  // b is also in between c and d
1962  E[3] = b[0];
1963  E[4] = b[1];
1964  E[5] = b[2];
1965  np++;
1966  }
1967  else
1968  {
1969  // then order is c a d b, intx is ad
1970  E[3] = d[0];
1971  E[4] = d[1];
1972  E[5] = d[2];
1973  np++;
1974  }
1975  }
1976  else
1977  {
1978  // b is less than c, so b c a d, intx is ac
1979  E[3] = c[0];
1980  E[4] = c[1];
1981  E[5] = c[2];
1982  np++; // what if E[0] is E[3]?
1983  }
1984  }
1985  else // c < d < a
1986  {
1987  if( dgtb ) // d is for sure in
1988  {
1989  E[0] = d[0];
1990  E[1] = d[1];
1991  E[2] = d[2];
1992  np++;
1993  if( bgtc ) // c<b<d<a
1994  {
1995  // another point is b
1996  E[3] = b[0];
1997  E[4] = b[1];
1998  E[5] = b[2];
1999  np++;
2000  }
2001  else // b<c<d<a
2002  {
2003  // another point is c
2004  E[3] = c[0];
2005  E[4] = c[1];
2006  E[5] = c[2];
2007  np++;
2008  }
2009  }
2010  else
2011  {
2012  // nothing, order is c, d < a, b
2013  }
2014  }
2015  }
2016  else // a < c < d
2017  {
2018  if( bgtc )
2019  {
2020  // c is for sure in
2021  E[0] = c[0];
2022  E[1] = c[1];
2023  E[2] = c[2];
2024  np++;
2025  if( dgtb )
2026  {
2027  // a < c < b < d; second point is b
2028  E[3] = b[0];
2029  E[4] = b[1];
2030  E[5] = b[2];
2031  np++;
2032  }
2033  else
2034  {
2035  // a < c < d < b; second point is d
2036  E[3] = d[0];
2037  E[4] = d[1];
2038  E[5] = d[2];
2039  np++;
2040  }
2041  }
2042  else // a, b < c < d
2043  {
2044  // nothing
2045  }
2046  }
2047  }
2048  // for the 2 points selected, see if it is only one?
2049  // no problem, maybe it will be collapsed later anyway
2050  if( np > 0 ) return MB_SUCCESS;
2051  return MB_FAILURE; // no intersection
2052  }
2053  {
2054  if( fabs( n1[0] ) <= fabs( n1[1] ) )
2055  {
2056  // resolve eq in x: n0 * x + n1 * y +n2*z = 0; y = -n2/n1*z -n0/n1*x
2057  // (u+v*x)^2+x^2=R2-z^2
2058  // (v^2+1)*x^2 + 2*u*v *x + u^2+z^2-R^2 = 0
2059  // delta = 4*u^2*v^2 - 4*(v^2-1)(u^2+z^2-R^2)
2060  // x1,2 =
2061  double u = -n1[2] / n1[1] * z, v = -n1[0] / n1[1];
2062  double a1 = v * v + 1, b1 = 2 * u * v, c1 = u * u + z * z - R2;
2063  double delta = b1 * b1 - 4 * a1 * c1;
2064  if( delta < -Tolerance ) return MB_FAILURE; // no intersection
2065  if( delta > Tolerance ) // 2 solutions possible
2066  {
2067  double x1 = ( -b1 + sqrt( delta ) ) / 2 / a1;
2068  double x2 = ( -b1 - sqrt( delta ) ) / 2 / a1;
2069  double y1 = u + v * x1;
2070  double y2 = u + v * x2;
2071  if( verify( a, b, c, d, x1, y1, z ) )
2072  {
2073  E[0] = x1;
2074  E[1] = y1;
2075  E[2] = z;
2076  np++;
2077  }
2078  if( verify( a, b, c, d, x2, y2, z ) )
2079  {
2080  E[3 * np + 0] = x2;
2081  E[3 * np + 1] = y2;
2082  E[3 * np + 2] = z;
2083  np++;
2084  }
2085  }
2086  else
2087  {
2088  // one solution
2089  double x1 = -b1 / 2 / a1;
2090  double y1 = u + v * x1;
2091  if( verify( a, b, c, d, x1, y1, z ) )
2092  {
2093  E[0] = x1;
2094  E[1] = y1;
2095  E[2] = z;
2096  np++;
2097  }
2098  }
2099  }
2100  else
2101  {
2102  // resolve eq in y, reverse
2103  // n0 * x + n1 * y +n2*z = 0; x = -n2/n0*z -n1/n0*y = u+v*y
2104  // (u+v*y)^2+y^2 -R2+z^2 =0
2105  // (v^2+1)*y^2 + 2*u*v *y + u^2+z^2-R^2 = 0
2106  //
2107  // x1,2 =
2108  double u = -n1[2] / n1[0] * z, v = -n1[1] / n1[0];
2109  double a1 = v * v + 1, b1 = 2 * u * v, c1 = u * u + z * z - R2;
2110  double delta = b1 * b1 - 4 * a1 * c1;
2111  if( delta < -Tolerance ) return MB_FAILURE; // no intersection
2112  if( delta > Tolerance ) // 2 solutions possible
2113  {
2114  double y1 = ( -b1 + sqrt( delta ) ) / 2 / a1;
2115  double y2 = ( -b1 - sqrt( delta ) ) / 2 / a1;
2116  double x1 = u + v * y1;
2117  double x2 = u + v * y2;
2118  if( verify( a, b, c, d, x1, y1, z ) )
2119  {
2120  E[0] = x1;
2121  E[1] = y1;
2122  E[2] = z;
2123  np++;
2124  }
2125  if( verify( a, b, c, d, x2, y2, z ) )
2126  {
2127  E[3 * np + 0] = x2;
2128  E[3 * np + 1] = y2;
2129  E[3 * np + 2] = z;
2130  np++;
2131  }
2132  }
2133  else
2134  {
2135  // one solution
2136  double y1 = -b1 / 2 / a1;
2137  double x1 = u + v * y1;
2138  if( verify( a, b, c, d, x1, y1, z ) )
2139  {
2140  E[0] = x1;
2141  E[1] = y1;
2142  E[2] = z;
2143  np++;
2144  }
2145  }
2146  }
2147  }
2148 
2149  if( np <= 0 ) return MB_FAILURE;
2150  return MB_SUCCESS;
2151 }
2152 
2153 #if 0
2154 ErrorCode set_edge_type_flag(Interface * mb, EntityHandle sf1)
2155 {
2156  Range cells;
2157  ErrorCode rval = mb->get_entities_by_dimension(sf1, 2, cells);
2158  if (MB_SUCCESS!= rval)
2159  return rval;
2160  Range edges;
2161  rval = mb->get_adjacencies(cells, 1, true, edges, Interface::UNION);
2162  if (MB_SUCCESS!= rval)
2163  return rval;
2164 
2165  Tag edgeTypeTag;
2166  int default_int=0;
2167  rval = mb->tag_get_handle("edge_type", 1, MB_TYPE_INTEGER, edgeTypeTag,
2168  MB_TAG_DENSE | MB_TAG_CREAT, &default_int);
2169  if (MB_SUCCESS!= rval)
2170  return rval;
2171  // add edges to the set? not yet, maybe later
2172  // if edge horizontal, set value to 1
2173  int type_constant_lat=1;
2174  for (Range::iterator eit=edges.begin(); eit!=edges.end(); ++eit)
2175  {
2176  EntityHandle edge = *eit;
2177  const EntityHandle *conn=0;
2178  int num_n=0;
2179  rval = mb->get_connectivity(edge, conn, num_n );
2180  if (MB_SUCCESS!= rval)
2181  return rval;
2182  double coords[6];
2183  rval = mb->get_coords(conn, 2, coords);
2184  if (MB_SUCCESS!= rval)
2185  return rval;
2186  if (fabs( coords[2]-coords[5] )< 1.e-6 )
2187  {
2188  rval = mb->tag_set_data(edgeTypeTag, &edge, 1, &type_constant_lat);
2189  if (MB_SUCCESS!= rval)
2190  return rval;
2191  }
2192  }
2193 
2194  return MB_SUCCESS;
2195 }
2196 #endif
2197 
2198 // decide in a different metric if the corners of CS quad are
2199 // in the interior of an RLL quad
2201  double* red2dc,
2202  int nsRed,
2203  CartVect* bluec,
2204  int nsBlue,
2205  int* blueEdgeType,
2206  double* P,
2207  int* side,
2208  double epsil )
2209 {
2210  int extraPoints = 0;
2211  // first decide the blue z coordinates
2212  CartVect A( 0. ), B( 0. ), C( 0. ), D( 0. );
2213  for( int i = 0; i < nsBlue; i++ )
2214  {
2215  if( blueEdgeType[i] == 0 )
2216  {
2217  int iP1 = ( i + 1 ) % nsBlue;
2218  if( bluec[i][2] > bluec[iP1][2] )
2219  {
2220  A = bluec[i];
2221  B = bluec[iP1];
2222  C = bluec[( i + 2 ) % nsBlue];
2223  D = bluec[( i + 3 ) % nsBlue]; // it could be back to A, if triangle on top
2224  break;
2225  }
2226  }
2227  }
2228  if( nsBlue == 3 && B[2] < 0 )
2229  {
2230  // select D to be C
2231  D = C;
2232  C = B; // B is the south pole then
2233  }
2234  // so we have A, B, C, D, with A at the top, b going down, then C, D, D > C, A > B
2235  // AB is const longitude, BC and DA constant latitude
2236  // check now each of the red points if they are inside this rectangle
2237  for( int i = 0; i < nsRed; i++ )
2238  {
2239  CartVect& X = redc[i];
2240  if( X[2] > A[2] || X[2] < B[2] ) continue; // it is above or below the rectangle
2241  // now decide if it is between the planes OAB and OCD
2242  if( ( ( A * B ) % X >= -epsil ) && ( ( C * D ) % X >= -epsil ) )
2243  {
2244  side[i] = 1; //
2245  // it means point X is in the rectangle that we want , on the sphere
2246  // pass the coords 2d
2247  P[extraPoints * 2] = red2dc[2 * i];
2248  P[extraPoints * 2 + 1] = red2dc[2 * i + 1];
2249  extraPoints++;
2250  }
2251  }
2252  return extraPoints;
2253 }
2254 
2256 {
2257  ReadUtilIface* read_iface;
2258  MB_CHK_ERR( mb->query_interface( read_iface ) );
2259  // create the handle tag for the corresponding element / vertex
2260 
2261  EntityHandle dum = 0;
2262  Tag corrTag = 0; // it will be created here
2263  MB_CHK_ERR( mb->tag_get_handle( CORRTAGNAME, 1, MB_TYPE_HANDLE, corrTag, MB_TAG_DENSE | MB_TAG_CREAT, &dum ) );
2264 
2265  // give the same global id to new verts and cells created in the lagr(departure) mesh
2266  Tag gid = mb->globalId_tag();
2267 
2268  Range quads;
2269  MB_CHK_ERR( mb->get_entities_by_type( source_set, MBQUAD, quads ) );
2270 
2271  Range connecVerts;
2272  MB_CHK_ERR( mb->get_connectivity( quads, connecVerts ) );
2273 
2274  std::map< EntityHandle, EntityHandle > newNodes;
2275 
2276  std::vector< double* > coords;
2277  EntityHandle start_vert, start_elem, *connect;
2278  int num_verts = connecVerts.size();
2279  MB_CHK_ERR( read_iface->get_node_coords( 3, num_verts, 0, start_vert, coords ) );
2280 
2281  // fill it up
2282  int i = 0;
2283  for( Range::iterator vit = connecVerts.begin(); vit != connecVerts.end(); ++vit, i++ )
2284  {
2285  EntityHandle oldV = *vit;
2286  CartVect posi;
2287  MB_CHK_ERR( mb->get_coords( &oldV, 1, &( posi[0] ) ) );
2288 
2289  int global_id;
2290  MB_CHK_ERR( mb->tag_get_data( gid, &oldV, 1, &global_id ) );
2291  EntityHandle new_vert = start_vert + i;
2292  // Cppcheck warning (false positive): variable coords is assigned a value that is never used
2293  coords[0][i] = posi[0];
2294  coords[1][i] = posi[1];
2295  coords[2][i] = posi[2];
2296 
2297  newNodes[oldV] = new_vert;
2298  // set also the correspondent tag :)
2299  MB_CHK_ERR( mb->tag_set_data( corrTag, &oldV, 1, &new_vert ) );
2300 
2301  // also the other side
2302  // need to check if we really need this; the new vertex will never need the old vertex
2303  // we have the global id which is the same
2304  MB_CHK_ERR( mb->tag_set_data( corrTag, &new_vert, 1, &oldV ) );
2305  // set the global id on the corresponding vertex the same as the initial vertex
2306  MB_CHK_ERR( mb->tag_set_data( gid, &new_vert, 1, &global_id ) );
2307  }
2308  // now create new quads in order (in a sequence)
2309 
2310  MB_CHK_ERR( read_iface->get_element_connect( quads.size(), 4, MBQUAD, 0, start_elem, connect ) );
2311 
2312  int ie = 0;
2313  for( Range::iterator it = quads.begin(); it != quads.end(); ++it, ie++ )
2314  {
2315  EntityHandle q = *it;
2316  int nnodes;
2317  const EntityHandle* conn;
2318  MB_CHK_ERR( mb->get_connectivity( q, conn, nnodes ) );
2319  int global_id;
2320  MB_CHK_ERR( mb->tag_get_data( gid, &q, 1, &global_id ) );
2321 
2322  for( int ii = 0; ii < nnodes; ii++ )
2323  {
2324  EntityHandle v1 = conn[ii];
2325  connect[4 * ie + ii] = newNodes[v1];
2326  }
2327  EntityHandle newElement = start_elem + ie;
2328 
2329  // set the corresponding tag; not sure we need this one, from old to new
2330  MB_CHK_ERR( mb->tag_set_data( corrTag, &q, 1, &newElement ) );
2331  MB_CHK_ERR( mb->tag_set_data( corrTag, &newElement, 1, &q ) );
2332 
2333  // set the global id
2334  MB_CHK_ERR( mb->tag_set_data( gid, &newElement, 1, &global_id ) );
2335 
2336  MB_CHK_ERR( mb->add_entities( dest_set, &newElement, 1 ) );
2337  }
2338 
2339  MB_CHK_ERR( read_iface->update_adjacencies( start_elem, quads.size(), 4, connect ) );
2340 
2341  return MB_SUCCESS;
2342 }
2343 
2345  EntityHandle file_set,
2346  double merge_tol,
2347  std::vector< Tag >& tagList )
2348 {
2349  Range verts;
2350  MB_CHK_ERR( mb->get_entities_by_dimension( file_set, 0, verts ) );
2351  MB_CHK_ERR( mb->remove_entities( file_set, verts ) );
2352 
2353  MergeMesh mm( mb );
2354 
2355  // remove the vertices from the set, before merging
2356 
2357  MB_CHK_ERR( mm.merge_all( file_set, merge_tol ) );
2358 
2359  // now correct vertices that are repeated in polygons
2360  MB_CHK_ERR( remove_padded_vertices( mb, file_set, tagList ) );
2361  return MB_SUCCESS;
2362 }
2363 
2364 ErrorCode IntxUtils::remove_padded_vertices( Interface* mb, EntityHandle file_set, std::vector< Tag >& tagList )
2365 {
2366 
2367  // now correct vertices that are repeated in polygons
2368  Range cells;
2369  MB_CHK_ERR( mb->get_entities_by_dimension( file_set, 2, cells ) );
2370 
2371  Range verts;
2372  MB_CHK_ERR( mb->get_connectivity( cells, verts ) );
2373 
2374  Range modifiedCells; // will be deleted at the end; keep the gid
2375  Range newCells;
2376 
2377  for( Range::iterator cit = cells.begin(); cit != cells.end(); ++cit )
2378  {
2379  EntityHandle cell = *cit;
2380  const EntityHandle* connec = nullptr;
2381  int num_verts = 0;
2382  MB_CHK_SET_ERR( mb->get_connectivity( cell, connec, num_verts ), "Failed to get connectivity" );
2383 
2384  std::vector< EntityHandle > newConnec;
2385  newConnec.push_back( connec[0] ); // at least one vertex
2386  int index = 0;
2387  int new_size = 1;
2388  while( index < num_verts - 2 )
2389  {
2390  int next_index = ( index + 1 );
2391  if( connec[next_index] != newConnec[new_size - 1] )
2392  {
2393  newConnec.push_back( connec[next_index] );
2394  new_size++;
2395  }
2396  index++;
2397  }
2398  // add the last one only if different from previous and first node
2399  if( ( connec[num_verts - 1] != connec[num_verts - 2] ) && ( connec[num_verts - 1] != connec[0] ) )
2400  {
2401  newConnec.push_back( connec[num_verts - 1] );
2402  new_size++;
2403  }
2404  if( new_size < num_verts )
2405  {
2406  // cout << "new cell from " << cell << " has only " << new_size << " vertices \n";
2407  modifiedCells.insert( cell );
2408  // create a new cell with type triangle, quad or polygon
2409  EntityType type = MBTRI;
2410  if( new_size == 3 )
2411  type = MBTRI;
2412  else if( new_size == 4 )
2413  type = MBQUAD;
2414  else if( new_size > 4 )
2415  type = MBPOLYGON;
2416 
2417  // create new cell
2418  EntityHandle newCell;
2419  MB_CHK_SET_ERR( mb->create_element( type, &newConnec[0], new_size, newCell ), "Failed to create new cell" );
2420  // set the old id to the new element
2421  newCells.insert( newCell );
2422  double value; // use the same value to reset the tags, even if the tags are int (like Global ID)
2423  for( size_t i = 0; i < tagList.size(); i++ )
2424  {
2425  MB_CHK_SET_ERR( mb->tag_get_data( tagList[i], &cell, 1, (void*)( &value ) ),
2426  "Failed to get tag value" );
2427  MB_CHK_SET_ERR( mb->tag_set_data( tagList[i], &newCell, 1, (void*)( &value ) ),
2428  "Failed to set tag value on new cell" );
2429  }
2430  }
2431  }
2432 
2433  MB_CHK_SET_ERR( mb->remove_entities( file_set, modifiedCells ), "Failed to remove old cells from file set" );
2434  MB_CHK_SET_ERR( mb->delete_entities( modifiedCells ), "Failed to delete old cells" );
2435  MB_CHK_SET_ERR( mb->add_entities( file_set, newCells ), "Failed to add new cells to file set" );
2436  MB_CHK_SET_ERR( mb->add_entities( file_set, verts ), "Failed to add verts to the file set" );
2437 
2438  return MB_SUCCESS;
2439 }
2440 
2441 ErrorCode IntxUtils::max_diagonal( Interface* mb, Range cells, int max_edges, double& diagonal )
2442 {
2443  diagonal = 0.0;
2444  std::vector< CartVect > coords( max_edges ); // maximum number of nodes in a cell? hard coded ?
2445  for( auto it = cells.begin(); it != cells.end(); ++it )
2446  {
2447  // get the connectivity, then the coordinates
2448  EntityHandle cell = *it;
2449  const EntityHandle* connec = nullptr;
2450  int num_verts = 0;
2451  MB_CHK_SET_ERR( mb->get_connectivity( cell, connec, num_verts ), "Failed to get connectivity" );
2452  MB_CHK_SET_ERR( mb->get_coords( connec, num_verts, &( coords[0][0] ) ), "Failed to get coordinates" );
2453  // compute the max diagonal, in a double loop
2454  for( int i = 0; i < num_verts - 1; i++ )
2455  {
2456  for( int j = i + 1; j < num_verts; j++ )
2457  {
2458  double len_sq = ( coords[i] - coords[j] ).length_squared();
2459  if( len_sq > diagonal ) diagonal = len_sq;
2460  }
2461  }
2462  }
2463 
2464  // return the root of the diagonal
2465  diagonal = std::sqrt( diagonal );
2466  return MB_SUCCESS;
2467 }
2468 
2469 } // namespace moab