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