Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
mbpart.cpp
Go to the documentation of this file.
1 #include "moab/Core.hpp"
2 #include "moab/ProgOptions.hpp"
3 #include "moab/ReorderTool.hpp"
4 
5 #ifdef MOAB_HAVE_MPI
6 #include "moab/ParallelComm.hpp"
7 #endif
8 
9 #ifdef MOAB_HAVE_ZOLTAN
11 
12 #endif
13 
14 #ifdef MOAB_HAVE_METIS
16 typedef idx_t PartType;
17 #else
18 typedef int PartType;
19 #endif
20 
21 #include <iostream>
22 #include <sstream>
23 #include <cmath>
24 #include <cstdlib>
25 #include <list>
26 #include <ctime>
27 
29 
30 using namespace moab;
31 
32 std::string DEFAULT_TAGGEDSETS_TAG = "PARALLEL_PARTITION";
33 
34 const char DEFAULT_ZOLTAN_METHOD[] = "RCB";
35 #ifdef MOAB_HAVE_ZOLTAN
36 const char ZOLTAN_PARMETIS_METHOD[] = "PARMETIS";
37 const char ZOLTAN_OCTPART_METHOD[] = "OCTPART";
38 #endif
39 
40 const char METIS_DEFAULT_METHOD[] = "ML_KWAY";
41 /* const char METIS_ALTERNATIVE_METHOD[] = "ML_RB"; */
42 
43 const char BRIEF_DESC[] = "Use Zoltan or Metis to partition MOAB meshes for use on parallel computers";
44 std::ostringstream LONG_DESC;
45 
46 int main( int argc, char* argv[] )
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  MB_CHK_SET_ERR( mb.load_file( input_file.c_str(), 0, options ), "Failed to load input file: " + input_file );
331  }
332  else // load the material set(s)
333  {
334  MB_CHK_SET_ERR( mb.load_mesh( input_file.c_str(), &set_l[0], (int)set_l.size() ),
335  "Failed to load input mesh: " + input_file );
336  }
337  if( print_time )
338  std::cout << "Read input file in " << ( clock() - t ) / (double)CLOCKS_PER_SEC << " seconds" << std::endl;
339 
340  for( int dim = part_dim; dim >= 0; --dim )
341  {
342  int n;
343  rval = mb.get_number_entities_by_dimension( 0, dim, n );
344  if( MB_SUCCESS == rval && 0 != n )
345  {
346  part_dim = dim;
347  break;
348  }
349  }
350  if( part_dim < 0 )
351  {
352  std::cerr << input_file << " : file does not contain any mesh entities" << std::endl;
353  return 2;
354  }
355 
356  ReorderTool reorder_tool( &moab );
357 
358  for( int p = 0; p < power; p++ )
359  {
360  t = clock();
361 #ifdef MOAB_HAVE_ZOLTAN
362  if( moab_use_zoltan )
363  {
365  part_geom_mesh_size, num_parts, zoltan_method.c_str(),
366  ( !parm_method.empty() ? parm_method.c_str() : oct_method.c_str() ), imbal_tol,
367  part_dim, write_sets, write_tags, obj_weight, edge_weight, projection_type,
368  recompute_box_rcb, print_time ),
369  "Zoltan partitioner failed." );
370  }
371 #endif
372 #ifdef MOAB_HAVE_METIS
373  if( moab_use_metis )
374  {
375  MB_CHK_SET_ERR( metis_tool->partition_mesh( num_parts, metis_method.c_str(), part_dim, write_sets,
376  write_tags, partition_tagged_sets, partition_tagged_ents,
377  aggregating_tag.c_str(), print_time ),
378  "Metis partitioner failed." );
379  }
380 #endif
381 
382  if( print_time )
383  std::cout << "Generated " << num_parts << " part partitioning in "
384  << ( clock() - t ) / (double)CLOCKS_PER_SEC << " seconds" << std::endl;
385 
386  if( reorder && part_geom_mesh_size < 0. )
387  {
388  std::cout << "Reordering mesh for partition..." << std::endl;
389 
390  Tag tag, order;
392  "Partitioner did not create " + DEFAULT_TAGGEDSETS_TAG + " tag" );
393 
394  t = clock();
395  if( write_sets )
396  {
397  Range sets;
398  mb.get_entities_by_type_and_tag( 0, MBENTITYSET, &tag, 0, 1, sets );
399  MB_CHK_SET_ERR( reorder_tool.handle_order_from_sets_and_adj( sets, order ),
400  "Failed to calculate reordering." );
401  }
402  else
403  {
404  MB_CHK_SET_ERR( reorder_tool.handle_order_from_int_tag( tag, -1, order ),
405  "Failed to calculate reordering." );
406  }
407 
408  MB_CHK_SET_ERR( reorder_tool.reorder_entities( order ), "Failed to perform reordering." );
409 
410  MB_CHK_SET_ERR( mb.tag_delete( order ), "Failed to delete tag." );
411  if( print_time )
412  std::cout << "Reordered mesh in " << ( clock() - t ) / (double)CLOCKS_PER_SEC << " seconds"
413  << std::endl;
414  }
415 
416 #ifdef MOAB_HAVE_ZOLTAN
417  if( incl_closure )
418  {
419  MB_CHK_SET_ERR( zoltan_tool->include_closure(), "Closure inclusion failed." );
420  }
421 #endif
422 
423  std::ostringstream tmp_output_file;
424 
425  if( power > 1 )
426  {
427  // append num_parts to output filename
428  std::string::size_type idx = output_file.find_last_of( "." );
429  if( idx == std::string::npos )
430  {
431  tmp_output_file << output_file << "_" << num_parts;
432  if( part_geom_mesh_size < 0. )
433  tmp_output_file << ".h5m";
434  else
435  {
436  std::cerr << "output file type is not specified." << std::endl;
437  return 1;
438  }
439  }
440  else
441  {
442  tmp_output_file << output_file.substr( 0, idx ) << "_" << num_parts << output_file.substr( idx );
443  }
444  }
445  else
446  tmp_output_file << output_file;
447 
448  t = clock();
449  std::cout << "Saving file to " << output_file << "..." << std::endl;
450  if( part_geom_mesh_size < 0. )
451  {
452  MB_CHK_SET_ERR( mb.write_file( tmp_output_file.str().c_str() ),
453  tmp_output_file.str() << " : failed to write file." << std::endl );
454  }
455  else
456  {
457  MB_CHK_SET_ERR( MB_FAILURE, "Geometry will not be partitioned.\n" );
458  }
459 
460  if( print_time )
461  std::cout << "Wrote \"" << tmp_output_file.str() << "\" in " << ( clock() - t ) / (double)CLOCKS_PER_SEC
462  << " seconds" << std::endl;
463 
464 #ifdef MOAB_HAVE_ZOLTAN
465 
466  if( moab_use_zoltan && moab_partition_slave && p == 0 )
467  {
468  t = clock();
469  double master_radius, slave_radius;
470  if( rescale_spherical_radius )
471  {
472  EntityHandle rootset = 0;
473  Range masterverts;
474  MB_CHK_SET_ERR( mb.get_entities_by_dimension( rootset, 0, masterverts ),
475  "Can't create vertices on master set" );
476  double points[6];
477  EntityHandle mfrontback[2] = { masterverts[0], masterverts[masterverts.size() - 1] };
478  MB_CHK_ERR( mb.get_coords( &mfrontback[0], 2, points ) );
479  const double mr1 = std::sqrt( points[0] * points[0] + points[1] * points[1] + points[2] * points[2] );
480  const double mr2 = std::sqrt( points[3] * points[3] + points[4] * points[4] + points[5] * points[5] );
481  master_radius = 0.5 * ( mr1 + mr2 );
482  }
483  EntityHandle slaveset;
484  MB_CHK_SET_ERR( mb.create_meshset( moab::MESHSET_SET, slaveset ), "Can't create new set" );
485  MB_CHK_SET_ERR( mb.load_file( slave_file_name.c_str(), &slaveset, options ), "Can't load slave mesh" );
486  if( rescale_spherical_radius )
487  {
488  double points[6];
489  Range slaveverts;
490  MB_CHK_SET_ERR( mb.get_entities_by_dimension( slaveset, 0, slaveverts ),
491  "Can't create vertices on master set" );
492  EntityHandle sfrontback[2] = { slaveverts[0], slaveverts[slaveverts.size() - 1] };
493  MB_CHK_ERR( mb.get_coords( &sfrontback[0], 2, points ) );
494  const double sr1 = std::sqrt( points[0] * points[0] + points[1] * points[1] + points[2] * points[2] );
495  const double sr2 = std::sqrt( points[3] * points[3] + points[4] * points[4] + points[5] * points[5] );
496  slave_radius = 0.5 * ( sr1 + sr2 );
497  // Let us rescale both master and slave meshes to a unit sphere
498  MB_CHK_ERR( moab::IntxUtils::ScaleToRadius( &mb, slaveset, master_radius ) );
499  }
500 
501  MB_CHK_ERR(
502  zoltan_tool->partition_inferred_mesh( slaveset, num_parts, part_dim, write_sets, projection_type ) );
503 
504  if( rescale_spherical_radius )
505  {
506  // rescale the slave mesh back to its original radius
507  MB_CHK_ERR( moab::IntxUtils::ScaleToRadius( &mb, slaveset, slave_radius ) );
508  }
509 
510  if( print_time )
511  {
512  std::cout << "Time taken to infer slave mesh partitions = " << ( clock() - t ) / (double)CLOCKS_PER_SEC
513  << " seconds" << std::endl;
514  }
515 
516  size_t lastindex = slave_file_name.find_last_of( "." );
517  std::string inferred_output_file = slave_file_name.substr( 0, lastindex ) + "_inferred" +
518  slave_file_name.substr( lastindex, slave_file_name.size() );
519 
520  // Save the resulting mesh
521  std::cout << "Saving inferred file to " << inferred_output_file << "..." << std::endl;
522  MB_CHK_SET_ERR( mb.write_file( inferred_output_file.c_str(), 0, 0, &slaveset, 1 ),
523  inferred_output_file << " : failed to write file." << std::endl );
524  }
525 #endif
526 
527  num_parts *= 2;
528  }
529 
530 #ifdef MOAB_HAVE_ZOLTAN
531  delete zoltan_tool;
532 #endif
533 #ifdef MOAB_HAVE_METIS
534  delete metis_tool;
535 #endif
536 
537 #ifdef MOAB_HAVE_MPI
538  err = MPI_Finalize();
539  assert( MPI_SUCCESS == err );
540 #endif
541  return 0;
542 }