Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
umr.cpp
Go to the documentation of this file.
1 #include <iostream>
2 #include <vector>
3 #include <string>
4 #include <iomanip>
5 #include <fstream>
6 #if defined( __MINGW32__ )
7 #include <sys/time.h>
8 #else
9 #include <ctime>
10 #endif
11 
12 #include <ctime>
13 #include <cmath>
14 #include <cassert>
15 #include <cfloat>
16 
17 #include "moab/Core.hpp"
18 #include "moab/Interface.hpp"
20 #include "moab/NestedRefine.hpp"
21 
22 #ifdef MOAB_HAVE_MPI
23 #include "moab_mpi.h"
24 #include "moab/ParallelComm.hpp"
25 #include "MBParallelConventions.h"
26 #endif
27 
28 /* Exit values */
29 #define SUCCESS 0
30 #define USAGE_ERROR 1
31 #define NOT_IMPLEMENTED 2
32 
33 using namespace moab;
34 
35 static void print_usage( const char* name, std::ostream& stream )
36 {
37  stream << "Usage: " << name << " <options> <input_file> [<input_file2> ...]" << std::endl
38  << "Options: " << std::endl
39  << "\t-h - Print this help text and exit." << std::endl
40  << "\t-d - Dimension of the mesh." << std::endl
41  << "\t-n - Exact or a maximum number of levels for the hierarchy. Default 1." << std::endl
42  << "\t-L - Degree of refinement for each level. Pass an array or a number. It "
43  "is mandatory to pass dimension and num_levels before to use this option. If this flag "
44  "is not used, a default of deg 2 refinement is used. "
45  << std::endl
46  << "\t-V - Pass a desired volume (absolute) . This will generate a hierarchy "
47  "such that the maximum volume is reduced to the given amount approximately. The length "
48  "of the hierarchy can be constrained further if a maximum number of levels is passed. "
49  "It is mandatory to pass the dimension for this option. "
50  << std::endl
51  << "\t-q - Prints out the maximum volume of the mesh and exits the program. "
52  "This option can be used as a guide to volume constrainted mesh hierarchy later. "
53  << std::endl
54  << "\t-t - Print out the time taken to generate hierarchy." << std::endl
55  << "\t-s - Print out the mesh sizes of each level of the generated hierarchy." << std::endl
56  << "\t-o - Specify true for output files for the mesh levels of the hierarchy." << std::endl
57  //<< "\t-O option - Specify read option." << std::endl
58 #ifdef MOAB_HAVE_MPI
59  << "\t-p[0|1|2] - Read in parallel[0], optionally also doing resolve_shared_ents (1) "
60  "and exchange_ghosts (2)"
61  << std::endl
62 #endif
63  ;
64  exit( USAGE_ERROR );
65 }
66 
67 static void usage_error( const char* name )
68 {
69  print_usage( name, std::cerr );
70 #ifdef MOAB_HAVE_MPI
71  MPI_Finalize();
72 #endif
73  exit( USAGE_ERROR );
74 }
75 
76 bool parse_id_list( const char* string, int dim, int nval, std::vector< int >& results );
77 
78 bool make_opts_string( std::vector< std::string > options, std::string& opts );
79 
81  EntityHandle fileset,
82  int dim,
83  double desired_vol,
84  int& num_levels,
85  std::vector< int >& level_degs );
86 
87 ErrorCode get_max_volume( Core& mb, EntityHandle fileset, int dim, double& vmax );
88 
89 int main( int argc, char* argv[] )
90 {
91  int proc_id = 0, size = 1;
92 #ifdef MOAB_HAVE_MPI
93  MPI_Init( &argc, &argv );
94  MPI_Comm_rank( MPI_COMM_WORLD, &proc_id );
95  MPI_Comm_size( MPI_COMM_WORLD, &size );
96 #endif
97 
98  int num_levels = 0, dim = 0;
99  std::vector< int > level_degrees;
100  bool optimize = false;
101  bool do_flag = true;
102  bool print_times = false, print_size = false, output = false;
103  bool parallel = false, resolve_shared = false, exchange_ghosts = false;
104  bool printusage = false, parselevels = true;
105  bool qc_vol = false, only_quality = false;
106  double cvol = 0;
107  std::string infile;
108 
109  int i = 1;
110  while( i < argc )
111  {
112  if( !argv[i][0] && 0 == proc_id )
113  {
114  usage_error( argv[0] );
115 #ifdef MOAB_HAVE_MPI
116  MPI_Finalize();
117 #endif
118  }
119 
120  if( do_flag && argv[i][0] == '-' )
121  {
122  switch( argv[i][1] )
123  {
124  // do flag arguments:
125  case '-':
126  do_flag = false;
127  break;
128  case 't':
129  print_times = true;
130  break;
131  case 's':
132  print_size = true;
133  break;
134  case 'h':
135  case 'H':
136  print_usage( argv[0], std::cerr );
137  printusage = true;
138  break;
139  case 'd':
140  dim = atol( argv[i + 1] );
141  ++i;
142  break;
143  case 'n':
144  num_levels = atol( argv[i + 1] );
145  ++i;
146  break;
147  case 'L':
148  if( dim != 0 && num_levels != 0 )
149  {
150  parselevels = parse_id_list( argv[i + 1], dim, num_levels, level_degrees );
151  ++i;
152  }
153  else
154  {
155  print_usage( argv[0], std::cerr );
156  printusage = true;
157  }
158  break;
159  case 'V':
160  qc_vol = true;
161  cvol = strtod( argv[i + 1], NULL );
162  ++i;
163  break;
164  case 'q':
165  only_quality = true;
166  break;
167  case 'o':
168  output = true;
169  break;
170  case 'O':
171  optimize = true;
172  break;
173 #ifdef MOAB_HAVE_MPI
174  case 'p':
175  parallel = true;
176  resolve_shared = true;
177  if( argv[i][1] == '1' ) exchange_ghosts = true;
178  break;
179 #endif
180  default:
181  ++i;
182  switch( argv[i - 1][1] )
183  {
184  // case 'O': read_opts.push_back(argv[i]); break;
185  default:
186  std::cerr << "Invalid option: " << argv[i] << std::endl;
187  }
188  }
189  ++i;
190  }
191  // do file names
192  else
193  {
194  infile = argv[i];
195  ++i;
196  }
197  }
198 
199  if( infile.empty() && !printusage ) print_usage( argv[0], std::cerr );
200 
201  if( !parselevels ) exit( USAGE_ERROR );
202 
203 #ifdef MOAB_HAVE_MPI
204  parallel = true;
205  resolve_shared = true;
206 #endif
207 
209 
210  Core* moab = new Core;
211  Interface* mb = (Interface*)moab;
212  EntityHandle fileset;
213 
214  // Create a fileset
215  error = mb->create_meshset( MESHSET_SET, fileset );
216  MB_CHK_ERR( error );
217 
218  // Set the read options for parallel file loading
219  std::vector< std::string > read_opts, write_opts;
220  std::string read_options, write_options;
221 
222  if( parallel && size > 1 )
223  {
224  read_opts.push_back( "PARALLEL=READ_PART" );
225  read_opts.push_back( "PARTITION=PARALLEL_PARTITION" );
226  if( resolve_shared ) read_opts.push_back( "PARALLEL_RESOLVE_SHARED_ENTS" );
227  if( exchange_ghosts ) read_opts.push_back( "PARALLEL_GHOSTS=3.0.1" );
228 
229  /* if (output)
230  {
231  write_opts.push_back(";;PARALLEL=WRITE_PART");
232  std::cout<<"Here"<<std::endl;
233  }*/
234  }
235 
236  if( !make_opts_string( read_opts, read_options ) || !make_opts_string( write_opts, write_options ) )
237  {
238 #ifdef MOAB_HAVE_MPI
239  MPI_Finalize();
240 #endif
241  return USAGE_ERROR;
242  }
243 
244  // Load file
245  std::cout << "READ OPTS=" << (char*)read_options.c_str() << std::endl;
246  error = mb->load_file( infile.c_str(), &fileset, read_options.c_str() );
247  MB_CHK_ERR( error );
248 
249  // Create the nestedrefine instance
250 
251 #ifdef MOAB_HAVE_MPI
252  ParallelComm* pc = new ParallelComm( mb, MPI_COMM_WORLD );
253  NestedRefine* uref = new NestedRefine( moab, pc, fileset );
254 #else
255  NestedRefine* uref = new NestedRefine( moab );
256 #endif
257 
258  std::vector< EntityHandle > lsets;
259 
260  // std::cout<<"Read input file"<<std::endl;
261 
262  if( only_quality )
263  {
264  double vmax;
265  error = get_max_volume( *moab, fileset, dim, vmax );
266  MB_CHK_ERR( error );
267 #ifdef MOAB_HAVE_MPI
268  int rank = 0;
269  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
270  if( rank == 0 ) std::cout << "Maximum global volume = " << vmax << std::endl;
271 #else
272  std::cout << "Maximum volume = " << vmax << std::endl;
273 #endif
274  exit( SUCCESS );
275  }
276 
277  // If a volume constraint is given, find an optimal degree sequence to reach the desired volume
278  // constraint.
279  if( qc_vol )
280  {
281  error = get_degree_seq( *moab, fileset, dim, cvol, num_levels, level_degrees );
282  MB_CHK_ERR( error );
283 
284  if( dim == 0 ) print_usage( argv[0], std::cerr );
285  }
286 
287  if( num_levels == 0 ) num_levels = 1;
288 
289  int* ldeg = new int[num_levels];
290  // std::cout<<"level_degrees.size = "<<level_degrees.size()<<std::endl;
291  if( level_degrees.empty() )
292  {
293  for( int l = 0; l < num_levels; l++ )
294  ldeg[l] = 2;
295  }
296  else
297  {
298  for( int l = 0; l < num_levels; l++ )
299  ldeg[l] = level_degrees[l];
300  }
301 
302  std::cout << "Starting hierarchy generation" << std::endl;
303 
304  std::cout << "opt = " << optimize << std::endl;
305 
306  error = uref->generate_mesh_hierarchy( num_levels, ldeg, lsets, optimize );
307  MB_CHK_ERR( error );
308 
309  if( print_times )
310  {
311  std::cout << "Finished hierarchy generation in " << uref->timeall.tm_total << " secs" << std::endl;
312  if( parallel )
313  {
314  std::cout << "Time taken for refinement " << uref->timeall.tm_refine << " secs" << std::endl;
315  std::cout << "Time taken for resolving shared interface " << uref->timeall.tm_resolve << " secs"
316  << std::endl;
317  }
318  }
319  else
320  std::cout << "Finished hierarchy generation " << std::endl;
321 
322  if( print_size )
323  {
324  Range all_ents, ents[4];
325  error = mb->get_entities_by_handle( fileset, all_ents );
326  MB_CHK_ERR( error );
327 
328  for( int k = 0; k < 4; k++ )
329  ents[k] = all_ents.subset_by_dimension( k );
330 
331  std::cout << std::endl;
332  if( qc_vol )
333  {
334  double volume;
335  error = get_max_volume( *moab, fileset, dim, volume );
336  MB_CHK_ERR( error );
337  std::cout << "Mesh size for level 0"
338  << " :: nverts = " << ents[0].size() << ", nedges = " << ents[1].size()
339  << ", nfaces = " << ents[2].size() << ", ncells = " << ents[3].size() << " :: Vmax = " << volume
340  << std::endl;
341  }
342  else
343  std::cout << "Mesh size for level 0"
344  << " :: nverts = " << ents[0].size() << ", nedges = " << ents[1].size()
345  << ", nfaces = " << ents[2].size() << ", ncells = " << ents[3].size() << std::endl;
346 
347  for( int l = 0; l < num_levels; l++ )
348  {
349  all_ents.clear();
350  ents[0].clear();
351  ents[1].clear();
352  ents[2].clear();
353  ents[3].clear();
354  error = mb->get_entities_by_handle( lsets[l + 1], all_ents );
355  MB_CHK_ERR( error );
356 
357  for( int k = 0; k < 4; k++ )
358  ents[k] = all_ents.subset_by_dimension( k );
359 
360  // std::cout<<std::endl;
361 
362  if( qc_vol )
363  {
364  double volume;
365  error = get_max_volume( *moab, lsets[l + 1], dim, volume );
366  MB_CHK_ERR( error );
367  std::cout << "Mesh size for level " << l + 1 << " :: nverts = " << ents[0].size()
368  << ", nedges = " << ents[1].size() << ", nfaces = " << ents[2].size()
369  << ", ncells = " << ents[3].size() << " :: Vmax = " << volume << std::endl;
370  }
371  else
372  std::cout << "Mesh size for level " << l + 1 << " :: nverts = " << ents[0].size()
373  << ", nedges = " << ents[1].size() << ", nfaces = " << ents[2].size()
374  << ", ncells = " << ents[3].size() << std::endl;
375  }
376  }
377 
378  if( output )
379  {
380  for( int l = 0; l < num_levels; l++ )
381  {
382  std::string::size_type idx1 = infile.find_last_of( "\\/" );
383  std::string::size_type idx2 = infile.find_last_of( "." );
384  std::string file = infile.substr( idx1 + 1, idx2 - idx1 - 1 );
385  std::stringstream out;
386  if( parallel )
387  out << "_ML_" << l + 1 << ".h5m";
388  else
389  out << "_ML_" << l + 1 << ".vtk";
390  file = file + out.str();
391  const char* output_file = file.c_str();
392 #ifdef MOAB_HAVE_MPI
393  error = mb->write_file( output_file, 0, ";;PARALLEL=WRITE_PART", &lsets[l + 1], 1 );
394  MB_CHK_ERR( error );
395 #else
396  error = mb->write_file( output_file, 0, NULL, &lsets[l + 1], 1 );
397  MB_CHK_ERR( error );
398 #endif
399  // const char* output_file = file.c_str();
400  // error = mb->write_file(output_file, 0, write_options.c_str(), &lsets[l+1],
401  // 1);MB_CHK_ERR(error);
402  // mb->list_entity(lsets[l+1]);
403  // mb->write_file(output_file, 0, "PARALLEL=WRITE_PART", &lsets[l+1], 1);
404  }
405  }
406 
407  delete uref;
408 #ifdef MOAB_HAVE_MPI
409  delete pc;
410 #endif
411 
412  delete[] ldeg;
413  delete moab;
414 
415 #ifdef MOAB_HAVE_MPI
416  MPI_Finalize();
417 #endif
418 
419  exit( SUCCESS );
420 }
421 
423  EntityHandle fileset,
424  int dim,
425  double desired_vol,
426  int& num_levels,
427  std::vector< int >& level_degs )
428 {
429  // Find max volume
430  double vmax_global;
431  ErrorCode error = get_max_volume( mb, fileset, dim, vmax_global );
432  MB_CHK_ERR( error );
433 
434  int init_nl = num_levels;
435  num_levels = 0;
436  level_degs.clear();
437 
438  // Find a sequence that reduces the maximum volume to desired.
439  // desired_vol should be a fraction or absolute value ?
440 
441  // double remV = vmax_global*desired_vol/dim;
442  double Vdesired = desired_vol;
443  double try_x;
444  double remV = vmax_global;
445  int degs[3][3] = { { 5, 3, 2 }, { 25, 9, 4 }, { 0, 27, 8 } };
446 
447  if( dim == 1 || dim == 2 )
448  {
449  while( remV - Vdesired >= 0 )
450  {
451  try_x = degs[dim - 1][0];
452  if( ( remV / try_x - Vdesired ) >= 0 )
453  {
454  level_degs.push_back( 5 );
455  num_levels += 1;
456  remV /= try_x;
457  }
458  else
459  {
460  try_x = degs[dim - 1][1];
461  if( ( remV / try_x - Vdesired ) >= 0 )
462  {
463  level_degs.push_back( 3 );
464  num_levels += 1;
465  remV /= try_x;
466  }
467  else
468  {
469  try_x = degs[dim - 1][2];
470  if( ( remV / try_x - Vdesired ) >= 0 )
471  {
472  level_degs.push_back( 2 );
473  num_levels += 1;
474  remV /= try_x;
475  }
476  else
477  break;
478  }
479  }
480  }
481  }
482  else
483  {
484  while( remV - Vdesired >= 0 )
485  {
486  try_x = degs[dim - 1][1];
487  if( ( remV / try_x - Vdesired ) >= 0 )
488  {
489  level_degs.push_back( 3 );
490  num_levels += 1;
491  remV /= try_x;
492  }
493  else
494  {
495  try_x = degs[dim - 1][2];
496  if( ( remV / try_x - Vdesired ) >= 0 )
497  {
498  level_degs.push_back( 2 );
499  num_levels += 1;
500  remV /= try_x;
501  }
502  else
503  break;
504  }
505  }
506  }
507 
508  if( init_nl != 0 && init_nl < num_levels )
509  {
510  for( int i = level_degs.size(); i >= init_nl; i-- )
511  level_degs.pop_back();
512  num_levels = init_nl;
513  }
514 
515  return MB_SUCCESS;
516 }
517 
518 ErrorCode get_max_volume( Core& mb, EntityHandle fileset, int dim, double& vmax )
519 {
521  VerdictWrapper vw( &mb );
522  QualityType q;
523 
524  switch( dim )
525  {
526  case 1:
527  q = MB_LENGTH;
528  break;
529  case 2:
530  q = MB_AREA;
531  break;
532  case 3:
533  q = MB_VOLUME;
534  break;
535  default:
536  return MB_FAILURE;
537  }
538 
539  // Get all entities of the highest dimension which is passed as a command line argument.
540  Range allents, owned;
541  error = mb.get_entities_by_handle( fileset, allents );
542  MB_CHK_ERR( error );
543  owned = allents.subset_by_dimension( dim );
544  MB_CHK_ERR( error );
545 
546  // Get all owned entities
547 #ifdef MOAB_HAVE_MPI
548  int size = 1;
549  MPI_Comm_size( MPI_COMM_WORLD, &size );
550  int mpi_err;
551  if( size > 1 )
552  {
553  // filter the entities not owned, so we do not process them more than once
555  Range current = owned;
556  owned.clear();
557  error = pcomm->filter_pstatus( current, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1, &owned );
558  if( error != MB_SUCCESS )
559  {
560  MPI_Finalize();
561  return MB_FAILURE;
562  }
563  }
564 #endif
565 
566  double vmax_local = 0;
567  // Find the maximum volume of an entity in the owned mesh
568  for( Range::iterator it = owned.begin(); it != owned.end(); it++ )
569  {
570  double volume;
571  error = vw.quality_measure( *it, q, volume );
572  MB_CHK_ERR( error );
573  if( volume > vmax_local ) vmax_local = volume;
574  }
575 
576  // Get the global maximum
577  double vmax_global = vmax_local;
578 #ifdef MOAB_HAVE_MPI
579  mpi_err = MPI_Reduce( &vmax_local, &vmax_global, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD );
580  if( mpi_err )
581  {
582  MPI_Finalize();
583  return MB_FAILURE;
584  }
585 #endif
586 
587  vmax = vmax_global;
588 
589  return MB_SUCCESS;
590 }
591 
592 bool parse_id_list( const char* string, int dim, int nval, std::vector< int >& results )
593 {
594  bool okay = true;
595  char* mystr = strdup( string );
596  for( const char* ptr = strtok( mystr, "," ); ptr; ptr = strtok( 0, "," ) )
597  {
598  char* endptr;
599  int val = strtol( ptr, &endptr, 0 );
600 
601  if( dim == 1 || dim == 2 )
602  {
603  if( val != 2 && val != 3 && val != 5 )
604  {
605  std::cerr << "Not a valid degree for the passed dimension" << std::endl;
606  okay = false;
607  break;
608  }
609  }
610  else if( dim == 3 )
611  {
612  if( val != 2 && val != 3 )
613  {
614  std::cerr << "Not a valid degree for the passed dimension" << std::endl;
615  okay = false;
616  break;
617  }
618  }
619 
620  if( endptr == ptr || val <= 0 )
621  {
622  std::cerr << "Not a valid id: " << ptr << std::endl;
623  okay = false;
624  break;
625  }
626 
627  results.push_back( val );
628  }
629 
630  if( (int)results.size() < nval )
631  {
632  for( int i = results.size(); i <= nval - 1; i++ )
633  results.push_back( results[0] );
634  }
635  else if( (int)results.size() > nval )
636  {
637  for( int i = results.size(); i > nval; i-- )
638  results.pop_back();
639  }
640 
641  free( mystr );
642  return okay;
643 }
644 
645 bool make_opts_string( std::vector< std::string > options, std::string& opts )
646 {
647  opts.clear();
648  if( options.empty() ) return true;
649 
650  // choose a separator character
651  std::vector< std::string >::const_iterator i;
652  char separator = '\0';
653  const char* alt_separators = ";+,:\t\n";
654  for( const char* sep_ptr = alt_separators; *sep_ptr; ++sep_ptr )
655  {
656  bool seen = false;
657  for( i = options.begin(); i != options.end(); ++i )
658  if( i->find( *sep_ptr, 0 ) != std::string::npos )
659  {
660  seen = true;
661  break;
662  }
663  if( !seen )
664  {
665  separator = *sep_ptr;
666  break;
667  }
668  }
669  if( !separator )
670  {
671  std::cerr << "Error: cannot find separator character for options string" << std::endl;
672  return false;
673  }
674  if( separator != ';' )
675  {
676  opts = ";";
677  opts += separator;
678  }
679 
680  // concatenate options
681  i = options.begin();
682  opts += *i;
683  for( ++i; i != options.end(); ++i )
684  {
685  opts += separator;
686  opts += *i;
687  }
688 
689  return true;
690 }