Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
Intx2MeshOnSphere.cpp
Go to the documentation of this file.
1 /*
2  * Intx2MeshOnSphere.cpp
3  *
4  * Created on: Oct 3, 2012
5  */
6 
7 #ifdef _MSC_VER /* windows */
8 #define _USE_MATH_DEFINES // For M_PI
9 #endif
10 
13 #include "moab/GeomUtil.hpp"
14 #include "moab/BoundBox.hpp"
15 #include "moab/MeshTopoUtil.hpp"
16 #ifdef MOAB_HAVE_MPI
17 #include "moab/ParallelComm.hpp"
18 #endif
19 #include "MBTagConventions.hpp"
20 
21 #include <cassert>
22 
23 // #define ENABLE_DEBUG
24 // #define CHECK_CONVEXITY
25 namespace moab
26 {
27 
29  : Intx2Mesh( mbimpl ), areaMethod( amethod ), plane( 0 ), Rsrc( 0.0 ), Rdest( 0.0 )
30 {
31 }
32 
34 
35 /*
36  * return also the area for robustness verification
37  */
39 {
40  // get coordinates of the target quad, to decide the gnomonic plane
41  double cellArea = 0;
42 
43  int num_nodes;
44  ErrorCode rval = mb->get_connectivity( tgt, tgtConn, num_nodes );MB_CHK_ERR_RET_VAL( rval, cellArea );
45 
46  nsTgt = num_nodes;
47  // account for possible padded polygons
48  while( tgtConn[nsTgt - 2] == tgtConn[nsTgt - 1] && nsTgt > 3 )
49  nsTgt--;
50 
51  // CartVect coords[4];
52  rval = mb->get_coords( tgtConn, nsTgt, &( tgtCoords[0][0] ) );MB_CHK_ERR_RET_VAL( rval, cellArea );
53 
54  CartVect middle = tgtCoords[0];
55  for( int i = 1; i < nsTgt; i++ )
56  middle += tgtCoords[i];
57  middle = 1. / nsTgt * middle;
58 
59  IntxUtils::decide_gnomonic_plane( middle, plane ); // output the plane
60  for( int j = 0; j < nsTgt; j++ )
61  {
62  // populate coords in the plane for intersection
63  // they should be oriented correctly, positively
64  rval = IntxUtils::gnomonic_projection( tgtCoords[j], Rdest, plane, tgtCoords2D[2 * j], tgtCoords2D[2 * j + 1] );MB_CHK_ERR_RET_VAL( rval, cellArea );
65  }
66 
67  for( int j = 1; j < nsTgt - 1; j++ )
68  cellArea += IntxUtils::area2D( &tgtCoords2D[0], &tgtCoords2D[2 * j], &tgtCoords2D[2 * j + 2] );
69 
70  // take target coords in order and compute area in plane
71  return cellArea;
72 }
73 
74 /* the elements are convex for sure, then do a gnomonic projection of both,
75  * compute intersection in the plane, then go back to the sphere for the points
76  * */
78  EntityHandle src,
79  double* P,
80  int& nP,
81  double& area,
82  int markb[MAXEDGES],
83  int markr[MAXEDGES],
84  int& nsBlue,
85  int& nsTgt,
86  bool check_boxes_first )
87 {
88  // the area will be used from now on, to see how well we fill the target cell with polygons
89  // the points will be at most 40; they will describe a convex patch, after the points will be
90  // ordered and collapsed (eliminate doubles)
91 
92  // CartVect srccoords[4];
93  int num_nodes = 0;
94  MB_CHK_ERR( mb->get_connectivity( src, srcConn, num_nodes ) );
95  nsBlue = num_nodes;
96  // account for possible padded polygons
97  while( srcConn[nsBlue - 2] == srcConn[nsBlue - 1] && nsBlue > 3 )
98  nsBlue--;
99  MB_CHK_ERR( mb->get_coords( srcConn, nsBlue, &( srcCoords[0][0] ) ) );
100 
101  area = 0.;
102  nP = 0; // number of intersection points we are marking the boundary of src!
103  if( check_boxes_first )
104  {
105  // look at the boxes formed with vertices; if they are far away, return false early
106  // make sure the target is setup already
107  setup_tgt_cell( tgt, nsTgt ); // we do not need area here
108  // use here gnomonic plane (plane) to see where source is
109  bool overlap3d = GeomUtil::bounding_boxes_overlap( tgtCoords, nsTgt, srcCoords, nsBlue, box_error );
110  int planeb;
111  CartVect mid3 = ( srcCoords[0] + srcCoords[1] + srcCoords[2] ) / 3;
112  IntxUtils::decide_gnomonic_plane( mid3, planeb );
113  if( !overlap3d && ( plane != planeb ) ) // plane was set at setup_tgt_cell
114  return MB_SUCCESS; // no error, but no intersection, decide early to get out
115  // if same plane, still check for gnomonic plane in 2d
116  // if no overlap in 2d, get out
117  if( !overlap3d && plane == planeb ) // CHECK 2D too
118  {
119  for( int j = 0; j < nsBlue; j++ )
120  {
122  srcCoords2D[2 * j + 1] ) );
123  }
124  bool overlap2d = GeomUtil::bounding_boxes_overlap_2d( srcCoords2D, nsBlue, tgtCoords2D, nsTgt, box_error );
125  if( !overlap2d ) return MB_SUCCESS; // we are sure they are not overlapping in 2d , either
126  }
127  }
128 #ifdef ENABLE_DEBUG
129  if( dbg_1 )
130  {
131  std::cout << "tgt " << mb->id_from_handle( tgt ) << "\n";
132  for( int j = 0; j < nsTgt; j++ )
133  {
134  std::cout << tgtCoords[j] << "\n";
135  }
136  std::cout << "src " << mb->id_from_handle( src ) << "\n";
137  for( int j = 0; j < nsBlue; j++ )
138  {
139  std::cout << srcCoords[j] << "\n";
140  }
141  mb->list_entities( &tgt, 1 );
142  mb->list_entities( &src, 1 );
143  }
144 #endif
145 
146  for( int j = 0; j < nsBlue; j++ )
147  {
148  MB_CHK_ERR(
150  }
151 
152 #ifdef ENABLE_DEBUG
153  if( dbg_1 )
154  {
155  std::cout << "gnomonic plane: " << plane << "\n";
156  std::cout << " target src\n";
157  for( int j = 0; j < nsTgt; j++ )
158  {
159  std::cout << tgtCoords2D[2 * j] << " " << tgtCoords2D[2 * j + 1] << "\n";
160  }
161  for( int j = 0; j < nsBlue; j++ )
162  {
163  std::cout << srcCoords2D[2 * j] << " " << srcCoords2D[2 * j + 1] << "\n";
164  }
165  }
166 #endif
167 
168  MB_CHK_ERR( IntxUtils::EdgeIntersections2( srcCoords2D, nsBlue, tgtCoords2D, nsTgt, markb, markr, P, nP ) );
169 
170  int side[MAXEDGES] = { 0 }; // this refers to what side? source or tgt?
171  int extraPoints =
172  IntxUtils::borderPointsOfXinY2( srcCoords2D, nsBlue, tgtCoords2D, nsTgt, &( P[2 * nP] ), side, epsilon_area );
173  if( extraPoints >= 1 )
174  {
175  for( int k = 0; k < nsBlue; k++ )
176  {
177  if( side[k] )
178  {
179  // this means that vertex k of source is inside convex tgt; mark edges k-1 and k in
180  // src,
181  // as being "intersected" by tgt; (even though they might not be intersected by
182  // other edges, the fact that their apex is inside, is good enough)
183  markb[k] = 1;
184  markb[( k + nsBlue - 1 ) % nsBlue] =
185  1; // it is the previous edge, actually, but instead of doing -1, it is
186  // better to do modulo +3 (modulo 4)
187  // null side b for next call
188  side[k] = 0;
189  }
190  }
191  }
192  nP += extraPoints;
193 
194  extraPoints =
195  IntxUtils::borderPointsOfXinY2( tgtCoords2D, nsTgt, srcCoords2D, nsBlue, &( P[2 * nP] ), side, epsilon_area );
196  if( extraPoints >= 1 )
197  {
198  for( int k = 0; k < nsTgt; k++ )
199  {
200  if( side[k] )
201  {
202  // this is to mark that target edges k-1 and k are intersecting src
203  markr[k] = 1;
204  markr[( k + nsTgt - 1 ) % nsTgt] =
205  1; // it is the previous edge, actually, but instead of doing -1, it is
206  // better to do modulo +3 (modulo 4)
207  // null side b for next call
208  }
209  }
210  }
211  nP += extraPoints;
212 
213  // now sort and orient the points in P, such that they are forming a convex polygon
214  // this will be the foundation of our new mesh
215  // this works if the polygons are convex
216  IntxUtils::SortAndRemoveDoubles2( P, nP, epsilon_1 ); // nP should be at most 8 in the end ?
217  // if there are more than 3 points, some area will be positive
218 
219  if( nP >= 3 )
220  {
221  for( int k = 1; k < nP - 1; k++ )
222  area += IntxUtils::area2D( P, &P[2 * k], &P[2 * k + 2] );
223 #ifdef CHECK_CONVEXITY
224  // each edge should be large enough that we can compute angles between edges
225  for( int k = 0; k < nP; k++ )
226  {
227  int k1 = ( k + 1 ) % nP;
228  int k2 = ( k1 + 1 ) % nP;
229  double orientedArea = IntxUtils::area2D( &P[2 * k], &P[2 * k1], &P[2 * k2] );
230  if( orientedArea < 0 && fabs(orientedArea) > std::numeric_limits<double>::epsilon() )
231  {
232  std::cout << " oriented area is negative: " << orientedArea << " k:" << k << " target, src:" << tgt
233  << " " << src << " \n";
234  }
235  }
236 #endif
237  }
238 
239  return MB_SUCCESS; // no error
240 }
241 
242 // this method will also construct the triangles/quads/polygons in the new mesh
243 // if we accept planar polygons, we just save them
244 // also, we could just create new vertices every time, and merge only in the end;
245 // could be too expensive, and the tolerance for merging could be an
246 // interesting topic
247 ErrorCode Intx2MeshOnSphere::findNodes( EntityHandle tgt, int nsTgt, EntityHandle src, int nsBlue, double* iP, int nP )
248 {
249 #ifdef ENABLE_DEBUG
250  // first of all, check against target and source vertices
251  //
252  if( dbg_1 )
253  {
254  std::cout << "tgt, src, nP, P " << mb->id_from_handle( tgt ) << " " << mb->id_from_handle( src ) << " " << nP
255  << "\n";
256  for( int n = 0; n < nP; n++ )
257  std::cout << " \t" << iP[2 * n] << "\t" << iP[2 * n + 1] << "\n";
258  }
259 #endif
260 
261  // get the edges for the target triangle; the extra points will be on those edges, saved as
262  // lists (unordered)
263 
264  // first get the list of edges adjacent to the target cell
265  // use the neighTgtEdgeTag
266  EntityHandle adjTgtEdges[MAXEDGES];
267  MB_CHK_SET_ERR( mb->tag_get_data( neighTgtEdgeTag, &tgt, 1, &( adjTgtEdges[0] ) ), "can't get edge target tag" );
268  // we know that we have only nsTgt edges here; [nsTgt, MAXEDGES) are ignored, but it is small
269  // potatoes some of them will be handles to the initial vertices from source or target meshes
270 
271  std::vector< EntityHandle > foundIds;
272  foundIds.resize( nP );
273 #ifdef CHECK_CONVEXITY
274  int npBefore1 = nP;
275  int oldNodes = 0;
276  int otherIntx = 0;
277  moab::IntxAreaUtils areaAdaptor;
278 #endif
279  for( int i = 0; i < nP; i++ )
280  {
281  double* pp = &iP[2 * i]; // iP+2*i
282  // project the point back on the sphere
283  CartVect pos;
284  IntxUtils::reverse_gnomonic_projection( pp[0], pp[1], Rdest, plane, pos );
285  int found = 0;
286  // first, are they on vertices from target or src?
287  // priority is the target mesh (mb2?)
288  int j = 0;
289  EntityHandle outNode = (EntityHandle)0;
290  for( j = 0; j < nsTgt && !found; j++ )
291  {
292  // int node = tgtTri.v[j];
293  double d2 = IntxUtils::dist2( pp, &tgtCoords2D[2 * j] );
294  if( d2 < epsilon_1 / 1000 ) // two orders of magnitude smaller than it should, to avoid concave polygons
295  {
296 
297  foundIds[i] = tgtConn[j]; // no new node
298  found = 1;
299 #ifdef CHECK_CONVEXITY
300  oldNodes++;
301 #endif
302 #ifdef ENABLE_DEBUG
303  if( dbg_1 )
304  std::cout << " target node j:" << j << " id:" << mb->id_from_handle( tgtConn[j] )
305  << " 2d coords:" << tgtCoords2D[2 * j] << " " << tgtCoords2D[2 * j + 1] << " d2: " << d2
306  << " \n";
307 #endif
308  }
309  }
310 
311  for( j = 0; j < nsBlue && !found; j++ )
312  {
313  // int node = srcTri.v[j];
314  double d2 = IntxUtils::dist2( pp, &srcCoords2D[2 * j] );
315  if( d2 < epsilon_1 / 1000 )
316  {
317  // suspect is srcConn[j] corresponding in mbOut
318 
319  foundIds[i] = srcConn[j]; // no new node
320  found = 1;
321 #ifdef CHECK_CONVEXITY
322  oldNodes++;
323 #endif
324 #ifdef ENABLE_DEBUG
325  if( dbg_1 )
326  std::cout << " source node " << j << " " << mb->id_from_handle( srcConn[j] ) << " d2:" << d2
327  << " \n";
328 #endif
329  }
330  }
331 
332  if( !found )
333  {
334  // find the edge it belongs, first, on the red element
335  // look at the minimum area, not at the first below some tolerance
336  double minArea = 1.e+38;
337  int index_min = -1;
338  for( j = 0; j < nsTgt; j++ )
339  {
340  int j1 = ( j + 1 ) % nsTgt;
341  double area = fabs( IntxUtils::area2D( &tgtCoords2D[2 * j], &tgtCoords2D[2 * j1], pp ) );
342  // how to check if pp is between redCoords2D[j] and redCoords2D[j1] ?
343  // they should form a straight line; the sign should be -1
344  double checkx = IntxUtils::dist2( &tgtCoords2D[2 * j], pp ) +
345  IntxUtils::dist2( &tgtCoords2D[2 * j1], pp ) -
346  IntxUtils::dist2( &tgtCoords2D[2 * j], &tgtCoords2D[2 * j1] );
347  if( area < minArea && checkx < 2 * epsilon_1 ) // round off error or not?
348  {
349  index_min = j;
350  minArea = area;
351  }
352  }
353  // verify that index_min is valid
354  assert( index_min >= 0 );
355 
356  if( minArea < epsilon_1 / 2 ) // we found the smallest area, so we think we found the
357  // target edge it belongs
358  {
359  // found the edge; now find if there is a point in the list here
360  // std::vector<EntityHandle> * expts = extraNodesMap[tgtEdges[j]];
361  int indx = TgtEdges.index( adjTgtEdges[index_min] );
362  if( indx < 0 ) // CID 181166 (#1 of 1): Argument cannot be negative (NEGATIVE_RETURNS)
363  {
364  std::cerr << " error in adjacent target edge: " << mb->id_from_handle( adjTgtEdges[index_min] )
365  << "\n";
366  return MB_FAILURE;
367  }
368  std::vector< EntityHandle >* expts = extraNodesVec[indx];
369  // if the points pp is between extra points, then just give that id
370  // if not, create a new point, (check the id)
371  // get the coordinates of the extra points so far
372  int nbExtraNodesSoFar = expts->size();
373  if( nbExtraNodesSoFar > 0 )
374  {
375  std::vector< CartVect > coords1;
376  coords1.resize( nbExtraNodesSoFar );
377  mb->get_coords( &( *expts )[0], nbExtraNodesSoFar, &( coords1[0][0] ) );
378  // std::list<int>::iterator it;
379  for( int k = 0; k < nbExtraNodesSoFar && !found; k++ )
380  {
381  // int pnt = *it;
382  double d2 = ( pos - coords1[k] ).length();
383  if( d2 < 2 * epsilon_1 ) // is this below machine precision?
384  {
385  found = 1;
386  foundIds[i] = ( *expts )[k];
387 #ifdef CHECK_CONVEXITY
388  otherIntx++;
389 #endif
390  }
391  }
392  }
393  if( !found )
394  {
395  // create a new point in 2d (at the intersection)
396  // foundIds[i] = m_num2dPoints;
397  // expts.push_back(m_num2dPoints);
398  // need to create a new node in mbOut
399  // this will be on the edge, and it will be added to the local list
400  MB_CHK_ERR( mb->create_vertex( pos.array(), outNode ) );
401  ( *expts ).push_back( outNode );
402  // CID 181168; avoid leak storage error
403  MB_CHK_ERR( mb->add_entities( outSet, &outNode, 1 ) );
404  foundIds[i] = outNode;
405  found = 1;
406  }
407  }
408  }
409  if( !found )
410  {
411  std::cout << " target quad: ";
412  for( int j1 = 0; j1 < nsTgt; j1++ )
413  {
414  std::cout << tgtCoords2D[2 * j1] << " " << tgtCoords2D[2 * j1 + 1] << "\n";
415  }
416  std::cout << " a point pp is not on a target quad " << *pp << " " << pp[1] << " target quad "
417  << mb->id_from_handle( tgt ) << " \n";
418  return MB_FAILURE;
419  }
420  }
421 #ifdef ENABLE_DEBUG
422  if( dbg_1 )
423  {
424  std::cout << " candidate polygon: nP" << nP << " plane: " << plane << "\n";
425  for( int i1 = 0; i1 < nP; i1++ )
426  std::cout << iP[2 * i1] << " " << iP[2 * i1 + 1] << " " << foundIds[i1] << "\n";
427  }
428 #endif
429  // first, find out if we have nodes collapsed; shrink them
430  // we may have to reduce nP
431  // it is possible that some nodes are collapsed after intersection only
432  // nodes will always be in order (convex intersection)
433 #ifdef CHECK_CONVEXITY
434  int npBefore2 = nP;
435 #endif
436  correct_polygon( &foundIds[0], nP );
437  // now we can build the triangles, from P array, with foundIds
438  // we will put them in the out set
439  if( nP >= 3 )
440  {
441  EntityHandle polyNew;
442  MB_CHK_ERR( mb->create_element( MBPOLYGON, &foundIds[0], nP, polyNew ) );
443  MB_CHK_ERR( mb->add_entities( outSet, &polyNew, 1 ) );
444 
445  // tag it with the global ids from target and source elements
446  int globalID;
447  MB_CHK_ERR( mb->tag_get_data( gid, &src, 1, &globalID ) );
448  MB_CHK_ERR( mb->tag_set_data( srcParentTag, &polyNew, 1, &globalID ) );
449  // if(!parcomm->rank()) std::cout << "Setting parent for " << mb->id_from_handle(polyNew) <<
450  // " : Blue = " << globalID << ", " << mb->id_from_handle(src) << "\t\n";
451  MB_CHK_ERR( mb->tag_get_data( gid, &tgt, 1, &globalID ) );
452  MB_CHK_ERR( mb->tag_set_data( tgtParentTag, &polyNew, 1, &globalID ) );
453  // if(parcomm->rank()) std::cout << "Setting parent for " << mb->id_from_handle(polyNew) <<
454  // " : target = " << globalID << ", " << mb->id_from_handle(tgt) << "\n";
455 
456  counting++;
457  MB_CHK_ERR( mb->tag_set_data( countTag, &polyNew, 1, &counting ) );
458  if( orgSendProcTag )
459  {
460  int org_proc = -1;
461  MB_CHK_ERR( mb->tag_get_data( orgSendProcTag, &src, 1, &org_proc ) );
462  MB_CHK_ERR( mb->tag_set_data( orgSendProcTag, &polyNew, 1, &org_proc ) ); // yet another tag
463  }
464 #ifdef CHECK_CONVEXITY
465  // each edge should be large enough that we can compute angles between edges
466  std::vector< double > coords;
467  coords.resize( 3 * nP );
468  MB_CHK_ERR( mb->get_coords( &foundIds[0], nP, &coords[0] ) );
469  std::vector< CartVect > posi( nP );
470  MB_CHK_ERR( mb->get_coords( &foundIds[0], nP, &( posi[0][0] ) ) );
471 
472  for( int k = 0; k < nP; k++ )
473  {
474  int k1 = ( k + 1 ) % nP;
475  int k2 = ( k1 + 1 ) % nP;
476  double orientedArea =
477  areaAdaptor.area_spherical_triangle( &coords[3 * k], &coords[3 * k1], &coords[3 * k2], Rdest );
478  if( orientedArea < 0 )
479  {
480  std::cout << " np before 1 , 2, current " << npBefore1 << " " << npBefore2 << " " << nP << "\n";
481  for( int i = 0; i < nP; i++ )
482  {
483  int nexti = ( i + 1 ) % nP;
484  double lengthEdge = ( posi[i] - posi[nexti] ).length();
485  std::cout << " " << foundIds[i] << " edge en:" << lengthEdge << "\n";
486  }
487  std::cout << " old verts: " << oldNodes << " other intx:" << otherIntx << "\n";
488 
489  std::cout << "rank:" << my_rank << " oriented area in 3d is negative: " << orientedArea << " k:" << k
490  << " target, src:" << tgt << " " << src << " \n";
491  }
492  }
493 #endif
494 
495 #ifdef ENABLE_DEBUG
496  if( dbg_1 )
497  {
498  std::cout << "Counting: " << counting << "\n";
499  std::cout << " polygon " << mb->id_from_handle( polyNew ) << " nodes: " << nP << " :";
500  for( int i1 = 0; i1 < nP; i1++ )
501  std::cout << " " << mb->id_from_handle( foundIds[i1] );
502  std::cout << " plane: " << plane << "\n";
503  std::vector< CartVect > posi( nP );
504  mb->get_coords( &foundIds[0], nP, &( posi[0][0] ) );
505  for( int i1 = 0; i1 < nP; i1++ )
506  std::cout << foundIds[i1] << " " << posi[i1] << "\n";
507 
508  std::stringstream fff;
509  fff << "file0" << counting << ".vtk";
510  MB_CHK_ERR( mb->write_mesh( fff.str().c_str(), &outSet, 1 ) );
511  }
512 #endif
513  }
514  // else {
515  // std::cout << "[[FAILURE]] Number of vertices in polygon is less than 3\n";
516  // }
517  // disable_debug();
518  return MB_SUCCESS;
519 }
520 
522 {
523  EntityHandle dum = 0;
524 
525  Tag corrTag;
527  &dum ); // it should have been created
528  MB_CHK_SET_ERR( rval, "can't get correlation tag" );
529 
530  // get all polygons out of out_set; then see where are they coming from
531  Range polys;
532  MB_CHK_SET_ERR( mb->get_entities_by_dimension( out_set, 2, polys ), "can't get polygons out" );
533 
534  // rs2 is the target range, arrival; rs1 is src, departure;
535  // there is a connection between rs1 and rs2, through the corrTag
536  // corrTag is __correlation
537  // basically, mb->tag_get_data(corrTag, &(tgtPoly), 1, &srcPoly);
538  // also, mb->tag_get_data(corrTag, &(srcPoly), 1, &tgtPoly);
539  // we start from rs2 existing, then we have to update something
540 
541  // tagElem will have multiple tracers
542  int numTracers = 0;
543  MB_CHK_SET_ERR( mb->tag_get_length( tagElem, numTracers ), "can't get number of tracers in simulation" );
544  if( numTracers < 1 ) MB_CHK_SET_ERR( MB_FAILURE, "no tracers data" );
545 
546  std::vector< double > currentVals( rs2.size() * numTracers );
547  MB_CHK_SET_ERR( mb->tag_get_data( tagElem, rs2, &currentVals[0] ), "can't get existing tracers values" );
548 
549  // create new tuple list for tracers to other processors, from remote_cells
550 #ifdef MOAB_HAVE_MPI
551  if( remote_cells )
552  {
553  int n = remote_cells->get_n();
554  if( n > 0 )
555  {
556  remote_cells_with_tracers = new TupleList();
557  remote_cells_with_tracers->initialize( 2, 0, 1, numTracers,
558  n ); // tracers are in these tuples
559  remote_cells_with_tracers->enableWriteAccess();
560  for( int i = 0; i < n; i++ )
561  {
562  remote_cells_with_tracers->vi_wr[2 * i] = remote_cells->vi_wr[2 * i];
563  remote_cells_with_tracers->vi_wr[2 * i + 1] = remote_cells->vi_wr[2 * i + 1];
564  // remote_cells->vr_wr[i] = 0.; will have a different tuple for communication
565  remote_cells_with_tracers->vul_wr[i] =
566  remote_cells->vul_wr[i]; // this is the corresponding target cell (arrival)
567  for( int k = 0; k < numTracers; k++ )
568  remote_cells_with_tracers->vr_wr[numTracers * i + k] = 0; // initialize tracers to be transported
569  remote_cells_with_tracers->inc_n();
570  }
571  }
572  delete remote_cells;
573  remote_cells = nullptr;
574  }
575 #endif
576  // for each polygon, we have 2 indices: target and source parents
577  // we need index source to update index tgt?
578  std::vector< double > newValues( rs2.size() * numTracers,
579  0. ); // initialize with 0 all of them
580  // area of the polygon * conc on target (old) current quantity
581  // finally, divide by the area of the tgt
582  double check_intx_area = 0.;
583  moab::IntxAreaUtils intxAreas( this->areaMethod ); // use_lHuiller = true
584  for( Range::iterator it = polys.begin(); it != polys.end(); ++it )
585  {
586  EntityHandle poly = *it;
587  int srcIndex, tgtIndex;
588  MB_CHK_SET_ERR( mb->tag_get_data( srcParentTag, &poly, 1, &srcIndex ), "can't get source tag" );
589 
590  EntityHandle src = rs1[srcIndex - 1]; // big assumption, it should work for meshes where global id is the same
591  // as element handle (ordered from 1 to number of elements); should be OK for Homme meshes
592  MB_CHK_SET_ERR( mb->tag_get_data( tgtParentTag, &poly, 1, &tgtIndex ), "can't get target tag" );
593  // EntityHandle target = rs2[tgtIndex];
594  // big assumption here, target and source are "parallel" ;we should have an index from
595  // source to target (so a deformed source corresponds to an arrival tgt)
596  /// TODO: VSM: Its unclear whether we need the source or destination radius here.
597  double radius = Rsrc;
598  double areap = intxAreas.area_spherical_element( mb, poly, radius );
599  check_intx_area += areap;
600  // so the departure cell at time t (srcIndex) covers a portion of a tgtCell
601  // that quantity will be transported to the tgtCell at time t+dt
602  // the source corresponds to a target arrival
603  EntityHandle tgtArr;
604  rval = mb->tag_get_data( corrTag, &src, 1, &tgtArr );
605  if( 0 == tgtArr || MB_TAG_NOT_FOUND == rval )
606  {
607 #ifdef MOAB_HAVE_MPI
608  if( !remote_cells_with_tracers ) MB_CHK_SET_ERR( MB_FAILURE, "no remote cells, failure\n" );
609  // maybe the element is remote, from another processor
610  int global_id_src;
611  MB_CHK_SET_ERR( mb->tag_get_data( gid, &src, 1, &global_id_src ),
612  "can't get arrival target for corresponding source gid" );
613  // find the
614  int index_in_remote = remote_cells_with_tracers->find( 1, global_id_src );
615  if( index_in_remote == -1 )
616  MB_CHK_SET_ERR( MB_FAILURE, "can't find the global id element in remote cells\n" );
617  for( int k = 0; k < numTracers; k++ )
618  remote_cells_with_tracers->vr_wr[index_in_remote * numTracers + k] +=
619  currentVals[numTracers * ( tgtIndex - 1 ) + k] * areap;
620 #endif
621  }
622  else if( MB_SUCCESS == rval )
623  {
624  int arrTgtIndex = rs2.index( tgtArr );
625  if( -1 == arrTgtIndex ) MB_CHK_SET_ERR( MB_FAILURE, "can't find the target arrival index" );
626  for( int k = 0; k < numTracers; k++ )
627  newValues[numTracers * arrTgtIndex + k] += currentVals[( tgtIndex - 1 ) * numTracers + k] * areap;
628  }
629 
630  else
631  MB_CHK_SET_ERR( rval, "can't get arrival target for corresponding " );
632  }
633  // now, send back the remote_cells_with_tracers to the processors they came from, with the
634  // updated values for the tracer mass in a cell
635 #ifdef MOAB_HAVE_MPI
636  if( remote_cells_with_tracers )
637  {
638  // so this means that some cells will be sent back with tracer info to the procs they were
639  // sent from
640  ( parcomm->proc_config().crystal_router() )->gs_transfer( 1, *remote_cells_with_tracers, 0 );
641  // now, look at the global id, find the proper "tgt" cell with that index and update its
642  // mass
643  // remote_cells->print("remote cells after routing");
644  int n = remote_cells_with_tracers->get_n();
645  for( int j = 0; j < n; j++ )
646  {
647  EntityHandle tgtCell = remote_cells_with_tracers->vul_rd[j]; // entity handle sent back
648  int arrTgtIndex = rs2.index( tgtCell );
649  if( -1 == arrTgtIndex ) MB_CHK_SET_ERR( MB_FAILURE, "can't find the target arrival index" );
650  for( int k = 0; k < numTracers; k++ )
651  newValues[arrTgtIndex * numTracers + k] += remote_cells_with_tracers->vr_rd[j * numTracers + k];
652  }
653  }
654 #endif /* MOAB_HAVE_MPI */
655  // now divide by target area (current)
656  int j = 0;
657  Range::iterator iter = rs2.begin();
658  void* data = nullptr; // used for stored area
659  int count = 0;
660  std::vector< double > total_mass_local( numTracers, 0. );
661  while( iter != rs2.end() )
662  {
663  MB_CHK_SET_ERR( mb->tag_iterate( tagArea, iter, rs2.end(), count, data ), "can't tag iterate" );
664  double* ptrArea = (double*)data;
665  for( int i = 0; i < count; i++, ++iter, j++, ptrArea++ )
666  {
667  for( int k = 0; k < numTracers; k++ )
668  {
669  total_mass_local[k] += newValues[j * numTracers + k];
670  newValues[j * numTracers + k] /= ( *ptrArea );
671  }
672  }
673  }
674  MB_CHK_SET_ERR( mb->tag_set_data( tagElem, rs2, &newValues[0] ), "can't set new values tag" );
675 
676 #ifdef MOAB_HAVE_MPI
677  std::vector< double > total_mass( numTracers, 0. );
678  double total_intx_area = 0;
679  int mpi_err =
680  MPI_Reduce( &total_mass_local[0], &total_mass[0], numTracers, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD );
681  if( MPI_SUCCESS != mpi_err ) return MB_FAILURE;
682  // now reduce total area
683  mpi_err = MPI_Reduce( &check_intx_area, &total_intx_area, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD );
684  if( MPI_SUCCESS != mpi_err ) return MB_FAILURE;
685  if( my_rank == 0 )
686  {
687  for( int k = 0; k < numTracers; k++ )
688  std::cout << "total mass now tracer k=" << k + 1 << " " << total_mass[k] << "\n";
689  std::cout << "check: total intersection area: (4 * M_PI * R^2): " << 4 * M_PI * Rsrc * Rsrc << " "
690  << total_intx_area << "\n";
691  }
692 
693  if( remote_cells_with_tracers )
694  {
695  delete remote_cells_with_tracers;
696  remote_cells_with_tracers = nullptr;
697  }
698 #else
699  for( int k = 0; k < numTracers; k++ )
700  std::cout << "total mass now tracer k=" << k + 1 << " " << total_mass_local[k] << "\n";
701  std::cout << "check: total intersection area: (4 * M_PI * R^2): " << 4 * M_PI * Rsrc * Rsrc << " "
702  << check_intx_area << "\n";
703 #endif
704  return MB_SUCCESS;
705 }
706 
707 #ifdef MOAB_HAVE_MPI
708 ErrorCode Intx2MeshOnSphere::build_processor_euler_boxes( EntityHandle euler_set, Range& local_verts, bool gnomonic )
709 {
710  if( !gnomonic )
711  {
712  return Intx2Mesh::build_processor_euler_boxes( euler_set, local_verts, gnomonic );
713  }
714  // so here, we know that the logic is for gnomonic == true
715  localEnts.clear();
716  MB_CHK_SET_ERR( mb->get_entities_by_dimension( euler_set, 2, localEnts ), "can't get local ents" );
717 
718  MB_CHK_SET_ERR( mb->get_connectivity( localEnts, local_verts ), "can't get connectivity" );
719  int num_local_verts = (int)local_verts.size();
720 
721  assert( parcomm != nullptr );
722 
723  if( num_local_verts == 0 )
724  {
725  // it is probably point cloud, get the local vertices from set
726  MB_CHK_SET_ERR( mb->get_entities_by_dimension( euler_set, 0, local_verts ),
727  "can't get local vertices from set" );
728  num_local_verts = (int)local_verts.size();
729  localEnts = local_verts;
730  }
731  // will use 6 gnomonic planes to decide boxes for each gnomonic plane
732  // each gnomonic box will be 2d, min, max
733  double gnom_box[24];
734  for( int i = 0; i < 6; i++ )
735  {
736  gnom_box[4 * i] = gnom_box[4 * i + 1] = DBL_MAX;
737  gnom_box[4 * i + 2] = gnom_box[4 * i + 3] = -DBL_MAX;
738  }
739 
740  // there are 6 gnomonic planes; some elements could be on the corners, and affect multiple
741  // planes decide what gnomonic planes will be affected by each cell some elements could appear
742  // in multiple gnomonic planes !
743  std::vector< double > coords( 3 * num_local_verts );
744  MB_CHK_SET_ERR( mb->get_coords( local_verts, &coords[0] ), "can't get vertex coords" );
745  // decide each local vertex to what gnomonic plane it belongs
746 
747  std::vector< int > gnplane;
748  gnplane.resize( num_local_verts );
749  for( int i = 0; i < num_local_verts; i++ )
750  {
751  CartVect pos( &coords[3 * i] );
752  int pl;
754  gnplane[i] = pl;
755  }
756 
757  for( Range::iterator it = localEnts.begin(); it != localEnts.end(); ++it )
758  {
759  EntityHandle cell = *it;
760  EntityType typeCell = mb->type_from_handle( cell ); // could be vertex, for point cloud
761  // get coordinates, and decide gnomonic planes for it
762  int nnodes;
763  const EntityHandle* conn = nullptr;
764  EntityHandle c[1];
765  if( typeCell != MBVERTEX )
766  {
767  MB_CHK_SET_ERR( mb->get_connectivity( cell, conn, nnodes ), "can't get connectivity" );
768  }
769  else
770  {
771  nnodes = 1;
772  c[0] = cell; // actual node
773  conn = &c[0];
774  }
775  // get coordinates of vertices involved with this
776  std::vector< double > elco( 3 * nnodes );
777  std::set< int > planes;
778  for( int i = 0; i < nnodes; i++ )
779  {
780  int ix = local_verts.index( conn[i] );
781  planes.insert( gnplane[ix] );
782  for( int j = 0; j < 3; j++ )
783  {
784  elco[3 * i + j] = coords[3 * ix + j];
785  }
786  }
787  // now, augment the boxes for all planes involved
788  for( std::set< int >::iterator st = planes.begin(); st != planes.end(); ++st )
789  {
790  int pl = *st;
791  for( int i = 0; i < nnodes; i++ )
792  {
793  CartVect pos( &elco[3 * i] );
794  double c2[2];
795  IntxUtils::gnomonic_projection( pos, Rdest, pl, c2[0],
796  c2[1] ); // 2 coordinates
797  //
798  for( int k = 0; k < 2; k++ )
799  {
800  double val = c2[k];
801  if( val < gnom_box[4 * ( pl - 1 ) + k] ) gnom_box[4 * ( pl - 1 ) + k] = val; // min in k direction
802  if( val > gnom_box[4 * ( pl - 1 ) + 2 + k] )
803  gnom_box[4 * ( pl - 1 ) + 2 + k] = val; // max in k direction
804  }
805  }
806  }
807  }
808 
809  int numprocs = parcomm->proc_config().proc_size();
810  allBoxes.resize( 24 * numprocs ); // 6 gnomonic planes , 4 doubles for each for 2d box
811 
812  my_rank = parcomm->proc_config().proc_rank();
813  for( int k = 0; k < 24; k++ )
814  allBoxes[24 * my_rank + k] = gnom_box[k];
815 
816  // now communicate to get all boxes
817  int mpi_err;
818 #if ( MPI_VERSION >= 2 )
819  // use "in place" option
820  mpi_err = MPI_Allgather( MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, &allBoxes[0], 24, MPI_DOUBLE,
821  parcomm->proc_config().proc_comm() );
822 #else
823  {
824  std::vector< double > allBoxes_tmp( 24 * parcomm->proc_config().proc_size() );
825  mpi_err = MPI_Allgather( &allBoxes[24 * my_rank], 6, MPI_DOUBLE, &allBoxes_tmp[0], 24, MPI_DOUBLE,
826  parcomm->proc_config().proc_comm() );
827  allBoxes = allBoxes_tmp;
828  }
829 #endif
830  if( MPI_SUCCESS != mpi_err ) return MB_FAILURE;
831 
832 #ifdef VERBOSE
833  if( my_rank == 0 )
834  {
835  std::cout << " maximum number of vertices per cell are " << max_edges_1 << " on first mesh and " << max_edges_2
836  << " on second mesh \n";
837  for( int i = 0; i < numprocs; i++ )
838  {
839  std::cout << "task: " << i << " \n";
840  for( int pl = 1; pl <= 6; pl++ )
841  {
842  std::cout << " plane " << pl << " min: \t" << allBoxes[24 * i + 4 * ( pl - 1 )] << " \t"
843  << allBoxes[24 * i + 4 * ( pl - 1 ) + 1] << "\n";
844  std::cout << " \t max: \t" << allBoxes[24 * i + 4 * ( pl - 1 ) + 2] << " \t"
845  << allBoxes[24 * i + 4 * ( pl - 1 ) + 3] << "\n";
846  }
847  }
848  }
849 #endif
850 
851  return MB_SUCCESS;
852 }
853 //#define VERBOSE
854 // this will use the bounding boxes for the (euler)/ fix mesh that are already established
855 // will distribute the mesh to other procs, so that on each task, the covering set covers the local
856 // bounding box this means it will cover the second (local) mesh set; So the covering set will cover
857 // completely the second local mesh set (in intersection)
858 // now, when covering set needs to have extra layers, we will increase dramatically the box_eps, from something close to 0,
859 // to something larger than the "source mesh size" * sqrt(3) for each layer needed
860 // so the first step is finding the global diagonal mesh size in the source mesh
861 ErrorCode Intx2MeshOnSphere::construct_covering_set( EntityHandle& initial_distributed_set,
862  EntityHandle& covering_set,
863  bool gnomonic,
864  int nb_ghost_layers,
865  bool include_edges )
866 {
867  // primary element came from, in the joint communicator ; this will be forwarded by coverage
868  // mesh needed for tag migrate later on
869  int defaultInt = -1; // no processor, so it was not migrated from somewhere else
870  MB_CHK_SET_ERR( mb->tag_get_handle( "orig_sending_processor", 1, MB_TYPE_INTEGER, orgSendProcTag,
871  MB_TAG_DENSE | MB_TAG_CREAT, &defaultInt ),
872  "can't create original sending processor tag" );
873 
874  assert( parcomm != nullptr );
875  Range meshCells;
876  MB_CHK_SET_ERR( mb->get_entities_by_dimension( initial_distributed_set, 2, meshCells ),
877  "can't get cells by dimension from mesh set" );
878 
879  if( 1 == parcomm->proc_config().proc_size() )
880  {
881  // move all initial cells to coverage set
882  MB_CHK_SET_ERR( mb->add_entities( covering_set, meshCells ), "can't add primary ents to covering set" );
883  // if point cloud source, add vertices
884  if( 0 == meshCells.size() || max_edges_1 == 0 )
885  {
886  // add vertices from the source set
887  Range verts;
888  MB_CHK_SET_ERR( mb->get_entities_by_dimension( initial_distributed_set, 0, verts ),
889  "can't get vertices from mesh set" );
890  MB_CHK_SET_ERR( mb->add_entities( covering_set, verts ), "can't add primary ents to covering set" );
891  }
892  return MB_SUCCESS;
893  }
894 
895  // mark on the coverage mesh where this element came from
896  Tag sendProcTag; /// for coverage mesh, will store the sender
897  MB_CHK_SET_ERR( mb->tag_get_handle( "sending_processor", 1, MB_TYPE_INTEGER, sendProcTag,
898  MB_TAG_DENSE | MB_TAG_CREAT, &defaultInt ),
899  "can't create sending processor tag" );
900 
901  // this information needs to be forwarded to coverage mesh, if this mesh was already migrated
902  // from somewhere else
903  // look at the value of orgSendProcTag for one mesh cell; if -1, no need to forward that; if
904  // !=-1, we know that this mesh was migrated, we need to find out more about origin of cell
905  int orig_sender = -1;
906  EntityHandle oneCell = 0;
907  // decide if we need to transfer global DOFs info attached to each HOMME coarse cell; first we
908  // need to decide if the mesh has that tag; will affect the size of the tuple list involved in
909  // the crystal routing
910  int size_gdofs_tag = 0;
911  std::vector< int > valsDOFs;
912  Tag gdsTag = nullptr;
913  // do not check errors. If gdsTag == nullptr, then no tag found
914  mb->tag_get_handle( "GLOBAL_DOFS", gdsTag );
915 
916  // detect GRID_IMASK tag (set by SCRIP reader on masked meshes; default=1=unmasked)
917  int size_imask_tag = 0;
918  Tag imaskTag = nullptr;
919  mb->tag_get_handle( "GRID_IMASK", imaskTag );
920  if( imaskTag ) size_imask_tag = 1;
921 
922  if( meshCells.size() > 0 )
923  {
924  oneCell = meshCells[0]; // it is possible we do not have any cells, even after migration
925  MB_CHK_SET_ERR( mb->tag_get_data( orgSendProcTag, &oneCell, 1, &orig_sender ),
926  "can't get original sending processor value" );
927  if( gdsTag )
928  {
929  DataType dtype;
930  MB_CHK_SET_ERR( mb->tag_get_data_type( gdsTag, dtype ), "can't get tag data type" );
931  if( MB_TYPE_INTEGER == dtype )
932  {
933  // find the values on first cell
934  int lenTag = 0;
935  MB_CHK_SET_ERR( mb->tag_get_length( gdsTag, lenTag ), "can't get tag length" );
936  if( lenTag > 0 )
937  {
938  valsDOFs.resize( lenTag, -1 );
939  ErrorCode rval = mb->tag_get_data( gdsTag, &oneCell, 1, &valsDOFs[0] );
940  if( valsDOFs[0] > 0 && rval == moab::MB_SUCCESS )
941  {
942  // first value positive means we really need to transport this data during
943  // coverage
944  size_gdofs_tag = lenTag;
945  }
946  }
947  }
948  }
949  }
950 
951  // another collective call, to see if the mesh is migrated and if the GLOBAL_DOFS tag need to be
952  // transferred over to the coverage mesh. It is possible that there is no initial mesh source
953  // mesh on the task, so we do not know that info from the tag but TupleList needs to be sized
954  // uniformly for all tasks. Do a collective MPI_MAX to see if it is migrated and if we have
955  // (collectively) a GLOBAL_DOFS task
956 
957  int local_int_array[3], global_int_array[3];
958  local_int_array[0] = orig_sender;
959  local_int_array[1] = size_gdofs_tag;
960  local_int_array[2] = size_imask_tag;
961  // now reduce over all processors
962  int mpi_err =
963  MPI_Allreduce( local_int_array, global_int_array, 3, MPI_INT, MPI_MAX, parcomm->proc_config().proc_comm() );
964  if( MPI_SUCCESS != mpi_err ) return MB_FAILURE;
965  orig_sender = global_int_array[0];
966  size_gdofs_tag = global_int_array[1];
967  size_imask_tag = global_int_array[2];
968  // if GRID_IMASK is needed (some rank has it) but not present locally, get/create it with default=1
969  if( size_imask_tag && !imaskTag )
970  {
971  int def_val = 1;
972  mb->tag_get_handle( "GRID_IMASK", 1, MB_TYPE_INTEGER, imaskTag, MB_TAG_DENSE | MB_TAG_CREAT, &def_val );
973  }
974 #ifdef VERBOSE
975  std::cout << "proc: " << my_rank << " size_gdofs_tag:" << size_gdofs_tag << "\n";
976 #endif
977  valsDOFs.resize( size_gdofs_tag );
978 
979  // finally, we have correct global info, we can decide if mesh was migrated and if we have
980  // global dofs tag that need to be sent with coverage info
981  int migrated_mesh = 0;
982  if( orig_sender != -1 ) migrated_mesh = 1; //
983 
984  // if size_gdofs_tag>0, we are sure valsDOFs got resized to what we need
985 
986  // get all mesh verts1
987  Range mesh_verts;
988  MB_CHK_SET_ERR( mb->get_connectivity( meshCells, mesh_verts ), "can't get mesh vertices" );
989  size_t num_mesh_verts = mesh_verts.size();
990 
991  // now see the mesh points positions; to what boxes should we send them?
992  std::vector< double > coords_mesh( 3 * num_mesh_verts );
993  MB_CHK_SET_ERR( mb->get_coords( mesh_verts, &coords_mesh[0] ), "can't get mesh points position" );
994 
995  // decide gnomonic plane for each vertex, as in the compute boxes
996  std::vector< int > gnplane;
997  if( gnomonic )
998  {
999  gnplane.resize( num_mesh_verts );
1000  for( size_t i = 0; i < num_mesh_verts; i++ )
1001  {
1002  CartVect pos( &coords_mesh[3 * i] );
1003  int pl;
1005  gnplane[i] = pl;
1006  }
1007  }
1008 
1009  std::vector< int > gids( num_mesh_verts );
1010  MB_CHK_SET_ERR( mb->tag_get_data( gid, mesh_verts, &gids[0] ), "can't get vertices gids" );
1011 
1012  // ranges to send to each processor; will hold vertices and elements (quads/ polygons)
1013  // will look if the box of the mesh cell covers bounding box(es) (within tolerances)
1014  std::map< int, Range > Rto;
1015  int numprocs = parcomm->proc_config().proc_size();
1016 
1017  // now, box error is pretty small, in general
1018  // for bilinear mesh, we need an extra layer, which we will get by increasing the epsilon to catch the extra layer
1019  // it will depend on the size of the source mesh
1020  // so we will compute the max diagonal length for each cell, on the sphere, so we will modify box_error
1021  if( nb_ghost_layers > 0 )
1022  {
1023  double diagonal;
1024  MB_CHK_SET_ERR( IntxUtils::max_diagonal( mb, meshCells, max_edges_1, diagonal ), "can't get max diagonal" );
1025  //
1026  double global_diag = 0;
1027  mpi_err = MPI_Allreduce( &diagonal, &global_diag, 1, MPI_DOUBLE, MPI_MAX, parcomm->proc_config().proc_comm() );
1028  if( MPI_SUCCESS != mpi_err ) return MB_FAILURE;
1029  double extra_thickness = global_diag * nb_ghost_layers;
1030  if( gnomonic ) extra_thickness *= sqrt( 3. );
1031  box_error += extra_thickness; //
1032  if( !my_rank )
1033  std::cout << "ghost_layers:" << nb_ghost_layers << " max diagonal:" << global_diag
1034  << " extra thickness:" << extra_thickness << " box_error:" << box_error << "\n";
1035  }
1036  for( Range::iterator eit = meshCells.begin(); eit != meshCells.end(); ++eit )
1037  {
1038  EntityHandle q = *eit;
1039  const EntityHandle* conn;
1040  int num_nodes;
1041  MB_CHK_SET_ERR( mb->get_connectivity( q, conn, num_nodes ), "can't get connectivity on cell" );
1042 
1043  // first decide what planes need to consider
1044  std::set< int > planes; // if this list contains more than 3 planes, we have a very bad mesh!!!
1045  std::vector< double > elco( 3 * num_nodes );
1046  for( int i = 0; i < num_nodes; i++ )
1047  {
1048  EntityHandle v = conn[i];
1049  int index = mesh_verts.index( v );
1050  if( gnomonic ) planes.insert( gnplane[index] );
1051  for( int j = 0; j < 3; j++ )
1052  {
1053  elco[3 * i + j] = coords_mesh[3 * index + j]; // extract from coords
1054  }
1055  }
1056  if( gnomonic )
1057  {
1058  // now loop over all planes that need to be considered for this element
1059  for( std::set< int >::iterator st = planes.begin(); st != planes.end(); ++st )
1060  {
1061  int pl = *st; // gnomonic plane considered
1062  double qmin[2] = { DBL_MAX, DBL_MAX };
1063  double qmax[2] = { -DBL_MAX, -DBL_MAX };
1064  for( int i = 0; i < num_nodes; i++ )
1065  {
1066  CartVect dp( &elco[3 * i] ); // uses constructor for CartVect that takes a
1067  // pointer to double
1068  // gnomonic projection
1069  double c2[2];
1070  IntxUtils::gnomonic_projection( dp, Rsrc, pl, c2[0], c2[1] ); // 2 coordinates
1071  for( int j = 0; j < 2; j++ )
1072  {
1073  if( qmin[j] > c2[j] ) qmin[j] = c2[j];
1074  if( qmax[j] < c2[j] ) qmax[j] = c2[j];
1075  }
1076  }
1077  // now decide if processor p should be interested in this cell, by looking at plane pl
1078  // 2d box this is one of the few size n loops;
1079  for( int p = 0; p < numprocs; p++ ) // each cell q can be sent to more than one processor
1080  {
1081  double procMin1 = allBoxes[24 * p + 4 * ( pl - 1 )]; // these were determined before
1082  //
1083  if( procMin1 >= DBL_MAX ) // the processor has no targets on this plane
1084  continue;
1085  double procMin2 = allBoxes[24 * p + 4 * ( pl - 1 ) + 1];
1086  double procMax1 = allBoxes[24 * p + 4 * ( pl - 1 ) + 2];
1087  double procMax2 = allBoxes[24 * p + 4 * ( pl - 1 ) + 3];
1088  // test overlap of 2d boxes
1089  if( procMin1 > qmax[0] + box_error || procMin2 > qmax[1] + box_error ) continue; //
1090  if( qmin[0] > procMax1 + box_error || qmin[1] > procMax2 + box_error ) continue;
1091  // good to be inserted
1092  Rto[p].insert( q );
1093  }
1094  }
1095  }
1096  else // regular 3d box; one box per processor
1097  {
1098  for( int p = 0; p < numprocs; p++ )
1099  {
1100  BoundBox box( &allBoxes[6 * p] );
1101  bool insert = false;
1102  for( int i = 0; i < num_nodes; i++ )
1103  {
1104  if( box.contains_point( &elco[3 * i], box_error ) )
1105  {
1106  insert = true;
1107  break;
1108  }
1109  }
1110  if( insert ) Rto[p].insert( q );
1111  }
1112  }
1113  }
1114 
1115  // here, we will use crystal router to send each cell to designated tasks (mesh migration)
1116 
1117  // a better implementation would be to use pcomm send / recv entities; a good test case
1118  // pcomm send / receives uses point to point communication, not global gather / scatter
1119 
1120  // now, build TLv and TLq (tuple list for vertices and cells, separately sent)
1121  size_t numq = 0;
1122  size_t numv = 0;
1123 
1124  // merge the list of vertices and edges to be sent
1125  for( int p = 0; p < numprocs; p++ )
1126  {
1127  Range& range_to_P = Rto[p];
1128  if( include_edges )
1129  {
1130  Range edgesToP;
1131  MB_CHK_SET_ERR( mb->get_adjacencies( range_to_P, 1, false, edgesToP, Interface::UNION ),
1132  "can't get edges" );
1133  numq = numq + edgesToP.size();
1134  range_to_P.merge( edgesToP );
1135  }
1136  // add the vertices to it
1137  if( range_to_P.empty() ) continue; // nothing to send to proc p
1138 #ifdef VERBOSE
1139  std::cout << " proc : " << my_rank << " to proc " << p << " send " << range_to_P.size() << " cells "
1140  << " psize: " << range_to_P.psize() << "\n";
1141 #endif
1142  Range vertsToP;
1143  MB_CHK_SET_ERR( mb->get_connectivity( range_to_P, vertsToP ), "can't get connectivity" );
1144  numq = numq + range_to_P.size();
1145  numv = numv + vertsToP.size();
1146 
1147  range_to_P.merge( vertsToP );
1148  }
1149 
1150  TupleList TLv; // send vertices with a different tuple list
1151  TupleList TLq;
1152  TLv.initialize( 2, 0, 0, 3, numv ); // to proc, GLOBAL ID, 3 real coordinates
1153  TLv.enableWriteAccess();
1154 
1155  // add also GLOBAL_DOFS and GRID_IMASK info, if found on the mesh cell
1156  int sizeTuple =
1157  2 + max_edges_1 + migrated_mesh + size_gdofs_tag +
1158  size_imask_tag; // max edges could be up to MAXEDGES :) for polygons
1159  TLq.initialize( sizeTuple, 0, 0, 0,
1160  numq ); // to proc, elem GLOBAL ID, connectivity[max_edges] (global ID v), plus
1161  // original sender if set (migrated mesh case)
1162  // we will not send the entity handle, global ID should be more than enough
1163  // we will not need more than 2B vertices TODO 2B vertices or cells
1164  // if we need more than 2B, we will need to use a different marker anyway (GLOBAL ID is not
1165  // enough then)
1166 
1167  TLq.enableWriteAccess();
1168 #ifdef VERBOSE
1169  std::cout << "from proc " << my_rank << " send " << numv << " vertices and " << numq << " elements\n";
1170 #endif
1171 
1172  for( int to_proc = 0; to_proc < numprocs; to_proc++ )
1173  {
1174  if( to_proc == (int)my_rank ) continue;
1175  Range& range_to_P = Rto[to_proc];
1176  Range V = range_to_P.subset_by_type( MBVERTEX );
1177 
1178  for( Range::iterator it = V.begin(); it != V.end(); ++it )
1179  {
1180  EntityHandle v = *it;
1181  int index = mesh_verts.index( v ); //
1182  assert( -1 != index );
1183  int n = TLv.get_n(); // current size of tuple list
1184  TLv.vi_wr[2 * n] = to_proc; // send to processor
1185  TLv.vi_wr[2 * n + 1] = gids[index]; // global id needs index in the second_mesh_verts range
1186  TLv.vr_wr[3 * n] = coords_mesh[3 * index]; // departure position, of the node local_verts[i]
1187  TLv.vr_wr[3 * n + 1] = coords_mesh[3 * index + 1];
1188  TLv.vr_wr[3 * n + 2] = coords_mesh[3 * index + 2];
1189  TLv.inc_n(); // increment tuple list size
1190  }
1191  // also, prep the 2d cells for sending ...
1192  Range Q = subtract( range_to_P, mesh_verts ); // it could include edges too
1193  for( Range::iterator it = Q.begin(); it != Q.end(); ++it )
1194  {
1195  EntityHandle q = *it; // this is a second mesh cell (or src, lagrange set)
1196  int global_id;
1197  MB_CHK_SET_ERR( mb->tag_get_data( gid, &q, 1, &global_id ), "can't get gid for polygon" );
1198  int n = TLq.get_n(); // current size
1199  TLq.vi_wr[sizeTuple * n] = to_proc; //
1200  TLq.vi_wr[sizeTuple * n + 1] =
1201  global_id; // global id of element, used to identify it for debug purposes only
1202  const EntityHandle* conn4;
1203  int num_nodes; // could be up to MAXEDGES; max_edges?;
1204  MB_CHK_SET_ERR( mb->get_connectivity( q, conn4, num_nodes ), "can't get connectivity for cell" );
1205  if( num_nodes > max_edges_1 )
1206  {
1207  mb->list_entities( &q, 1 );
1208  MB_CHK_SET_ERR( MB_FAILURE, "too many nodes in a cell (" << num_nodes << "," << max_edges_1 << ")" );
1209  }
1210  for( int i = 0; i < num_nodes; i++ )
1211  {
1212  EntityHandle v = conn4[i];
1213  int index = mesh_verts.index( v );
1214  assert( -1 != index );
1215  TLq.vi_wr[sizeTuple * n + 2 + i] = gids[index];
1216  }
1217  for( int k = num_nodes; k < max_edges_1; k++ )
1218  {
1219  TLq.vi_wr[sizeTuple * n + 2 + k] =
1220  0; // fill the rest of node ids with 0; we know that the node ids start from 1!
1221  }
1222  int currentIndexIntTuple = 2 + max_edges_1;
1223  // is the mesh migrated before or not?
1224  if( migrated_mesh )
1225  {
1226  // case of extra work, maybe need to check if it is ghost ? yes, next loop !
1227  MB_CHK_SET_ERR( mb->tag_get_data( orgSendProcTag, &q, 1, &orig_sender ),
1228  "can't get original sender for polygon, in migrate scenario" );
1229  TLq.vi_wr[sizeTuple * n + currentIndexIntTuple] = orig_sender; // should be different than -1
1230  currentIndexIntTuple++;
1231  }
1232  // GLOBAL_DOFS info, if available
1233  if( size_gdofs_tag )
1234  {
1235  MB_CHK_SET_ERR( mb->tag_get_data( gdsTag, &q, 1, &valsDOFs[0] ), "can't get gdofs data in HOMME" );
1236  for( int i = 0; i < size_gdofs_tag; i++ )
1237  {
1238  TLq.vi_wr[sizeTuple * n + currentIndexIntTuple + i] =
1239  valsDOFs[i]; // should be different than 0 or -1
1240  }
1241  }
1242  // GRID_IMASK info, if available (default=1=unmasked when tag exists but value not set)
1243  if( size_imask_tag )
1244  {
1245  int maskVal = 1; // default: unmasked
1246  mb->tag_get_data( imaskTag, &q, 1, &maskVal );
1247  TLq.vi_wr[sizeTuple * n + currentIndexIntTuple + size_gdofs_tag] = maskVal;
1248  }
1249 
1250  TLq.inc_n(); // increment tuple list size
1251  }
1252  } // end for loop over total number of processors
1253 
1254  // now we are done populating the tuples; route them to the appropriate processors
1255  // this does the communication magic
1256  ( parcomm->proc_config().crystal_router() )->gs_transfer( 1, TLv, 0 );
1257  ( parcomm->proc_config().crystal_router() )->gs_transfer( 1, TLq, 0 );
1258 
1259  // the first mesh elements are in localEnts; we do not need them at all
1260 
1261  // maps from global ids to new vertex and cell handles, that are added
1262 
1263  std::map< int, EntityHandle > globalID_to_vertex_handle;
1264  // we already have some vertices from second mesh set; they are already in the processor, even
1265  // before receiving other verts from neighbors this is an inverse map from gid to vertex handle,
1266  // which is local here, we do not want to duplicate vertices their identifier is the global ID!!
1267  // it must be unique per mesh ! (I mean, first mesh, source); gid for second mesh is not needed here
1268  int k = 0;
1269  for( Range::iterator vit = mesh_verts.begin(); vit != mesh_verts.end(); ++vit, k++ )
1270  {
1271  globalID_to_vertex_handle[gids[k]] = *vit;
1272  }
1273  /*std::map<int, EntityHandle> globalID_to_eh;*/ // do we need this one?
1274  globalID_to_eh.clear(); // we need it now in case of extra work, to not duplicate cells
1275  globalID_to_edgeh.clear(); // we need it now in case of extra work, to not duplicate edges
1276 
1277  // now, look at every TLv, and see if we have to create a vertex there or not
1278  int n = TLv.get_n(); // the size of the points received
1279  for( int i = 0; i < n; i++ )
1280  {
1281  int globalId = TLv.vi_rd[2 * i + 1];
1282  if( globalID_to_vertex_handle.find( globalId ) ==
1283  globalID_to_vertex_handle.end() ) // we do not have locally this vertex (yet)
1284  // so we have to create it, and add to the inverse map
1285  {
1286  EntityHandle new_vert;
1287  double dp_pos[3] = { TLv.vr_wr[3 * i], TLv.vr_wr[3 * i + 1], TLv.vr_wr[3 * i + 2] };
1288  MB_CHK_SET_ERR( mb->create_vertex( dp_pos, new_vert ), "can't create new vertex " );
1289  globalID_to_vertex_handle[globalId] = new_vert; // now add it to the map
1290  // set the GLOBAL ID tag on the new vertex
1291  MB_CHK_SET_ERR( mb->tag_set_data( gid, &new_vert, 1, &globalId ),
1292  "can't set global ID tag on new vertex " );
1293  }
1294  }
1295 
1296  // now, all necessary vertices should be created
1297  // look in the local list of 2d cells for this proc, and add all those cells to covering set
1298  // also
1299 
1300  Range& local = Rto[my_rank];
1301  Range local_q = local.subset_by_dimension( 2 );
1302  Range local_e = local.subset_by_dimension( 1 );
1303 
1304  for( Range::iterator it = local_q.begin(); it != local_q.end(); ++it )
1305  {
1306  EntityHandle q = *it; // these are from source cells, local
1307  int gid_el;
1308  MB_CHK_SET_ERR( mb->tag_get_data( gid, &q, 1, &gid_el ), "can't get global id of cell " );
1309  assert( gid_el >= 0 );
1310  globalID_to_eh[gid_el] = q; // do we need this? yes, now we do; parent tags are now using it heavily
1311  MB_CHK_SET_ERR( mb->tag_set_data( sendProcTag, &q, 1, &my_rank ), "can't set sender for cell" );
1312  }
1313 
1314  if( include_edges )
1315  {
1316  for( Range::iterator it = local_e.begin(); it != local_e.end(); ++it )
1317  {
1318  EntityHandle q = *it; // these are from source edge cells, local
1319  int gid_el;
1320  MB_CHK_SET_ERR( mb->tag_get_data( gid, &q, 1, &gid_el ), "can't get global id of edge " );
1321  assert( gid_el >= 0 );
1322  globalID_to_edgeh[gid_el] = q; // do we need this? yes, now we do; parent tags are now using it heavily
1323  // the edges might be shared, but set this anyway; I do not think we need it TODO: investigate if we need this
1324  MB_CHK_SET_ERR( mb->tag_set_data( sendProcTag, &q, 1, &my_rank ), "can't set sender for cell" );
1325  }
1326  }
1327  // now look at all elements received through; we do not want to duplicate them
1328  n = TLq.get_n(); // number of elements received by this processor
1329  // a cell should be received from one proc only; so why are we so worried about duplicated
1330  // elements? a vertex can be received from multiple sources, that is fine
1331 
1332  for( int i = 0; i < n; i++ )
1333  {
1334  bool isEdge =
1335  TLq.vi_rd[sizeTuple * i + 4] == 0; // this would be the third node id; if 0, it means edge entity handle
1336  int globalIdEl = TLq.vi_rd[sizeTuple * i + 1];
1337  // int from_proc=TLq.vi_rd[sizeTuple * i ]; // we do not need from_proc anymore
1338 
1339  // do we already have a cell with this global ID, represented?
1340  // yes, it could happen for extraWork !
1341  if( ( globalID_to_eh.find( globalIdEl ) != globalID_to_eh.end() ) && ( !isEdge ) ) continue;
1342  if( ( globalID_to_edgeh.find( globalIdEl ) != globalID_to_edgeh.end() ) && ( isEdge ) ) continue;
1343  // construct the conn edge, triangle , quad or polygon
1344  EntityHandle new_conn[MAXEDGES]; // we should use std::vector with max_edges_1
1345  int nnodes = -1;
1346  for( int j = 0; j < max_edges_1; j++ )
1347  {
1348  int vgid = TLq.vi_rd[sizeTuple * i + 2 + j]; // vertex global ID
1349  if( vgid == 0 ) new_conn[j] = 0; // this can actually happen for polygon mesh (when we have less
1350  // number of vertices than max_edges)
1351  // also it could happen if we send edges in coverage set
1352  else
1353  {
1354  assert( globalID_to_vertex_handle.find( vgid ) != globalID_to_vertex_handle.end() );
1355  new_conn[j] = globalID_to_vertex_handle[vgid];
1356  nnodes = j + 1; // nodes are at the beginning, and are variable number
1357  }
1358  }
1359  EntityHandle new_element;
1360  //
1361  EntityType entType = MBEDGE;
1362  if( nnodes == 3 ) entType = MBTRI;
1363  if( nnodes == 4 ) entType = MBQUAD;
1364  if( nnodes > 4 ) entType = MBPOLYGON;
1365  MB_CHK_SET_ERR( mb->create_element( entType, new_conn, nnodes, new_element ),
1366  "can't create new element for second mesh " );
1367 
1368  if( isEdge ) // it could be true only for incldue_edges true
1369  globalID_to_edgeh[globalIdEl] = new_element; // it is actually an edge in this case
1370  else
1371  globalID_to_eh[globalIdEl] = new_element;
1372  local_q.insert( new_element );
1373  // set the global ID now
1374  MB_CHK_SET_ERR( mb->tag_set_data( gid, &new_element, 1, &globalIdEl ), "can't set gid for cell " );
1375 
1376  int currentIndexIntTuple = 2 + max_edges_1;
1377  if( migrated_mesh )
1378  {
1379  orig_sender = TLq.vi_wr[sizeTuple * i + currentIndexIntTuple];
1380  MB_CHK_SET_ERR( mb->tag_set_data( orgSendProcTag, &new_element, 1, &orig_sender ),
1381  "can't set original sender for cell, in migrate scenario" );
1382  currentIndexIntTuple++; // add one more
1383  }
1384 
1385  // store also the processor this coverage element came from
1386  int from_proc = TLq.vi_rd[sizeTuple * i];
1387  MB_CHK_SET_ERR( mb->tag_set_data( sendProcTag, &new_element, 1, &from_proc ), "can't set sender for cell" );
1388 
1389  // check if we need to retrieve and set GLOBAL_DOFS data
1390  if( size_gdofs_tag )
1391  {
1392  for( int j = 0; j < size_gdofs_tag; j++ )
1393  {
1394  valsDOFs[j] = TLq.vi_wr[sizeTuple * i + currentIndexIntTuple + j];
1395  }
1396  MB_CHK_SET_ERR( mb->tag_set_data( gdsTag, &new_element, 1, &valsDOFs[0] ),
1397  "can't set GLOBAL_DOFS data on coverage mesh" );
1398  }
1399  // check if we need to retrieve and set GRID_IMASK data on covering source cells
1400  if( size_imask_tag )
1401  {
1402  int maskVal = TLq.vi_rd[sizeTuple * i + currentIndexIntTuple + size_gdofs_tag];
1403  MB_CHK_SET_ERR( mb->tag_set_data( imaskTag, &new_element, 1, &maskVal ),
1404  "can't set GRID_IMASK data on coverage mesh" );
1405  }
1406  }
1407 
1408  // now, add to the covering_set the elements created in the local_q range
1409  MB_CHK_SET_ERR( mb->add_entities( covering_set, local_q ), "can't add entities to new mesh set " );
1410 #ifdef VERBOSE
1411  std::cout << " proc " << my_rank << " add " << local_q.size() << " cells to covering set \n";
1412 #endif
1413  return MB_SUCCESS;
1414 }
1415 
1416 #endif // MOAB_HAVE_MPI
1417 //#undef VERBOSE
1418 #undef CHECK_CONVEXITY
1419 
1420 } /* namespace moab */