#include "moab/Core.hpp"
#include "moab/ProgOptions.hpp"
#include "moab/ReorderTool.hpp"
#include <iostream>
#include <sstream>
#include <cmath>
#include <cstdlib>
#include <list>
#include <ctime>
#include "moab/IntxMesh/IntxUtils.hpp"
Go to the source code of this file.
Typedefs | |
typedef int | PartType |
Functions | |
int | main (int argc, char *argv[]) |
Variables | |
std::string | DEFAULT_TAGGEDSETS_TAG = "PARALLEL_PARTITION" |
const char | DEFAULT_ZOLTAN_METHOD [] = "RCB" |
const char | METIS_DEFAULT_METHOD [] = "ML_KWAY" |
const char | BRIEF_DESC [] = "Use Zoltan or Metis to partition MOAB meshes for use on parallel computers" |
std::ostringstream | LONG_DESC |
typedef int PartType |
Definition at line 18 of file mbpart.cpp.
int main | ( | int | argc, |
char * | argv[] | ||
) |
Definition at line 46 of file mbpart.cpp.
47 {
48 #ifdef MOAB_HAVE_MPI
49 int err = MPI_Init( &argc, &argv );
50 if( err )
51 {
52 std::cerr << "MPI_Init failed. Aborting." << std::endl;
53 return 3;
54 }
55 #endif
56 Core moab;
57 Interface& mb = moab;
58 std::vector< int > set_l;
59
60 #ifdef MOAB_HAVE_ZOLTAN
61 bool moab_use_zoltan = false;
62 #endif
63 #ifdef MOAB_HAVE_METIS
64 bool moab_use_metis = false;
65 #endif
66
67 LONG_DESC << "This utility invokes the ZoltanPartitioner or MetisPartitioner component of MOAB"
68 "to partition a mesh/geometry."
69 << std::endl
70 << "If no partitioning method is specified, the defaults are: "
71 << "for Zoltan=\"" << DEFAULT_ZOLTAN_METHOD << "\" and Metis=\"" << METIS_DEFAULT_METHOD << " method"
72 << std::endl;
73
74 ProgOptions opts( LONG_DESC.str(), BRIEF_DESC );
75
76 int part_dim = 3;
77 opts.addOpt< int >( "dimension",
78 "Specify dimension of entities to partition."
79 " Default is largest in file.",
80 &part_dim, ProgOptions::int_flag );
81
82 std::string zoltan_method, parm_method, oct_method, metis_method;
83 #ifdef MOAB_HAVE_ZOLTAN
84 opts.addOpt< std::string >( "zoltan,z",
85 "(Zoltan) Specify Zoltan partition method. "
86 "One of RR, RCB, RIB, HFSC, PHG, or Hypergraph (PHG and Hypergraph "
87 "are synonymous).",
88 &zoltan_method );
89 #ifdef MOAB_HAVE_PARMETIS
90 opts.addOpt< std::string >( "parmetis,p", "(Zoltan+PARMetis) Specify PARMetis partition method.", &parm_method );
91 #endif // MOAB_HAVE_PARMETIS
92 opts.addOpt< std::string >( "octpart,o", "(Zoltan) Specify OctPart partition method.", &oct_method );
93
94 bool incl_closure = false;
95 opts.addOpt< void >( "include_closure,c", "Include element closure for part sets.", &incl_closure );
96
97 bool recompute_box_rcb = false;
98 opts.addOpt< void >( "recompute_rcb_box,b", "recompute box in rcb cuts", &recompute_box_rcb );
99 #endif // MOAB_HAVE_ZOLTAN
100
101 double imbal_tol = 1.03;
102 opts.addOpt< double >( "imbalance,i", "Imbalance tolerance (used in PHG/Hypergraph method)", &imbal_tol );
103
104 #ifdef MOAB_HAVE_METIS
105 opts.addOpt< std::string >( "metis,m", "(Metis) Specify Metis partition method. One of ML_RB or ML_KWAY.",
106 &metis_method );
107 #endif // MOAB_HAVE_METIS
108
109 bool write_sets = true, write_tags = false;
110 opts.addOpt< void >( "sets,s", "Write partition as tagged sets (Default)", &write_sets );
111 opts.addOpt< void >( "tags,t", "Write partition by tagging entities", &write_tags );
112
113 int power = -1;
114 opts.addOpt< int >( "power,M", "Generate multiple partitions, in powers of 2, up to 2^(pow)", &power );
115
116 bool reorder = false;
117 opts.addOpt< void >( "reorder,R", "Reorder mesh to group entities by partition", &reorder );
118
119 double part_geom_mesh_size = -1.0;
120 #ifdef MOAB_HAVE_ZOLTAN
121 int obj_weight = 0;
122 opts.addOpt< int >( "vertex_w,v", "(Zoltan) Number of weights associated with a graph vertex." );
123
124 int edge_weight = 0;
125 opts.addOpt< int >( "edge_w,e", "(Zoltan) Number of weights associated with an edge." );
126
127 bool moab_partition_slave = false;
128 std::string slave_file_name = "";
129 opts.addOpt< std::string >( "inferred",
130 "(Zoltan) Specify inferred slave mesh file name to impose "
131 "partition based on cuts computed for original master mesh.",
132 &slave_file_name );
133
134 bool rescale_spherical_radius = false;
135 opts.addOpt< void >( "scale_sphere",
136 "(Zoltan) If the meshes are defined on a sphere, rescale radius as needed "
137 "(in combination with --inferred)",
138 &rescale_spherical_radius );
139
140 #endif // MOAB_HAVE_ZOLTAN
141
142 long num_parts;
143 opts.addOpt< std::vector< int > >( "set_l,l",
144 "Load material set(s) with specified ids (comma separated) for partition" );
145
146 opts.addRequiredArg< int >( "#parts", "Number of parts in partition" );
147
148 std::string input_file, output_file;
149 opts.addRequiredArg< std::string >( "input_file", "Mesh/geometry to partition", &input_file );
150 opts.addRequiredArg< std::string >( "output_file", "File to which to write partitioned mesh/geometry",
151 &output_file );
152
153 bool print_time = false;
154 opts.addOpt< void >( ",T", "Print CPU time for each phase.", &print_time );
155
156 int projection_type = 0;
157 opts.addOpt< int >( "project_on_sphere,p",
158 "use spherical coordinates (1) or gnomonic projection (2) for partitioning ",
159 &projection_type );
160 #ifdef MOAB_HAVE_METIS
161 bool partition_tagged_sets = false;
162 opts.addOpt< void >( "taggedsets,x", "(Metis) Partition tagged sets.", &partition_tagged_sets );
163
164 bool partition_tagged_ents = false;
165 opts.addOpt< void >( "taggedents,y", "(Metis) Partition tagged ents.", &partition_tagged_ents );
166
167 std::string aggregating_tag;
168 opts.addOpt< std::string >( "aggregatingtag,a",
169 "(Metis) Specify aggregating tag to partion tagged sets or tagged entities.",
170 &aggregating_tag );
171
172 std::string aggregating_bc_tag;
173 opts.addOpt< std::string >( "aggregatingBCtag,B",
174 "(Metis) Specify boundary id tag name used to group cells with same boundary ids.",
175 &aggregating_bc_tag );
176
177 std::string boundaryIds;
178 std::vector< int > BCids;
179 opts.addOpt< std::string >( "aggregatingBCids,I",
180 " (Metis) Specify id or ids of boundaries to be aggregated before "
181 "partitioning (all elements with same boundary id will be in the "
182 "same partition). Comma separated e.g. -I 1,2,5 ",
183 &boundaryIds );
184 #endif // MOAB_HAVE_METIS
185
186 bool assign_global_ids = false;
187 opts.addOpt< void >( "globalIds,j", "Assign GLOBAL_ID tag to entities", &assign_global_ids );
188
189 opts.parseCommandLine( argc, argv );
190
191 #ifdef MOAB_HAVE_ZOLTAN
192 if( !zoltan_method.empty() )
193 moab_use_zoltan = true;
194 else
195 #endif
196 #ifdef MOAB_HAVE_METIS
197 if( !metis_method.empty() )
198 moab_use_metis = true;
199 else
200 #endif
201 MB_SET_ERR( MB_FAILURE, "Specify either Zoltan or Metis partitioner type" );
202
203 #ifdef MOAB_HAVE_ZOLTAN
204 ZoltanPartitioner* zoltan_tool = NULL;
205 // check if partition geometry, if it is, should get mesh size for the geometry
206 if( part_geom_mesh_size != -1.0 && part_geom_mesh_size <= 0.0 )
207 {
208 std::cerr << part_geom_mesh_size << ": invalid geometry partition mesh size." << std::endl << std::endl;
209 opts.printHelp();
210 return EXIT_FAILURE;
211 }
212
213 if( slave_file_name.size() ) moab_partition_slave = true;
214
215 if( moab_use_zoltan )
216 {
217 if( part_geom_mesh_size < 0. )
218 {
219 // partition mesh we have no ParallelComm here, so we will create one later
220 zoltan_tool = new ZoltanPartitioner( &mb, NULL, false, argc, argv );
221 }
222 else
223 {
224 // partition geometry
225 MB_CHK_SET_ERR( MB_FAILURE, "Geometry will not be partitioned.\n" );
226 }
227 zoltan_tool->set_global_id_option( assign_global_ids );
228 }
229
230 if( zoltan_method.empty() && parm_method.empty() && oct_method.empty() ) zoltan_method = DEFAULT_ZOLTAN_METHOD;
231 if( !parm_method.empty() ) zoltan_method = ZOLTAN_PARMETIS_METHOD;
232 if( !oct_method.empty() ) zoltan_method = ZOLTAN_OCTPART_METHOD;
233 #endif // MOAB_HAVE_ZOLTAN
234
235 #ifdef MOAB_HAVE_METIS
236 MetisPartitioner* metis_tool = NULL;
237 if( moab_use_metis && !metis_tool )
238 {
239 metis_tool = new MetisPartitioner( &mb, false );
240 metis_tool->set_global_id_option( assign_global_ids );
241 }
242
243 if( ( aggregating_tag.empty() && partition_tagged_sets ) || ( aggregating_tag.empty() && partition_tagged_ents ) )
244 aggregating_tag = DEFAULT_TAGGEDSETS_TAG;
245 if( !write_sets && !write_tags ) write_sets = true;
246
247 if( !boundaryIds.empty() )
248 {
249 std::vector< std::string > ids;
250 std::stringstream ss( boundaryIds );
251 std::string item;
252 while( std::getline( ss, item, ',' ) )
253 {
254 ids.push_back( item );
255 }
256 for( unsigned int i = 0; i < ids.size(); i++ )
257 BCids.push_back( std::atoi( ids[i].c_str() ) );
258 }
259
260 if( metis_method.empty() )
261 {
262 metis_method = METIS_DEFAULT_METHOD;
263 }
264
265 #endif // MOAB_HAVE_METIS
266
267 if( !write_sets && !write_tags ) write_sets = true;
268
269 if( -1 == power )
270 {
271 num_parts = opts.getReqArg< int >( "#parts" );
272 power = 1;
273 }
274 else if( power < 1 || power > 18 )
275 {
276 std::cerr << power << ": invalid power for multiple partitions. Expected value in [1,18]" << std::endl
277 << std::endl;
278 opts.printHelp();
279 return EXIT_FAILURE;
280 }
281 else
282 {
283 num_parts = 2;
284 }
285
286 if( part_dim < 0 || part_dim > 3 )
287 {
288 std::cerr << part_dim << " : invalid dimension" << std::endl << std::endl;
289 opts.printHelp();
290 return EXIT_FAILURE;
291 }
292
293 if( imbal_tol < 0.0 )
294 {
295 std::cerr << imbal_tol << ": invalid imbalance tolerance" << std::endl << std::endl;
296 opts.printHelp();
297 return EXIT_FAILURE;
298 }
299
300 bool load_msets = false;
301 if( opts.getOpt( "set_l,l", &set_l ) )
302 {
303 load_msets = true;
304 if( set_l.size() <= 0 )
305 {
306 std::cerr << " No material set id's to load" << std::endl << std::endl;
307 opts.printHelp();
308 return EXIT_FAILURE;
309 }
310 }
311
312 if( num_parts <= 1 )
313 {
314 std::cerr << "** Please specify #parts = " << num_parts << " to be greater than 1." << std::endl << std::endl;
315 opts.printHelp();
316 return EXIT_FAILURE;
317 }
318
319 clock_t t = clock();
320
321 const char* options = NULL;
322 ErrorCode rval;
323 #ifdef MOAB_HAVE_ZOLTAN
324 if( part_geom_mesh_size > 0. ) options = "FACET_DISTANCE_TOLERANCE=0.1";
325 #endif // MOAB_HAVE_ZOLTAN
326
327 std::cout << "Loading file " << input_file << "..." << std::endl;
328 if( load_msets == false )
329 {
330 rval = mb.load_file( input_file.c_str(), 0, options );MB_CHK_SET_ERR( rval, "Failed to load input file: " + input_file );
331 }
332 else // load the material set(s)
333 {
334 rval = mb.load_mesh( input_file.c_str(), &set_l[0], (int)set_l.size() );MB_CHK_SET_ERR( rval, "Failed to load input mesh: " + input_file );
335 }
336 if( print_time )
337 std::cout << "Read input file in " << ( clock() - t ) / (double)CLOCKS_PER_SEC << " seconds" << std::endl;
338
339 for( int dim = part_dim; dim >= 0; --dim )
340 {
341 int n;
342 rval = mb.get_number_entities_by_dimension( 0, dim, n );
343 if( MB_SUCCESS == rval && 0 != n )
344 {
345 part_dim = dim;
346 break;
347 }
348 }
349 if( part_dim < 0 )
350 {
351 std::cerr << input_file << " : file does not contain any mesh entities" << std::endl;
352 return 2;
353 }
354
355 ReorderTool reorder_tool( &moab );
356
357 for( int p = 0; p < power; p++ )
358 {
359 t = clock();
360 #ifdef MOAB_HAVE_ZOLTAN
361 if( moab_use_zoltan )
362 {
363 rval = zoltan_tool->partition_mesh_and_geometry(
364 part_geom_mesh_size, num_parts, zoltan_method.c_str(),
365 ( !parm_method.empty() ? parm_method.c_str() : oct_method.c_str() ), imbal_tol, part_dim, write_sets,
366 write_tags, obj_weight, edge_weight, projection_type, recompute_box_rcb, print_time );MB_CHK_SET_ERR( rval, "Zoltan partitioner failed." );
367 }
368 #endif
369 #ifdef MOAB_HAVE_METIS
370 if( moab_use_metis )
371 {
372 rval = metis_tool->partition_mesh( num_parts, metis_method.c_str(), part_dim, write_sets, write_tags,
373 partition_tagged_sets, partition_tagged_ents, aggregating_tag.c_str(),
374 print_time );MB_CHK_SET_ERR( rval, "Metis partitioner failed." );
375 }
376 #endif
377
378 if( print_time )
379 std::cout << "Generated " << num_parts << " part partitioning in "
380 << ( clock() - t ) / (double)CLOCKS_PER_SEC << " seconds" << std::endl;
381
382 if( reorder && part_geom_mesh_size < 0. )
383 {
384 std::cout << "Reordering mesh for partition..." << std::endl;
385
386 Tag tag, order;
387 rval = mb.tag_get_handle( DEFAULT_TAGGEDSETS_TAG.c_str(), 1, MB_TYPE_INTEGER, tag );MB_CHK_SET_ERR( rval, "Partitioner did not create " + DEFAULT_TAGGEDSETS_TAG + " tag" );
388
389 t = clock();
390 if( write_sets )
391 {
392 Range sets;
393 mb.get_entities_by_type_and_tag( 0, MBENTITYSET, &tag, 0, 1, sets );
394 rval = reorder_tool.handle_order_from_sets_and_adj( sets, order );MB_CHK_SET_ERR( rval, "Failed to calculate reordering." );
395 }
396 else
397 {
398 rval = reorder_tool.handle_order_from_int_tag( tag, -1, order );MB_CHK_SET_ERR( rval, "Failed to calculate reordering." );
399 }
400
401 rval = reorder_tool.reorder_entities( order );MB_CHK_SET_ERR( rval, "Failed to perform reordering." );
402
403 rval = mb.tag_delete( order );MB_CHK_SET_ERR( rval, "Failed to delete tag." );
404 if( print_time )
405 std::cout << "Reordered mesh in " << ( clock() - t ) / (double)CLOCKS_PER_SEC << " seconds"
406 << std::endl;
407 }
408
409 #ifdef MOAB_HAVE_ZOLTAN
410 if( incl_closure )
411 {
412 rval = zoltan_tool->include_closure();MB_CHK_SET_ERR( rval, "Closure inclusion failed." );
413 }
414 #endif
415
416 std::ostringstream tmp_output_file;
417
418 if( power > 1 )
419 {
420 // append num_parts to output filename
421 std::string::size_type idx = output_file.find_last_of( "." );
422 if( idx == std::string::npos )
423 {
424 tmp_output_file << output_file << "_" << num_parts;
425 if( part_geom_mesh_size < 0. )
426 tmp_output_file << ".h5m";
427 else
428 {
429 std::cerr << "output file type is not specified." << std::endl;
430 return 1;
431 }
432 }
433 else
434 {
435 tmp_output_file << output_file.substr( 0, idx ) << "_" << num_parts << output_file.substr( idx );
436 }
437 }
438 else
439 tmp_output_file << output_file;
440
441 t = clock();
442 std::cout << "Saving file to " << output_file << "..." << std::endl;
443 if( part_geom_mesh_size < 0. )
444 {
445 rval = mb.write_file( tmp_output_file.str().c_str() );MB_CHK_SET_ERR( rval, tmp_output_file.str() << " : failed to write file." << std::endl );
446 }
447 else
448 {
449 MB_CHK_SET_ERR( MB_FAILURE, "Geometry will not be partitioned.\n" );
450 }
451
452 if( print_time )
453 std::cout << "Wrote \"" << tmp_output_file.str() << "\" in " << ( clock() - t ) / (double)CLOCKS_PER_SEC
454 << " seconds" << std::endl;
455
456 #ifdef MOAB_HAVE_ZOLTAN
457
458 if( moab_use_zoltan && moab_partition_slave && p == 0 )
459 {
460 t = clock();
461 double master_radius, slave_radius;
462 if( rescale_spherical_radius )
463 {
464 EntityHandle rootset = 0;
465 Range masterverts;
466 rval = mb.get_entities_by_dimension( rootset, 0, masterverts );MB_CHK_SET_ERR( rval, "Can't create vertices on master set" );
467 double points[6];
468 EntityHandle mfrontback[2] = { masterverts[0], masterverts[masterverts.size() - 1] };
469 rval = mb.get_coords( &mfrontback[0], 2, points );MB_CHK_ERR( rval );
470 const double mr1 = std::sqrt( points[0] * points[0] + points[1] * points[1] + points[2] * points[2] );
471 const double mr2 = std::sqrt( points[3] * points[3] + points[4] * points[4] + points[5] * points[5] );
472 master_radius = 0.5 * ( mr1 + mr2 );
473 }
474 EntityHandle slaveset;
475 rval = mb.create_meshset( moab::MESHSET_SET, slaveset );MB_CHK_SET_ERR( rval, "Can't create new set" );
476 rval = mb.load_file( slave_file_name.c_str(), &slaveset, options );MB_CHK_SET_ERR( rval, "Can't load slave mesh" );
477 if( rescale_spherical_radius )
478 {
479 double points[6];
480 Range slaveverts;
481 rval = mb.get_entities_by_dimension( slaveset, 0, slaveverts );MB_CHK_SET_ERR( rval, "Can't create vertices on master set" );
482 EntityHandle sfrontback[2] = { slaveverts[0], slaveverts[slaveverts.size() - 1] };
483 rval = mb.get_coords( &sfrontback[0], 2, points );MB_CHK_ERR( rval );
484 const double sr1 = std::sqrt( points[0] * points[0] + points[1] * points[1] + points[2] * points[2] );
485 const double sr2 = std::sqrt( points[3] * points[3] + points[4] * points[4] + points[5] * points[5] );
486 slave_radius = 0.5 * ( sr1 + sr2 );
487 // Let us rescale both master and slave meshes to a unit sphere
488 rval = moab::IntxUtils::ScaleToRadius( &mb, slaveset, master_radius );MB_CHK_ERR( rval );
489 }
490
491 rval = zoltan_tool->partition_inferred_mesh( slaveset, num_parts, part_dim, write_sets, projection_type );MB_CHK_ERR( rval );
492
493 if( rescale_spherical_radius )
494 {
495 // rescale the slave mesh back to its original radius
496 rval = moab::IntxUtils::ScaleToRadius( &mb, slaveset, slave_radius );MB_CHK_ERR( rval );
497 }
498
499 if( print_time )
500 {
501 std::cout << "Time taken to infer slave mesh partitions = " << ( clock() - t ) / (double)CLOCKS_PER_SEC
502 << " seconds" << std::endl;
503 }
504
505 size_t lastindex = slave_file_name.find_last_of( "." );
506 std::string inferred_output_file = slave_file_name.substr( 0, lastindex ) + "_inferred" +
507 slave_file_name.substr( lastindex, slave_file_name.size() );
508
509 // Save the resulting mesh
510 std::cout << "Saving inferred file to " << inferred_output_file << "..." << std::endl;
511 rval = mb.write_file( inferred_output_file.c_str(), 0, 0, &slaveset, 1 );MB_CHK_SET_ERR( rval, inferred_output_file << " : failed to write file." << std::endl );
512 }
513 #endif
514
515 num_parts *= 2;
516 }
517
518 #ifdef MOAB_HAVE_ZOLTAN
519 delete zoltan_tool;
520 #endif
521 #ifdef MOAB_HAVE_METIS
522 delete metis_tool;
523 #endif
524
525 #ifdef MOAB_HAVE_MPI
526 err = MPI_Finalize();
527 assert( MPI_SUCCESS == err );
528 #endif
529 return 0;
530 }
References ProgOptions::addOpt(), ProgOptions::addRequiredArg(), BRIEF_DESC, moab::Core::create_meshset(), DEFAULT_TAGGEDSETS_TAG, DEFAULT_ZOLTAN_METHOD, dim, ErrorCode, moab::Core::get_coords(), moab::Core::get_entities_by_dimension(), moab::Core::get_entities_by_type_and_tag(), moab::Core::get_number_entities_by_dimension(), ProgOptions::getOpt(), ProgOptions::getReqArg(), moab::ReorderTool::handle_order_from_int_tag(), moab::ReorderTool::handle_order_from_sets_and_adj(), ZoltanPartitioner::include_closure(), input_file, ProgOptions::int_flag, moab::Core::load_file(), moab::Core::load_mesh(), LONG_DESC, mb, MB_CHK_ERR, MB_CHK_SET_ERR, MB_SET_ERR, MB_SUCCESS, MB_TYPE_INTEGER, MBENTITYSET, MESHSET_SET, METIS_DEFAULT_METHOD, ProgOptions::parseCommandLine(), ZoltanPartitioner::partition_inferred_mesh(), MetisPartitioner::partition_mesh(), ZoltanPartitioner::partition_mesh_and_geometry(), print_time(), ProgOptions::printHelp(), moab::ReorderTool::reorder_entities(), moab::IntxUtils::ScaleToRadius(), PartitionerBase< T >::set_global_id_option(), moab::Range::size(), moab::Core::tag_delete(), moab::Core::tag_get_handle(), and moab::Core::write_file().
const char BRIEF_DESC[] = "Use Zoltan or Metis to partition MOAB meshes for use on parallel computers" |
Definition at line 43 of file mbpart.cpp.
Referenced by main().
std::string DEFAULT_TAGGEDSETS_TAG = "PARALLEL_PARTITION" |
Definition at line 32 of file mbpart.cpp.
Referenced by main().
const char DEFAULT_ZOLTAN_METHOD[] = "RCB" |
Definition at line 34 of file mbpart.cpp.
Referenced by main().
std::ostringstream LONG_DESC |
Definition at line 44 of file mbpart.cpp.
Referenced by main().
const char METIS_DEFAULT_METHOD[] = "ML_KWAY" |
Definition at line 40 of file mbpart.cpp.
Referenced by main().