Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 /* $Id$ 00002 * 00003 * MOAB, a Mesh-Oriented datABase, is a software component for creating, 00004 * storing and accessing finite element mesh data. 00005 * 00006 * Copyright 2004 Sandia Corporation. Under the terms of Contract 00007 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government 00008 * retains certain rights in this software. 00009 * 00010 * This library is free software; you can redistribute it and/or 00011 * modify it under the terms of the GNU Lesser General Public 00012 * License as published by the Free Software Foundation; either 00013 * version 2.1 of the License, or (at your option) any later version. 00014 * 00015 */ 00016 00017 /** Get a mesh from MOAB and write a Zoltan partition set back into MOAB and to 00018 * a file 00019 * 00020 * 00021 */ 00022 00023 #include <iostream> 00024 #include <cassert> 00025 #include <sstream> 00026 #include <algorithm> 00027 00028 #include "moab/ZoltanPartitioner.hpp" 00029 #include "moab/Interface.hpp" 00030 #include "Internals.hpp" 00031 #include "moab/Range.hpp" 00032 #include "moab/WriteUtilIface.hpp" 00033 #include "moab/MeshTopoUtil.hpp" 00034 #include "MBTagConventions.hpp" 00035 #include "moab/CN.hpp" 00036 // used for gnomonic projection 00037 #include "moab/IntxMesh/IntxUtils.hpp" 00038 00039 #ifdef MOAB_HAVE_CGM 00040 #include "CGMConfig.h" 00041 #include <limits> 00042 #include "RefEntity.hpp" 00043 #include "RefFace.hpp" 00044 #include "RefEdge.hpp" 00045 #include "RefVertex.hpp" 00046 #include "Body.hpp" 00047 #include "TopologyEntity.hpp" 00048 #include "CastTo.hpp" 00049 #include "CABodies.hpp" 00050 #include "TDParallel.hpp" 00051 #include "TDUniqueId.hpp" 00052 #endif 00053 00054 using namespace moab; 00055 00056 #define RR \ 00057 if( MB_SUCCESS != result ) return result 00058 00059 static double* Points = NULL; 00060 static int* GlobalIds = NULL; 00061 static int NumPoints = 0; 00062 static int* NumEdges = NULL; 00063 static int* NborGlobalId = NULL; 00064 static int* NborProcs = NULL; 00065 static double* ObjWeights = NULL; 00066 static double* EdgeWeights = NULL; 00067 static int* Parts = NULL; 00068 00069 const bool debug = false; 00070 00071 ZoltanPartitioner::ZoltanPartitioner( Interface* impl, 00072 #ifdef MOAB_HAVE_MPI 00073 ParallelComm* parcomm, 00074 #endif 00075 const bool use_coords, 00076 int argc, 00077 char** argv 00078 #ifdef MOAB_HAVE_CGM 00079 , 00080 GeometryQueryTool* gqt 00081 #endif 00082 00083 ) 00084 : PartitionerBase< int >( impl, 00085 use_coords 00086 #ifdef MOAB_HAVE_MPI 00087 , 00088 parcomm 00089 #endif 00090 ), 00091 myZZ( NULL ), myNumPts( 0 ), argcArg( argc ), argvArg( argv ) 00092 #ifdef MOAB_HAVE_CGM 00093 , 00094 gti( gqt ) 00095 #endif 00096 { 00097 } 00098 00099 ZoltanPartitioner::~ZoltanPartitioner() 00100 { 00101 if( NULL != myZZ ) delete myZZ; 00102 } 00103 00104 ErrorCode ZoltanPartitioner::balance_mesh( const char* zmethod, 00105 const char* other_method, 00106 const bool write_as_sets, 00107 const bool write_as_tags ) 00108 { 00109 if( !strcmp( zmethod, "RR" ) && !strcmp( zmethod, "RCB" ) && !strcmp( zmethod, "RIB" ) && 00110 !strcmp( zmethod, "HSFC" ) && !strcmp( zmethod, "Hypergraph" ) && !strcmp( zmethod, "PHG" ) && 00111 !strcmp( zmethod, "PARMETIS" ) && !strcmp( zmethod, "OCTPART" ) ) 00112 { 00113 std::cout << "ERROR node " << mbpc->proc_config().proc_rank() << ": Method must be " 00114 << "RR, RCB, RIB, HSFC, Hypergraph (PHG), PARMETIS, or OCTPART" << std::endl; 00115 return MB_FAILURE; 00116 } 00117 00118 std::vector< double > pts; // x[0], y[0], z[0], ... from MOAB 00119 std::vector< int > ids; // point ids from MOAB 00120 std::vector< int > adjs, length; 00121 Range elems; 00122 00123 // Get a mesh from MOAB and divide it across processors. 00124 00125 ErrorCode result; 00126 00127 if( mbpc->proc_config().proc_rank() == 0 ) 00128 { 00129 result = assemble_graph( 3, pts, ids, adjs, length, elems );RR; 00130 } 00131 00132 myNumPts = mbInitializePoints( (int)ids.size(), &pts[0], &ids[0], &adjs[0], &length[0] ); 00133 00134 // Initialize Zoltan. This is a C call. The simple C++ code 00135 // that creates Zoltan objects does not keep track of whether 00136 // Zoltan_Initialize has been called. 00137 00138 float version; 00139 00140 Zoltan_Initialize( argcArg, argvArg, &version ); 00141 00142 // Create Zoltan object. This calls Zoltan_Create. 00143 if( NULL == myZZ ) myZZ = new Zoltan( mbpc->comm() ); 00144 00145 if( NULL == zmethod || !strcmp( zmethod, "RCB" ) ) 00146 SetRCB_Parameters(); 00147 else if( !strcmp( zmethod, "RIB" ) ) 00148 SetRIB_Parameters(); 00149 else if( !strcmp( zmethod, "HSFC" ) ) 00150 SetHSFC_Parameters(); 00151 else if( !strcmp( zmethod, "Hypergraph" ) || !strcmp( zmethod, "PHG" ) ) 00152 if( NULL == other_method ) 00153 SetHypergraph_Parameters( "auto" ); 00154 else 00155 SetHypergraph_Parameters( other_method ); 00156 else if( !strcmp( zmethod, "PARMETIS" ) ) 00157 { 00158 if( NULL == other_method ) 00159 SetPARMETIS_Parameters( "RepartGDiffusion" ); 00160 else 00161 SetPARMETIS_Parameters( other_method ); 00162 } 00163 else if( !strcmp( zmethod, "OCTPART" ) ) 00164 { 00165 if( NULL == other_method ) 00166 SetOCTPART_Parameters( "2" ); 00167 else 00168 SetOCTPART_Parameters( other_method ); 00169 } 00170 00171 // Call backs: 00172 00173 myZZ->Set_Num_Obj_Fn( mbGetNumberOfAssignedObjects, NULL ); 00174 myZZ->Set_Obj_List_Fn( mbGetObjectList, NULL ); 00175 myZZ->Set_Num_Geom_Fn( mbGetObjectSize, NULL ); 00176 myZZ->Set_Geom_Multi_Fn( mbGetObject, NULL ); 00177 myZZ->Set_Num_Edges_Multi_Fn( mbGetNumberOfEdges, NULL ); 00178 myZZ->Set_Edge_List_Multi_Fn( mbGetEdgeList, NULL ); 00179 00180 // Perform the load balancing partitioning 00181 00182 int changes; 00183 int numGidEntries; 00184 int numLidEntries; 00185 int numImport; 00186 ZOLTAN_ID_PTR importGlobalIds; 00187 ZOLTAN_ID_PTR importLocalIds; 00188 int* importProcs; 00189 int* importToPart; 00190 int numExport; 00191 ZOLTAN_ID_PTR exportGlobalIds; 00192 ZOLTAN_ID_PTR exportLocalIds; 00193 int* exportProcs; 00194 int* exportToPart; 00195 00196 int rc = myZZ->LB_Partition( changes, numGidEntries, numLidEntries, numImport, importGlobalIds, importLocalIds, 00197 importProcs, importToPart, numExport, exportGlobalIds, exportLocalIds, exportProcs, 00198 exportToPart ); 00199 00200 rc = mbGlobalSuccess( rc ); 00201 00202 if( !rc ) 00203 { 00204 mbPrintGlobalResult( "==============Result==============", myNumPts, numImport, numExport, changes ); 00205 } 00206 else 00207 { 00208 return MB_FAILURE; 00209 } 00210 00211 // take results & write onto MOAB partition sets 00212 00213 int* assignment; 00214 00215 mbFinalizePoints( (int)ids.size(), numExport, exportLocalIds, exportProcs, &assignment ); 00216 00217 if( mbpc->proc_config().proc_rank() == 0 ) 00218 { 00219 result = write_partition( mbpc->proc_config().proc_size(), elems, assignment, write_as_sets, write_as_tags ); 00220 00221 if( MB_SUCCESS != result ) return result; 00222 00223 free( (int*)assignment ); 00224 } 00225 00226 // Free the memory allocated for lists returned by LB_Parition() 00227 00228 myZZ->LB_Free_Part( &importGlobalIds, &importLocalIds, &importProcs, &importToPart ); 00229 myZZ->LB_Free_Part( &exportGlobalIds, &exportLocalIds, &exportProcs, &exportToPart ); 00230 00231 // Implementation note: A Zoltan object contains an MPI communicator. 00232 // When the Zoltan object is destroyed, it uses it's MPI communicator. 00233 // So it is important that the Zoltan object is destroyed before 00234 // the MPI communicator is destroyed. To ensure this, dynamically 00235 // allocate the Zoltan object, so you can explicitly destroy it. 00236 // If you create a Zoltan object on the stack, it's destructor will 00237 // be invoked atexit, possibly after the communicator's 00238 // destructor. 00239 00240 return MB_SUCCESS; 00241 } 00242 00243 ErrorCode ZoltanPartitioner::repartition( std::vector< double >& x, 00244 std::vector< double >& y, 00245 std::vector< double >& z, 00246 int StartID, 00247 const char* zmethod, 00248 Range& localGIDs ) 00249 { 00250 // 00251 int nprocs = mbpc->proc_config().proc_size(); 00252 int rank = mbpc->proc_config().proc_rank(); 00253 clock_t t = clock(); 00254 // form pts and ids, as in assemble_graph 00255 std::vector< double > pts; // x[0], y[0], z[0], ... from MOAB 00256 pts.resize( x.size() * 3 ); 00257 std::vector< int > ids; // point ids from MOAB 00258 ids.resize( x.size() ); 00259 for( size_t i = 0; i < x.size(); i++ ) 00260 { 00261 pts[3 * i] = x[i]; 00262 pts[3 * i + 1] = y[i]; 00263 pts[3 * i + 2] = z[i]; 00264 ids[i] = StartID + (int)i; 00265 } 00266 // do not get out until done! 00267 00268 Points = &pts[0]; 00269 GlobalIds = &ids[0]; 00270 NumPoints = (int)x.size(); 00271 NumEdges = NULL; 00272 NborGlobalId = NULL; 00273 NborProcs = NULL; 00274 ObjWeights = NULL; 00275 EdgeWeights = NULL; 00276 Parts = NULL; 00277 00278 float version; 00279 if( rank == 0 ) std::cout << "Initializing zoltan..." << std::endl; 00280 00281 Zoltan_Initialize( argcArg, argvArg, &version ); 00282 00283 // Create Zoltan object. This calls Zoltan_Create. 00284 if( NULL == myZZ ) myZZ = new Zoltan( mbpc->comm() ); 00285 00286 if( NULL == zmethod || !strcmp( zmethod, "RCB" ) ) 00287 SetRCB_Parameters(); 00288 else if( !strcmp( zmethod, "RIB" ) ) 00289 SetRIB_Parameters(); 00290 else if( !strcmp( zmethod, "HSFC" ) ) 00291 SetHSFC_Parameters(); 00292 00293 // set # requested partitions 00294 char buff[10]; 00295 sprintf( buff, "%d", nprocs ); 00296 int retval = myZZ->Set_Param( "NUM_GLOBAL_PARTITIONS", buff ); 00297 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00298 00299 // request all, import and export 00300 retval = myZZ->Set_Param( "RETURN_LISTS", "ALL" ); 00301 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00302 00303 myZZ->Set_Num_Obj_Fn( mbGetNumberOfAssignedObjects, NULL ); 00304 myZZ->Set_Obj_List_Fn( mbGetObjectList, NULL ); 00305 myZZ->Set_Num_Geom_Fn( mbGetObjectSize, NULL ); 00306 myZZ->Set_Geom_Multi_Fn( mbGetObject, NULL ); 00307 myZZ->Set_Num_Edges_Multi_Fn( mbGetNumberOfEdges, NULL ); 00308 myZZ->Set_Edge_List_Multi_Fn( mbGetEdgeList, NULL ); 00309 00310 // Perform the load balancing partitioning 00311 00312 int changes; 00313 int numGidEntries; 00314 int numLidEntries; 00315 int num_import; 00316 ZOLTAN_ID_PTR import_global_ids, import_local_ids; 00317 int* import_procs; 00318 int* import_to_part; 00319 int num_export; 00320 ZOLTAN_ID_PTR export_global_ids, export_local_ids; 00321 int *assign_procs, *assign_parts; 00322 00323 if( rank == 0 ) 00324 std::cout << "Computing partition using " << ( zmethod ? zmethod : "RCB" ) << " method for " << nprocs 00325 << " processors..." << std::endl; 00326 00327 retval = myZZ->LB_Partition( changes, numGidEntries, numLidEntries, num_import, import_global_ids, import_local_ids, 00328 import_procs, import_to_part, num_export, export_global_ids, export_local_ids, 00329 assign_procs, assign_parts ); 00330 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00331 00332 if( rank == 0 ) 00333 { 00334 std::cout << " time to LB_partition " << ( clock() - t ) / (double)CLOCKS_PER_SEC << "s. \n"; 00335 t = clock(); 00336 } 00337 00338 std::sort( import_global_ids, import_global_ids + num_import, std::greater< int >() ); 00339 std::sort( export_global_ids, export_global_ids + num_export, std::greater< int >() ); 00340 00341 Range iniGids( (EntityHandle)StartID, (EntityHandle)StartID + x.size() - 1 ); 00342 Range imported, exported; 00343 std::copy( import_global_ids, import_global_ids + num_import, range_inserter( imported ) ); 00344 std::copy( export_global_ids, export_global_ids + num_export, range_inserter( exported ) ); 00345 localGIDs = subtract( iniGids, exported ); 00346 localGIDs = unite( localGIDs, imported ); 00347 // Free data structures allocated by Zoltan::LB_Partition 00348 retval = myZZ->LB_Free_Part( &import_global_ids, &import_local_ids, &import_procs, &import_to_part ); 00349 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00350 retval = myZZ->LB_Free_Part( &export_global_ids, &export_local_ids, &assign_procs, &assign_parts ); 00351 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00352 00353 return MB_SUCCESS; 00354 } 00355 00356 ErrorCode ZoltanPartitioner::partition_inferred_mesh( EntityHandle sfileset, 00357 size_t num_parts, 00358 int part_dim, 00359 const bool write_as_sets, 00360 int projection_type ) 00361 { 00362 ErrorCode result; 00363 00364 moab::Range elverts; 00365 result = mbImpl->get_entities_by_dimension( sfileset, part_dim, elverts );RR; 00366 00367 std::vector< double > elcoords( elverts.size() * 3 ); 00368 result = mbImpl->get_coords( elverts, &elcoords[0] );RR; 00369 00370 std::vector< std::vector< EntityHandle > > part_assignments( num_parts ); 00371 int part, proc; 00372 00373 // Loop over coordinates 00374 for( size_t iel = 0; iel < elverts.size(); iel++ ) 00375 { 00376 // Gather coordinates into temporary array 00377 double* ecoords = &elcoords[iel * 3]; 00378 00379 // do a projection if needed 00380 if( projection_type > 0 ) IntxUtils::transform_coordinates( ecoords, projection_type ); 00381 00382 // Compute the coordinate's part assignment 00383 myZZ->LB_Point_PP_Assign( ecoords, proc, part ); 00384 00385 // Store the part assignment in the return array 00386 part_assignments[part].push_back( elverts[iel] ); 00387 } 00388 00389 // get the partition set tag 00390 Tag part_set_tag = mbpc->partition_tag(); 00391 00392 // If some entity sets exist, let us delete those first. 00393 if( write_as_sets ) 00394 { 00395 moab::Range oldpsets; 00396 result = mbImpl->get_entities_by_type_and_tag( sfileset, MBENTITYSET, &part_set_tag, NULL, 1, oldpsets, 00397 Interface::UNION );RR; 00398 result = mbImpl->remove_entities( sfileset, oldpsets );RR; 00399 } 00400 00401 size_t nparts_assigned = 0; 00402 for( size_t partnum = 0; partnum < num_parts; ++partnum ) 00403 { 00404 std::vector< EntityHandle >& partvec = part_assignments[partnum]; 00405 00406 nparts_assigned += ( partvec.size() ? 1 : 0 ); 00407 00408 if( write_as_sets ) 00409 { 00410 EntityHandle partNset; 00411 result = mbImpl->create_meshset( moab::MESHSET_SET, partNset );RR; 00412 if( partvec.size() ) 00413 { 00414 result = mbImpl->add_entities( partNset, &partvec[0], partvec.size() );RR; 00415 } 00416 result = mbImpl->add_parent_child( sfileset, partNset );RR; 00417 00418 // part numbering should start from 0 00419 int ipartnum = (int)partnum; 00420 00421 // assign partitions as a sparse tag by grouping elements under sets 00422 result = mbImpl->tag_set_data( part_set_tag, &partNset, 1, &ipartnum );RR; 00423 } 00424 else 00425 { 00426 /* assign as a dense vector to all elements 00427 allocate integer-size partitions */ 00428 std::vector< int > assignment( partvec.size(), 00429 partnum ); // initialize all values to partnum 00430 result = mbImpl->tag_set_data( part_set_tag, &partvec[0], partvec.size(), &assignment[0] );RR; 00431 } 00432 } 00433 00434 if( nparts_assigned != num_parts ) 00435 { 00436 std::cout << "WARNING: The inference yielded lesser number of parts (" << nparts_assigned 00437 << ") than requested by user (" << num_parts << ").\n"; 00438 } 00439 00440 return MB_SUCCESS; 00441 } 00442 00443 ErrorCode ZoltanPartitioner::partition_mesh_and_geometry( const double part_geom_mesh_size, 00444 const int nparts, 00445 const char* zmethod, 00446 const char* other_method, 00447 double imbal_tol, 00448 const int part_dim, 00449 const bool write_as_sets, 00450 const bool write_as_tags, 00451 const int obj_weight, 00452 const int edge_weight, 00453 #ifdef MOAB_HAVE_CGM 00454 const bool part_surf, 00455 const bool ghost, 00456 #else 00457 const bool, 00458 const bool, 00459 #endif 00460 const int projection_type, 00461 const bool recompute_rcb_box, 00462 const bool print_time ) 00463 { 00464 // should only be called in serial 00465 if( mbpc->proc_config().proc_size() != 1 ) 00466 { 00467 std::cout << "ZoltanPartitioner::partition_mesh_and_geometry must be called in serial." << std::endl; 00468 return MB_FAILURE; 00469 } 00470 clock_t t = clock(); 00471 if( NULL != zmethod && strcmp( zmethod, "RR" ) && strcmp( zmethod, "RCB" ) && strcmp( zmethod, "RIB" ) && 00472 strcmp( zmethod, "HSFC" ) && strcmp( zmethod, "Hypergraph" ) && strcmp( zmethod, "PHG" ) && 00473 strcmp( zmethod, "PARMETIS" ) && strcmp( zmethod, "OCTPART" ) ) 00474 { 00475 std::cout << "ERROR node " << mbpc->proc_config().proc_rank() << ": Method must be " 00476 << "RCB, RIB, HSFC, Hypergraph (PHG), PARMETIS, or OCTPART" << std::endl; 00477 return MB_FAILURE; 00478 } 00479 00480 bool part_geom = false; 00481 if( 0 == strcmp( zmethod, "RR" ) || 0 == strcmp( zmethod, "RCB" ) || 0 == strcmp( zmethod, "RIB" ) || 00482 0 == strcmp( zmethod, "HSFC" ) ) 00483 part_geom = true; // so no adjacency / edges needed 00484 std::vector< double > pts; // x[0], y[0], z[0], ... from MOAB 00485 std::vector< int > ids; // point ids from MOAB 00486 std::vector< int > adjs, length, parts; 00487 std::vector< double > obj_weights, edge_weights; 00488 Range elems; 00489 #ifdef MOAB_HAVE_CGM 00490 DLIList< RefEntity* > entities; 00491 #endif 00492 00493 // Get a mesh from MOAB and diide it across processors. 00494 00495 ErrorCode result = MB_SUCCESS; 00496 00497 // short-circuit everything if RR partition is requested 00498 if( !strcmp( zmethod, "RR" ) ) 00499 { 00500 if( part_geom_mesh_size < 0. ) 00501 { 00502 // get all elements 00503 result = mbImpl->get_entities_by_dimension( 0, part_dim, elems );RR; 00504 if( elems.empty() ) return MB_FAILURE; 00505 // make a trivial assignment vector 00506 std::vector< int > assign_vec( elems.size() ); 00507 int num_per = elems.size() / nparts; 00508 int extra = elems.size() % nparts; 00509 if( extra ) num_per++; 00510 int nstart = 0; 00511 for( int i = 0; i < nparts; i++ ) 00512 { 00513 if( i == extra ) num_per--; 00514 std::fill( &assign_vec[nstart], &assign_vec[nstart + num_per], i ); 00515 nstart += num_per; 00516 } 00517 00518 result = write_partition( nparts, elems, &assign_vec[0], write_as_sets, write_as_tags );RR; 00519 } 00520 else 00521 { 00522 #ifdef MOAB_HAVE_CGM 00523 result = partition_round_robin( nparts );RR; 00524 #endif 00525 } 00526 00527 return result; 00528 } 00529 00530 std::cout << "Assembling graph..." << std::endl; 00531 00532 if( part_geom_mesh_size < 0. ) 00533 { 00534 // if (!part_geom) { 00535 result = assemble_graph( part_dim, pts, ids, adjs, length, elems, part_geom, projection_type );RR; 00536 } 00537 else 00538 { 00539 #ifdef MOAB_HAVE_CGM 00540 result = assemble_graph( part_dim, pts, ids, adjs, length, obj_weights, edge_weights, parts, entities, 00541 part_geom_mesh_size, nparts );RR; 00542 00543 if( debug ) 00544 { 00545 int n_ids = ids.size(); 00546 entities.reset(); 00547 int i_leng = 0; 00548 for( int i = 0; i < n_ids; i++ ) 00549 { 00550 std::cout << "graph_input_ids[" << i << "]=" << ids[i] << ",obj_weights=" << obj_weights[i] 00551 << ",entity_id=" << entities.get_and_step()->id() << ",part=" << parts[i] << std::endl; 00552 for( int j = 0; j < length[i]; j++ ) 00553 { 00554 std::cout << "adjs[" << j << "]=" << adjs[i_leng] << ",edge_weights=" << edge_weights[i_leng] 00555 << std::endl; 00556 i_leng++; 00557 } 00558 } 00559 } 00560 #endif 00561 } 00562 if( print_time ) 00563 { 00564 std::cout << " time to assemble graph: " << ( clock() - t ) / (double)CLOCKS_PER_SEC << "s. \n"; 00565 t = clock(); 00566 } 00567 double* o_wgt = NULL; 00568 double* e_wgt = NULL; 00569 if( obj_weights.size() > 0 ) o_wgt = &obj_weights[0]; 00570 if( edge_weights.size() > 0 ) e_wgt = &edge_weights[0]; 00571 00572 myNumPts = mbInitializePoints( (int)ids.size(), &pts[0], &ids[0], &adjs[0], &length[0], o_wgt, e_wgt, &parts[0], 00573 part_geom ); 00574 00575 // Initialize Zoltan. This is a C call. The simple C++ code 00576 // that creates Zoltan objects does not keep track of whether 00577 // Zoltan_Initialize has been called. 00578 00579 if( print_time ) 00580 { 00581 std::cout << " time to initialize points: " << ( clock() - t ) / (double)CLOCKS_PER_SEC << "s. \n"; 00582 t = clock(); 00583 } 00584 float version; 00585 00586 std::cout << "Initializing zoltan..." << std::endl; 00587 00588 Zoltan_Initialize( argcArg, argvArg, &version ); 00589 00590 // Create Zoltan object. This calls Zoltan_Create. 00591 if( NULL == myZZ ) myZZ = new Zoltan( mbpc->comm() ); 00592 00593 if( NULL == zmethod || !strcmp( zmethod, "RCB" ) ) 00594 { 00595 if( projection_type == 2 ) 00596 SetRCB_Parameters( true ); 00597 else 00598 SetRCB_Parameters( recompute_rcb_box ); 00599 } 00600 else if( !strcmp( zmethod, "RIB" ) ) 00601 SetRIB_Parameters(); 00602 else if( !strcmp( zmethod, "HSFC" ) ) 00603 SetHSFC_Parameters(); 00604 else if( !strcmp( zmethod, "Hypergraph" ) || !strcmp( zmethod, "PHG" ) ) 00605 { 00606 if( NULL == other_method || ( other_method[0] == '\0' ) ) 00607 SetHypergraph_Parameters( "auto" ); 00608 else 00609 SetHypergraph_Parameters( other_method ); 00610 00611 if( imbal_tol ) 00612 { 00613 std::ostringstream str; 00614 str << imbal_tol; 00615 myZZ->Set_Param( "IMBALANCE_TOL", str.str().c_str() ); // no debug messages 00616 } 00617 } 00618 else if( !strcmp( zmethod, "PARMETIS" ) ) 00619 { 00620 if( NULL == other_method ) 00621 SetPARMETIS_Parameters( "RepartGDiffusion" ); 00622 else 00623 SetPARMETIS_Parameters( other_method ); 00624 } 00625 else if( !strcmp( zmethod, "OCTPART" ) ) 00626 { 00627 if( NULL == other_method ) 00628 SetOCTPART_Parameters( "2" ); 00629 else 00630 SetOCTPART_Parameters( other_method ); 00631 } 00632 00633 // set # requested partitions 00634 char buff[10]; 00635 sprintf( buff, "%d", nparts ); 00636 int retval = myZZ->Set_Param( "NUM_GLOBAL_PARTITIONS", buff ); 00637 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00638 00639 // request only partition assignments 00640 retval = myZZ->Set_Param( "RETURN_LISTS", "PARTITION ASSIGNMENTS" ); 00641 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00642 00643 if( obj_weight > 0 ) 00644 { 00645 std::ostringstream str; 00646 str << obj_weight; 00647 retval = myZZ->Set_Param( "OBJ_WEIGHT_DIM", str.str().c_str() ); 00648 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00649 } 00650 00651 if( edge_weight > 0 ) 00652 { 00653 std::ostringstream str; 00654 str << edge_weight; 00655 retval = myZZ->Set_Param( "EDGE_WEIGHT_DIM", str.str().c_str() ); 00656 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00657 } 00658 00659 // Call backs: 00660 00661 myZZ->Set_Num_Obj_Fn( mbGetNumberOfAssignedObjects, NULL ); 00662 myZZ->Set_Obj_List_Fn( mbGetObjectList, NULL ); 00663 myZZ->Set_Num_Geom_Fn( mbGetObjectSize, NULL ); 00664 myZZ->Set_Geom_Multi_Fn( mbGetObject, NULL ); 00665 myZZ->Set_Num_Edges_Multi_Fn( mbGetNumberOfEdges, NULL ); 00666 myZZ->Set_Edge_List_Multi_Fn( mbGetEdgeList, NULL ); 00667 if( part_geom_mesh_size > 0. ) 00668 { 00669 myZZ->Set_Part_Multi_Fn( mbGetPart, NULL ); 00670 } 00671 00672 // Perform the load balancing partitioning 00673 00674 int changes; 00675 int numGidEntries; 00676 int numLidEntries; 00677 int dumnum1; 00678 ZOLTAN_ID_PTR dum_local, dum_global; 00679 int *dum1, *dum2; 00680 int num_assign; 00681 ZOLTAN_ID_PTR assign_gid, assign_lid; 00682 int *assign_procs, *assign_parts; 00683 00684 std::cout << "Computing partition using " << ( zmethod ? zmethod : "RCB" ) << " method for " << nparts 00685 << " processors..." << std::endl; 00686 #ifndef NDEBUG 00687 #if 0 00688 if (NULL == zmethod || !strcmp(zmethod, "RCB")) 00689 Zoltan_Generate_Files(myZZ->Get_C_Handle(), (char*)zmethod, 1, 1, 0, 0); 00690 00691 if ( !strcmp(zmethod, "PHG")) 00692 Zoltan_Generate_Files(myZZ->Get_C_Handle(), (char*)zmethod, 1, 0, 1, 1); 00693 #endif 00694 #endif 00695 retval = myZZ->LB_Partition( changes, numGidEntries, numLidEntries, dumnum1, dum_global, dum_local, dum1, dum2, 00696 num_assign, assign_gid, assign_lid, assign_procs, assign_parts ); 00697 if( ZOLTAN_OK != retval ) return MB_FAILURE; 00698 00699 if( print_time ) 00700 { 00701 std::cout << " time to LB_partition " << ( clock() - t ) / (double)CLOCKS_PER_SEC << "s. \n"; 00702 t = clock(); 00703 } 00704 // take results & write onto MOAB partition sets 00705 std::cout << "Saving partition information to MOAB..." << std::endl; 00706 00707 if( part_geom_mesh_size < 0. ) 00708 { 00709 // if (!part_geom) { 00710 result = write_partition( nparts, elems, assign_parts, write_as_sets, write_as_tags ); 00711 } 00712 else 00713 { 00714 #ifdef MOAB_HAVE_CGM 00715 result = write_partition( nparts, entities, assign_parts, obj_weights, part_surf, ghost ); 00716 #endif 00717 } 00718 00719 if( print_time ) 00720 { 00721 std::cout << " time to write partition in memory " << ( clock() - t ) / (double)CLOCKS_PER_SEC << "s. \n"; 00722 t = clock(); 00723 } 00724 00725 if( MB_SUCCESS != result ) return result; 00726 00727 // Free the memory allocated for lists returned by LB_Parition() 00728 myZZ->LB_Free_Part( &assign_gid, &assign_lid, &assign_procs, &assign_parts ); 00729 00730 return MB_SUCCESS; 00731 } 00732 00733 ErrorCode ZoltanPartitioner::include_closure() 00734 { 00735 ErrorCode result; 00736 Range ents; 00737 Range adjs; 00738 std::cout << "Adding closure..." << std::endl; 00739 00740 for( Range::iterator rit = partSets.begin(); rit != partSets.end(); ++rit ) 00741 { 00742 00743 // get the top-dimensional entities in the part 00744 result = mbImpl->get_entities_by_handle( *rit, ents, true );RR; 00745 00746 if( ents.empty() ) continue; 00747 00748 // get intermediate-dimensional adjs and add to set 00749 for( int d = mbImpl->dimension_from_handle( *ents.begin() ) - 1; d >= 1; d-- ) 00750 { 00751 adjs.clear(); 00752 result = mbImpl->get_adjacencies( ents, d, false, adjs, Interface::UNION );RR; 00753 result = mbImpl->add_entities( *rit, adjs );RR; 00754 } 00755 00756 // now get vertices and add to set; only need to do for ents, not for adjs 00757 adjs.clear(); 00758 result = mbImpl->get_adjacencies( ents, 0, false, adjs, Interface::UNION );RR; 00759 result = mbImpl->add_entities( *rit, adjs );RR; 00760 00761 ents.clear(); 00762 } 00763 00764 // now go over non-part entity sets, looking for contained entities 00765 Range sets, part_ents; 00766 result = mbImpl->get_entities_by_type( 0, MBENTITYSET, sets );RR; 00767 for( Range::iterator rit = sets.begin(); rit != sets.end(); ++rit ) 00768 { 00769 // skip parts 00770 if( partSets.find( *rit ) != partSets.end() ) continue; 00771 00772 // get entities in this set, recursively 00773 ents.clear(); 00774 result = mbImpl->get_entities_by_handle( *rit, ents, true );RR; 00775 00776 // now check over all parts 00777 for( Range::iterator rit2 = partSets.begin(); rit2 != partSets.end(); ++rit2 ) 00778 { 00779 part_ents.clear(); 00780 result = mbImpl->get_entities_by_handle( *rit2, part_ents, false );RR; 00781 Range int_range = intersect( ents, part_ents ); 00782 if( !int_range.empty() ) 00783 { 00784 // non-empty intersection, add to part set 00785 result = mbImpl->add_entities( *rit2, &( *rit ), 1 );RR; 00786 } 00787 } 00788 } 00789 00790 // finally, mark all the part sets as having the closure 00791 Tag closure_tag; 00792 result = 00793 mbImpl->tag_get_handle( "INCLUDES_CLOSURE", 1, MB_TYPE_INTEGER, closure_tag, MB_TAG_SPARSE | MB_TAG_CREAT );RR; 00794 00795 std::vector< int > closure_vals( partSets.size(), 1 ); 00796 result = mbImpl->tag_set_data( closure_tag, partSets, &closure_vals[0] );RR; 00797 00798 return MB_SUCCESS; 00799 } 00800 00801 ErrorCode ZoltanPartitioner::assemble_graph( const int dimension, 00802 std::vector< double >& coords, 00803 std::vector< int >& moab_ids, 00804 std::vector< int >& adjacencies, 00805 std::vector< int >& length, 00806 Range& elems, 00807 bool part_geom, 00808 int projection_type ) 00809 { 00810 // assemble a graph with vertices equal to elements of specified dimension, edges 00811 // signified by list of other elements to which an element is connected 00812 00813 // get the elements of that dimension 00814 ErrorCode result = mbImpl->get_entities_by_dimension( 0, dimension, elems ); 00815 if( MB_SUCCESS != result || elems.empty() ) return result; 00816 00817 // assign global ids 00818 if( assign_global_ids ) 00819 { 00820 EntityHandle rootset = 0; 00821 result = mbpc->assign_global_ids( rootset, dimension, 1, true, true );RR; 00822 } 00823 00824 // now assemble the graph, calling MeshTopoUtil to get bridge adjacencies through d-1 00825 // dimensional neighbors 00826 MeshTopoUtil mtu( mbImpl ); 00827 Range adjs; 00828 // can use a fixed-size array 'cuz the number of lower-dimensional neighbors is limited 00829 // by MBCN 00830 int neighbors[5 * MAX_SUB_ENTITIES]; 00831 double avg_position[3]; 00832 int moab_id; 00833 00834 // get the global id tag handle 00835 Tag gid = mbImpl->globalId_tag(); 00836 00837 for( Range::iterator rit = elems.begin(); rit != elems.end(); ++rit ) 00838 { 00839 00840 if( !part_geom ) 00841 { 00842 // get bridge adjacencies 00843 adjs.clear(); 00844 result = mtu.get_bridge_adjacencies( *rit, ( dimension > 0 ? dimension - 1 : 3 ), dimension, adjs );RR; 00845 00846 // get the graph vertex ids of those 00847 if( !adjs.empty() ) 00848 { 00849 assert( adjs.size() < 5 * MAX_SUB_ENTITIES ); 00850 result = mbImpl->tag_get_data( gid, adjs, neighbors );RR; 00851 } 00852 00853 // copy those into adjacencies vector 00854 length.push_back( (int)adjs.size() ); 00855 std::copy( neighbors, neighbors + adjs.size(), std::back_inserter( adjacencies ) ); 00856 } 00857 00858 // get average position of vertices 00859 result = mtu.get_average_position( *rit, avg_position );RR; 00860 00861 // get the graph vertex id for this element 00862 result = mbImpl->tag_get_data( gid, &( *rit ), 1, &moab_id );RR; 00863 00864 // copy those into coords vector 00865 moab_ids.push_back( moab_id ); 00866 // transform coordinates to spherical coordinates, if requested 00867 if( projection_type > 0 ) IntxUtils::transform_coordinates( avg_position, projection_type ); 00868 00869 std::copy( avg_position, avg_position + 3, std::back_inserter( coords ) ); 00870 } 00871 00872 if( debug ) 00873 { 00874 std::cout << "Length vector: " << std::endl; 00875 std::copy( length.begin(), length.end(), std::ostream_iterator< int >( std::cout, ", " ) ); 00876 std::cout << std::endl; 00877 std::cout << "Adjacencies vector: " << std::endl; 00878 std::copy( adjacencies.begin(), adjacencies.end(), std::ostream_iterator< int >( std::cout, ", " ) ); 00879 std::cout << std::endl; 00880 std::cout << "Moab_ids vector: " << std::endl; 00881 std::copy( moab_ids.begin(), moab_ids.end(), std::ostream_iterator< int >( std::cout, ", " ) ); 00882 std::cout << std::endl; 00883 std::cout << "Coords vector: " << std::endl; 00884 std::copy( coords.begin(), coords.end(), std::ostream_iterator< double >( std::cout, ", " ) ); 00885 std::cout << std::endl; 00886 } 00887 00888 return MB_SUCCESS; 00889 } 00890 00891 #ifdef MOAB_HAVE_CGM 00892 ErrorCode ZoltanPartitioner::assemble_graph( const int /* dimension */, 00893 std::vector< double >& /* coords */, 00894 std::vector< int >& moab_ids, 00895 std::vector< int >& adjacencies, 00896 std::vector< int >& length, 00897 std::vector< double >& obj_weights, 00898 std::vector< double >& edge_weights, 00899 std::vector< int >& parts, 00900 DLIList< RefEntity* >& entities, 00901 const double part_geom_mesh_size, 00902 const int n_part ) 00903 { 00904 // get body vertex weights 00905 DLIList< RefEntity* > body_list; 00906 gti->ref_entity_list( "body", body_list, CUBIT_FALSE ); 00907 DLIList< RefFace* > all_shared_surfs; 00908 int n_bodies = body_list.size(); 00909 int body_map_index = 1; 00910 int surf_map_index = n_bodies + 1; 00911 int n_bodies_proc = n_bodies / n_part; // # of entities per processor 00912 int i_bodies_proc = n_bodies_proc; // entity index limit for each processor 00913 int proc = 0; 00914 00915 body_list.reset(); 00916 for( int i = 0; i < n_bodies; i++ ) 00917 { 00918 // assign part in round-robin 00919 if( i == i_bodies_proc ) 00920 { 00921 proc++; 00922 if( proc < n_part ) 00923 i_bodies_proc += n_bodies_proc; 00924 else 00925 { 00926 proc %= n_part; 00927 i_bodies_proc++; 00928 } 00929 } 00930 parts.push_back( proc ); 00931 00932 // volume mesh generation load estimation 00933 RefEntity* body = body_list.get_and_step(); 00934 double vertex_w = body->measure(); 00935 vertex_w /= part_geom_mesh_size * part_geom_mesh_size * part_geom_mesh_size; 00936 vertex_w = 3.8559e-5 * vertex_w * log( vertex_w ); 00937 00938 // add child non-interface face weights 00939 DLIList< RefFace* > shared_surfs; 00940 DLIList< RefFace* > faces; 00941 ( dynamic_cast< TopologyEntity* >( body ) )->ref_faces( faces ); 00942 int n_face = faces.size(); 00943 faces.reset(); 00944 for( int j = 0; j < n_face; j++ ) 00945 { 00946 RefFace* face = faces.get_and_step(); 00947 TopologyEntity* te = CAST_TO( face, TopologyEntity ); 00948 if( te->bridge_manager()->number_of_bridges() > 1 ) 00949 { // shared 00950 shared_surfs.append( face ); 00951 } 00952 else 00953 { // non-shared 00954 vertex_w += estimate_face_mesh_load( face, face->measure() ); 00955 } 00956 } 00957 00958 int temp_index = body_map_index++; 00959 body_vertex_map[body->id()] = temp_index; 00960 moab_ids.push_back( temp_index ); // add body as graph vertex 00961 obj_weights.push_back( vertex_w ); // add weight for body graph vertex 00962 00963 if( debug ) 00964 { 00965 std::cout << "body=" << body->id() << ",graph_id=" << temp_index << ",weight=" << vertex_w 00966 << ",part=" << proc << std::endl; 00967 } 00968 00969 // treat shared surfaces connected to this body 00970 int n_shared = shared_surfs.size(); 00971 shared_surfs.reset(); 00972 for( int k = 0; k < n_shared; k++ ) 00973 { // add adjacencies 00974 RefFace* face = shared_surfs.get_and_step(); 00975 std::map< int, int >::iterator iter = surf_vertex_map.find( face->id() ); 00976 if( iter != surf_vertex_map.end() ) 00977 { 00978 temp_index = ( *iter ).second; 00979 } 00980 else 00981 { 00982 temp_index = surf_map_index++; 00983 surf_vertex_map[face->id()] = temp_index; 00984 all_shared_surfs.append( face ); 00985 } 00986 adjacencies.push_back( temp_index ); 00987 double tmp_sw = estimate_face_comm_load( face, part_geom_mesh_size ); 00988 edge_weights.push_back( tmp_sw ); 00989 00990 if( debug ) 00991 { 00992 std::cout << "adjac=" << temp_index << ",weight=" << tmp_sw << std::endl; 00993 } 00994 } 00995 length.push_back( n_shared ); 00996 } 00997 entities += body_list; 00998 00999 // add shared surface as graph vertex 01000 int n_all_shared_surf = all_shared_surfs.size(); 01001 all_shared_surfs.reset(); 01002 for( int i = 0; i < n_all_shared_surf; i++ ) 01003 { 01004 RefEntity* face = all_shared_surfs.get_and_step(); 01005 moab_ids.push_back( surf_vertex_map[face->id()] ); 01006 double face_mesh_load = estimate_face_mesh_load( face, part_geom_mesh_size ); 01007 obj_weights.push_back( face_mesh_load ); 01008 01009 // set surface object graph edges 01010 int min_ind = -1; 01011 double min_load = std::numeric_limits< double >::max(); 01012 ; 01013 DLIList< Body* > parents; 01014 dynamic_cast< TopologyEntity* >( face )->bodies( parents ); 01015 int n_parents = parents.size(); 01016 parents.reset(); 01017 for( int j = 0; j < n_parents; j++ ) 01018 { 01019 int body_gid = body_vertex_map[parents.get_and_step()->id()]; 01020 adjacencies.push_back( body_gid ); // add adjacency 01021 edge_weights.push_back( estimate_face_comm_load( face, part_geom_mesh_size ) ); 01022 01023 if( obj_weights[body_gid - 1] < min_load ) 01024 { 01025 min_ind = body_gid - 1; 01026 min_load = obj_weights[min_ind]; 01027 } 01028 } 01029 length.push_back( n_parents ); 01030 entities.append( dynamic_cast< RefEntity* >( face ) ); 01031 obj_weights[min_ind] += face_mesh_load; 01032 parts.push_back( parts[min_ind] ); 01033 01034 if( debug ) 01035 { 01036 std::cout << "shared_surf=" << face->id() << ",graph_id=" << surf_vertex_map[face->id()] 01037 << ",weight=" << face_mesh_load << ",part=" << parts[min_ind] << std::endl; 01038 } 01039 } 01040 01041 for( size_t i = 0; i < obj_weights.size(); i++ ) 01042 if( obj_weights[i] < 1. ) obj_weights[i] = 1.; 01043 for( size_t i = 0; i < edge_weights.size(); i++ ) 01044 if( edge_weights[i] < 1. ) edge_weights[i] = 1.; 01045 01046 return MB_SUCCESS; 01047 } 01048 01049 double ZoltanPartitioner::estimate_face_mesh_load( RefEntity* face, const double h ) 01050 { 01051 GeometryType type = CAST_TO( face, RefFace )->geometry_type(); 01052 double n = face->measure() / h / h; 01053 double n_logn = n * log( n ); 01054 01055 if( type == PLANE_SURFACE_TYPE ) 01056 { 01057 return 1.536168737505151e-4 * n_logn; 01058 } 01059 else if( type == SPLINE_SURFACE_TYPE ) 01060 { 01061 return 5.910511018383144e-4 * n_logn; 01062 } 01063 else if( type == CONE_SURFACE_TYPE || type == SPHERE_SURFACE_TYPE || type == TORUS_SURFACE_TYPE ) 01064 { 01065 return 2.352511671418708e-4 * n_logn; 01066 } 01067 01068 return 0.0; 01069 } 01070 01071 double ZoltanPartitioner::estimate_face_comm_load( RefEntity* face, const double h ) 01072 { 01073 double peri = 0.; 01074 #if( ( CGM_MAJOR_VERSION == 14 && CGM_MINOR_VERSION > 2 ) || CGM_MAJOR_VERSION >= 15 ) 01075 DLIList< DLIList< RefEdge* > > ref_edge_loops; 01076 #else 01077 DLIList< DLIList< RefEdge* >* > ref_edge_loops; 01078 #endif 01079 CAST_TO( face, RefFace )->ref_edge_loops( ref_edge_loops ); 01080 ref_edge_loops.reset(); 01081 01082 #if( ( CGM_MAJOR_VERSION == 14 && CGM_MINOR_VERSION > 2 ) || CGM_MAJOR_VERSION >= 15 ) 01083 for( int i = 0; i < ref_edge_loops.size(); i++ ) 01084 { 01085 DLIList< RefEdge* > eloop = ref_edge_loops.get_and_step(); 01086 eloop.reset(); 01087 for( int j = 0; j < eloop.size(); j++ ) 01088 { 01089 peri += eloop.get_and_step()->measure(); 01090 } 01091 } 01092 #else 01093 for( int i = 0; i < ref_edge_loops.size(); i++ ) 01094 { 01095 DLIList< RefEdge* >* eloop = ref_edge_loops.get_and_step(); 01096 eloop->reset(); 01097 for( int j = 0; j < eloop->size(); j++ ) 01098 { 01099 peri += eloop->get_and_step()->measure(); 01100 } 01101 } 01102 #endif 01103 01104 // return 104*face->measure()/sqrt(3)/h/h + 56/3*peri/h; 01105 return ( 104 * face->measure() / sqrt( 3 ) / h / h + 56 / 3 * peri / h ) / 700000.; 01106 } 01107 01108 ErrorCode ZoltanPartitioner::write_partition( const int nparts, 01109 DLIList< RefEntity* > entities, 01110 const int* assignment, 01111 std::vector< double >& obj_weights, 01112 const bool part_surf, 01113 const bool ghost ) 01114 { 01115 ErrorCode result; 01116 01117 // actuate CA_BODIES and turn on auto flag for other attributes 01118 CGMApp::instance()->attrib_manager()->register_attrib_type( CA_BODIES, "bodies", "BODIES", CABodies_creator, 01119 CUBIT_TRUE, CUBIT_TRUE, CUBIT_TRUE, CUBIT_TRUE, 01120 CUBIT_TRUE, CUBIT_FALSE ); 01121 CGMApp::instance()->attrib_manager()->auto_flag( CUBIT_TRUE ); 01122 01123 // set partition info to Body at first 01124 int n_entities = entities.size(); 01125 entities.reset(); 01126 for( int i = 0; i < n_entities; i++ ) 01127 { 01128 int proc = assignment[i]; 01129 DLIList< int > shared_procs; 01130 RefEntity* entity = entities.get_and_step(); 01131 01132 if( entity->entity_type_info() == typeid( Body ) ) 01133 { 01134 shared_procs.append( proc ); 01135 TDParallel* td_par = (TDParallel*)entity->get_TD( &TDParallel::is_parallel ); 01136 if( td_par == NULL ) td_par = new TDParallel( entity, NULL, &shared_procs ); 01137 01138 if( debug ) 01139 { 01140 std::cout << "body" << entity->id() << "_is_partitioned_to_p" << proc << std::endl; 01141 } 01142 01143 // assign to volumes, it should be removed in future 01144 DLIList< RefVolume* > volumes; 01145 ( dynamic_cast< TopologyEntity* >( entity ) )->ref_volumes( volumes ); 01146 int n_vol = volumes.size(); 01147 volumes.reset(); 01148 for( int j = 0; j < n_vol; j++ ) 01149 { 01150 RefEntity* vol = volumes.get_and_step(); 01151 td_par = (TDParallel*)vol->get_TD( &TDParallel::is_parallel ); 01152 if( td_par == NULL ) td_par = new TDParallel( vol, NULL, &shared_procs ); 01153 } 01154 } 01155 } 01156 01157 // set partition info to shared surfaces 01158 entities.reset(); 01159 for( int i = 0; i < n_entities; i++ ) 01160 { 01161 int proc = assignment[i]; 01162 DLIList< int > shared_procs; 01163 RefEntity* entity = entities.get_and_step(); 01164 01165 if( entity->entity_type_info() == typeid( RefFace ) ) 01166 { // surface 01167 DLIList< Body* > parents; 01168 ( dynamic_cast< TopologyEntity* >( entity ) )->bodies( parents ); 01169 int n_parents = parents.size(); 01170 if( n_parents != 2 ) 01171 { // check # of parents 01172 std::cerr << "# of parent bodies of interface surface should be 2." << std::endl; 01173 return MB_FAILURE; 01174 } 01175 shared_procs.append( proc ); // local proc 01176 parents.reset(); 01177 for( int j = 0; j < 2; j++ ) 01178 { 01179 RefEntity* parent = parents.get_and_step(); 01180 TDParallel* parent_td = (TDParallel*)parent->get_TD( &TDParallel::is_parallel ); 01181 01182 if( parent_td == NULL ) 01183 { 01184 std::cerr << "parent body should have balanced process." << std::endl; 01185 return MB_FAILURE; 01186 } 01187 int temp_proc = parent_td->get_charge_proc(); 01188 if( temp_proc != proc ) shared_procs.append( temp_proc ); // remote proc 01189 } 01190 01191 if( shared_procs.size() > 1 ) 01192 { // if it is shared by 2 processors 01193 int merge_id = TDUniqueId::get_unique_id( entity ); 01194 TDParallel* td_par = (TDParallel*)entity->get_TD( &TDParallel::is_parallel ); 01195 if( td_par == NULL ) td_par = new TDParallel( entity, NULL, &shared_procs, NULL, merge_id, 1 ); 01196 01197 if( debug ) 01198 { 01199 std::cout << "surf" << entity->id() << "_is_partitioned_to_p"; 01200 for( int j = 0; j < shared_procs.size(); j++ ) 01201 { 01202 std::cout << "," << shared_procs[j]; 01203 } 01204 std::cout << std::endl; 01205 } 01206 } 01207 } 01208 } 01209 01210 // do non-shared surface partition too 01211 if( part_surf ) 01212 { 01213 result = partition_surface( nparts, entities, assignment, obj_weights );RR; 01214 } 01215 01216 // partition shared edges and vertex 01217 result = partition_child_entities( 1, nparts, part_surf, ghost );RR; 01218 result = partition_child_entities( 0, nparts, part_surf );RR; 01219 01220 if( debug ) 01221 { 01222 entities.reset(); 01223 for( int i = 0; i < n_entities; i++ ) 01224 { 01225 RefEntity* entity = entities.get_and_step(); 01226 if( entity->entity_type_info() == typeid( Body ) ) 01227 { 01228 TDParallel* td_par = (TDParallel*)entity->get_TD( &TDParallel::is_parallel ); 01229 CubitString st = entity->entity_name(); 01230 DLIList< int >* sp = td_par->get_shared_proc_list(); 01231 int n_sp = sp->size(); 01232 sp->reset(); 01233 for( int j = 0; j < n_sp; j++ ) 01234 { 01235 std::cout << "partitioned_" << st.c_str() << ",proc=" << sp->get_and_step() << std::endl; 01236 } 01237 DLIList< int >* gp = td_par->get_ghost_proc_list(); 01238 int n_gp = gp->size(); 01239 sp->reset(); 01240 for( int j = 0; j < n_gp; j++ ) 01241 { 01242 std::cout << "partitioned_" << st.c_str() << ",ghost=" << gp->get_and_step() << std::endl; 01243 } 01244 } 01245 } 01246 } 01247 01248 return MB_SUCCESS; 01249 } 01250 01251 ErrorCode ZoltanPartitioner::partition_surface( const int nparts, 01252 DLIList< RefEntity* > entities, 01253 const int* assignment, 01254 std::vector< double >& obj_weights ) 01255 { 01256 int i; 01257 double ave_load = 0.; 01258 double* loads = new double[nparts]; 01259 for( i = 0; i < nparts; i++ ) 01260 loads[i] = 0.; 01261 01262 int n_entities = entities.size(); 01263 entities.reset(); 01264 for( i = 0; i < n_entities; i++ ) 01265 { 01266 loads[assignment[i]] += obj_weights[i]; 01267 ave_load += obj_weights[i]; 01268 } 01269 ave_load /= nparts; 01270 01271 if( debug ) 01272 { 01273 for( i = 0; i < nparts; i++ ) 01274 { 01275 std::cout << "loads_before_surface_partition[" << i << "]=" << loads[i] << std::endl; 01276 } 01277 } 01278 01279 int n_iter = 0; 01280 bool b_stop = false; 01281 do 01282 { 01283 // get max and min load processors 01284 int max_proc = nparts, min_proc = 0; 01285 double min_load = std::numeric_limits< double >::max(); 01286 double max_load = std::numeric_limits< double >::min(); 01287 for( i = 0; i < nparts; i++ ) 01288 { 01289 if( loads[i] < min_load ) 01290 { 01291 min_load = loads[i]; 01292 min_proc = i; 01293 } 01294 if( loads[i] > max_load ) 01295 { 01296 max_load = loads[i]; 01297 max_proc = i; 01298 } 01299 } 01300 01301 double load_diff = max_load - ave_load; 01302 if( load_diff > ave_load / 10. ) 01303 { 01304 bool b_moved = false; 01305 entities.reset(); 01306 for( i = 0; i < n_entities; i++ ) 01307 { 01308 if( b_moved ) break; 01309 if( assignment[i] == max_proc && // find maximum load processor bodies 01310 entities[i]->entity_type_info() == typeid( Body ) ) 01311 { 01312 DLIList< RefFace* > faces; 01313 ( dynamic_cast< TopologyEntity* >( entities[i] ) )->ref_faces( faces ); 01314 int n_face = faces.size(); 01315 faces.reset(); 01316 for( int j = 0; j < n_face; j++ ) 01317 { 01318 RefFace* face = faces.get_and_step(); 01319 TopologyEntity* te = CAST_TO( face, TopologyEntity ); 01320 if( te->bridge_manager()->number_of_bridges() < 2 ) 01321 { // non-shared 01322 TDParallel* td_par = (TDParallel*)face->get_TD( &TDParallel::is_parallel ); 01323 if( td_par == NULL ) 01324 { // only consider unpartitioned surfaces 01325 double face_load = face->measure(); 01326 if( load_diff > min_load + face_load - ave_load ) 01327 { 01328 loads[max_proc] -= face_load; 01329 loads[min_proc] += face_load; 01330 int merge_id = TDUniqueId::get_unique_id( face ); 01331 DLIList< int > shared_procs; 01332 shared_procs.append( min_proc ); 01333 shared_procs.append( max_proc ); 01334 td_par = new TDParallel( face, NULL, &shared_procs, NULL, merge_id, 1 ); 01335 01336 if( debug ) 01337 { 01338 std::cout << "non-shared surface " << face->id() << " is moved from p" 01339 << max_proc << " to p" << min_proc << std::endl; 01340 } 01341 b_moved = true; 01342 break; 01343 } 01344 } 01345 } 01346 } 01347 } 01348 } 01349 } 01350 else 01351 b_stop = true; 01352 01353 n_iter++; 01354 } while( !b_stop && n_iter < 50 ); 01355 01356 if( debug ) 01357 { 01358 for( i = 0; i < nparts; i++ ) 01359 { 01360 std::cout << "loads_after_surface_partition[" << i << "]=" << loads[i] << std::endl; 01361 } 01362 } 01363 01364 delete loads; 01365 return MB_SUCCESS; 01366 } 01367 01368 ErrorCode ZoltanPartitioner::partition_round_robin( const int n_part ) 01369 { 01370 int i, j, k; 01371 double* loads = new double[n_part]; // estimated loads for each processor 01372 double* ve_loads = new double[n_part]; // estimated loads for each processor 01373 for( i = 0; i < n_part; i++ ) 01374 { 01375 loads[i] = 0.0; 01376 ve_loads[i] = 0.0; 01377 } 01378 DLIList< RefEntity* > body_entity_list; 01379 gti->ref_entity_list( "body", body_entity_list, CUBIT_FALSE ); 01380 int n_entity = body_entity_list.size(); 01381 int n_entity_proc = n_entity / n_part; // # of entities per processor 01382 int i_entity_proc = n_entity_proc; // entity index limit for each processor 01383 int proc = 0; 01384 RefEntity* entity; 01385 01386 // assign processors to bodies 01387 body_entity_list.reset(); 01388 for( i = 0; i < n_entity; i++ ) 01389 { 01390 if( i == i_entity_proc ) 01391 { 01392 proc++; 01393 if( proc < n_part ) 01394 i_entity_proc += n_entity_proc; 01395 else 01396 { 01397 proc %= n_part; 01398 i_entity_proc++; 01399 } 01400 } 01401 01402 // assign to bodies 01403 entity = body_entity_list.get_and_step(); 01404 DLIList< int > shared_procs; 01405 shared_procs.append( proc ); 01406 TDParallel* td_par = (TDParallel*)entity->get_TD( &TDParallel::is_parallel ); 01407 if( td_par == NULL ) td_par = new TDParallel( entity, NULL, &shared_procs ); 01408 loads[proc] += entity->measure(); 01409 01410 // assign to volumes, it should be removed in future 01411 DLIList< RefVolume* > volumes; 01412 ( dynamic_cast< TopologyEntity* >( entity ) )->ref_volumes( volumes ); 01413 int n_vol = volumes.size(); 01414 volumes.reset(); 01415 for( j = 0; j < n_vol; j++ ) 01416 { 01417 RefEntity* vol = volumes.get_and_step(); 01418 td_par = (TDParallel*)vol->get_TD( &TDParallel::is_parallel ); 01419 if( td_par == NULL ) td_par = new TDParallel( vol, NULL, &shared_procs ); 01420 } 01421 01422 // add local surface load 01423 DLIList< RefFace* > faces; 01424 ( dynamic_cast< TopologyEntity* >( entity ) )->ref_faces( faces ); 01425 int n_face = faces.size(); 01426 faces.reset(); 01427 for( j = 0; j < n_face; j++ ) 01428 { 01429 RefFace* face = faces.get_and_step(); 01430 TopologyEntity* te = CAST_TO( face, TopologyEntity ); 01431 if( te->bridge_manager()->number_of_bridges() < 2 ) loads[proc] = loads[proc] + face->measure(); 01432 } 01433 01434 // Get all child entities 01435 DLIList< RefEntity* > child_list; 01436 RefEntity::get_all_child_ref_entities( body_entity_list, child_list ); 01437 int n_child = child_list.size(); 01438 01439 // assign processors to interface entities 01440 child_list.reset(); 01441 for( j = 0; j < n_child; j++ ) 01442 { 01443 entity = child_list.get_and_step(); 01444 TopologyEntity* te = CAST_TO( entity, TopologyEntity ); 01445 01446 if( te->bridge_manager()->number_of_bridges() > 1 ) 01447 { 01448 DLIList< Body* > parent_bodies; 01449 DLIList< int > child_shared_procs; // Shared processors of each child entity 01450 ( dynamic_cast< TopologyEntity* >( entity ) )->bodies( parent_bodies ); 01451 int n_parent = parent_bodies.size(); 01452 01453 for( k = 0; k < n_parent; k++ ) 01454 { 01455 RefEntity* parent_vol = CAST_TO( parent_bodies.get_and_step(), RefEntity ); 01456 TDParallel* parent_td = (TDParallel*)parent_vol->get_TD( &TDParallel::is_parallel ); 01457 01458 if( parent_td == NULL ) 01459 { 01460 PRINT_ERROR( "parent Volume has to be partitioned." ); 01461 delete[] loads; 01462 delete[] ve_loads; 01463 return MB_FAILURE; 01464 } 01465 child_shared_procs.append_unique( parent_td->get_charge_proc() ); 01466 } 01467 01468 if( child_shared_procs.size() > 1 ) 01469 { // if it is interface 01470 td_par = (TDParallel*)entity->get_TD( &TDParallel::is_parallel ); 01471 if( td_par == NULL ) 01472 { 01473 int merge_id = TDUniqueId::get_unique_id( entity ); 01474 if( entity->entity_type_info() == typeid( RefFace ) ) 01475 { // face 01476 if( child_shared_procs.size() != 2 ) 01477 { 01478 PRINT_ERROR( "Error: # of shared processors of interface surface " 01479 "should be 2." ); 01480 delete[] loads; 01481 delete[] ve_loads; 01482 return MB_FAILURE; 01483 } 01484 01485 // balance interface surface loads 01486 if( loads[child_shared_procs[0]] > loads[child_shared_procs[1]] ) 01487 child_shared_procs.reverse(); 01488 01489 loads[child_shared_procs[0]] = loads[child_shared_procs[0]] + entity->measure(); 01490 td_par = new TDParallel( entity, NULL, &child_shared_procs, NULL, merge_id, 1 ); 01491 } // face 01492 else if( entity->entity_type_info() == typeid( RefEdge ) || 01493 entity->entity_type_info() == typeid( RefVertex ) ) 01494 { // edge or vertex 01495 // balance interface surface loads 01496 int min_p = child_shared_procs[0]; 01497 int n_shared_proc = child_shared_procs.size(); 01498 for( k = 1; k < n_shared_proc; k++ ) 01499 { 01500 if( ve_loads[child_shared_procs[k]] < ve_loads[min_p] ) min_p = child_shared_procs[k]; 01501 } 01502 ve_loads[min_p] = ve_loads[min_p] + entity->measure(); 01503 child_shared_procs.remove( min_p ); 01504 child_shared_procs.insert_first( min_p ); 01505 td_par = new TDParallel( entity, NULL, &child_shared_procs, NULL, merge_id, 1 ); 01506 } // edge or vertex 01507 } // if (td_par == NULL) 01508 } // if it is interface 01509 } // if (te->bridge_manager()->number_of_bridges() > 1) 01510 } // for (j = 0; j < n_child; j++) 01511 } // for (i = 0; i < n_entity; i++) 01512 01513 delete[] loads; 01514 delete[] ve_loads; 01515 01516 return MB_SUCCESS; 01517 } 01518 01519 // partition child entities to one of parent entity shared processors 01520 ErrorCode ZoltanPartitioner::partition_child_entities( const int dim, 01521 const int n_part, 01522 const bool part_surf, 01523 const bool ghost ) 01524 { 01525 DLIList< RefEntity* > entity_list; 01526 if( dim == 0 ) 01527 gti->ref_entity_list( "vertex", entity_list, CUBIT_FALSE ); 01528 else if( dim == 1 ) 01529 gti->ref_entity_list( "curve", entity_list, CUBIT_FALSE ); 01530 else 01531 { 01532 std::cerr << "Dimention should be from 0 to 1." << std::endl; 01533 return MB_FAILURE; 01534 } 01535 01536 int i, j, k; 01537 int n_entity = entity_list.size(); 01538 double* loads = new double[n_part]; 01539 for( i = 0; i < n_part; i++ ) 01540 loads[i] = 0.; 01541 entity_list.reset(); 01542 01543 for( i = 0; i < n_entity; i++ ) 01544 { // for all entities 01545 RefEntity* entity = entity_list.get_and_step(); 01546 TopologyEntity* te = CAST_TO( entity, TopologyEntity ); 01547 01548 if( !part_surf && te->bridge_manager()->number_of_bridges() < 2 ) continue; 01549 01550 DLIList< int > shared_procs; 01551 DLIList< Body* > parents; 01552 ( dynamic_cast< TopologyEntity* >( entity ) )->bodies( parents ); 01553 int n_parents = parents.size(); 01554 std::set< int > s_proc; 01555 parents.reset(); 01556 01557 // get shared processors from parent bodies 01558 for( j = 0; j < n_parents; j++ ) 01559 { 01560 RefEntity* parent = parents.get_and_step(); 01561 TDParallel* parent_td = (TDParallel*)parent->get_TD( &TDParallel::is_parallel ); 01562 01563 if( parent_td != NULL ) 01564 { 01565 DLIList< int >* parent_procs = parent_td->get_shared_proc_list(); 01566 int n_shared = parent_procs->size(); 01567 parent_procs->reset(); 01568 for( k = 0; k < n_shared; k++ ) 01569 { 01570 int p = parent_procs->get_and_step(); 01571 s_proc.insert( p ); 01572 } 01573 } 01574 } 01575 01576 if( part_surf ) 01577 { // also get shared procs from parent surfaces 01578 DLIList< RefFace* > parent_faces; 01579 ( dynamic_cast< TopologyEntity* >( entity ) )->ref_faces( parent_faces ); 01580 int n_pface = parent_faces.size(); 01581 parent_faces.reset(); 01582 01583 // get shared processors from parent faces 01584 for( j = 0; j < n_pface; j++ ) 01585 { 01586 RefEntity* parent = parent_faces.get_and_step(); 01587 TDParallel* parent_td = (TDParallel*)parent->get_TD( &TDParallel::is_parallel ); 01588 01589 if( parent_td != NULL ) 01590 { 01591 DLIList< int >* parent_procs = parent_td->get_shared_proc_list(); 01592 int n_shared = parent_procs->size(); 01593 parent_procs->reset(); 01594 01595 for( k = 0; k < n_shared; k++ ) 01596 { 01597 int p = parent_procs->get_and_step(); 01598 s_proc.insert( p ); 01599 } 01600 } 01601 } 01602 } 01603 01604 // find the minimum load processor and put it 01605 // at the front of the shared_procs list 01606 if( s_proc.size() > 1 ) 01607 { 01608 int min_proc = 0; 01609 double min_load = std::numeric_limits< double >::max(); 01610 std::set< int >::iterator iter = s_proc.begin(); 01611 std::set< int >::iterator end_iter = s_proc.end(); 01612 for( ; iter != end_iter; ++iter ) 01613 { 01614 if( loads[*iter] < min_load ) 01615 { 01616 min_load = loads[*iter]; 01617 min_proc = *iter; 01618 } 01619 } 01620 01621 if( dim == 1 ) 01622 loads[min_proc] += entity->measure(); 01623 else if( dim == 0 ) 01624 loads[min_proc] += 1.; 01625 shared_procs.append( min_proc ); 01626 iter = s_proc.begin(); 01627 end_iter = s_proc.end(); 01628 for( ; iter != end_iter; ++iter ) 01629 { 01630 if( *iter != min_proc ) 01631 { 01632 shared_procs.append( *iter ); 01633 } 01634 } 01635 01636 // add ghost geometries to shared processors for edge 01637 if( ghost ) 01638 { 01639 parents.reset(); 01640 for( j = 0; j < n_parents; j++ ) 01641 { // for all parent bodies 01642 RefEntity* parent_vol = CAST_TO( parents.get_and_step(), RefEntity ); 01643 TDParallel* parent_td = (TDParallel*)parent_vol->get_TD( &TDParallel::is_parallel ); 01644 int n_shared_proc = shared_procs.size(); 01645 01646 for( k = 0; k < n_shared_proc; k++ ) 01647 { 01648 parent_td->add_ghost_proc( shared_procs[k] ); 01649 } 01650 } 01651 } 01652 01653 // initialize tool data 01654 int merge_id = TDUniqueId::get_unique_id( entity ); 01655 TDParallel* td_par = (TDParallel*)entity->get_TD( &TDParallel::is_parallel ); 01656 if( td_par == NULL ) td_par = new TDParallel( entity, NULL, &shared_procs, NULL, merge_id, 1 ); 01657 } 01658 } 01659 01660 delete loads; 01661 return MB_SUCCESS; 01662 } 01663 #endif 01664 01665 ErrorCode ZoltanPartitioner::write_partition( const int nparts, 01666 Range& elems, 01667 const int* assignment, 01668 const bool write_as_sets, 01669 const bool write_as_tags ) 01670 { 01671 ErrorCode result; 01672 01673 // get the partition set tag 01674 Tag part_set_tag; 01675 int dum_id = -1, i; 01676 result = mbImpl->tag_get_handle( "PARALLEL_PARTITION", 1, MB_TYPE_INTEGER, part_set_tag, 01677 MB_TAG_SPARSE | MB_TAG_CREAT, &dum_id );RR; 01678 01679 // get any sets already with this tag, and clear them 01680 Range tagged_sets; 01681 result = 01682 mbImpl->get_entities_by_type_and_tag( 0, MBENTITYSET, &part_set_tag, NULL, 1, tagged_sets, Interface::UNION );RR; 01683 if( !tagged_sets.empty() ) 01684 { 01685 result = mbImpl->clear_meshset( tagged_sets );RR; 01686 if( !write_as_sets ) 01687 { 01688 result = mbImpl->tag_delete_data( part_set_tag, tagged_sets );RR; 01689 } 01690 } 01691 01692 if( write_as_sets ) 01693 { 01694 // first, create partition sets and store in vector 01695 partSets.clear(); 01696 01697 if( nparts > (int)tagged_sets.size() ) 01698 { 01699 // too few partition sets - create missing ones 01700 int num_new = nparts - tagged_sets.size(); 01701 for( i = 0; i < num_new; i++ ) 01702 { 01703 EntityHandle new_set; 01704 result = mbImpl->create_meshset( MESHSET_SET, new_set );RR; 01705 tagged_sets.insert( new_set ); 01706 } 01707 } 01708 else if( nparts < (int)tagged_sets.size() ) 01709 { 01710 // too many partition sets - delete extras 01711 int num_del = tagged_sets.size() - nparts; 01712 for( i = 0; i < num_del; i++ ) 01713 { 01714 EntityHandle old_set = tagged_sets.pop_back(); 01715 result = mbImpl->delete_entities( &old_set, 1 );RR; 01716 } 01717 } 01718 // assign partition sets to vector 01719 partSets.swap( tagged_sets ); 01720 01721 // write a tag to those sets denoting they're partition sets, with a value of the 01722 // proc number 01723 int* dum_ids = new int[nparts]; 01724 for( i = 0; i < nparts; i++ ) 01725 dum_ids[i] = i; 01726 01727 result = mbImpl->tag_set_data( part_set_tag, partSets, dum_ids );RR; 01728 // found out by valgrind when we run mbpart 01729 delete[] dum_ids; 01730 dum_ids = NULL; 01731 01732 // assign entities to the relevant sets 01733 std::vector< EntityHandle > tmp_part_sets; 01734 // int N = (int)elems.size(); 01735 std::copy( partSets.begin(), partSets.end(), std::back_inserter( tmp_part_sets ) ); 01736 /*Range::reverse_iterator riter; 01737 for (i = N-1, riter = elems.rbegin(); riter != elems.rend(); ++riter, i--) { 01738 int assigned_part = assignment[i]; 01739 part_ranges[assigned_part].insert(*riter); 01740 //result = mbImpl->add_entities(tmp_part_sets[assignment[i]], &(*rit), 1); RR; 01741 }*/ 01742 01743 Range::iterator rit; 01744 for( i = 0, rit = elems.begin(); rit != elems.end(); ++rit, i++ ) 01745 { 01746 result = mbImpl->add_entities( tmp_part_sets[assignment[i]], &( *rit ), 1 );RR; 01747 } 01748 /*for (i=0; i<nparts; i++) 01749 { 01750 result = mbImpl->add_entities(tmp_part_sets[i], part_ranges[i]); RR; 01751 } 01752 delete [] part_ranges;*/ 01753 // check for empty sets, warn if there are any 01754 Range empty_sets; 01755 for( rit = partSets.begin(); rit != partSets.end(); ++rit ) 01756 { 01757 int num_ents = 0; 01758 result = mbImpl->get_number_entities_by_handle( *rit, num_ents ); 01759 if( MB_SUCCESS != result || !num_ents ) empty_sets.insert( *rit ); 01760 } 01761 if( !empty_sets.empty() ) 01762 { 01763 std::cout << "WARNING: " << empty_sets.size() << " empty sets in partition: "; 01764 for( rit = empty_sets.begin(); rit != empty_sets.end(); ++rit ) 01765 std::cout << *rit << " "; 01766 std::cout << std::endl; 01767 } 01768 } 01769 01770 if( write_as_tags ) 01771 { 01772 // allocate integer-size partitions 01773 result = mbImpl->tag_set_data( part_set_tag, elems, assignment );RR; 01774 } 01775 01776 return MB_SUCCESS; 01777 } 01778 01779 void ZoltanPartitioner::SetRCB_Parameters( const bool recompute_rcb_box ) 01780 { 01781 if( mbpc->proc_config().proc_rank() == 0 ) std::cout << "\nRecursive Coordinate Bisection" << std::endl; 01782 // General parameters: 01783 01784 myZZ->Set_Param( "DEBUG_LEVEL", "0" ); // no debug messages 01785 myZZ->Set_Param( "LB_METHOD", "RCB" ); // recursive coordinate bisection 01786 // myZZ->Set_Param( "RCB_RECOMPUTE_BOX", "1" ); // recompute RCB box if needed ? 01787 01788 // RCB parameters: 01789 01790 myZZ->Set_Param( "RCB_OUTPUT_LEVEL", "1" ); 01791 myZZ->Set_Param( "KEEP_CUTS", "1" ); // save decomposition so that we can infer partitions 01792 // myZZ->Set_Param("RCB_RECTILINEAR_BLOCKS", "1"); // don't split point on boundary 01793 if( recompute_rcb_box ) myZZ->Set_Param( "RCB_RECOMPUTE_BOX", "1" ); 01794 } 01795 01796 void ZoltanPartitioner::SetRIB_Parameters() 01797 { 01798 if( mbpc->proc_config().proc_rank() == 0 ) std::cout << "\nRecursive Inertial Bisection" << std::endl; 01799 // General parameters: 01800 01801 myZZ->Set_Param( "DEBUG_LEVEL", "0" ); // no debug messages 01802 myZZ->Set_Param( "LB_METHOD", "RIB" ); // Recursive Inertial Bisection 01803 01804 // RIB parameters: 01805 01806 myZZ->Set_Param( "KEEP_CUTS", "1" ); // save decomposition 01807 myZZ->Set_Param( "AVERAGE_CUTS", "1" ); 01808 } 01809 01810 void ZoltanPartitioner::SetHSFC_Parameters() 01811 { 01812 if( mbpc->proc_config().proc_rank() == 0 ) std::cout << "\nHilbert Space Filling Curve" << std::endl; 01813 // General parameters: 01814 01815 myZZ->Set_Param( "DEBUG_LEVEL", "0" ); // no debug messages 01816 myZZ->Set_Param( "LB_METHOD", "HSFC" ); // perform Hilbert space filling curve 01817 01818 // HSFC parameters: 01819 01820 myZZ->Set_Param( "KEEP_CUTS", "1" ); // save decomposition 01821 } 01822 01823 void ZoltanPartitioner::SetHypergraph_Parameters( const char* phg_method ) 01824 { 01825 if( mbpc->proc_config().proc_rank() == 0 ) std::cout << "\nHypergraph (or PHG): " << std::endl; 01826 // General parameters: 01827 01828 myZZ->Set_Param( "DEBUG_LEVEL", "0" ); // no debug messages 01829 myZZ->Set_Param( "LB_METHOD", "Hypergraph" ); // Hypergraph (or PHG) 01830 01831 // Hypergraph (or PHG) parameters: 01832 myZZ->Set_Param( "PHG_COARSEPARTITION_METHOD", phg_method ); // CoarsePartitionMethod 01833 } 01834 01835 void ZoltanPartitioner::SetPARMETIS_Parameters( const char* parmetis_method ) 01836 { 01837 if( mbpc->proc_config().proc_rank() == 0 ) std::cout << "\nPARMETIS: " << parmetis_method << std::endl; 01838 // General parameters: 01839 01840 myZZ->Set_Param( "DEBUG_LEVEL", "0" ); // no debug messages 01841 myZZ->Set_Param( "LB_METHOD", "PARMETIS" ); // the ParMETIS library 01842 01843 // PARMETIS parameters: 01844 01845 myZZ->Set_Param( "PARMETIS_METHOD", parmetis_method ); // method in the library 01846 } 01847 01848 void ZoltanPartitioner::SetOCTPART_Parameters( const char* oct_method ) 01849 { 01850 if( mbpc->proc_config().proc_rank() == 0 ) std::cout << "\nOctree Partitioning: " << oct_method << std::endl; 01851 // General parameters: 01852 01853 myZZ->Set_Param( "DEBUG_LEVEL", "0" ); // no debug messages 01854 myZZ->Set_Param( "LB_METHOD", "OCTPART" ); // octree partitioning 01855 01856 // OCTPART parameters: 01857 01858 myZZ->Set_Param( "OCT_METHOD", oct_method ); // the SFC to be used 01859 myZZ->Set_Param( "OCT_OUTPUT_LEVEL", "3" ); 01860 } 01861 01862 int ZoltanPartitioner::mbInitializePoints( int npts, 01863 double* pts, 01864 int* ids, 01865 int* adjs, 01866 int* length, 01867 double* obj_weights, 01868 double* edge_weights, 01869 int* parts, 01870 bool part_geom ) 01871 { 01872 unsigned int i; 01873 int j; 01874 int *numPts, *nborProcs = NULL; 01875 int sum, ptsPerProc, ptsAssigned, mySize; 01876 MPI_Status stat; 01877 double* sendPts; 01878 int* sendIds; 01879 int* sendEdges = NULL; 01880 int* sendNborId = NULL; 01881 int* sendProcs; 01882 01883 if( mbpc->proc_config().proc_rank() == 0 ) 01884 { 01885 /* divide pts to start */ 01886 01887 numPts = (int*)malloc( sizeof( int ) * mbpc->proc_config().proc_size() ); 01888 ptsPerProc = npts / mbpc->proc_config().proc_size(); 01889 ptsAssigned = 0; 01890 01891 for( i = 0; i < mbpc->proc_config().proc_size() - 1; i++ ) 01892 { 01893 numPts[i] = ptsPerProc; 01894 ptsAssigned += ptsPerProc; 01895 } 01896 01897 numPts[mbpc->proc_config().proc_size() - 1] = npts - ptsAssigned; 01898 01899 mySize = numPts[mbpc->proc_config().proc_rank()]; 01900 sendPts = pts + ( 3 * numPts[0] ); 01901 sendIds = ids + numPts[0]; 01902 sum = 0; // possible no adjacency sent 01903 if( !part_geom ) 01904 { 01905 sendEdges = length + numPts[0]; 01906 01907 for( j = 0; j < numPts[0]; j++ ) 01908 sum += length[j]; 01909 01910 sendNborId = adjs + sum; 01911 01912 for( j = numPts[0]; j < npts; j++ ) 01913 sum += length[j]; 01914 01915 nborProcs = (int*)malloc( sizeof( int ) * sum ); 01916 } 01917 for( j = 0; j < sum; j++ ) 01918 if( ( i = adjs[j] / ptsPerProc ) < mbpc->proc_config().proc_size() ) 01919 nborProcs[j] = i; 01920 else 01921 nborProcs[j] = mbpc->proc_config().proc_size() - 1; 01922 01923 sendProcs = nborProcs + ( sendNborId - adjs ); 01924 01925 for( i = 1; i < mbpc->proc_config().proc_size(); i++ ) 01926 { 01927 MPI_Send( &numPts[i], 1, MPI_INT, i, 0x00, mbpc->comm() ); 01928 MPI_Send( sendPts, 3 * numPts[i], MPI_DOUBLE, i, 0x01, mbpc->comm() ); 01929 MPI_Send( sendIds, numPts[i], MPI_INT, i, 0x03, mbpc->comm() ); 01930 MPI_Send( sendEdges, numPts[i], MPI_INT, i, 0x06, mbpc->comm() ); 01931 sum = 0; 01932 01933 for( j = 0; j < numPts[i]; j++ ) 01934 sum += sendEdges[j]; 01935 01936 MPI_Send( sendNborId, sum, MPI_INT, i, 0x07, mbpc->comm() ); 01937 MPI_Send( sendProcs, sum, MPI_INT, i, 0x08, mbpc->comm() ); 01938 sendPts += ( 3 * numPts[i] ); 01939 sendIds += numPts[i]; 01940 sendEdges += numPts[i]; 01941 sendNborId += sum; 01942 sendProcs += sum; 01943 } 01944 01945 free( numPts ); 01946 } 01947 else 01948 { 01949 MPI_Recv( &mySize, 1, MPI_INT, 0, 0x00, mbpc->comm(), &stat ); 01950 pts = (double*)malloc( sizeof( double ) * 3 * mySize ); 01951 ids = (int*)malloc( sizeof( int ) * mySize ); 01952 length = (int*)malloc( sizeof( int ) * mySize ); 01953 if( obj_weights != NULL ) obj_weights = (double*)malloc( sizeof( double ) * mySize ); 01954 MPI_Recv( pts, 3 * mySize, MPI_DOUBLE, 0, 0x01, mbpc->comm(), &stat ); 01955 MPI_Recv( ids, mySize, MPI_INT, 0, 0x03, mbpc->comm(), &stat ); 01956 MPI_Recv( length, mySize, MPI_INT, 0, 0x06, mbpc->comm(), &stat ); 01957 sum = 0; 01958 01959 for( j = 0; j < mySize; j++ ) 01960 sum += length[j]; 01961 01962 adjs = (int*)malloc( sizeof( int ) * sum ); 01963 if( edge_weights != NULL ) edge_weights = (double*)malloc( sizeof( double ) * sum ); 01964 nborProcs = (int*)malloc( sizeof( int ) * sum ); 01965 MPI_Recv( adjs, sum, MPI_INT, 0, 0x07, mbpc->comm(), &stat ); 01966 MPI_Recv( nborProcs, sum, MPI_INT, 0, 0x08, mbpc->comm(), &stat ); 01967 } 01968 01969 Points = pts; 01970 GlobalIds = ids; 01971 NumPoints = mySize; 01972 NumEdges = length; 01973 NborGlobalId = adjs; 01974 NborProcs = nborProcs; 01975 ObjWeights = obj_weights; 01976 EdgeWeights = edge_weights; 01977 Parts = parts; 01978 01979 return mySize; 01980 } 01981 01982 void ZoltanPartitioner::mbFinalizePoints( int npts, 01983 int numExport, 01984 ZOLTAN_ID_PTR exportLocalIDs, 01985 int* exportProcs, 01986 int** assignment ) 01987 { 01988 int* MyAssignment; 01989 int i; 01990 int numPts; 01991 MPI_Status stat; 01992 int* recvA; 01993 01994 /* assign pts to start */ 01995 01996 if( mbpc->proc_config().proc_rank() == 0 ) 01997 MyAssignment = (int*)malloc( sizeof( int ) * npts ); 01998 else 01999 MyAssignment = (int*)malloc( sizeof( int ) * NumPoints ); 02000 02001 for( i = 0; i < NumPoints; i++ ) 02002 MyAssignment[i] = mbpc->proc_config().proc_rank(); 02003 02004 for( i = 0; i < numExport; i++ ) 02005 MyAssignment[exportLocalIDs[i]] = exportProcs[i]; 02006 02007 if( mbpc->proc_config().proc_rank() == 0 ) 02008 { 02009 /* collect pts */ 02010 recvA = MyAssignment + NumPoints; 02011 02012 for( i = 1; i < (int)mbpc->proc_config().proc_size(); i++ ) 02013 { 02014 MPI_Recv( &numPts, 1, MPI_INT, i, 0x04, mbpc->comm(), &stat ); 02015 MPI_Recv( recvA, numPts, MPI_INT, i, 0x05, mbpc->comm(), &stat ); 02016 recvA += numPts; 02017 } 02018 02019 *assignment = MyAssignment; 02020 } 02021 else 02022 { 02023 MPI_Send( &NumPoints, 1, MPI_INT, 0, 0x04, mbpc->comm() ); 02024 MPI_Send( MyAssignment, NumPoints, MPI_INT, 0, 0x05, mbpc->comm() ); 02025 free( MyAssignment ); 02026 } 02027 } 02028 02029 int ZoltanPartitioner::mbGlobalSuccess( int rc ) 02030 { 02031 int fail = 0; 02032 unsigned int i; 02033 int* vals = (int*)malloc( mbpc->proc_config().proc_size() * sizeof( int ) ); 02034 02035 MPI_Allgather( &rc, 1, MPI_INT, vals, 1, MPI_INT, mbpc->comm() ); 02036 02037 for( i = 0; i < mbpc->proc_config().proc_size(); i++ ) 02038 { 02039 if( vals[i] != ZOLTAN_OK ) 02040 { 02041 if( 0 == mbpc->proc_config().proc_rank() ) 02042 { 02043 mbShowError( vals[i], "Result on process " ); 02044 } 02045 fail = 1; 02046 } 02047 } 02048 02049 free( vals ); 02050 return fail; 02051 } 02052 02053 void ZoltanPartitioner::mbPrintGlobalResult( const char* s, int begin, int import, int exp, int change ) 02054 { 02055 unsigned int i; 02056 int* v1 = (int*)malloc( 4 * sizeof( int ) ); 02057 int* v2 = NULL; 02058 int* v; 02059 02060 v1[0] = begin; 02061 v1[1] = import; 02062 v1[2] = exp; 02063 v1[3] = change; 02064 02065 if( mbpc->proc_config().proc_rank() == 0 ) 02066 { 02067 v2 = (int*)malloc( 4 * mbpc->proc_config().proc_size() * sizeof( int ) ); 02068 } 02069 02070 MPI_Gather( v1, 4, MPI_INT, v2, 4, MPI_INT, 0, mbpc->comm() ); 02071 02072 if( mbpc->proc_config().proc_rank() == 0 ) 02073 { 02074 fprintf( stdout, "======%s======\n", s ); 02075 for( i = 0, v = v2; i < mbpc->proc_config().proc_size(); i++, v += 4 ) 02076 { 02077 fprintf( stdout, "%u: originally had %d, import %d, exp %d, %s\n", i, v[0], v[1], v[2], 02078 v[3] ? "a change of partitioning" : "no change" ); 02079 } 02080 fprintf( stdout, "==========================================\n" ); 02081 fflush( stdout ); 02082 02083 free( v2 ); 02084 } 02085 02086 free( v1 ); 02087 } 02088 02089 void ZoltanPartitioner::mbShowError( int val, const char* s ) 02090 { 02091 if( s ) printf( "%s ", s ); 02092 02093 switch( val ) 02094 { 02095 case ZOLTAN_OK: 02096 printf( "%d: SUCCESSFUL\n", mbpc->proc_config().proc_rank() ); 02097 break; 02098 case ZOLTAN_WARN: 02099 printf( "%d: WARNING\n", mbpc->proc_config().proc_rank() ); 02100 break; 02101 case ZOLTAN_FATAL: 02102 printf( "%d: FATAL ERROR\n", mbpc->proc_config().proc_rank() ); 02103 break; 02104 case ZOLTAN_MEMERR: 02105 printf( "%d: MEMORY ALLOCATION ERROR\n", mbpc->proc_config().proc_rank() ); 02106 break; 02107 default: 02108 printf( "%d: INVALID RETURN CODE\n", mbpc->proc_config().proc_rank() ); 02109 break; 02110 } 02111 } 02112 02113 /********************** 02114 ** call backs 02115 **********************/ 02116 02117 int mbGetNumberOfAssignedObjects( void* /* userDefinedData */, int* err ) 02118 { 02119 *err = 0; 02120 return NumPoints; 02121 } 02122 02123 void mbGetObjectList( void* /* userDefinedData */, 02124 int /* numGlobalIds */, 02125 int /* numLids */, 02126 ZOLTAN_ID_PTR gids, 02127 ZOLTAN_ID_PTR lids, 02128 int wgt_dim, 02129 float* obj_wgts, 02130 int* err ) 02131 { 02132 for( int i = 0; i < NumPoints; i++ ) 02133 { 02134 gids[i] = GlobalIds[i]; 02135 lids[i] = i; 02136 if( wgt_dim > 0 ) obj_wgts[i] = ObjWeights[i]; 02137 } 02138 02139 *err = 0; 02140 } 02141 02142 int mbGetObjectSize( void* /* userDefinedData */, int* err ) 02143 { 02144 *err = 0; 02145 return 3; 02146 } 02147 02148 void mbGetObject( void* /* userDefinedData */, 02149 int /* numGlobalIds */, 02150 int /* numLids */, 02151 int numObjs, 02152 ZOLTAN_ID_PTR /* gids */, 02153 ZOLTAN_ID_PTR lids, 02154 int numDim, 02155 double* pts, 02156 int* err ) 02157 { 02158 int i, id, id3; 02159 int next = 0; 02160 02161 if( numDim != 3 ) 02162 { 02163 *err = 1; 02164 return; 02165 } 02166 02167 for( i = 0; i < numObjs; i++ ) 02168 { 02169 id = lids[i]; 02170 02171 if( ( id < 0 ) || ( id >= NumPoints ) ) 02172 { 02173 *err = 1; 02174 return; 02175 } 02176 02177 id3 = lids[i] * 3; 02178 02179 pts[next++] = Points[id3]; 02180 pts[next++] = Points[id3 + 1]; 02181 pts[next++] = Points[id3 + 2]; 02182 } 02183 } 02184 02185 void mbGetNumberOfEdges( void* /* userDefinedData */, 02186 int /* numGlobalIds */, 02187 int /* numLids */, 02188 int numObjs, 02189 ZOLTAN_ID_PTR /* gids */, 02190 ZOLTAN_ID_PTR lids, 02191 int* numEdges, 02192 int* err ) 02193 { 02194 int i, id; 02195 int next = 0; 02196 02197 for( i = 0; i < numObjs; i++ ) 02198 { 02199 id = lids[i]; 02200 02201 if( ( id < 0 ) || ( id >= NumPoints ) ) 02202 { 02203 *err = 1; 02204 return; 02205 } 02206 02207 numEdges[next++] = NumEdges[id]; 02208 } 02209 } 02210 02211 void mbGetEdgeList( void* /* userDefinedData */, 02212 int /* numGlobalIds */, 02213 int /* numLids */, 02214 int numObjs, 02215 ZOLTAN_ID_PTR /* gids */, 02216 ZOLTAN_ID_PTR lids, 02217 int* /* numEdges */, 02218 ZOLTAN_ID_PTR nborGlobalIds, 02219 int* nborProcs, 02220 int wgt_dim, 02221 float* edge_wgts, 02222 int* err ) 02223 { 02224 int i, id, idSum, j; 02225 int next = 0; 02226 02227 for( i = 0; i < numObjs; i++ ) 02228 { 02229 id = lids[i]; 02230 02231 if( ( id < 0 ) || ( id >= NumPoints ) ) 02232 { 02233 *err = 1; 02234 return; 02235 } 02236 02237 idSum = 0; 02238 02239 for( j = 0; j < id; j++ ) 02240 idSum += NumEdges[j]; 02241 02242 for( j = 0; j < NumEdges[id]; j++ ) 02243 { 02244 nborGlobalIds[next] = NborGlobalId[idSum]; 02245 nborProcs[next] = NborProcs[idSum]; 02246 if( wgt_dim > 0 ) edge_wgts[next] = EdgeWeights[idSum]; 02247 next++; 02248 idSum++; 02249 } 02250 } 02251 } 02252 02253 void mbGetPart( void* /* userDefinedData */, 02254 int /* numGlobalIds */, 02255 int /* numLids */, 02256 int numObjs, 02257 ZOLTAN_ID_PTR /* gids */, 02258 ZOLTAN_ID_PTR lids, 02259 int* part, 02260 int* err ) 02261 { 02262 int i, id; 02263 int next = 0; 02264 02265 for( i = 0; i < numObjs; i++ ) 02266 { 02267 id = lids[i]; 02268 02269 if( ( id < 0 ) || ( id >= NumPoints ) ) 02270 { 02271 *err = 1; 02272 return; 02273 } 02274 02275 part[next++] = Parts[id]; 02276 } 02277 } 02278 02279 // new methods for partition in parallel, used by migrate in iMOAB 02280 ErrorCode ZoltanPartitioner::partition_owned_cells( Range& primary, 02281 std::multimap< int, int >& extraGraphEdges, 02282 std::map< int, int > procs, 02283 int& numNewPartitions, 02284 std::map< int, Range >& distribution, 02285 int met, 02286 std::vector< char >& ZoltanBuffer ) 02287 { 02288 // start copy 02289 MeshTopoUtil mtu( mbImpl ); 02290 Range adjs; 02291 02292 std::vector< int > adjacencies; 02293 std::vector< int > ids; 02294 ids.resize( primary.size() ); 02295 std::vector< int > length; 02296 std::vector< double > coords; 02297 std::vector< int > nbor_proc; 02298 // can use a fixed-size array 'cuz the number of lower-dimensional neighbors is limited 02299 // by MBCN 02300 int neighbors[5 * MAX_SUB_ENTITIES]; 02301 int neib_proc[5 * MAX_SUB_ENTITIES]; 02302 double avg_position[3]; 02303 int moab_id; 02304 int primaryDim = mbImpl->dimension_from_handle( *primary.rbegin() ); 02305 // get the global id tag handle 02306 Tag gid = mbImpl->globalId_tag(); 02307 02308 ErrorCode rval = mbImpl->tag_get_data( gid, primary, &ids[0] );MB_CHK_ERR( rval ); 02309 02310 // mbpc is member in base class, PartitionerBase 02311 int rank = mbpc->rank(); // current rank , will be put on regular neighbors 02312 int i = 0; 02313 for( Range::iterator rit = primary.begin(); rit != primary.end(); ++rit, i++ ) 02314 { 02315 EntityHandle cell = *rit; 02316 // get bridge adjacencies for each cell 02317 if( 1 == met ) 02318 { 02319 adjs.clear(); 02320 rval = mtu.get_bridge_adjacencies( cell, ( primaryDim > 0 ? primaryDim - 1 : 3 ), primaryDim, adjs );MB_CHK_ERR( rval ); 02321 02322 // get the graph vertex ids of those 02323 if( !adjs.empty() ) 02324 { 02325 assert( adjs.size() < 5 * MAX_SUB_ENTITIES ); 02326 rval = mbImpl->tag_get_data( gid, adjs, neighbors );MB_CHK_ERR( rval ); 02327 } 02328 // if adjacent to neighbor partitions, add to the list 02329 int size_adjs = (int)adjs.size(); 02330 moab_id = ids[i]; 02331 for( int k = 0; k < size_adjs; k++ ) 02332 neib_proc[k] = rank; // current rank 02333 if( extraGraphEdges.find( moab_id ) != extraGraphEdges.end() ) 02334 { 02335 // it means that the current cell is adjacent to a cell in another partition ; maybe 02336 // a few 02337 std::pair< std::multimap< int, int >::iterator, std::multimap< int, int >::iterator > ret; 02338 ret = extraGraphEdges.equal_range( moab_id ); 02339 for( std::multimap< int, int >::iterator it = ret.first; it != ret.second; ++it ) 02340 { 02341 int otherID = it->second; 02342 neighbors[size_adjs] = otherID; // the id of the other cell, across partition 02343 neib_proc[size_adjs] = procs[otherID]; // this is how we built this map, the cell id maps to 02344 // what proc it came from 02345 size_adjs++; 02346 } 02347 } 02348 // copy those into adjacencies vector 02349 length.push_back( size_adjs ); 02350 std::copy( neighbors, neighbors + size_adjs, std::back_inserter( adjacencies ) ); 02351 std::copy( neib_proc, neib_proc + size_adjs, std::back_inserter( nbor_proc ) ); 02352 } 02353 else if( 2 <= met ) // include 2 RCB or 3, RCB + gnomonic projection 02354 { 02355 if( TYPE_FROM_HANDLE( cell ) == MBVERTEX ) 02356 { 02357 rval = mbImpl->get_coords( &cell, 1, avg_position );MB_CHK_ERR( rval ); 02358 } 02359 else 02360 { 02361 rval = mtu.get_average_position( cell, avg_position );MB_CHK_ERR( rval ); 02362 } 02363 if( 3 <= met ) 02364 { 02365 IntxUtils::transform_coordinates( avg_position, 2 ); // 2 means gnomonic projection 02366 } 02367 std::copy( avg_position, avg_position + 3, std::back_inserter( coords ) ); 02368 } 02369 } 02370 02371 #ifdef VERBOSE 02372 std::stringstream ff2; 02373 ff2 << "zoltanInput_" << mbpc->rank() << ".txt"; 02374 std::ofstream ofs; 02375 ofs.open( ff2.str().c_str(), std::ofstream::out ); 02376 ofs << "Length vector: " << std::endl; 02377 std::copy( length.begin(), length.end(), std::ostream_iterator< int >( ofs, ", " ) ); 02378 ofs << std::endl; 02379 ofs << "Adjacencies vector: " << std::endl; 02380 std::copy( adjacencies.begin(), adjacencies.end(), std::ostream_iterator< int >( ofs, ", " ) ); 02381 ofs << std::endl; 02382 ofs << "Moab_ids vector: " << std::endl; 02383 std::copy( ids.begin(), ids.end(), std::ostream_iterator< int >( ofs, ", " ) ); 02384 ofs << std::endl; 02385 ofs << "Coords vector: " << std::endl; 02386 std::copy( coords.begin(), coords.end(), std::ostream_iterator< double >( ofs, ", " ) ); 02387 ofs << std::endl; 02388 ofs.close(); 02389 #endif 02390 02391 // these are static var in this file, and used in the callbacks 02392 Points = NULL; 02393 if( 1 != met ) Points = &coords[0]; 02394 GlobalIds = &ids[0]; 02395 NumPoints = (int)ids.size(); 02396 NumEdges = &length[0]; 02397 NborGlobalId = &adjacencies[0]; 02398 NborProcs = &nbor_proc[0]; 02399 ObjWeights = NULL; 02400 EdgeWeights = NULL; 02401 Parts = NULL; 02402 02403 float version; 02404 if( mbpc->rank() == 0 ) std::cout << "Initializing zoltan..." << std::endl; 02405 02406 Zoltan_Initialize( argcArg, argvArg, &version ); 02407 02408 // Create Zoltan object. This calls Zoltan_Create. 02409 // old code 02410 if( met <= 4 ) 02411 { 02412 02413 if( NULL == myZZ ) myZZ = new Zoltan( mbpc->comm() ); 02414 02415 // set # requested partitions 02416 char buff[10]; 02417 sprintf( buff, "%d", numNewPartitions ); 02418 int retval = myZZ->Set_Param( "NUM_GLOBAL_PARTITIONS", buff ); 02419 if( ZOLTAN_OK != retval ) return MB_FAILURE; 02420 02421 // request parts assignment 02422 retval = myZZ->Set_Param( "RETURN_LISTS", "PARTS" ); 02423 if( ZOLTAN_OK != retval ) return MB_FAILURE; 02424 02425 myZZ->Set_Num_Obj_Fn( mbGetNumberOfAssignedObjects, NULL ); 02426 myZZ->Set_Obj_List_Fn( mbGetObjectList, NULL ); 02427 // due to a bug in zoltan, if method is graph partitioning, do not pass coordinates!! 02428 if( 2 <= met ) 02429 { 02430 myZZ->Set_Num_Geom_Fn( mbGetObjectSize, NULL ); 02431 myZZ->Set_Geom_Multi_Fn( mbGetObject, NULL ); 02432 if( 3 <= met ) 02433 SetRCB_Parameters( /*const bool recompute_rcb_box*/ true ); // recompute rcb box 02434 else 02435 SetRCB_Parameters( /*const bool recompute_rcb_box*/ false ); // recompute rcb box // is it faster ? 02436 } 02437 else if( 1 == met ) 02438 { 02439 myZZ->Set_Num_Edges_Multi_Fn( mbGetNumberOfEdges, NULL ); 02440 myZZ->Set_Edge_List_Multi_Fn( mbGetEdgeList, NULL ); 02441 SetHypergraph_Parameters( "auto" ); 02442 } 02443 02444 // Perform the load balancing partitioning 02445 02446 int changes; 02447 int numGidEntries; 02448 int numLidEntries; 02449 int num_import; 02450 ZOLTAN_ID_PTR import_global_ids, import_local_ids; 02451 int* import_procs; 02452 int* import_to_part; 02453 int num_export; 02454 ZOLTAN_ID_PTR export_global_ids, export_local_ids; 02455 int *assign_procs, *assign_parts; 02456 02457 if( mbpc->rank() == 0 ) 02458 std::cout << "Computing partition using method (1-graph, 2-geom):" << met << " for " << numNewPartitions 02459 << " parts..." << std::endl; 02460 02461 #ifndef NDEBUG 02462 #if 0 02463 static int counter=0; // it may be possible to call function multiple times in a simulation 02464 // give a way to not overwrite the files 02465 // it should work only with a modified version of Zoltan 02466 std::stringstream basename; 02467 if (1==met) 02468 { 02469 basename << "phg_" << counter++; 02470 Zoltan_Generate_Files(myZZ->Get_C_Handle(), (char*)(basename.str().c_str()), 1, 0, 1, 0); 02471 } 02472 else if (2==met) 02473 { 02474 basename << "rcb_" << counter++; 02475 Zoltan_Generate_Files(myZZ->Get_C_Handle(), (char*)(basename.str().c_str()), 1, 1, 0, 0); 02476 } 02477 #endif 02478 #endif 02479 retval = myZZ->LB_Partition( changes, numGidEntries, numLidEntries, num_import, import_global_ids, 02480 import_local_ids, import_procs, import_to_part, num_export, export_global_ids, 02481 export_local_ids, assign_procs, assign_parts ); 02482 if( ZOLTAN_OK != retval ) return MB_FAILURE; 02483 02484 #ifdef VERBOSE 02485 std::stringstream ff3; 02486 ff3 << "zoltanOutput_" << mbpc->rank() << ".txt"; 02487 std::ofstream ofs3; 02488 ofs3.open( ff3.str().c_str(), std::ofstream::out ); 02489 ofs3 << " export elements on rank " << rank << " \n"; 02490 ofs3 << "\t index \t gb_id \t local \t proc \t part \n"; 02491 for( int k = 0; k < num_export; k++ ) 02492 { 02493 ofs3 << "\t" << k << "\t" << export_global_ids[k] << "\t" << export_local_ids[k] << "\t" << assign_procs[k] 02494 << "\t" << assign_parts[k] << "\n"; 02495 } 02496 ofs3.close(); 02497 #endif 02498 02499 // basically each local cell is assigned to a part 02500 02501 // new code: if method == 4, we need to serialize, and send it to root of the coupler 02502 // here, we serialize it; sending it will happen in the calling method, where we have access to 02503 // the root of the coupler, which will store the buffer 02504 if( 4 == met ) 02505 { 02506 if( 0 == rank ) 02507 { 02508 size_t bufSize = myZZ->Serialize_Size(); 02509 /* Then allocate the buffer */ 02510 ZoltanBuffer.resize( bufSize ); 02511 int ierr = myZZ->Serialize( bufSize, &ZoltanBuffer[0] ); 02512 if( ierr != 0 ) MB_CHK_ERR( MB_FAILURE ); 02513 } 02514 } 02515 assert( num_export == (int)primary.size() ); 02516 for( i = 0; i < num_export; i++ ) 02517 { 02518 EntityHandle cell = primary[export_local_ids[i]]; 02519 distribution[assign_parts[i]].insert( cell ); 02520 } 02521 02522 Zoltan::LB_Free_Part( &import_global_ids, &import_local_ids, &import_procs, &import_to_part ); 02523 Zoltan::LB_Free_Part( &export_global_ids, &export_local_ids, &assign_procs, &assign_parts ); 02524 02525 delete myZZ; 02526 myZZ = NULL; 02527 } 02528 else if( 5 == met ) 02529 { 02530 if( NULL == myZZ ) myZZ = new Zoltan( mbpc->comm() ); 02531 // zoltan buffer is only on rank 0 right now 02532 // broadcast it first: 02533 int rank = mbpc->rank(); 02534 size_t bufSize; 02535 if( rank == 0 ) bufSize = ZoltanBuffer.size(); 02536 MPI_Bcast( (char*)&bufSize, sizeof( bufSize ), MPI_CHAR, 0, mbpc->comm() ); 02537 if( 0 != rank ) ZoltanBuffer.resize( bufSize ); 02538 02539 MPI_Bcast( &ZoltanBuffer[0], bufSize, MPI_CHAR, 0, mbpc->comm() ); 02540 // deserialize on each task 02541 int ierr = myZZ->Deserialize( ZoltanBuffer.size(), &ZoltanBuffer[0] ); 02542 if( ierr != 0 ) MB_CHK_ERR( MB_FAILURE ); 02543 02544 // use here the partitioning !! 02545 /* code in inferred partitions: 02546 // Compute the coordinate's part assignment 02547 myZZ->LB_Point_PP_Assign( ecoords, proc, part ); 02548 02549 // Store the part assignment in the return array 02550 part_assignments[part].push_back( elverts[iel] );*/ 02551 i = 0; 02552 for( Range::iterator rit = primary.begin(); rit != primary.end(); ++rit, i++ ) 02553 { 02554 EntityHandle cell = *rit; 02555 int proc = 0, part = 0; 02556 // coords are calculated in advance, contain the centers of the cells, maybe in gnomonic plane 02557 myZZ->LB_Point_PP_Assign( &coords[3 * i], proc, part ); 02558 distribution[part].insert( cell ); 02559 } 02560 // TODO 02561 delete myZZ; 02562 myZZ = NULL; 02563 } 02564 02565 // clear arrays that were resized locally, to free up local memory 02566 02567 std::vector< int >().swap( adjacencies ); 02568 std::vector< int >().swap( ids ); 02569 std::vector< int >().swap( length ); 02570 std::vector< int >().swap( nbor_proc ); 02571 std::vector< double >().swap( coords ); 02572 02573 return MB_SUCCESS; 02574 }