Mesh Oriented datABase  (version 5.5.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 #ifdef _MSC_VER /* windows */
7 #define _USE_MATH_DEFINES // For M_PI
8 #endif
9 
10 #ifdef WIN32 /* windows */
11 #define _USE_MATH_DEFINES // For M_PI
12 #endif
13 #include <cmath>
14 #include <cassert>
15 #include <iostream>
16 // this is for sstream
17 #include <sstream>
18 
20 // this is from mbcoupler; maybe it should be moved somewhere in moab src
21 // right now, add a dependency to mbcoupler
22 // #include "ElemUtil.hpp"
23 #include "moab/MergeMesh.hpp"
24 #include "moab/ReadUtilIface.hpp"
25 #include "MBTagConventions.hpp"
26 #define CHECKNEGATIVEAREA
27 
28 #ifdef CHECKNEGATIVEAREA
29 #include <iomanip>
30 #endif
31 #include <queue>
32 #include <map>
33 
34 #ifdef MOAB_HAVE_TEMPESTREMAP
35 #include "GridElements.h"
36 #endif
37 
38 namespace moab
39 {
40 
41 #define CORRTAGNAME "__correspondent"
42 #define MAXEDGES 10
43 
44 int IntxUtils::borderPointsOfXinY2( double* X, int nX, double* Y, int nY, double* P, int* side, double epsilon_area )
45 {
46  // 2 triangles, 3 corners, is the corner of X in Y?
47  // Y must have a positive area
48  /*
49  */
50  int extraPoint = 0;
51  for( int i = 0; i < nX; i++ )
52  {
53  // compute double the area of all nY triangles formed by a side of Y and a corner of X; if
54  // one is negative, stop (negative means it is outside; X and Y are all oriented such that
55  // they are positive oriented;
56  // if one area is negative, it means it is outside the convex region, for sure)
57  double* A = X + 2 * i;
58 
59  int inside = 1;
60  for( int j = 0; j < nY; j++ )
61  {
62  double* B = Y + 2 * j;
63  int j1 = ( j + 1 ) % nY;
64  double* C = Y + 2 * j1; // no copy of data
65 
66  double area2 = ( B[0] - A[0] ) * ( C[1] - A[1] ) - ( C[0] - A[0] ) * ( B[1] - A[1] );
67  if( area2 < -epsilon_area )
68  {
69  inside = 0;
70  break;
71  }
72  }
73  if( inside )
74  {
75  side[i] = 1; // so vertex i of X is inside the convex region formed by Y
76  // so side has nX dimension (first array)
77  P[extraPoint * 2] = A[0];
78  P[extraPoint * 2 + 1] = A[1];
79  extraPoint++;
80  }
81  }
82  return extraPoint;
83 }
84 
85 // used to order according to angle, so it can remove double easily
87 {
88  double angle;
89  int index;
90 };
91 
93 {
94  return lhs.angle < rhs.angle;
95 }
96 
97 // nP might be modified too, we will remove duplicates if found
98 int IntxUtils::SortAndRemoveDoubles2( double* P, int& nP, double epsilon_1 )
99 {
100  if( nP < 2 ) return 0; // nothing to do
101 
102  // center of gravity for the points
103  double c[2] = { 0., 0. };
104  int k = 0;
105  for( k = 0; k < nP; k++ )
106  {
107  c[0] += P[2 * k];
108  c[1] += P[2 * k + 1];
109  }
110  c[0] /= nP;
111  c[1] /= nP;
112 
113  // how many? we dimensioned P at MAXEDGES*10; so we imply we could have at most 5*MAXEDGES
114  // intersection points
115  struct angleAndIndex pairAngleIndex[5 * MAXEDGES];
116 
117  for( k = 0; k < nP; k++ )
118  {
119  double x = P[2 * k] - c[0], y = P[2 * k + 1] - c[1];
120  if( x != 0. || y != 0. )
121  {
122  pairAngleIndex[k].angle = atan2( y, x );
123  }
124  else
125  {
126  pairAngleIndex[k].angle = 0;
127  // it would mean that the cells are touching at a vertex
128  }
129  pairAngleIndex[k].index = k;
130  }
131 
132  // this should be faster than the bubble sort we had before
133  std::sort( pairAngleIndex, pairAngleIndex + nP, angleCompare );
134  // copy now to a new double array
135  double PCopy[10 * MAXEDGES]; // the same dimension as P; very conservative, but faster than
136  // reallocate for a vector
137  for( k = 0; k < nP; k++ ) // index will show where it should go now;
138  {
139  int ck = pairAngleIndex[k].index;
140  PCopy[2 * k] = P[2 * ck];
141  PCopy[2 * k + 1] = P[2 * ck + 1];
142  }
143  // now copy from PCopy over original P location
144  std::copy( PCopy, PCopy + 2 * nP, P );
145 
146  // eliminate duplicates, finally
147 
148  int i = 0, j = 1; // the next one; j may advance faster than i
149  // check the unit
150  // double epsilon_1 = 1.e-5; // these are cm; 2 points are the same if the distance is less
151  // than 1.e-5 cm
152  while( j < nP )
153  {
154  double d2 = dist2( &P[2 * i], &P[2 * j] );
155  if( d2 > epsilon_1 )
156  {
157  i++;
158  P[2 * i] = P[2 * j];
159  P[2 * i + 1] = P[2 * j + 1];
160  }
161  j++;
162  }
163  // test also the last point with the first one (index 0)
164  // the first one could be at -PI; last one could be at +PI, according to atan2 span
165 
166  double d2 = dist2( P, &P[2 * i] ); // check the first and last points (ordered from -pi to +pi)
167  if( d2 > epsilon_1 )
168  {
169  nP = i + 1;
170  }
171  else
172  nP = i; // effectively delete the last point (that would have been the same with first)
173  if( nP == 0 ) nP = 1; // we should be left with at least one point we already tested if nP is 0 originally
174  return 0;
175 }
176 
177 // the marks will show what edges of blue intersect the red
178 
180  int nsBlue,
181  double* red,
182  int nsRed,
183  int* markb,
184  int* markr,
185  double* points,
186  int& nPoints )
187 {
188  /* EDGEINTERSECTIONS computes edge intersections of two elements
189  [P,n]=EdgeIntersections(X,Y) computes for the two given elements * red
190  and blue ( stored column wise )
191  (point coordinates are stored column-wise, in counter clock
192  order) the points P where their edges intersect. In addition,
193  in n the indices of which neighbors of red are also intersecting
194  with blue are given.
195  */
196 
197  // points is an array with enough slots (24 * 2 doubles)
198  nPoints = 0;
199  for( int i = 0; i < MAXEDGES; i++ )
200  {
201  markb[i] = markr[i] = 0;
202  }
203 
204  for( int i = 0; i < nsBlue; i++ )
205  {
206  for( int j = 0; j < nsRed; j++ )
207  {
208  double b[2];
209  double a[2][2]; // 2*2
210  int iPlus1 = ( i + 1 ) % nsBlue;
211  int jPlus1 = ( j + 1 ) % nsRed;
212  for( int k = 0; k < 2; k++ )
213  {
214  b[k] = red[2 * j + k] - blue[2 * i + k];
215  // row k of a: a(k, 0), a(k, 1)
216  a[k][0] = blue[2 * iPlus1 + k] - blue[2 * i + k];
217  a[k][1] = red[2 * j + k] - red[2 * jPlus1 + k];
218  }
219  double delta = a[0][0] * a[1][1] - a[0][1] * a[1][0];
220  if( fabs( delta ) > 1.e-14 ) // this is close to machine epsilon
221  {
222  // not parallel
223  double alfa = ( b[0] * a[1][1] - a[0][1] * b[1] ) / delta;
224  double beta = ( -b[0] * a[1][0] + b[1] * a[0][0] ) / delta;
225  if( 0 <= alfa && alfa <= 1. && 0 <= beta && beta <= 1. )
226  {
227  // the intersection is good
228  for( int k = 0; k < 2; k++ )
229  {
230  points[2 * nPoints + k] = blue[2 * i + k] + alfa * ( blue[2 * iPlus1 + k] - blue[2 * i + k] );
231  }
232  markb[i] = 1; // so neighbor number i of blue will be considered too.
233  markr[j] = 1; // this will be used in advancing red around blue quad
234  nPoints++;
235  }
236  }
237  // the case delta ~ 0. will be considered by the interior points logic
238  }
239  }
240  return MB_SUCCESS;
241 }
242 
243 // special one, for intersection between rll (constant latitude) and cs quads
245  CartVect* bluec,
246  int* blueEdgeType,
247  int nsBlue,
248  double* red,
249  CartVect* redc,
250  int nsRed,
251  int* markb,
252  int* markr,
253  int plane,
254  double R,
255  double* points,
256  int& nPoints )
257 {
258  // if blue edge type is 1, intersect in 3d then project to 2d by gnomonic projection
259  // everything else the same (except if there are 2 points resulting, which is rare)
260  for( int i = 0; i < 4; i++ )
261  { // always at most 4 , so maybe don't bother
262  markb[i] = markr[i] = 0;
263  }
264 
265  for( int i = 0; i < nsBlue; i++ )
266  {
267  int iPlus1 = ( i + 1 ) % nsBlue;
268  if( blueEdgeType[i] == 0 ) // old style, just 2d
269  {
270  for( int j = 0; j < nsRed; j++ )
271  {
272  double b[2];
273  double a[2][2]; // 2*2
274 
275  int jPlus1 = ( j + 1 ) % nsRed;
276  for( int k = 0; k < 2; k++ )
277  {
278  b[k] = red[2 * j + k] - blue[2 * i + k];
279  // row k of a: a(k, 0), a(k, 1)
280  a[k][0] = blue[2 * iPlus1 + k] - blue[2 * i + k];
281  a[k][1] = red[2 * j + k] - red[2 * jPlus1 + k];
282  }
283  double delta = a[0][0] * a[1][1] - a[0][1] * a[1][0];
284  if( fabs( delta ) > 1.e-14 )
285  {
286  // not parallel
287  double alfa = ( b[0] * a[1][1] - a[0][1] * b[1] ) / delta;
288  double beta = ( -b[0] * a[1][0] + b[1] * a[0][0] ) / delta;
289  if( 0 <= alfa && alfa <= 1. && 0 <= beta && beta <= 1. )
290  {
291  // the intersection is good
292  for( int k = 0; k < 2; k++ )
293  {
294  points[2 * nPoints + k] =
295  blue[2 * i + k] + alfa * ( blue[2 * iPlus1 + k] - blue[2 * i + k] );
296  }
297  markb[i] = 1; // so neighbor number i of blue will be considered too.
298  markr[j] = 1; // this will be used in advancing red around blue quad
299  nPoints++;
300  }
301  } // if the edges are too "parallel", skip them
302  }
303  }
304  else // edge type is 1, so use 3d intersection
305  {
306  CartVect& C = bluec[i];
307  CartVect& D = bluec[iPlus1];
308  for( int j = 0; j < nsRed; j++ )
309  {
310  int jPlus1 = ( j + 1 ) % nsRed; // nsRed is just 4, forget about it, usually
311  CartVect& A = redc[j];
312  CartVect& B = redc[jPlus1];
313  int np = 0;
314  double E[9];
315  intersect_great_circle_arc_with_clat_arc( A.array(), B.array(), C.array(), D.array(), R, E, np );
316  if( np == 0 ) continue;
317  if( np >= 2 )
318  {
319  std::cout << "intersection with 2 points :" << A << B << C << D << "\n";
320  }
321  for( int k = 0; k < np; k++ )
322  {
323  gnomonic_projection( CartVect( E + k * 3 ), R, plane, points[2 * nPoints],
324  points[2 * nPoints + 1] );
325  nPoints++;
326  }
327  markb[i] = 1; // so neighbor number i of blue will be considered too.
328  markr[j] = 1; // this will be used in advancing red around blue quad
329  }
330  }
331  }
332  return MB_SUCCESS;
333 }
334 
335 // vec utils related to gnomonic projection on a sphere
336 
337 // vec utils
338 
339 /*
340  *
341  * position on a sphere of radius R
342  * if plane specified, use it; if not, return the plane, and the point in the plane
343  * there are 6 planes, numbered 1 to 6
344  * plane 1: x=R, plane 2: y=R, 3: x=-R, 4: y=-R, 5: z=-R, 6: z=R
345  *
346  * projection on the plane will preserve the orientation, such that a triangle, quad pointing
347  * outside the sphere will have a positive orientation in the projection plane
348  */
349 void IntxUtils::decide_gnomonic_plane( const CartVect& pos, int& plane )
350 {
351  // decide plane, based on max x, y, z
352  if( fabs( pos[0] ) < fabs( pos[1] ) )
353  {
354  if( fabs( pos[2] ) < fabs( pos[1] ) )
355  {
356  // pos[1] is biggest
357  if( pos[1] > 0 )
358  plane = 2;
359  else
360  plane = 4;
361  }
362  else
363  {
364  // pos[2] is biggest
365  if( pos[2] < 0 )
366  plane = 5;
367  else
368  plane = 6;
369  }
370  }
371  else
372  {
373  if( fabs( pos[2] ) < fabs( pos[0] ) )
374  {
375  // pos[0] is the greatest
376  if( pos[0] > 0 )
377  plane = 1;
378  else
379  plane = 3;
380  }
381  else
382  {
383  // pos[2] is biggest
384  if( pos[2] < 0 )
385  plane = 5;
386  else
387  plane = 6;
388  }
389  }
390  return;
391 }
392 
393 // point on a sphere is projected on one of six planes, decided earlier
394 ErrorCode IntxUtils::gnomonic_projection( const CartVect& pos, double R, int plane, double& c1, double& c2 )
395 {
396  double alfa = 1.; // the new point will be on line alfa*pos
397 
398  switch( plane )
399  {
400  case 1: {
401  // the plane with x = R; x>y, x>z
402  // c1->y, c2->z
403  alfa = R / pos[0];
404  c1 = alfa * pos[1];
405  c2 = alfa * pos[2];
406  break;
407  }
408  case 2: {
409  // y = R -> zx
410  alfa = R / pos[1];
411  c1 = alfa * pos[2];
412  c2 = alfa * pos[0];
413  break;
414  }
415  case 3: {
416  // x=-R, -> yz
417  alfa = -R / pos[0];
418  c1 = -alfa * pos[1]; // the sign is to preserve orientation
419  c2 = alfa * pos[2];
420  break;
421  }
422  case 4: {
423  // y = -R
424  alfa = -R / pos[1];
425  c1 = -alfa * pos[2]; // the sign is to preserve orientation
426  c2 = alfa * pos[0];
427  break;
428  }
429  case 5: {
430  // z = -R
431  alfa = -R / pos[2];
432  c1 = -alfa * pos[0]; // the sign is to preserve orientation
433  c2 = alfa * pos[1];
434  break;
435  }
436  case 6: {
437  alfa = R / pos[2];
438  c1 = alfa * pos[0];
439  c2 = alfa * pos[1];
440  break;
441  }
442  default:
443  return MB_FAILURE; // error
444  }
445 
446  return MB_SUCCESS; // no error
447 }
448 
449 // given the position on plane (one out of 6), find out the position on sphere
451  const double& c2,
452  double R,
453  int plane,
454  CartVect& pos )
455 {
456 
457  // the new point will be on line beta*pos
458  double len = sqrt( c1 * c1 + c2 * c2 + R * R );
459  double beta = R / len; // it is less than 1, in general
460 
461  switch( plane )
462  {
463  case 1: {
464  // the plane with x = R; x>y, x>z
465  // c1->y, c2->z
466  pos[0] = beta * R;
467  pos[1] = c1 * beta;
468  pos[2] = c2 * beta;
469  break;
470  }
471  case 2: {
472  // y = R -> zx
473  pos[1] = R * beta;
474  pos[2] = c1 * beta;
475  pos[0] = c2 * beta;
476  break;
477  }
478  case 3: {
479  // x=-R, -> yz
480  pos[0] = -R * beta;
481  pos[1] = -c1 * beta; // the sign is to preserve orientation
482  pos[2] = c2 * beta;
483  break;
484  }
485  case 4: {
486  // y = -R
487  pos[1] = -R * beta;
488  pos[2] = -c1 * beta; // the sign is to preserve orientation
489  pos[0] = c2 * beta;
490  break;
491  }
492  case 5: {
493  // z = -R
494  pos[2] = -R * beta;
495  pos[0] = -c1 * beta; // the sign is to preserve orientation
496  pos[1] = c2 * beta;
497  break;
498  }
499  case 6: {
500  pos[2] = R * beta;
501  pos[0] = c1 * beta;
502  pos[1] = c2 * beta;
503  break;
504  }
505  default:
506  return MB_FAILURE; // error
507  }
508 
509  return MB_SUCCESS; // no error
510 }
511 
512 void IntxUtils::gnomonic_unroll( double& c1, double& c2, double R, int plane )
513 {
514  double tmp;
515  switch( plane )
516  {
517  case 1:
518  break; // nothing
519  case 2: // rotate + 90
520  tmp = c1;
521  c1 = -c2;
522  c2 = tmp;
523  c1 = c1 + 2 * R;
524  break;
525  case 3:
526  c1 = c1 + 4 * R;
527  break;
528  case 4: // rotate with -90 x-> -y; y -> x
529 
530  tmp = c1;
531  c1 = c2;
532  c2 = -tmp;
533  c1 = c1 - 2 * R;
534  break;
535  case 5: // South Pole
536  // rotate 180 then move to (-2, -2)
537  c1 = -c1 - 2. * R;
538  c2 = -c2 - 2. * R;
539  break;
540  ;
541  case 6: // North Pole
542  c1 = c1 - 2. * R;
543  c2 = c2 + 2. * R;
544  break;
545  }
546  return;
547 }
548 // given a mesh on the sphere, project all centers in 6 gnomonic planes, or project mesh too
550  EntityHandle inSet,
551  double R,
552  bool centers_only,
553  EntityHandle& outSet )
554 {
555  std::string parTagName( "PARALLEL_PARTITION" );
556  Tag part_tag;
557  Range partSets;
558  ErrorCode rval = mb->tag_get_handle( parTagName.c_str(), part_tag );
559  if( MB_SUCCESS == rval && part_tag != 0 )
560  {
561  rval = mb->get_entities_by_type_and_tag( inSet, MBENTITYSET, &part_tag, NULL, 1, partSets, Interface::UNION );MB_CHK_ERR( rval );
562  }
563  rval = ScaleToRadius( mb, inSet, 1.0 );MB_CHK_ERR( rval );
564  // Get all entities of dimension 2
565  Range inputRange; // get
566  rval = mb->get_entities_by_dimension( inSet, 1, inputRange );MB_CHK_ERR( rval );
567  rval = mb->get_entities_by_dimension( inSet, 2, inputRange );MB_CHK_ERR( rval );
568 
569  std::map< EntityHandle, int > partsAssign;
570  std::map< int, EntityHandle > newPartSets;
571  if( !partSets.empty() )
572  {
573  // get all cells, and assign parts
574  for( Range::iterator setIt = partSets.begin(); setIt != partSets.end(); ++setIt )
575  {
576  EntityHandle pSet = *setIt;
577  Range ents;
578  rval = mb->get_entities_by_handle( pSet, ents );MB_CHK_ERR( rval );
579  int val;
580  rval = mb->tag_get_data( part_tag, &pSet, 1, &val );MB_CHK_ERR( rval );
581  // create a new set with the same part id tag, in the outSet
582  EntityHandle newPartSet;
583  rval = mb->create_meshset( MESHSET_SET, newPartSet );MB_CHK_ERR( rval );
584  rval = mb->tag_set_data( part_tag, &newPartSet, 1, &val );MB_CHK_ERR( rval );
585  newPartSets[val] = newPartSet;
586  rval = mb->add_entities( outSet, &newPartSet, 1 );MB_CHK_ERR( rval );
587  for( Range::iterator it = ents.begin(); it != ents.end(); ++it )
588  {
589  partsAssign[*it] = val;
590  }
591  }
592  }
593 
594  if( centers_only )
595  {
596  for( Range::iterator it = inputRange.begin(); it != inputRange.end(); ++it )
597  {
599  EntityHandle cell = *it;
600  rval = mb->get_coords( &cell, 1, center.array() );MB_CHK_ERR( rval );
601  int plane;
602  decide_gnomonic_plane( center, plane );
603  double c[3];
604  c[2] = 0.;
605  gnomonic_projection( center, R, plane, c[0], c[1] );
606 
607  gnomonic_unroll( c[0], c[1], R, plane );
608 
610  rval = mb->create_vertex( c, vertex );MB_CHK_ERR( rval );
611  rval = mb->add_entities( outSet, &vertex, 1 );MB_CHK_ERR( rval );
612  }
613  }
614  else
615  {
616  // distribute the cells to 6 planes, based on the center
617  Range subranges[6];
618  for( Range::iterator it = inputRange.begin(); it != inputRange.end(); ++it )
619  {
621  EntityHandle cell = *it;
622  rval = mb->get_coords( &cell, 1, center.array() );MB_CHK_ERR( rval );
623  int plane;
624  decide_gnomonic_plane( center, plane );
625  subranges[plane - 1].insert( cell );
626  }
627  for( int i = 1; i <= 6; i++ )
628  {
629  Range verts;
630  rval = mb->get_connectivity( subranges[i - 1], verts );MB_CHK_ERR( rval );
631  std::map< EntityHandle, EntityHandle > corr;
632  for( Range::iterator vt = verts.begin(); vt != verts.end(); ++vt )
633  {
634  CartVect vect;
635  EntityHandle v = *vt;
636  rval = mb->get_coords( &v, 1, vect.array() );MB_CHK_ERR( rval );
637  double c[3];
638  c[2] = 0.;
639  gnomonic_projection( vect, R, i, c[0], c[1] );
640  gnomonic_unroll( c[0], c[1], R, i );
642  rval = mb->create_vertex( c, vertex );MB_CHK_ERR( rval );
643  corr[v] = vertex; // for new connectivity
644  }
645  EntityHandle new_conn[20]; // max edges in 2d ?
646  for( Range::iterator eit = subranges[i - 1].begin(); eit != subranges[i - 1].end(); ++eit )
647  {
648  EntityHandle eh = *eit;
649  const EntityHandle* conn = NULL;
650  int num_nodes;
651  rval = mb->get_connectivity( eh, conn, num_nodes );MB_CHK_ERR( rval );
652  // build a new vertex array
653  for( int j = 0; j < num_nodes; j++ )
654  new_conn[j] = corr[conn[j]];
655  EntityType type = mb->type_from_handle( eh );
656  EntityHandle newCell;
657  rval = mb->create_element( type, new_conn, num_nodes, newCell );MB_CHK_ERR( rval );
658  rval = mb->add_entities( outSet, &newCell, 1 );MB_CHK_ERR( rval );
659  std::map< EntityHandle, int >::iterator mit = partsAssign.find( eh );
660  if( mit != partsAssign.end() )
661  {
662  int val = mit->second;
663  rval = mb->add_entities( newPartSets[val], &newCell, 1 );MB_CHK_ERR( rval );
664  }
665  }
666  }
667  }
668 
669  return MB_SUCCESS;
670 }
671 void IntxUtils::transform_coordinates( double* avg_position, int projection_type )
672 {
673  if( projection_type == 1 )
674  {
675  double R =
676  avg_position[0] * avg_position[0] + avg_position[1] * avg_position[1] + avg_position[2] * avg_position[2];
677  R = sqrt( R );
678  double lat = asin( avg_position[2] / R );
679  double lon = atan2( avg_position[1], avg_position[0] );
680  avg_position[0] = lon;
681  avg_position[1] = lat;
682  avg_position[2] = R;
683  }
684  else if( projection_type == 2 ) // gnomonic projection
685  {
686  CartVect pos( avg_position );
687  int gplane;
688  IntxUtils::decide_gnomonic_plane( pos, gplane );
689 
690  IntxUtils::gnomonic_projection( pos, 1.0, gplane, avg_position[0], avg_position[1] );
691  avg_position[2] = 0;
692  IntxUtils::gnomonic_unroll( avg_position[0], avg_position[1], 1.0, gplane );
693  }
694 }
695 /*
696  *
697  use physical_constants, only : dd_pi
698  type(cartesian3D_t), intent(in) :: cart
699  type(spherical_polar_t) :: sphere
700 
701  sphere%r=distance(cart)
702  sphere%lat=ASIN(cart%z/sphere%r)
703  sphere%lon=0
704 
705  ! ==========================================================
706  ! enforce three facts:
707  !
708  ! 1) lon at poles is defined to be zero
709  !
710  ! 2) Grid points must be separated by about .01 Meter (on earth)
711  ! from pole to be considered "not the pole".
712  !
713  ! 3) range of lon is { 0<= lon < 2*pi }
714  !
715  ! ==========================================================
716 
717  if (distance(cart) >= DIST_THRESHOLD) then
718  sphere%lon=ATAN2(cart%y,cart%x)
719  if (sphere%lon<0) then
720  sphere%lon=sphere%lon+2*DD_PI
721  end if
722  end if
723 
724  end function cart_to_spherical
725  */
727 {
728  SphereCoords res;
729  res.R = cart3d.length();
730  if( res.R < 0 )
731  {
732  res.lon = res.lat = 0.;
733  return res;
734  }
735  res.lat = asin( cart3d[2] / res.R );
736  res.lon = atan2( cart3d[1], cart3d[0] );
737  if( res.lon < 0 ) res.lon += 2 * M_PI; // M_PI is defined in math.h? it seems to be true, although
738  // there are some defines it depends on :(
739  // #if defined __USE_BSD || defined __USE_XOPEN ???
740 
741  return res;
742 }
743 
744 /*
745  * ! ===================================================================
746  ! spherical_to_cart:
747  ! converts spherical polar {lon,lat} to 3D cartesian {x,y,z}
748  ! on unit sphere
749  ! ===================================================================
750 
751  function spherical_to_cart(sphere) result (cart)
752 
753  type(spherical_polar_t), intent(in) :: sphere
754  type(cartesian3D_t) :: cart
755 
756  cart%x=sphere%r*COS(sphere%lat)*COS(sphere%lon)
757  cart%y=sphere%r*COS(sphere%lat)*SIN(sphere%lon)
758  cart%z=sphere%r*SIN(sphere%lat)
759 
760  end function spherical_to_cart
761  */
763 {
764  CartVect res;
765  res[0] = sc.R * cos( sc.lat ) * cos( sc.lon ); // x coordinate
766  res[1] = sc.R * cos( sc.lat ) * sin( sc.lon ); // y
767  res[2] = sc.R * sin( sc.lat ); // z
768  return res;
769 }
770 
772 {
773  Range nodes;
774  ErrorCode rval = mb->get_entities_by_type( set, MBVERTEX, nodes, true ); // recursive
775  if( rval != moab::MB_SUCCESS ) return rval;
776 
777  // one by one, get the node and project it on the sphere, with a radius given
778  // the center of the sphere is at 0,0,0
779  for( Range::iterator nit = nodes.begin(); nit != nodes.end(); ++nit )
780  {
781  EntityHandle nd = *nit;
782  CartVect pos;
783  rval = mb->get_coords( &nd, 1, (double*)&( pos[0] ) );
784  if( rval != moab::MB_SUCCESS ) return rval;
785  double len = pos.length();
786  if( len == 0. ) return MB_FAILURE;
787  pos = R / len * pos;
788  rval = mb->set_coords( &nd, 1, (double*)&( pos[0] ) );
789  if( rval != moab::MB_SUCCESS ) return rval;
790  }
791  return MB_SUCCESS;
792 }
793 
794 // assume they are one the same sphere
795 double IntxAreaUtils::spherical_angle( double* A, double* B, double* C, double Radius )
796 {
797  // the angle by definition is between the planes OAB and OBC
798  CartVect a( A );
799  CartVect b( B );
800  CartVect c( C );
801  double err1 = a.length_squared() - Radius * Radius;
802  if( fabs( err1 ) > 0.0001 )
803  {
804  std::cout << " error in input " << a << " radius: " << Radius << " error:" << err1 << "\n";
805  }
806  CartVect normalOAB = a * b;
807  CartVect normalOCB = c * b;
808  return angle( normalOAB, normalOCB );
809 }
810 
811 // could be bigger than M_PI;
812 // angle at B could be bigger than M_PI, if the orientation is such that ABC points toward the
813 // interior
814 double IntxUtils::oriented_spherical_angle( double* A, double* B, double* C )
815 {
816  // assume the same radius, sphere at origin
817  CartVect a( A ), b( B ), c( C );
818  CartVect normalOAB = a * b;
819  CartVect normalOCB = c * b;
820  CartVect orient = ( c - b ) * ( a - b );
821  double ang = angle( normalOAB, normalOCB ); // this is between 0 and M_PI
822  if( ang != ang )
823  {
824  // signal of a nan
825  std::cout << a << " " << b << " " << c << "\n";
826  std::cout << ang << "\n";
827  }
828  if( orient % b < 0 ) return ( 2 * M_PI - ang ); // the other angle, supplement
829 
830  return ang;
831 }
832 
833 double IntxAreaUtils::area_spherical_triangle( double* A, double* B, double* C, double Radius )
834 {
835  switch( m_eAreaMethod )
836  {
837  case Girard:
838  return area_spherical_triangle_girard( A, B, C, Radius );
839 #ifdef MOAB_HAVE_TEMPESTREMAP
840  case GaussQuadrature:
841  return area_spherical_triangle_GQ( A, B, C, Radius );
842 #endif
843  case lHuiller:
844  default:
845  return area_spherical_triangle_lHuiller( A, B, C, Radius );
846  }
847 }
848 
849 double IntxAreaUtils::area_spherical_polygon( double* A, int N, double Radius, int* sign )
850 {
851  switch( m_eAreaMethod )
852  {
853  case Girard:
854  return area_spherical_polygon_girard( A, N, Radius );
855 #ifdef MOAB_HAVE_TEMPESTREMAP
856  case GaussQuadrature:
857  return area_spherical_polygon_GQ( A, N, Radius );
858 #endif
859  case lHuiller:
860  default:
861  return area_spherical_polygon_lHuiller( A, N, Radius, sign );
862  }
863 }
864 
865 double IntxAreaUtils::area_spherical_triangle_girard( double* A, double* B, double* C, double Radius )
866 {
867  double correction = spherical_angle( A, B, C, Radius ) + spherical_angle( B, C, A, Radius ) +
868  spherical_angle( C, A, B, Radius ) - M_PI;
869  double area = Radius * Radius * correction;
870  // now, is it negative or positive? is it pointing toward the center or outward?
871  CartVect a( A ), b( B ), c( C );
872  CartVect abc = ( b - a ) * ( c - a );
873  if( abc % a > 0 ) // dot product positive, means ABC points out
874  return area;
875  else
876  return -area;
877 }
878 
879 double IntxAreaUtils::area_spherical_polygon_girard( double* A, int N, double Radius )
880 {
881  // this should work for non-convex polygons too
882  // assume that the A, A+3, ..., A+3*(N-1) are the coordinates
883  //
884  if( N <= 2 ) return 0.;
885  double sum_angles = 0.;
886  for( int i = 0; i < N; i++ )
887  {
888  int i1 = ( i + 1 ) % N;
889  int i2 = ( i + 2 ) % N;
890  sum_angles += IntxUtils::oriented_spherical_angle( A + 3 * i, A + 3 * i1, A + 3 * i2 );
891  }
892  double correction = sum_angles - ( N - 2 ) * M_PI;
893  return Radius * Radius * correction;
894 }
895 
896 double IntxAreaUtils::area_spherical_polygon_lHuiller( double* A, int N, double Radius, int* sign )
897 {
898  // This should work for non-convex polygons too
899  // In the input vector A, assume that the A, A+3, ..., A+3*(N-1) are the coordinates
900  // We also assume that the orientation is positive;
901  // If negative orientation, the area will be negative
902  if( N <= 2 ) return 0.;
903 
904  int lsign = 1; // assume positive orientain
905  double area = 0.;
906  for( int i = 1; i < N - 1; i++ )
907  {
908  int i1 = i + 1;
909  double areaTriangle = area_spherical_triangle_lHuiller( A, A + 3 * i, A + 3 * i1, Radius );
910  if( areaTriangle < 0 )
911  lsign = -1; // signal that we have at least one triangle with negative orientation ;
912  // possible nonconvex polygon
913  area += areaTriangle;
914  }
915  if( sign ) *sign = lsign;
916 
917  return area;
918 }
919 
920 #ifdef MOAB_HAVE_TEMPESTREMAP
921 double IntxAreaUtils::area_spherical_polygon_GQ( double* A, int N, double Radius )
922 {
923  // this should work for non-convex polygons too
924  // In the input vector A, assume that the A, A+3, ..., A+3*(N-1) are the coordinates
925  // We also assume that the orientation is positive;
926  // If negative orientation, the area can be negative
927  if( N <= 2 ) return 0.;
928 
929  // assume positive orientain
930  double area = 0.;
931  for( int i = 1; i < N - 1; i++ )
932  {
933  int i1 = i + 1;
934  area += area_spherical_triangle_GQ( A, A + 3 * i, A + 3 * i1, Radius );
935  }
936  return area;
937 }
938 
939 /* compute the area by using Gauss-Quadratures; use TR interfaces directly */
940 double IntxAreaUtils::area_spherical_triangle_GQ( double* ptA, double* ptB, double* ptC, double )
941 {
942  Face face( 3 );
943  NodeVector nodes( 3 );
944  nodes[0] = Node( ptA[0], ptA[1], ptA[2] );
945  nodes[1] = Node( ptB[0], ptB[1], ptB[2] );
946  nodes[2] = Node( ptC[0], ptC[1], ptC[2] );
947  face.SetNode( 0, 0 );
948  face.SetNode( 1, 1 );
949  face.SetNode( 2, 2 );
950  return CalculateFaceArea( face, nodes );
951 }
952 #endif
953 
954 /*
955  * l'Huiller's formula for spherical triangle
956  * http://williams.best.vwh.net/avform.htm
957  * a, b, c are arc measures in radians, too
958  * A, B, C are angles on the sphere, for which we already have formula
959  * c
960  * A -------B
961  * \ |
962  * \ |
963  * \b |a
964  * \ |
965  * \ |
966  * \ |
967  * \C|
968  * \|
969  *
970  * (The angle at B is not necessarily a right angle)
971  *
972  * sin(a) sin(b) sin(c)
973  * ----- = ------ = ------
974  * sin(A) sin(B) sin(C)
975  *
976  * In terms of the sides (this is excess, as before, but numerically stable)
977  *
978  * E = 4*atan(sqrt(tan(s/2)*tan((s-a)/2)*tan((s-b)/2)*tan((s-c)/2)))
979  */
980 double IntxAreaUtils::area_spherical_triangle_lHuiller( double* ptA, double* ptB, double* ptC, double Radius )
981 {
982 
983  // now, a is angle BOC, O is origin
984  CartVect vA( ptA ), vB( ptB ), vC( ptC );
985  double a = angle( vB, vC );
986  double b = angle( vC, vA );
987  double c = angle( vA, vB );
988  int sign = 1;
989  if( ( vA * vB ) % vC < 0 ) sign = -1;
990  double s = ( a + b + c ) / 2;
991  double tmp = tan( s / 2 ) * tan( ( s - a ) / 2 ) * tan( ( s - b ) / 2 ) * tan( ( s - c ) / 2 );
992  if( tmp < 0. ) tmp = 0.;
993 
994  double E = 4 * atan( sqrt( tmp ) );
995  if( E != E ) std::cout << " NaN at spherical triangle area \n";
996 
997  double area = sign * E * Radius * Radius;
998 
999 #ifdef CHECKNEGATIVEAREA
1000  if( area < 0 )
1001  {
1002  std::cout << "negative area: " << area << "\n";
1003  std::cout << std::setprecision( 15 );
1004  std::cout << "vA: " << vA << "\n";
1005  std::cout << "vB: " << vB << "\n";
1006  std::cout << "vC: " << vC << "\n";
1007  std::cout << "sign: " << sign << "\n";
1008  std::cout << " a: " << a << "\n";
1009  std::cout << " b: " << b << "\n";
1010  std::cout << " c: " << c << "\n";
1011  }
1012 #endif
1013 
1014  return area;
1015 }
1016 #undef CHECKNEGATIVEAREA
1017 
1019 {
1020  // Get all entities of dimension 2
1021  Range inputRange;
1022  ErrorCode rval = mb->get_entities_by_dimension( set, 2, inputRange );MB_CHK_ERR_RET_VAL( rval, -1.0 );
1023 
1024  // Filter by elements that are owned by current process
1025  std::vector< int > ownerinfo( inputRange.size(), -1 );
1026  Tag intxOwnerTag;
1027  rval = mb->tag_get_handle( "ORIG_PROC", intxOwnerTag );
1028  if( MB_SUCCESS == rval )
1029  {
1030  rval = mb->tag_get_data( intxOwnerTag, inputRange, &ownerinfo[0] );MB_CHK_ERR_RET_VAL( rval, -1.0 );
1031  }
1032 
1033  // compare total area with 4*M_PI * R^2
1034  int ie = 0;
1035  double total_area = 0.;
1036  for( Range::iterator eit = inputRange.begin(); eit != inputRange.end(); ++eit )
1037  {
1038 
1039  // All zero/positive owner data represents ghosted elems
1040  if( ownerinfo[ie++] >= 0 ) continue;
1041 
1042  EntityHandle eh = *eit;
1043  const double elem_area = this->area_spherical_element( mb, eh, R );
1044 
1045  // check whether the area of the spherical element is positive.
1046  assert( elem_area > 0 );
1047 
1048  // sum up the contribution
1049  total_area += elem_area;
1050  }
1051 
1052  // return total mesh area
1053  return total_area;
1054 }
1055 
1057 {
1058  // get the nodes, then the coordinates
1059  const EntityHandle* verts;
1060  int nsides;
1061  ErrorCode rval = mb->get_connectivity( elem, verts, nsides );MB_CHK_ERR_RET_VAL( rval, -1.0 );
1062 
1063  // account for possible padded polygons
1064  while( verts[nsides - 2] == verts[nsides - 1] && nsides > 3 )
1065  nsides--;
1066 
1067  // get coordinates
1068  std::vector< double > coords( 3 * nsides );
1069  rval = mb->get_coords( verts, nsides, &coords[0] );MB_CHK_ERR_RET_VAL( rval, -1.0 );
1070 
1071  // compute and return the area of the polygonal element
1072  return area_spherical_polygon( &coords[0], nsides, R );
1073 }
1074 
1076 {
1077  SphereCoords sph1 = cart_to_spherical( p1 );
1078  SphereCoords sph2 = cart_to_spherical( p2 );
1079  // radius should be the same
1080  return sph1.R *
1081  acos( sin( sph1.lon ) * sin( sph2.lon ) + cos( sph1.lat ) * cos( sph2.lat ) * cos( sph2.lon - sph2.lon ) );
1082 }
1083 
1084 // break the nonconvex quads into triangles; remove the quad from the set? yes.
1085 // maybe radius is not needed;
1086 //
1088 {
1089  // look at each polygon; compute all angles; if one is reflex, break that angle with
1090  // the next triangle; put the 2 new polys in the set;
1091  // still look at the next poly
1092  // replace it with 2 triangles, and remove from set;
1093  // it should work for all polygons / tested first for case 1, with dt 0.5 (too much deformation)
1094  // get all entities of dimension 2
1095  // then get the connectivity, etc
1096 
1097  Range inputRange;
1098  ErrorCode rval = mb->get_entities_by_dimension( lset, 2, inputRange );MB_CHK_ERR( rval );
1099 
1100  Tag corrTag = 0;
1101  EntityHandle dumH = 0;
1102  rval = mb->tag_get_handle( CORRTAGNAME, 1, MB_TYPE_HANDLE, corrTag, MB_TAG_DENSE, &dumH );
1103  if( rval == MB_TAG_NOT_FOUND ) corrTag = 0;
1104 
1105  Tag gidTag;
1106  rval = mb->tag_get_handle( "GLOBAL_ID", 1, MB_TYPE_INTEGER, gidTag, MB_TAG_DENSE );MB_CHK_ERR( rval );
1107 
1108  std::vector< double > coords;
1109  coords.resize( 3 * MAXEDGES ); // at most 10 vertices per polygon
1110  // we should create a queue with new polygons that need processing for reflex angles
1111  // (obtuse)
1112  std::queue< EntityHandle > newPolys;
1113  int brokenPolys = 0;
1114  Range::iterator eit = inputRange.begin();
1115  while( eit != inputRange.end() || !newPolys.empty() )
1116  {
1117  EntityHandle eh;
1118  if( eit != inputRange.end() )
1119  {
1120  eh = *eit;
1121  ++eit;
1122  }
1123  else
1124  {
1125  eh = newPolys.front();
1126  newPolys.pop();
1127  }
1128  // get the nodes, then the coordinates
1129  const EntityHandle* verts;
1130  int num_nodes;
1131  rval = mb->get_connectivity( eh, verts, num_nodes );MB_CHK_ERR( rval );
1132  int nsides = num_nodes;
1133  // account for possible padded polygons
1134  while( verts[nsides - 2] == verts[nsides - 1] && nsides > 3 )
1135  nsides--;
1136  EntityHandle corrHandle = 0;
1137  if( corrTag )
1138  {
1139  rval = mb->tag_get_data( corrTag, &eh, 1, &corrHandle );MB_CHK_ERR( rval );
1140  }
1141  int gid = 0;
1142  rval = mb->tag_get_data( gidTag, &eh, 1, &gid );MB_CHK_ERR( rval );
1143  coords.resize( 3 * nsides );
1144  if( nsides < 4 ) continue; // if already triangles, don't bother
1145  // get coordinates
1146  rval = mb->get_coords( verts, nsides, &coords[0] );MB_CHK_ERR( rval );
1147  // compute each angle
1148  bool alreadyBroken = false;
1149 
1150  for( int i = 0; i < nsides; i++ )
1151  {
1152  double* A = &coords[3 * i];
1153  double* B = &coords[3 * ( ( i + 1 ) % nsides )];
1154  double* C = &coords[3 * ( ( i + 2 ) % nsides )];
1155  double angle = IntxUtils::oriented_spherical_angle( A, B, C );
1156  if( angle - M_PI > 0. ) // even almost reflex is bad; break it!
1157  {
1158  if( alreadyBroken )
1159  {
1160  mb->list_entities( &eh, 1 );
1161  mb->list_entities( verts, nsides );
1162  double* D = &coords[3 * ( ( i + 3 ) % nsides )];
1163  std::cout << "ABC: " << angle << " \n";
1164  std::cout << "BCD: " << IntxUtils::oriented_spherical_angle( B, C, D ) << " \n";
1165  std::cout << "CDA: " << IntxUtils::oriented_spherical_angle( C, D, A ) << " \n";
1166  std::cout << "DAB: " << IntxUtils::oriented_spherical_angle( D, A, B ) << " \n";
1167  std::cout << " this cell has at least 2 angles > 180, it has serious issues\n";
1168 
1169  return MB_FAILURE;
1170  }
1171  // the bad angle is at i+1;
1172  // create 1 triangle and one polygon; add the polygon to the input range, so
1173  // it will be processed too
1174  // also, add both to the set :) and remove the original polygon from the set
1175  // break the next triangle, even though not optimal
1176  // so create the triangle i+1, i+2, i+3; remove i+2 from original list
1177  // even though not optimal in general, it is good enough.
1178  EntityHandle conn3[3] = { verts[( i + 1 ) % nsides], verts[( i + 2 ) % nsides],
1179  verts[( i + 3 ) % nsides] };
1180  // create a polygon with num_nodes-1 vertices, and connectivity
1181  // verts[i+1], verts[i+3], (all except i+2)
1182  std::vector< EntityHandle > conn( nsides - 1 );
1183  for( int j = 1; j < nsides; j++ )
1184  {
1185  conn[j - 1] = verts[( i + j + 2 ) % nsides];
1186  }
1187  EntityHandle newElement;
1188  rval = mb->create_element( MBTRI, conn3, 3, newElement );MB_CHK_ERR( rval );
1189 
1190  rval = mb->add_entities( lset, &newElement, 1 );MB_CHK_ERR( rval );
1191  if( corrTag )
1192  {
1193  rval = mb->tag_set_data( corrTag, &newElement, 1, &corrHandle );MB_CHK_ERR( rval );
1194  }
1195  rval = mb->tag_set_data( gidTag, &newElement, 1, &gid );MB_CHK_ERR( rval );
1196  if( nsides == 4 )
1197  {
1198  // create another triangle
1199  rval = mb->create_element( MBTRI, &conn[0], 3, newElement );MB_CHK_ERR( rval );
1200  }
1201  else
1202  {
1203  // create another polygon, and add it to the inputRange
1204  rval = mb->create_element( MBPOLYGON, &conn[0], nsides - 1, newElement );MB_CHK_ERR( rval );
1205  newPolys.push( newElement ); // because it has less number of edges, the
1206  // reverse should work to find it.
1207  }
1208  rval = mb->add_entities( lset, &newElement, 1 );MB_CHK_ERR( rval );
1209  if( corrTag )
1210  {
1211  rval = mb->tag_set_data( corrTag, &newElement, 1, &corrHandle );MB_CHK_ERR( rval );
1212  }
1213  rval = mb->tag_set_data( gidTag, &newElement, 1, &gid );MB_CHK_ERR( rval );
1214  rval = mb->remove_entities( lset, &eh, 1 );MB_CHK_ERR( rval );
1215  brokenPolys++;
1216  alreadyBroken = true; // get out of the loop, element is broken
1217  }
1218  }
1219  }
1220  if( brokenPolys > 0 )
1221  {
1222  std::cout << "on local process " << my_rank << ", " << brokenPolys
1223  << " concave polygons were decomposed in convex ones \n";
1224 #ifdef VERBOSE
1225  std::stringstream fff;
1226  fff << "file_set" << mb->id_from_handle( lset ) << "rk_" << my_rank << ".h5m";
1227  rval = mb->write_file( fff.str().c_str(), 0, 0, &lset, 1 );MB_CHK_ERR( rval );
1228  std::cout << "wrote new file set: " << fff.str() << "\n";
1229 #endif
1230  }
1231  return MB_SUCCESS;
1232 }
1233 
1234 // looking at quad connectivity, collapse to triangle if 2 nodes equal
1235 // then delete the old quad
1237 {
1238  Range quads;
1239  ErrorCode rval = mb->get_entities_by_type( set, MBQUAD, quads );MB_CHK_ERR( rval );
1240  Tag gid;
1241  gid = mb->globalId_tag();
1242  for( Range::iterator qit = quads.begin(); qit != quads.end(); ++qit )
1243  {
1244  EntityHandle quad = *qit;
1245  const EntityHandle* conn4 = NULL;
1246  int num_nodes = 0;
1247  rval = mb->get_connectivity( quad, conn4, num_nodes );MB_CHK_ERR( rval );
1248  for( int i = 0; i < num_nodes; i++ )
1249  {
1250  int next_node_index = ( i + 1 ) % num_nodes;
1251  if( conn4[i] == conn4[next_node_index] )
1252  {
1253  // form a triangle and delete the quad
1254  // first get the global id, to set it on triangle later
1255  int global_id = 0;
1256  rval = mb->tag_get_data( gid, &quad, 1, &global_id );MB_CHK_ERR( rval );
1257  int i2 = ( i + 2 ) % num_nodes;
1258  int i3 = ( i + 3 ) % num_nodes;
1259  EntityHandle conn3[3] = { conn4[i], conn4[i2], conn4[i3] };
1260  EntityHandle tri;
1261  rval = mb->create_element( MBTRI, conn3, 3, tri );MB_CHK_ERR( rval );
1262  mb->add_entities( set, &tri, 1 );
1263  mb->remove_entities( set, &quad, 1 );
1264  mb->delete_entities( &quad, 1 );
1265  rval = mb->tag_set_data( gid, &tri, 1, &global_id );MB_CHK_ERR( rval );
1266  }
1267  }
1268  }
1269  return MB_SUCCESS;
1270 }
1271 
1273 {
1274  Range cells2d;
1275  ErrorCode rval = mb->get_entities_by_dimension( set, 2, cells2d );
1276  if( MB_SUCCESS != rval ) return rval;
1277  for( Range::iterator qit = cells2d.begin(); qit != cells2d.end(); ++qit )
1278  {
1279  EntityHandle cell = *qit;
1280  const EntityHandle* conn = NULL;
1281  int num_nodes = 0;
1282  rval = mb->get_connectivity( cell, conn, num_nodes );
1283  if( MB_SUCCESS != rval ) return rval;
1284  if( num_nodes < 3 ) return MB_FAILURE;
1285 
1286  double coords[9];
1287  rval = mb->get_coords( conn, 3, coords );
1288  if( MB_SUCCESS != rval ) return rval;
1289 
1290  double area;
1291  if( R > 0 )
1292  area = area_spherical_triangle_lHuiller( coords, coords + 3, coords + 6, R );
1293  else
1294  area = IntxUtils::area2D( coords, coords + 3, coords + 6 );
1295  if( area < 0 )
1296  {
1297  // compute all area, do not revert if total area is positive
1298  std::vector< double > coords2( 3 * num_nodes );
1299  // get coordinates
1300  rval = mb->get_coords( conn, num_nodes, &coords2[0] );
1301  if( MB_SUCCESS != rval ) return MB_FAILURE;
1302  double totArea = area_spherical_polygon_lHuiller( &coords2[0], num_nodes, R );
1303  if( totArea < 0 )
1304  {
1305  std::vector< EntityHandle > newconn( num_nodes );
1306  for( int i = 0; i < num_nodes; i++ )
1307  {
1308  newconn[num_nodes - 1 - i] = conn[i];
1309  }
1310  rval = mb->set_connectivity( cell, &newconn[0], num_nodes );
1311  if( MB_SUCCESS != rval ) return rval;
1312  }
1313  else
1314  {
1315  std::cout << " nonconvex problem first area:" << area << " total area: " << totArea << std::endl;
1316  }
1317  }
1318  }
1319  return MB_SUCCESS;
1320 }
1321 
1322 // distance along a great circle on a sphere of radius 1
1323 // page 4
1324 double IntxUtils::distance_on_sphere( double la1, double te1, double la2, double te2 )
1325 {
1326  return acos( sin( te1 ) * sin( te2 ) + cos( te1 ) * cos( te2 ) * cos( la1 - la2 ) );
1327 }
1328 
1329 /*
1330  * given 2 great circle arcs, AB and CD, compute the unique intersection point, if it exists
1331  * in between
1332  */
1333 ErrorCode IntxUtils::intersect_great_circle_arcs( double* A, double* B, double* C, double* D, double R, double* E )
1334 {
1335  // first verify A, B, C, D are on the same sphere
1336  double R2 = R * R;
1337  const double Tolerance = 1.e-12 * R2;
1338 
1339  CartVect a( A ), b( B ), c( C ), d( D );
1340 
1341  if( fabs( a.length_squared() - R2 ) + fabs( b.length_squared() - R2 ) + fabs( c.length_squared() - R2 ) +
1342  fabs( d.length_squared() - R2 ) >
1343  10 * Tolerance )
1344  return MB_FAILURE;
1345 
1346  CartVect n1 = a * b;
1347  if( n1.length_squared() < Tolerance ) return MB_FAILURE;
1348 
1349  CartVect n2 = c * d;
1350  if( n2.length_squared() < Tolerance ) return MB_FAILURE;
1351  CartVect n3 = n1 * n2;
1352  n3.normalize();
1353 
1354  n3 = R * n3;
1355  // the intersection is either n3 or -n3
1356  CartVect n4 = a * n3, n5 = n3 * b;
1357  if( n1 % n4 >= -Tolerance && n1 % n5 >= -Tolerance )
1358  {
1359  // n3 is good for ab, see if it is good for cd
1360  n4 = c * n3;
1361  n5 = n3 * d;
1362  if( n2 % n4 >= -Tolerance && n2 % n5 >= -Tolerance )
1363  {
1364  E[0] = n3[0];
1365  E[1] = n3[1];
1366  E[2] = n3[2];
1367  }
1368  else
1369  return MB_FAILURE;
1370  }
1371  else
1372  {
1373  // try -n3
1374  n3 = -n3;
1375  n4 = a * n3, n5 = n3 * b;
1376  if( n1 % n4 >= -Tolerance && n1 % n5 >= -Tolerance )
1377  {
1378  // n3 is good for ab, see if it is good for cd
1379  n4 = c * n3;
1380  n5 = n3 * d;
1381  if( n2 % n4 >= -Tolerance && n2 % n5 >= -Tolerance )
1382  {
1383  E[0] = n3[0];
1384  E[1] = n3[1];
1385  E[2] = n3[2];
1386  }
1387  else
1388  return MB_FAILURE;
1389  }
1390  else
1391  return MB_FAILURE;
1392  }
1393 
1394  return MB_SUCCESS;
1395 }
1396 
1397 // verify that result is in between a and b on a great circle arc, and between c and d on a constant
1398 // latitude arc
1399 static bool verify( CartVect a, CartVect b, CartVect c, CartVect d, double x, double y, double z )
1400 {
1401  // to check, the point has to be between a and b on a great arc, and between c and d on a const
1402  // lat circle
1403  CartVect s( x, y, z );
1404  CartVect n1 = a * b;
1405  CartVect n2 = a * s;
1406  CartVect n3 = s * b;
1407  if( n1 % n2 < 0 || n1 % n3 < 0 ) return false;
1408 
1409  // do the same for c, d, s, in plane z=0
1410  c[2] = d[2] = s[2] = 0.; // bring everything in the same plane, z=0;
1411 
1412  n1 = c * d;
1413  n2 = c * s;
1414  n3 = s * d;
1415  if( n1 % n2 < 0 || n1 % n3 < 0 ) return false;
1416 
1417  return true;
1418 }
1419 
1421  double* B,
1422  double* C,
1423  double* D,
1424  double R,
1425  double* E,
1426  int& np )
1427 {
1428  const double distTol = R * 1.e-6;
1429  const double Tolerance = R * R * 1.e-12; // radius should be 1, usually
1430  np = 0; // number of points in intersection
1431  CartVect a( A ), b( B ), c( C ), d( D );
1432  // check input first
1433  double R2 = R * R;
1434  if( fabs( a.length_squared() - R2 ) + fabs( b.length_squared() - R2 ) + fabs( c.length_squared() - R2 ) +
1435  fabs( d.length_squared() - R2 ) >
1436  10 * Tolerance )
1437  return MB_FAILURE;
1438 
1439  if( ( a - b ).length_squared() < Tolerance ) return MB_FAILURE;
1440  if( ( c - d ).length_squared() < Tolerance ) // edges are too short
1441  return MB_FAILURE;
1442 
1443  // CD is the const latitude arc
1444  if( fabs( C[2] - D[2] ) > distTol ) // cd is not on the same z (constant latitude)
1445  return MB_FAILURE;
1446 
1447  if( fabs( R - C[2] ) < distTol || fabs( R + C[2] ) < distTol ) return MB_FAILURE; // too close to the poles
1448 
1449  // find the points on the circle P(teta) = (r*sin(teta), r*cos(teta), C[2]) that are on the
1450  // great circle arc AB normal to the AB circle:
1451  CartVect n1 = a * b; // the normal to the great circle arc (circle)
1452  // solve the system of equations:
1453  /*
1454  * n1%(x, y, z) = 0 // on the great circle
1455  * z = C[2];
1456  * x^2+y^2+z^2 = R^2
1457  */
1458  double z = C[2];
1459  if( fabs( n1[0] ) + fabs( n1[1] ) < 2 * Tolerance )
1460  {
1461  // it is the Equator; check if the const lat edge is Equator too
1462  if( fabs( C[2] ) > distTol )
1463  {
1464  return MB_FAILURE; // no intx, too far from Eq
1465  }
1466  else
1467  {
1468  // all points are on the equator
1469  //
1470  CartVect cd = c * d;
1471  // by convention, c<d, positive is from c to d
1472  // is a or b between c , d?
1473  CartVect ca = c * a;
1474  CartVect ad = a * d;
1475  CartVect cb = c * b;
1476  CartVect bd = b * d;
1477  bool agtc = ( ca % cd >= -Tolerance ); // a>c?
1478  bool dgta = ( ad % cd >= -Tolerance ); // d>a?
1479  bool bgtc = ( cb % cd >= -Tolerance ); // b>c?
1480  bool dgtb = ( bd % cd >= -Tolerance ); // d>b?
1481  if( agtc )
1482  {
1483  if( dgta )
1484  {
1485  // a is for sure a point
1486  E[0] = a[0];
1487  E[1] = a[1];
1488  E[2] = a[2];
1489  np++;
1490  if( bgtc )
1491  {
1492  if( dgtb )
1493  {
1494  // b is also in between c and d
1495  E[3] = b[0];
1496  E[4] = b[1];
1497  E[5] = b[2];
1498  np++;
1499  }
1500  else
1501  {
1502  // then order is c a d b, intx is ad
1503  E[3] = d[0];
1504  E[4] = d[1];
1505  E[5] = d[2];
1506  np++;
1507  }
1508  }
1509  else
1510  {
1511  // b is less than c, so b c a d, intx is ac
1512  E[3] = c[0];
1513  E[4] = c[1];
1514  E[5] = c[2];
1515  np++; // what if E[0] is E[3]?
1516  }
1517  }
1518  else // c < d < a
1519  {
1520  if( dgtb ) // d is for sure in
1521  {
1522  E[0] = d[0];
1523  E[1] = d[1];
1524  E[2] = d[2];
1525  np++;
1526  if( bgtc ) // c<b<d<a
1527  {
1528  // another point is b
1529  E[3] = b[0];
1530  E[4] = b[1];
1531  E[5] = b[2];
1532  np++;
1533  }
1534  else // b<c<d<a
1535  {
1536  // another point is c
1537  E[3] = c[0];
1538  E[4] = c[1];
1539  E[5] = c[2];
1540  np++;
1541  }
1542  }
1543  else
1544  {
1545  // nothing, order is c, d < a, b
1546  }
1547  }
1548  }
1549  else // a < c < d
1550  {
1551  if( bgtc )
1552  {
1553  // c is for sure in
1554  E[0] = c[0];
1555  E[1] = c[1];
1556  E[2] = c[2];
1557  np++;
1558  if( dgtb )
1559  {
1560  // a < c < b < d; second point is b
1561  E[3] = b[0];
1562  E[4] = b[1];
1563  E[5] = b[2];
1564  np++;
1565  }
1566  else
1567  {
1568  // a < c < d < b; second point is d
1569  E[3] = d[0];
1570  E[4] = d[1];
1571  E[5] = d[2];
1572  np++;
1573  }
1574  }
1575  else // a, b < c < d
1576  {
1577  // nothing
1578  }
1579  }
1580  }
1581  // for the 2 points selected, see if it is only one?
1582  // no problem, maybe it will be collapsed later anyway
1583  if( np > 0 ) return MB_SUCCESS;
1584  return MB_FAILURE; // no intersection
1585  }
1586  {
1587  if( fabs( n1[0] ) <= fabs( n1[1] ) )
1588  {
1589  // resolve eq in x: n0 * x + n1 * y +n2*z = 0; y = -n2/n1*z -n0/n1*x
1590  // (u+v*x)^2+x^2=R2-z^2
1591  // (v^2+1)*x^2 + 2*u*v *x + u^2+z^2-R^2 = 0
1592  // delta = 4*u^2*v^2 - 4*(v^2-1)(u^2+z^2-R^2)
1593  // x1,2 =
1594  double u = -n1[2] / n1[1] * z, v = -n1[0] / n1[1];
1595  double a1 = v * v + 1, b1 = 2 * u * v, c1 = u * u + z * z - R2;
1596  double delta = b1 * b1 - 4 * a1 * c1;
1597  if( delta < -Tolerance ) return MB_FAILURE; // no intersection
1598  if( delta > Tolerance ) // 2 solutions possible
1599  {
1600  double x1 = ( -b1 + sqrt( delta ) ) / 2 / a1;
1601  double x2 = ( -b1 - sqrt( delta ) ) / 2 / a1;
1602  double y1 = u + v * x1;
1603  double y2 = u + v * x2;
1604  if( verify( a, b, c, d, x1, y1, z ) )
1605  {
1606  E[0] = x1;
1607  E[1] = y1;
1608  E[2] = z;
1609  np++;
1610  }
1611  if( verify( a, b, c, d, x2, y2, z ) )
1612  {
1613  E[3 * np + 0] = x2;
1614  E[3 * np + 1] = y2;
1615  E[3 * np + 2] = z;
1616  np++;
1617  }
1618  }
1619  else
1620  {
1621  // one solution
1622  double x1 = -b1 / 2 / a1;
1623  double y1 = u + v * x1;
1624  if( verify( a, b, c, d, x1, y1, z ) )
1625  {
1626  E[0] = x1;
1627  E[1] = y1;
1628  E[2] = z;
1629  np++;
1630  }
1631  }
1632  }
1633  else
1634  {
1635  // resolve eq in y, reverse
1636  // n0 * x + n1 * y +n2*z = 0; x = -n2/n0*z -n1/n0*y = u+v*y
1637  // (u+v*y)^2+y^2 -R2+z^2 =0
1638  // (v^2+1)*y^2 + 2*u*v *y + u^2+z^2-R^2 = 0
1639  //
1640  // x1,2 =
1641  double u = -n1[2] / n1[0] * z, v = -n1[1] / n1[0];
1642  double a1 = v * v + 1, b1 = 2 * u * v, c1 = u * u + z * z - R2;
1643  double delta = b1 * b1 - 4 * a1 * c1;
1644  if( delta < -Tolerance ) return MB_FAILURE; // no intersection
1645  if( delta > Tolerance ) // 2 solutions possible
1646  {
1647  double y1 = ( -b1 + sqrt( delta ) ) / 2 / a1;
1648  double y2 = ( -b1 - sqrt( delta ) ) / 2 / a1;
1649  double x1 = u + v * y1;
1650  double x2 = u + v * y2;
1651  if( verify( a, b, c, d, x1, y1, z ) )
1652  {
1653  E[0] = x1;
1654  E[1] = y1;
1655  E[2] = z;
1656  np++;
1657  }
1658  if( verify( a, b, c, d, x2, y2, z ) )
1659  {
1660  E[3 * np + 0] = x2;
1661  E[3 * np + 1] = y2;
1662  E[3 * np + 2] = z;
1663  np++;
1664  }
1665  }
1666  else
1667  {
1668  // one solution
1669  double y1 = -b1 / 2 / a1;
1670  double x1 = u + v * y1;
1671  if( verify( a, b, c, d, x1, y1, z ) )
1672  {
1673  E[0] = x1;
1674  E[1] = y1;
1675  E[2] = z;
1676  np++;
1677  }
1678  }
1679  }
1680  }
1681 
1682  if( np <= 0 ) return MB_FAILURE;
1683  return MB_SUCCESS;
1684 }
1685 
1686 #if 0
1687 ErrorCode set_edge_type_flag(Interface * mb, EntityHandle sf1)
1688 {
1689  Range cells;
1690  ErrorCode rval = mb->get_entities_by_dimension(sf1, 2, cells);
1691  if (MB_SUCCESS!= rval)
1692  return rval;
1693  Range edges;
1694  rval = mb->get_adjacencies(cells, 1, true, edges, Interface::UNION);
1695  if (MB_SUCCESS!= rval)
1696  return rval;
1697 
1698  Tag edgeTypeTag;
1699  int default_int=0;
1700  rval = mb->tag_get_handle("edge_type", 1, MB_TYPE_INTEGER, edgeTypeTag,
1701  MB_TAG_DENSE | MB_TAG_CREAT, &default_int);
1702  if (MB_SUCCESS!= rval)
1703  return rval;
1704  // add edges to the set? not yet, maybe later
1705  // if edge horizontal, set value to 1
1706  int type_constant_lat=1;
1707  for (Range::iterator eit=edges.begin(); eit!=edges.end(); ++eit)
1708  {
1709  EntityHandle edge = *eit;
1710  const EntityHandle *conn=0;
1711  int num_n=0;
1712  rval = mb->get_connectivity(edge, conn, num_n );
1713  if (MB_SUCCESS!= rval)
1714  return rval;
1715  double coords[6];
1716  rval = mb->get_coords(conn, 2, coords);
1717  if (MB_SUCCESS!= rval)
1718  return rval;
1719  if (fabs( coords[2]-coords[5] )< 1.e-6 )
1720  {
1721  rval = mb->tag_set_data(edgeTypeTag, &edge, 1, &type_constant_lat);
1722  if (MB_SUCCESS!= rval)
1723  return rval;
1724  }
1725  }
1726 
1727  return MB_SUCCESS;
1728 }
1729 #endif
1730 
1731 // decide in a different metric if the corners of CS quad are
1732 // in the interior of an RLL quad
1734  double* red2dc,
1735  int nsRed,
1736  CartVect* bluec,
1737  int nsBlue,
1738  int* blueEdgeType,
1739  double* P,
1740  int* side,
1741  double epsil )
1742 {
1743  int extraPoints = 0;
1744  // first decide the blue z coordinates
1745  CartVect A( 0. ), B( 0. ), C( 0. ), D( 0. );
1746  for( int i = 0; i < nsBlue; i++ )
1747  {
1748  if( blueEdgeType[i] == 0 )
1749  {
1750  int iP1 = ( i + 1 ) % nsBlue;
1751  if( bluec[i][2] > bluec[iP1][2] )
1752  {
1753  A = bluec[i];
1754  B = bluec[iP1];
1755  C = bluec[( i + 2 ) % nsBlue];
1756  D = bluec[( i + 3 ) % nsBlue]; // it could be back to A, if triangle on top
1757  break;
1758  }
1759  }
1760  }
1761  if( nsBlue == 3 && B[2] < 0 )
1762  {
1763  // select D to be C
1764  D = C;
1765  C = B; // B is the south pole then
1766  }
1767  // so we have A, B, C, D, with A at the top, b going down, then C, D, D > C, A > B
1768  // AB is const longitude, BC and DA constant latitude
1769  // check now each of the red points if they are inside this rectangle
1770  for( int i = 0; i < nsRed; i++ )
1771  {
1772  CartVect& X = redc[i];
1773  if( X[2] > A[2] || X[2] < B[2] ) continue; // it is above or below the rectangle
1774  // now decide if it is between the planes OAB and OCD
1775  if( ( ( A * B ) % X >= -epsil ) && ( ( C * D ) % X >= -epsil ) )
1776  {
1777  side[i] = 1; //
1778  // it means point X is in the rectangle that we want , on the sphere
1779  // pass the coords 2d
1780  P[extraPoints * 2] = red2dc[2 * i];
1781  P[extraPoints * 2 + 1] = red2dc[2 * i + 1];
1782  extraPoints++;
1783  }
1784  }
1785  return extraPoints;
1786 }
1787 
1789 {
1790  ReadUtilIface* read_iface;
1791  ErrorCode rval = mb->query_interface( read_iface );MB_CHK_ERR( rval );
1792  // create the handle tag for the corresponding element / vertex
1793 
1794  EntityHandle dum = 0;
1795  Tag corrTag = 0; // it will be created here
1796  rval = mb->tag_get_handle( CORRTAGNAME, 1, MB_TYPE_HANDLE, corrTag, MB_TAG_DENSE | MB_TAG_CREAT, &dum );MB_CHK_ERR( rval );
1797 
1798  // give the same global id to new verts and cells created in the lagr(departure) mesh
1799  Tag gid = mb->globalId_tag();
1800 
1801  Range quads;
1802  rval = mb->get_entities_by_type( source_set, MBQUAD, quads );MB_CHK_ERR( rval );
1803 
1804  Range connecVerts;
1805  rval = mb->get_connectivity( quads, connecVerts );MB_CHK_ERR( rval );
1806 
1807  std::map< EntityHandle, EntityHandle > newNodes;
1808 
1809  std::vector< double* > coords;
1810  EntityHandle start_vert, start_elem, *connect;
1811  int num_verts = connecVerts.size();
1812  rval = read_iface->get_node_coords( 3, num_verts, 0, start_vert, coords );
1813  if( MB_SUCCESS != rval ) return rval;
1814  // fill it up
1815  int i = 0;
1816  for( Range::iterator vit = connecVerts.begin(); vit != connecVerts.end(); ++vit, i++ )
1817  {
1818  EntityHandle oldV = *vit;
1819  CartVect posi;
1820  rval = mb->get_coords( &oldV, 1, &( posi[0] ) );MB_CHK_ERR( rval );
1821 
1822  int global_id;
1823  rval = mb->tag_get_data( gid, &oldV, 1, &global_id );MB_CHK_ERR( rval );
1824  EntityHandle new_vert = start_vert + i;
1825  // Cppcheck warning (false positive): variable coords is assigned a value that is never used
1826  coords[0][i] = posi[0];
1827  coords[1][i] = posi[1];
1828  coords[2][i] = posi[2];
1829 
1830  newNodes[oldV] = new_vert;
1831  // set also the correspondent tag :)
1832  rval = mb->tag_set_data( corrTag, &oldV, 1, &new_vert );MB_CHK_ERR( rval );
1833 
1834  // also the other side
1835  // need to check if we really need this; the new vertex will never need the old vertex
1836  // we have the global id which is the same
1837  rval = mb->tag_set_data( corrTag, &new_vert, 1, &oldV );MB_CHK_ERR( rval );
1838  // set the global id on the corresponding vertex the same as the initial vertex
1839  rval = mb->tag_set_data( gid, &new_vert, 1, &global_id );MB_CHK_ERR( rval );
1840  }
1841  // now create new quads in order (in a sequence)
1842 
1843  rval = read_iface->get_element_connect( quads.size(), 4, MBQUAD, 0, start_elem, connect );
1844  if( MB_SUCCESS != rval ) return rval;
1845  int ie = 0;
1846  for( Range::iterator it = quads.begin(); it != quads.end(); ++it, ie++ )
1847  {
1848  EntityHandle q = *it;
1849  int nnodes;
1850  const EntityHandle* conn;
1851  rval = mb->get_connectivity( q, conn, nnodes );MB_CHK_ERR( rval );
1852  int global_id;
1853  rval = mb->tag_get_data( gid, &q, 1, &global_id );MB_CHK_ERR( rval );
1854 
1855  for( int ii = 0; ii < nnodes; ii++ )
1856  {
1857  EntityHandle v1 = conn[ii];
1858  connect[4 * ie + ii] = newNodes[v1];
1859  }
1860  EntityHandle newElement = start_elem + ie;
1861 
1862  // set the corresponding tag; not sure we need this one, from old to new
1863  rval = mb->tag_set_data( corrTag, &q, 1, &newElement );MB_CHK_ERR( rval );
1864  rval = mb->tag_set_data( corrTag, &newElement, 1, &q );MB_CHK_ERR( rval );
1865 
1866  // set the global id
1867  rval = mb->tag_set_data( gid, &newElement, 1, &global_id );MB_CHK_ERR( rval );
1868 
1869  rval = mb->add_entities( dest_set, &newElement, 1 );MB_CHK_ERR( rval );
1870  }
1871 
1872  rval = read_iface->update_adjacencies( start_elem, quads.size(), 4, connect );MB_CHK_ERR( rval );
1873 
1874  return MB_SUCCESS;
1875 }
1876 
1878  EntityHandle file_set,
1879  double merge_tol,
1880  std::vector< Tag >& tagList )
1881 {
1882  Range verts;
1883  ErrorCode rval = mb->get_entities_by_dimension( file_set, 0, verts );MB_CHK_ERR( rval );
1884  rval = mb->remove_entities( file_set, verts );MB_CHK_ERR( rval );
1885 
1886  MergeMesh mm( mb );
1887 
1888  // remove the vertices from the set, before merging
1889 
1890  rval = mm.merge_all( file_set, merge_tol );MB_CHK_ERR( rval );
1891 
1892  // now correct vertices that are repeated in polygons
1893  rval = remove_padded_vertices( mb, file_set, tagList );
1894  return MB_SUCCESS;
1895 }
1896 
1897 ErrorCode IntxUtils::remove_padded_vertices( Interface* mb, EntityHandle file_set, std::vector< Tag >& tagList )
1898 {
1899 
1900  // now correct vertices that are repeated in polygons
1901  Range cells;
1902  ErrorCode rval = mb->get_entities_by_dimension( file_set, 2, cells );MB_CHK_ERR( rval );
1903 
1904  Range verts;
1905  rval = mb->get_connectivity( cells, verts );MB_CHK_ERR( rval );
1906 
1907  Range modifiedCells; // will be deleted at the end; keep the gid
1908  Range newCells;
1909 
1910  for( Range::iterator cit = cells.begin(); cit != cells.end(); ++cit )
1911  {
1912  EntityHandle cell = *cit;
1913  const EntityHandle* connec = NULL;
1914  int num_verts = 0;
1915  rval = mb->get_connectivity( cell, connec, num_verts );MB_CHK_SET_ERR( rval, "Failed to get connectivity" );
1916 
1917  std::vector< EntityHandle > newConnec;
1918  newConnec.push_back( connec[0] ); // at least one vertex
1919  int index = 0;
1920  int new_size = 1;
1921  while( index < num_verts - 2 )
1922  {
1923  int next_index = ( index + 1 );
1924  if( connec[next_index] != newConnec[new_size - 1] )
1925  {
1926  newConnec.push_back( connec[next_index] );
1927  new_size++;
1928  }
1929  index++;
1930  }
1931  // add the last one only if different from previous and first node
1932  if( ( connec[num_verts - 1] != connec[num_verts - 2] ) && ( connec[num_verts - 1] != connec[0] ) )
1933  {
1934  newConnec.push_back( connec[num_verts - 1] );
1935  new_size++;
1936  }
1937  if( new_size < num_verts )
1938  {
1939  // cout << "new cell from " << cell << " has only " << new_size << " vertices \n";
1940  modifiedCells.insert( cell );
1941  // create a new cell with type triangle, quad or polygon
1942  EntityType type = MBTRI;
1943  if( new_size == 3 )
1944  type = MBTRI;
1945  else if( new_size == 4 )
1946  type = MBQUAD;
1947  else if( new_size > 4 )
1948  type = MBPOLYGON;
1949 
1950  // create new cell
1951  EntityHandle newCell;
1952  rval = mb->create_element( type, &newConnec[0], new_size, newCell );MB_CHK_SET_ERR( rval, "Failed to create new cell" );
1953  // set the old id to the new element
1954  newCells.insert( newCell );
1955  double value; // use the same value to reset the tags, even if the tags are int (like Global ID)
1956  for( size_t i = 0; i < tagList.size(); i++ )
1957  {
1958  rval = mb->tag_get_data( tagList[i], &cell, 1, (void*)( &value ) );MB_CHK_SET_ERR( rval, "Failed to get tag value" );
1959  rval = mb->tag_set_data( tagList[i], &newCell, 1, (void*)( &value ) );MB_CHK_SET_ERR( rval, "Failed to set tag value on new cell" );
1960  }
1961  }
1962  }
1963 
1964  rval = mb->remove_entities( file_set, modifiedCells );MB_CHK_SET_ERR( rval, "Failed to remove old cells from file set" );
1965  rval = mb->delete_entities( modifiedCells );MB_CHK_SET_ERR( rval, "Failed to delete old cells" );
1966  rval = mb->add_entities( file_set, newCells );MB_CHK_SET_ERR( rval, "Failed to add new cells to file set" );
1967  rval = mb->add_entities( file_set, verts );MB_CHK_SET_ERR( rval, "Failed to add verts to the file set" );
1968 
1969  return MB_SUCCESS;
1970 }
1971 
1972 } // namespace moab