Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 #include "moab/ParallelMergeMesh.hpp" 00002 #include "moab/Core.hpp" 00003 #include "moab/CartVect.hpp" 00004 #include "moab/BoundBox.hpp" 00005 #include "moab/Skinner.hpp" 00006 #include "moab/MergeMesh.hpp" 00007 #include "moab/CN.hpp" 00008 #include <cfloat> 00009 #include <algorithm> 00010 00011 #ifdef MOAB_HAVE_MPI 00012 #include "moab_mpi.h" 00013 #endif 00014 00015 namespace moab 00016 { 00017 00018 // Constructor 00019 /*Get Merge Data and tolerance*/ 00020 ParallelMergeMesh::ParallelMergeMesh( ParallelComm* pc, const double epsilon ) : myPcomm( pc ), myEps( epsilon ) 00021 { 00022 myMB = pc->get_moab(); 00023 mySkinEnts.resize( 4 ); 00024 } 00025 00026 // Have a wrapper function on the actual merge to avoid memory leaks 00027 // Merges elements within a proximity of epsilon 00028 ErrorCode ParallelMergeMesh::merge( EntityHandle levelset, bool skip_local_merge, int dim ) 00029 { 00030 ErrorCode rval = PerformMerge( levelset, skip_local_merge, dim );MB_CHK_ERR( rval ); 00031 CleanUp(); 00032 return rval; 00033 } 00034 00035 // Perform the merge 00036 ErrorCode ParallelMergeMesh::PerformMerge( EntityHandle levelset, bool skip_local_merge, int dim ) 00037 { 00038 // Get the mesh dimension 00039 ErrorCode rval; 00040 if( dim < 0 ) 00041 { 00042 rval = myMB->get_dimension( dim );MB_CHK_ERR( rval ); 00043 } 00044 00045 // Get the local skin elements 00046 rval = PopulateMySkinEnts( levelset, dim, skip_local_merge ); 00047 // If there is only 1 proc, we can return now 00048 if( rval != MB_SUCCESS || myPcomm->size() == 1 ) 00049 { 00050 return rval; 00051 } 00052 00053 // Determine the global bounding box 00054 double gbox[6]; 00055 rval = GetGlobalBox( gbox );MB_CHK_ERR( rval ); 00056 00057 /* Assemble The Destination Tuples */ 00058 // Get a list of tuples which contain (toProc, handle, x,y,z) 00059 myTup.initialize( 1, 0, 1, 3, mySkinEnts[0].size() ); 00060 rval = PopulateMyTup( gbox );MB_CHK_ERR( rval ); 00061 00062 /* Gather-Scatter Tuple 00063 -tup comes out as (remoteProc,handle,x,y,z) */ 00064 myCD.initialize( myPcomm->comm() ); 00065 00066 // 1 represents dynamic tuple, 0 represents index of the processor to send to 00067 myCD.gs_transfer( 1, myTup, 0 ); 00068 00069 /* Sort By X,Y,Z 00070 -Utilizes a custom quick sort incorporating epsilon*/ 00071 SortTuplesByReal( myTup, myEps ); 00072 00073 // Initialize another tuple list for matches 00074 myMatches.initialize( 2, 0, 2, 0, mySkinEnts[0].size() ); 00075 00076 // ID the matching tuples 00077 rval = PopulateMyMatches();MB_CHK_ERR( rval ); 00078 00079 // We can free up the tuple myTup now 00080 myTup.reset(); 00081 00082 /*Gather-Scatter Again*/ 00083 // 1 represents dynamic list, 0 represents proc index to send tuple to 00084 myCD.gs_transfer( 1, myMatches, 0 ); 00085 // We can free up the crystal router now 00086 myCD.reset(); 00087 00088 // Sort the matches tuple list 00089 SortMyMatches(); 00090 00091 // Tag the shared elements 00092 rval = TagSharedElements( dim );MB_CHK_ERR( rval ); 00093 00094 // Free up the matches tuples 00095 myMatches.reset(); 00096 return rval; 00097 } 00098 00099 // Sets mySkinEnts with all of the skin entities on the processor 00100 ErrorCode ParallelMergeMesh::PopulateMySkinEnts( const EntityHandle meshset, int dim, bool skip_local_merge ) 00101 { 00102 /*Merge Mesh Locally*/ 00103 // Get all dim dimensional entities 00104 Range ents; 00105 ErrorCode rval = myMB->get_entities_by_dimension( meshset, dim, ents );MB_CHK_ERR( rval ); 00106 00107 if( ents.empty() && dim == 3 ) 00108 { 00109 dim--; 00110 rval = myMB->get_entities_by_dimension( meshset, dim, ents );MB_CHK_ERR( rval ); // maybe dimension 2 00111 } 00112 00113 // Merge Mesh Locally 00114 if( !skip_local_merge ) 00115 { 00116 MergeMesh merger( myMB, false ); 00117 merger.merge_entities( ents, myEps ); 00118 // We can return if there is only 1 proc 00119 if( rval != MB_SUCCESS || myPcomm->size() == 1 ) 00120 { 00121 return rval; 00122 } 00123 00124 // Rebuild the ents range 00125 ents.clear(); 00126 rval = myMB->get_entities_by_dimension( meshset, dim, ents );MB_CHK_ERR( rval ); 00127 } 00128 00129 /*Get Skin 00130 -Get Range of all dimensional entities 00131 -skinEnts[i] is the skin entities of dimension i*/ 00132 Skinner skinner( myMB ); 00133 for( int skin_dim = dim; skin_dim >= 0; skin_dim-- ) 00134 { 00135 rval = skinner.find_skin( meshset, ents, skin_dim, mySkinEnts[skin_dim] );MB_CHK_ERR( rval ); 00136 } 00137 return MB_SUCCESS; 00138 } 00139 00140 // Determine the global assembly box 00141 ErrorCode ParallelMergeMesh::GetGlobalBox( double* gbox ) 00142 { 00143 ErrorCode rval; 00144 00145 /*Get Bounding Box*/ 00146 BoundBox box; 00147 if( mySkinEnts[0].size() != 0 ) 00148 { 00149 rval = box.update( *myMB, mySkinEnts[0] );MB_CHK_ERR( rval ); 00150 } 00151 00152 // Invert the max 00153 box.bMax *= -1; 00154 00155 /*Communicate to all processors*/ 00156 MPI_Allreduce( (void*)&box, gbox, 6, MPI_DOUBLE, MPI_MIN, myPcomm->comm() ); 00157 00158 /*Assemble Global Bounding Box*/ 00159 // Flip the max back 00160 for( int i = 3; i < 6; i++ ) 00161 { 00162 gbox[i] *= -1; 00163 } 00164 return MB_SUCCESS; 00165 } 00166 00167 // Assemble the tuples with their processor destination 00168 ErrorCode ParallelMergeMesh::PopulateMyTup( double* gbox ) 00169 { 00170 /*Figure out how do partition the global box*/ 00171 double lengths[3]; 00172 int parts[3]; 00173 ErrorCode rval = PartitionGlobalBox( gbox, lengths, parts );MB_CHK_ERR( rval ); 00174 00175 /* Get Skin Coordinates, Vertices */ 00176 double* x = new double[mySkinEnts[0].size()]; 00177 double* y = new double[mySkinEnts[0].size()]; 00178 double* z = new double[mySkinEnts[0].size()]; 00179 rval = myMB->get_coords( mySkinEnts[0], x, y, z ); 00180 if( rval != MB_SUCCESS ) 00181 { 00182 // Prevent Memory Leak 00183 delete[] x; 00184 delete[] y; 00185 delete[] z; 00186 return rval; 00187 } 00188 00189 // Initialize variable to be used in the loops 00190 std::vector< int > toProcs; 00191 int xPart, yPart, zPart, xEps, yEps, zEps, baseProc; 00192 unsigned long long tup_i = 0, tup_ul = 0, tup_r = 0, count = 0; 00193 // These are boolean to determine if the vertex is on close enough to a given border 00194 bool xDup, yDup, zDup; 00195 bool canWrite = myTup.get_writeEnabled(); 00196 if( !canWrite ) myTup.enableWriteAccess(); 00197 // Go through each vertex 00198 for( Range::iterator it = mySkinEnts[0].begin(); it != mySkinEnts[0].end(); ++it ) 00199 { 00200 xDup = false; 00201 yDup = false; 00202 zDup = false; 00203 // Figure out which x,y,z partition the element is in. 00204 xPart = static_cast< int >( floor( ( x[count] - gbox[0] ) / lengths[0] ) ); 00205 xPart = ( xPart < parts[0] ? xPart : parts[0] - 1 ); // Make sure it stays within the bounds 00206 00207 yPart = static_cast< int >( floor( ( y[count] - gbox[1] ) / lengths[1] ) ); 00208 yPart = ( yPart < parts[1] ? yPart : parts[1] - 1 ); // Make sure it stays within the bounds 00209 00210 zPart = static_cast< int >( floor( ( z[count] - gbox[2] ) / lengths[2] ) ); 00211 zPart = ( zPart < parts[2] ? zPart : parts[2] - 1 ); // Make sure it stays within the bounds 00212 00213 // Figure out the partition with the addition of Epsilon 00214 xEps = static_cast< int >( floor( ( x[count] - gbox[0] + myEps ) / lengths[0] ) ); 00215 yEps = static_cast< int >( floor( ( y[count] - gbox[1] + myEps ) / lengths[1] ) ); 00216 zEps = static_cast< int >( floor( ( z[count] - gbox[2] + myEps ) / lengths[2] ) ); 00217 00218 // Figure out if the vertex needs to be sent to multiple procs 00219 xDup = ( xPart != xEps && xEps < parts[0] ); 00220 yDup = ( yPart != yEps && yEps < parts[1] ); 00221 zDup = ( zPart != zEps && zEps < parts[2] ); 00222 00223 // Add appropriate processors to the vector 00224 baseProc = xPart + yPart * parts[0] + zPart * parts[0] * parts[1]; 00225 toProcs.push_back( baseProc ); 00226 if( xDup ) 00227 { 00228 toProcs.push_back( baseProc + 1 ); // Get partition to the right 00229 } 00230 if( yDup ) 00231 { 00232 // Partition up 1 00233 toProcs.push_back( baseProc + parts[0] ); 00234 } 00235 if( zDup ) 00236 { 00237 // Partition above 1 00238 toProcs.push_back( baseProc + parts[0] * parts[1] ); 00239 } 00240 if( xDup && yDup ) 00241 { 00242 // Partition up 1 and right 1 00243 toProcs.push_back( baseProc + parts[0] + 1 ); 00244 } 00245 if( xDup && zDup ) 00246 { 00247 // Partition right 1 and above 1 00248 toProcs.push_back( baseProc + parts[0] * parts[1] + 1 ); 00249 } 00250 if( yDup && zDup ) 00251 { 00252 // Partition up 1 and above 1 00253 toProcs.push_back( baseProc + parts[0] * parts[1] + parts[0] ); 00254 } 00255 if( xDup && yDup && zDup ) 00256 { 00257 // Partition right 1, up 1, and above 1 00258 toProcs.push_back( baseProc + parts[0] * parts[1] + parts[0] + 1 ); 00259 } 00260 // Grow the tuple list if necessary 00261 while( myTup.get_n() + toProcs.size() >= myTup.get_max() ) 00262 { 00263 myTup.resize( myTup.get_max() ? myTup.get_max() + myTup.get_max() / 2 + 1 : 2 ); 00264 } 00265 00266 // Add each proc as a tuple 00267 for( std::vector< int >::iterator proc = toProcs.begin(); proc != toProcs.end(); ++proc ) 00268 { 00269 myTup.vi_wr[tup_i++] = *proc; 00270 myTup.vul_wr[tup_ul++] = *it; 00271 myTup.vr_wr[tup_r++] = x[count]; 00272 myTup.vr_wr[tup_r++] = y[count]; 00273 myTup.vr_wr[tup_r++] = z[count]; 00274 myTup.inc_n(); 00275 } 00276 count++; 00277 toProcs.clear(); 00278 } 00279 delete[] x; 00280 delete[] y; 00281 delete[] z; 00282 if( !canWrite ) myTup.disableWriteAccess(); 00283 return MB_SUCCESS; 00284 } 00285 00286 // Partition the global box by the number of procs 00287 ErrorCode ParallelMergeMesh::PartitionGlobalBox( double* gbox, double* lengths, int* parts ) 00288 { 00289 // Determine the length of each side 00290 double xLen = gbox[3] - gbox[0]; 00291 double yLen = gbox[4] - gbox[1]; 00292 double zLen = gbox[5] - gbox[2]; 00293 // avoid division by zero for deciding the way to partition the global box 00294 // make all sides of the box at least myEps 00295 // it can be zero in some cases, so not possible to compare the ratios :) for best division 00296 if (xLen < myEps) xLen = myEps; 00297 if (yLen < myEps) yLen = myEps; 00298 if (zLen < myEps) zLen = myEps; 00299 unsigned numProcs = myPcomm->size(); 00300 00301 // Partition sides from the longest to shortest lengths 00302 // If x is the longest side 00303 if( xLen >= yLen && xLen >= zLen ) 00304 { 00305 parts[0] = PartitionSide( xLen, yLen * zLen, numProcs, true ); 00306 numProcs /= parts[0]; 00307 // If y is second longest 00308 if( yLen >= zLen ) 00309 { 00310 parts[1] = PartitionSide( yLen, zLen, numProcs, false ); 00311 parts[2] = numProcs / parts[1]; 00312 } 00313 // If z is the longer 00314 else 00315 { 00316 parts[2] = PartitionSide( zLen, yLen, numProcs, false ); 00317 parts[1] = numProcs / parts[2]; 00318 } 00319 } 00320 // If y is the longest side 00321 else if( yLen >= zLen ) 00322 { 00323 parts[1] = PartitionSide( yLen, xLen * zLen, numProcs, true ); 00324 numProcs /= parts[1]; 00325 // If x is the second longest 00326 if( xLen >= zLen ) 00327 { 00328 parts[0] = PartitionSide( xLen, zLen, numProcs, false ); 00329 parts[2] = numProcs / parts[0]; 00330 } 00331 // If z is the second longest 00332 else 00333 { 00334 parts[2] = PartitionSide( zLen, xLen, numProcs, false ); 00335 parts[0] = numProcs / parts[2]; 00336 } 00337 } 00338 // If z is the longest side 00339 else 00340 { 00341 parts[2] = PartitionSide( zLen, xLen * yLen, numProcs, true ); 00342 numProcs /= parts[2]; 00343 // If x is the second longest 00344 if( xLen >= yLen ) 00345 { 00346 parts[0] = PartitionSide( xLen, yLen, numProcs, false ); 00347 parts[1] = numProcs / parts[0]; 00348 } 00349 // If y is the second longest 00350 else 00351 { 00352 parts[1] = PartitionSide( yLen, xLen, numProcs, false ); 00353 parts[0] = numProcs / parts[1]; 00354 } 00355 } 00356 00357 // Divide up each side to give the lengths 00358 lengths[0] = xLen / (double)parts[0]; 00359 lengths[1] = yLen / (double)parts[1]; 00360 lengths[2] = zLen / (double)parts[2]; 00361 return MB_SUCCESS; 00362 } 00363 00364 // Partition a side based on the length ratios 00365 int ParallelMergeMesh::PartitionSide( double sideLen, double restLen, unsigned numProcs, bool altRatio ) 00366 { 00367 // If theres only 1 processor, then just return 1 00368 if( numProcs == 1 ) 00369 { 00370 return 1; 00371 } 00372 // Initialize with the ratio of 1 proc 00373 double ratio = -DBL_MAX; 00374 unsigned factor = 1; 00375 // We need to be able to save the last ratio and factor (for comparison) 00376 double oldRatio = ratio; 00377 double oldFactor = 1; 00378 00379 // This is the ratio were shooting for 00380 double goalRatio = sideLen / restLen; 00381 00382 // Calculate the divisor and numerator power 00383 // This avoid if statements in the loop and is useful since both calculations are similar 00384 double divisor, p; 00385 if( altRatio ) 00386 { 00387 divisor = (double)numProcs * sideLen; 00388 p = 3; 00389 } 00390 else 00391 { 00392 divisor = (double)numProcs; 00393 p = 2; 00394 } 00395 00396 // Find each possible factor 00397 for( unsigned i = 2; i <= numProcs / 2; i++ ) 00398 { 00399 // If it is a factor... 00400 if( numProcs % i == 0 ) 00401 { 00402 // We need to save the past factor 00403 oldRatio = ratio; 00404 oldFactor = factor; 00405 // There are 2 different ways to calculate the ratio: 00406 // Comparing 1 side to 2 sides: (i*i*i)/(numProcs*x) 00407 // Justification: We have a ratio x:y:z (side Lengths) == a:b:c (procs). So a=kx, 00408 // b=ky, c=kz. Also, abc=n (numProcs) => bc = n/a. Also, a=kx => k=a/x => 1/k=x/a And so 00409 // x/(yz) == (kx)/(kyz) == (kx)/(kykz(1/k)) == a/(bc(x/a)) == a/((n/a)(x/a)) == a^3/(nx). 00410 // Comparing 1 side to 1 side: (i*i)/numprocs 00411 // Justification: i/(n/i) == i^2/n 00412 ratio = pow( (double)i, p ) / divisor; 00413 factor = i; 00414 // Once we have passed the goal ratio, we can break since we'll only move away from the 00415 // goal ratio 00416 if( ratio >= goalRatio ) 00417 { 00418 break; 00419 } 00420 } 00421 } 00422 // If we haven't reached the goal ratio yet, check out factor = numProcs 00423 if( ratio < goalRatio ) 00424 { 00425 oldRatio = ratio; 00426 oldFactor = factor; 00427 factor = numProcs; 00428 ratio = pow( (double)numProcs, p ) / divisor; 00429 } 00430 00431 // Figure out if our oldRatio is better than ratio 00432 if( fabs( ratio - goalRatio ) > fabs( oldRatio - goalRatio ) ) 00433 { 00434 factor = oldFactor; 00435 } 00436 // Return our findings 00437 return factor; 00438 } 00439 00440 // Id the tuples that are matching 00441 ErrorCode ParallelMergeMesh::PopulateMyMatches() 00442 { 00443 // Counters for accessing tuples more efficiently 00444 unsigned long i = 0, mat_i = 0, mat_ul = 0, j = 0, tup_r = 0; 00445 double eps2 = myEps * myEps; 00446 00447 uint tup_mi, tup_ml, tup_mul, tup_mr; 00448 myTup.getTupleSize( tup_mi, tup_ml, tup_mul, tup_mr ); 00449 00450 bool canWrite = myMatches.get_writeEnabled(); 00451 if( !canWrite ) myMatches.enableWriteAccess(); 00452 00453 while( ( i + 1 ) < myTup.get_n() ) 00454 { 00455 // Proximity Comparison 00456 double xi = myTup.vr_rd[tup_r], yi = myTup.vr_rd[tup_r + 1], zi = myTup.vr_rd[tup_r + 2]; 00457 00458 bool done = false; 00459 while( !done ) 00460 { 00461 j++; 00462 tup_r += tup_mr; 00463 if( j >= myTup.get_n() ) 00464 { 00465 break; 00466 } 00467 CartVect cv( myTup.vr_rd[tup_r] - xi, myTup.vr_rd[tup_r + 1] - yi, myTup.vr_rd[tup_r + 2] - zi ); 00468 if( cv.length_squared() > eps2 ) 00469 { 00470 done = true; 00471 } 00472 } 00473 // Allocate the tuple list before adding matches 00474 while( myMatches.get_n() + ( j - i ) * ( j - i - 1 ) >= myMatches.get_max() ) 00475 { 00476 myMatches.resize( myMatches.get_max() ? myMatches.get_max() + myMatches.get_max() / 2 + 1 : 2 ); 00477 } 00478 00479 // We now know that tuples [i to j) exclusive match. 00480 // If n tuples match, n*(n-1) match tuples will be made 00481 // tuples are of the form (proc1,proc2,handle1,handle2) 00482 if( i + 1 < j ) 00483 { 00484 int kproc = i * tup_mi; 00485 unsigned long khand = i * tup_mul; 00486 for( unsigned long k = i; k < j; k++ ) 00487 { 00488 int lproc = kproc + tup_mi; 00489 unsigned long lhand = khand + tup_mul; 00490 for( unsigned long l = k + 1; l < j; l++ ) 00491 { 00492 myMatches.vi_wr[mat_i++] = myTup.vi_rd[kproc]; // proc1 00493 myMatches.vi_wr[mat_i++] = myTup.vi_rd[lproc]; // proc2 00494 myMatches.vul_wr[mat_ul++] = myTup.vul_rd[khand]; // handle1 00495 myMatches.vul_wr[mat_ul++] = myTup.vul_rd[lhand]; // handle2 00496 myMatches.inc_n(); 00497 00498 myMatches.vi_wr[mat_i++] = myTup.vi_rd[lproc]; // proc1 00499 myMatches.vi_wr[mat_i++] = myTup.vi_rd[kproc]; // proc2 00500 myMatches.vul_wr[mat_ul++] = myTup.vul_rd[lhand]; // handle1 00501 myMatches.vul_wr[mat_ul++] = myTup.vul_rd[khand]; // handle2 00502 myMatches.inc_n(); 00503 lproc += tup_mi; 00504 lhand += tup_mul; 00505 } 00506 kproc += tup_mi; 00507 khand += tup_mul; 00508 } // End for(int k... 00509 } 00510 i = j; 00511 } // End while(i+1<tup.n) 00512 00513 if( !canWrite ) myMatches.disableWriteAccess(); 00514 return MB_SUCCESS; 00515 } 00516 00517 // Sort the matching tuples so that vertices can be tagged accurately 00518 ErrorCode ParallelMergeMesh::SortMyMatches() 00519 { 00520 TupleList::buffer buf( mySkinEnts[0].size() ); 00521 // Sorts are necessary to check for doubles 00522 // Sort by remote handle 00523 myMatches.sort( 3, &buf ); 00524 // Sort by matching proc 00525 myMatches.sort( 1, &buf ); 00526 // Sort by local handle 00527 myMatches.sort( 2, &buf ); 00528 buf.reset(); 00529 return MB_SUCCESS; 00530 } 00531 00532 // Tag the shared elements using existing PComm functionality 00533 ErrorCode ParallelMergeMesh::TagSharedElements( int dim ) 00534 { 00535 // Manipulate the matches list to tag vertices and entities 00536 // Set up proc ents 00537 Range proc_ents; 00538 ErrorCode rval; 00539 00540 // get the entities in the partition sets 00541 for( Range::iterator rit = myPcomm->partitionSets.begin(); rit != myPcomm->partitionSets.end(); ++rit ) 00542 { 00543 Range tmp_ents; 00544 rval = myMB->get_entities_by_handle( *rit, tmp_ents, true ); 00545 if( MB_SUCCESS != rval ) 00546 { 00547 return rval; 00548 } 00549 proc_ents.merge( tmp_ents ); 00550 } 00551 if( myMB->dimension_from_handle( *proc_ents.rbegin() ) != myMB->dimension_from_handle( *proc_ents.begin() ) ) 00552 { 00553 Range::iterator lower = proc_ents.lower_bound( CN::TypeDimensionMap[0].first ), 00554 upper = proc_ents.upper_bound( CN::TypeDimensionMap[dim - 1].second ); 00555 proc_ents.erase( lower, upper ); 00556 } 00557 00558 // This vector doesn't appear to be used but its in resolve_shared_ents 00559 int maxp = -1; 00560 std::vector< int > sharing_procs( MAX_SHARING_PROCS ); 00561 std::fill( sharing_procs.begin(), sharing_procs.end(), maxp ); 00562 00563 // get ents shared by 1 or n procs 00564 std::map< std::vector< int >, std::vector< EntityHandle > > proc_nranges; 00565 Range proc_verts; 00566 rval = myMB->get_adjacencies( proc_ents, 0, false, proc_verts, Interface::UNION ); 00567 if( rval != MB_SUCCESS ) 00568 { 00569 return rval; 00570 } 00571 00572 rval = myPcomm->tag_shared_verts( myMatches, proc_nranges, proc_verts ); 00573 if( rval != MB_SUCCESS ) 00574 { 00575 return rval; 00576 } 00577 00578 // get entities shared by 1 or n procs 00579 rval = myPcomm->get_proc_nvecs( dim, dim - 1, &mySkinEnts[0], proc_nranges ); 00580 if( rval != MB_SUCCESS ) 00581 { 00582 return rval; 00583 } 00584 00585 // create the sets for each interface; store them as tags on 00586 // the interface instance 00587 Range iface_sets; 00588 rval = myPcomm->create_interface_sets( proc_nranges ); 00589 if( rval != MB_SUCCESS ) 00590 { 00591 return rval; 00592 } 00593 // establish comm procs and buffers for them 00594 std::set< unsigned int > procs; 00595 rval = myPcomm->get_interface_procs( procs, true ); 00596 if( rval != MB_SUCCESS ) 00597 { 00598 return rval; 00599 } 00600 00601 // resolve shared entity remote handles; implemented in ghost cell exchange 00602 // code because it's so similar 00603 rval = myPcomm->exchange_ghost_cells( -1, -1, 0, true, true ); 00604 if( rval != MB_SUCCESS ) 00605 { 00606 return rval; 00607 } 00608 // now build parent/child links for interface sets 00609 rval = myPcomm->create_iface_pc_links(); 00610 return rval; 00611 } 00612 00613 // Make sure to free up any allocated data 00614 // Need to avoid a double free 00615 void ParallelMergeMesh::CleanUp() 00616 { 00617 // The reset operation is now safe and avoids a double free() 00618 myMatches.reset(); 00619 myTup.reset(); 00620 myCD.reset(); 00621 } 00622 00623 // Simple quick sort to real 00624 void ParallelMergeMesh::SortTuplesByReal( TupleList& tup, double eps ) 00625 { 00626 bool canWrite = tup.get_writeEnabled(); 00627 if( !canWrite ) tup.enableWriteAccess(); 00628 00629 uint mi, ml, mul, mr; 00630 tup.getTupleSize( mi, ml, mul, mr ); 00631 PerformRealSort( tup, 0, tup.get_n(), eps, mr ); 00632 00633 if( !canWrite ) tup.disableWriteAccess(); 00634 } 00635 00636 // Swap around tuples 00637 void ParallelMergeMesh::SwapTuples( TupleList& tup, unsigned long a, unsigned long b ) 00638 { 00639 if( a == b ) return; 00640 00641 uint mi, ml, mul, mr; 00642 tup.getTupleSize( mi, ml, mul, mr ); 00643 00644 // Swap mi 00645 unsigned long a_val = a * mi, b_val = b * mi; 00646 for( unsigned long i = 0; i < mi; i++ ) 00647 { 00648 sint t = tup.vi_rd[a_val]; 00649 tup.vi_wr[a_val] = tup.vi_rd[b_val]; 00650 tup.vi_wr[b_val] = t; 00651 a_val++; 00652 b_val++; 00653 } 00654 // Swap ml 00655 a_val = a * ml; 00656 b_val = b * ml; 00657 for( unsigned long i = 0; i < ml; i++ ) 00658 { 00659 slong t = tup.vl_rd[a_val]; 00660 tup.vl_wr[a_val] = tup.vl_rd[b_val]; 00661 tup.vl_wr[b_val] = t; 00662 a_val++; 00663 b_val++; 00664 } 00665 // Swap mul 00666 a_val = a * mul; 00667 b_val = b * mul; 00668 for( unsigned long i = 0; i < mul; i++ ) 00669 { 00670 Ulong t = tup.vul_rd[a_val]; 00671 tup.vul_wr[a_val] = tup.vul_rd[b_val]; 00672 tup.vul_wr[b_val] = t; 00673 a_val++; 00674 b_val++; 00675 } 00676 // Swap mr 00677 a_val = a * mr; 00678 b_val = b * mr; 00679 for( unsigned long i = 0; i < mr; i++ ) 00680 { 00681 realType t = tup.vr_rd[a_val]; 00682 tup.vr_wr[a_val] = tup.vr_rd[b_val]; 00683 tup.vr_wr[b_val] = t; 00684 a_val++; 00685 b_val++; 00686 } 00687 } 00688 00689 // Perform the sorting of a tuple by real 00690 // To sort an entire tuple_list, call (tup,0,tup.n,epsilon) 00691 void ParallelMergeMesh::PerformRealSort( TupleList& tup, 00692 unsigned long left, 00693 unsigned long right, 00694 double eps, 00695 uint tup_mr ) 00696 { 00697 // If list size is only 1 or 0 return 00698 if( left + 1 >= right ) 00699 { 00700 return; 00701 } 00702 unsigned long swap = left, tup_l = left * tup_mr, tup_t = tup_l + tup_mr; 00703 00704 // Swap the median with the left position for a (hopefully) better split 00705 SwapTuples( tup, left, ( left + right ) / 2 ); 00706 00707 // Partition the data 00708 for( unsigned long t = left + 1; t < right; t++ ) 00709 { 00710 // If the left value(pivot) is greater than t_val, swap it into swap 00711 if( TupleGreaterThan( tup, tup_l, tup_t, eps, tup_mr ) ) 00712 { 00713 swap++; 00714 SwapTuples( tup, swap, t ); 00715 } 00716 tup_t += tup_mr; 00717 } 00718 00719 // Swap so that position swap is in the correct position 00720 SwapTuples( tup, left, swap ); 00721 00722 // Sort left and right of swap 00723 PerformRealSort( tup, left, swap, eps, tup_mr ); 00724 PerformRealSort( tup, swap + 1, right, eps, tup_mr ); 00725 } 00726 00727 // Note, this takes the actual tup.vr[] index (aka i*tup.mr) 00728 bool ParallelMergeMesh::TupleGreaterThan( TupleList& tup, 00729 unsigned long vrI, 00730 unsigned long vrJ, 00731 double eps, 00732 uint tup_mr ) 00733 { 00734 unsigned check = 0; 00735 while( check < tup_mr ) 00736 { 00737 // If the values are the same 00738 if( fabs( tup.vr_rd[vrI + check] - tup.vr_rd[vrJ + check] ) <= eps ) 00739 { 00740 check++; 00741 continue; 00742 } 00743 // If I greater than J 00744 else if( tup.vr_rd[vrI + check] > tup.vr_rd[vrJ + check] ) 00745 { 00746 return true; 00747 } 00748 // If J greater than I 00749 else 00750 { 00751 return false; 00752 } 00753 } 00754 // All Values are the same 00755 return false; 00756 } 00757 00758 } // End namespace moab