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