![]() |
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
00009 #include
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+1partitionSets.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