MOAB: Mesh Oriented datABase  (version 5.5.0)
kd_tree_tool.cpp
Go to the documentation of this file.
1 #include "moab/Core.hpp"
2 #include "moab/CartVect.hpp"
4 #include "moab/GeomUtil.hpp"
5 #include "Internals.hpp"
6 #include "moab/Range.hpp"
7 #include "moab/FileOptions.hpp"
8 #include <iostream>
9 #include <iomanip>
10 #include <cstdio>
11 #include <limits>
12 #include <sstream>
13 #include <cstdlib>
14 #include <ctime>
15 #if !defined( _MSC_VER ) && !defined( __MINGW32__ )
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #endif
20 
21 using namespace moab;
22 
23 const int MAX_TAG_VALUE = 32; // maximum value to use when tagging entities with tree cell number
24 const char* TAG_NAME = "TREE_CELL";
25 
26 std::string clock_to_string( clock_t t );
27 std::string mem_to_string( unsigned long mem );
28 
29 void build_tree( Interface* interface, const Range& elems, FileOptions& opts );
30 void build_grid( Interface* iface, const double dims[3] );
31 void delete_existing_tree( Interface* interface );
32 void print_stats( Interface* interface );
33 void tag_elements( Interface* interface );
34 void tag_vertices( Interface* interface );
35 void write_tree_blocks( Interface* interface, const char* file );
36 
37 static void usage( bool err = true )
38 {
39  std::ostream& s = err ? std::cerr : std::cout;
40  s << "kd_tree_tool [-d <int>] [-2|-3] [-n <int>] [-u|-p|-m|-v] [-N <int>] [-s|-S] <input file> "
41  "<output file>"
42  << std::endl
43  << "kd_tree_tool [-d <int>] -G <dims> [-s|-S] <output file>" << std::endl
44  << "kd_tree_tool [-h]" << std::endl;
45  if( !err )
46  {
47  s << "Tool to build adaptive kd-Tree" << std::endl;
48  s << " -d <int> Specify maximum depth for tree. Default: 30" << std::endl
49  << " -n <int> Specify maximum entities per leaf. Default: 6" << std::endl
50  << " -2 Build tree from surface elements. Default: yes" << std::endl
51  << " -3 Build tree from volume elements. Default: yes" << std::endl
52  << " -u Use 'SUBDIVISION' scheme for tree construction" << std::endl
53  << " -p Use 'SUBDIVISION_SNAP' tree construction algorithm." << std::endl
54  << " -m Use 'VERTEX_MEDIAN' tree construction algorithm." << std::endl
55  << " -v Use 'VERTEX_SAMPLE' tree construction algorithm." << std::endl
56  << " -N <int> Specify candidate split planes per axis. Default: 3" << std::endl
57  << " -t Tag elements will tree cell number." << std::endl
58  << " -T Write tree boxes to file." << std::endl
59  << " -G <dims> Generate grid - no input elements. Dims must be " << std::endl
60  << " HxWxD or H." << std::endl
61  << " -s Use range-based sets for tree nodes" << std::endl
62  << " -S Use vector-based sets for tree nodes" << std::endl
63  << std::endl;
64  }
65  exit( err );
66 }
67 
68 /*
69 #if !defined(_MSC_VER) && !defined(__MINGW32__)
70 static void memory_use( unsigned long& vsize, unsigned long& rss )
71 {
72  char buffer[512];
73  int filp = open( "/proc/self/stat", O_RDONLY );
74  ssize_t r = read( filp, buffer, sizeof(buffer)-1 );
75  close( filp );
76  if (r < 0) r = 0;
77  vsize = rss = 0;
78  buffer[r] = '\0';
79  sscanf( buffer, "%*d %*s %*c " // pid command state
80  "%*d %*d " // ppid pgrp
81  "%*d %*d %*d " // session tty_nr tpgid
82  "%*u " // flags
83  "%*u %*u %*u %*u " // minflt cminflt majflt cmajflt
84  "%*u %*u %*d %*d " // utime stime cutime cstime
85  "%*d %*d %*d " // priority nice (unused)
86  "%*d %*u " // itrealval starttime
87  "%lu %lu", &vsize, &rss );
88  rss *= getpagesize();
89 }
90 #else
91 static void memory_use( unsigned long& vsize, unsigned long& rss )
92  { vsize = rss = 0; }
93 #endif
94 */
95 
96 static int parseint( int& i, int argc, char* argv[] )
97 {
98  char* end;
99  ++i;
100  if( i == argc )
101  {
102  std::cerr << "Expected value following '" << argv[i - 1] << "'" << std::endl;
103  usage();
104  }
105 
106  int result = strtol( argv[i], &end, 0 );
107  if( result < 0 || *end )
108  {
109  std::cerr << "Expected positive integer following '" << argv[i - 1] << "'" << std::endl;
110  usage();
111  }
112 
113  return result;
114 }
115 
116 static void parsedims( int& i, int argc, char* argv[], double dims[3] )
117 {
118  char* end;
119  ++i;
120  if( i == argc )
121  {
122  std::cerr << "Expected value following '" << argv[i - 1] << "'" << std::endl;
123  usage();
124  }
125 
126  dims[0] = strtod( argv[i], &end );
127  if( *end == '\0' )
128  {
129  dims[2] = dims[1] = dims[0];
130  return;
131  }
132  else if( *end != 'x' && *end != 'X' )
133  goto error;
134 
135  ++end;
136  dims[1] = strtod( end, &end );
137  if( *end == '\0' )
138  {
139  dims[2] = dims[1];
140  return;
141  }
142  else if( *end != 'x' && *end != 'X' )
143  goto error;
144 
145  ++end;
146  dims[2] = strtod( end, &end );
147  if( *end != '\0' ) goto error;
148 
149  return;
150 error:
151  std::cerr << "Invaild dimension specification." << std::endl;
152  usage();
153 }
154 
155 int main( int argc, char* argv[] )
156 {
157  bool surf_elems = false, vol_elems = false;
158  const char* input_file = 0;
159  const char* output_file = 0;
160  const char* tree_file = 0;
161  std::ostringstream options;
162  bool tag_elems = false;
163  clock_t load_time, build_time, stat_time, tag_time, write_time, block_time;
164  bool make_grid = false;
165  double dims[3];
166 
167  for( int i = 1; i < argc; ++i )
168  {
169  if( argv[i][0] != '-' )
170  {
171  if( !input_file )
172  input_file = argv[i];
173  else if( !output_file )
174  output_file = argv[i];
175  else
176  usage();
177  continue;
178  }
179 
180  if( !argv[i][1] || argv[i][2] ) usage();
181 
182  switch( argv[i][1] )
183  {
184  case '2':
185  surf_elems = true;
186  break;
187  case '3':
188  vol_elems = true;
189  break;
190  case 'S':
191  options << "MESHSET_FLAGS=" << MESHSET_ORDERED << ";";
192  break;
193  case 's':
194  break;
195  case 'd':
196  options << "MAX_DEPTH=" << parseint( i, argc, argv ) << ";";
197  break;
198  case 'n':
199  options << "MAX_PER_LEAF=" << parseint( i, argc, argv ) << ";";
200  break;
201  case 'u':
202  options << "CANDIDATE_PLANE_SET=" << AdaptiveKDTree::SUBDIVISION << ";";
203  break;
204  case 'p':
205  options << "CANDIDATE_PLANE_SET=" << AdaptiveKDTree::SUBDIVISION_SNAP << ";";
206  break;
207  case 'm':
208  options << "CANDIDATE_PLANE_SET=" << AdaptiveKDTree::VERTEX_MEDIAN << ";";
209  break;
210  case 'v':
211  options << "CANDIDATE_PLANE_SET=" << AdaptiveKDTree::VERTEX_SAMPLE << ";";
212  break;
213  case 'N':
214  options << "SPLITS_PER_DIR=" << parseint( i, argc, argv ) << ";";
215  break;
216  case 't':
217  tag_elems = true;
218  break;
219  case 'T':
220  tree_file = argv[++i];
221  break;
222  case 'G':
223  make_grid = true;
224  parsedims( i, argc, argv, dims );
225  break;
226  case 'h':
227  usage( false );
228  break;
229  default:
230  usage();
231  }
232  }
233 
234  // this test relies on not cleaning up trees
235  options << "CLEAN_UP=false;";
236 
237  if( make_grid != !output_file ) usage();
238 
239  // default to both
240  if( !surf_elems && !vol_elems ) surf_elems = vol_elems = true;
241 
242  ErrorCode rval;
243  Core moab_core;
244  Interface* interface = &moab_core;
245  FileOptions opts( options.str().c_str() );
246 
247  if( make_grid )
248  {
249  load_time = 0;
250  output_file = input_file;
251  input_file = 0;
252  build_time = clock();
253  build_grid( interface, dims );
254  build_time = clock() - build_time;
255  }
256  else
257  {
258  load_time = clock();
259  rval = interface->load_mesh( input_file );
260  if( MB_SUCCESS != rval )
261  {
262  std::cerr << "Error reading file: " << input_file << std::endl;
263  exit( 2 );
264  }
265  load_time = clock() - load_time;
266 
267  delete_existing_tree( interface );
268 
269  std::cout << "Building tree..." << std::endl;
270  build_time = clock();
271  Range elems;
272  if( !surf_elems )
273  {
274  interface->get_entities_by_dimension( 0, 3, elems );
275  }
276  else
277  {
278  interface->get_entities_by_dimension( 0, 2, elems );
279  if( vol_elems )
280  {
281  Range tmp;
282  interface->get_entities_by_dimension( 0, 3, tmp );
283  elems.merge( tmp );
284  }
285  }
286 
287  build_tree( interface, elems, opts );
288  build_time = clock() - build_time;
289  }
290 
291  std::cout << "Calculating stats..." << std::endl;
292  AdaptiveKDTree tool( interface );
293  Range range;
294  tool.find_all_trees( range );
295  if( range.size() != 1 )
296  {
297  if( range.empty() )
298  std::cerr << "Internal error: Failed to retreive tree." << std::endl;
299  else
300  std::cerr << "Internal error: Multiple tree roots." << std::endl;
301  exit( 5 );
302  }
303  tool.print();
304 
305  stat_time = clock() - build_time;
306 
307  if( tag_elems )
308  {
309  std::cout << "Tagging tree..." << std::endl;
310  tag_elements( interface );
311  tag_vertices( interface );
312  }
313  tag_time = clock() - stat_time;
314 
315  std::cout << "Writing file... ";
316  std::cout.flush();
317  rval = interface->write_mesh( output_file );
318  if( MB_SUCCESS != rval )
319  {
320  std::cerr << "Error writing file: " << output_file << std::endl;
321  exit( 3 );
322  }
323  write_time = clock() - tag_time;
324  std::cout << "Wrote " << output_file << std::endl;
325 
326  if( tree_file )
327  {
328  std::cout << "Writing tree block rep...";
329  std::cout.flush();
330  write_tree_blocks( interface, tree_file );
331  std::cout << "Wrote " << tree_file << std::endl;
332  }
333  block_time = clock() - write_time;
334 
335  std::cout << "Times: "
336  << " Load"
337  << " Build"
338  << " Stats"
339  << " Write";
340  if( tag_elems ) std::cout << "Tag Sets";
341  if( tree_file ) std::cout << "Block ";
342  std::cout << std::endl;
343 
344  std::cout << " " << std::setw( 8 ) << clock_to_string( load_time ) << std::setw( 8 )
345  << clock_to_string( build_time ) << std::setw( 8 ) << clock_to_string( stat_time ) << std::setw( 8 )
346  << clock_to_string( write_time );
347  if( tag_elems ) std::cout << std::setw( 8 ) << clock_to_string( tag_time );
348  if( tree_file ) std::cout << std::setw( 8 ) << clock_to_string( block_time );
349  std::cout << std::endl;
350 
351  return 0;
352 }
353 
354 void delete_existing_tree( Interface* interface )
355 {
356  Range trees;
357  AdaptiveKDTree tool( interface );
358 
359  tool.find_all_trees( trees );
360  if( !trees.empty() ) std::cout << "Destroying existing tree(s) contained in file" << std::endl;
361  for( Range::iterator i = trees.begin(); i != trees.end(); ++i )
362  tool.reset_tree();
363 
364  trees.clear();
365  tool.find_all_trees( trees );
366  if( !trees.empty() )
367  {
368  std::cerr << "Failed to destroy existing trees. Aborting" << std::endl;
369  exit( 5 );
370  }
371 }
372 
373 void build_tree( Interface* interface, const Range& elems, FileOptions& opts )
374 {
375  EntityHandle root = 0;
376 
377  if( elems.empty() )
378  {
379  std::cerr << "No elements from which to build tree." << std::endl;
380  exit( 4 );
381  }
382 
383  AdaptiveKDTree tool( interface, elems, &root, &opts );
384 }
385 
386 void build_grid( Interface* interface, const double dims[3] )
387 {
388  ErrorCode rval;
389  EntityHandle root = 0;
390  AdaptiveKDTree tool( interface );
391  AdaptiveKDTreeIter iter;
392  AdaptiveKDTree::Plane plane;
393 
394  double min[3] = { -0.5 * dims[0], -0.5 * dims[1], -0.5 * dims[2] };
395  double max[3] = { 0.5 * dims[0], 0.5 * dims[1], 0.5 * dims[2] };
396  rval = tool.create_root( min, max, root );
397  if( MB_SUCCESS != rval || !root )
398  {
399  std::cerr << "Failed to create tree root." << std::endl;
400  exit( 4 );
401  }
402 
403  rval = tool.get_tree_iterator( root, iter );
404  if( MB_SUCCESS != rval )
405  {
406  std::cerr << "Failed to get tree iterator." << std::endl;
407  }
408 
409  do
410  {
411  while( iter.depth() < tool.get_max_depth() )
412  {
413  plane.norm = iter.depth() % 3;
414  plane.coord = 0.5 * ( iter.box_min()[plane.norm] + iter.box_max()[plane.norm] );
415  rval = tool.split_leaf( iter, plane );
416  if( MB_SUCCESS != rval )
417  {
418  std::cerr << "Failed to split tree leaf at depth " << iter.depth() << std::endl;
419  exit( 4 );
420  }
421  }
422  } while( ( rval = iter.step() ) == MB_SUCCESS );
423 
424  if( rval != MB_ENTITY_NOT_FOUND )
425  {
426  std::cerr << "Error stepping KDTree iterator." << std::endl;
427  exit( 4 );
428  }
429 }
430 
431 std::string clock_to_string( clock_t t )
432 {
433  char unit[5] = "s";
434  char buffer[256];
435  double dt = t / (double)CLOCKS_PER_SEC;
436  // if (dt > 300) {
437  // dt /= 60;
438  // strcpy( unit, "min" );
439  //}
440  // if (dt > 300) {
441  // dt /= 60;
442  // strcpy( unit, "hr" );
443  //}
444  // if (dt > 100) {
445  // dt /= 24;
446  // strcpy( unit, "days" );
447  //}
448  sprintf( buffer, "%0.2f%s", dt, unit );
449  return buffer;
450 }
451 
452 static int hash_handle( EntityHandle handle )
453 {
454  EntityID h = ID_FROM_HANDLE( handle );
455  return (int)( ( h * 13 + 7 ) % MAX_TAG_VALUE ) + 1;
456 }
457 
459 {
460  EntityHandle root;
461  Range range;
462  AdaptiveKDTree tool( moab );
463 
464  tool.find_all_trees( range );
465  if( range.size() != 1 )
466  {
467  if( range.empty() )
468  std::cerr << "Internal error: Failed to retreive tree." << std::endl;
469  else
470  std::cerr << "Internal error: Multiple tree roots." << std::endl;
471  exit( 5 );
472  }
473 
474  root = *range.begin();
475  range.clear();
476 
477  Tag tag;
478  int zero = 0;
479  moab->tag_get_handle( TAG_NAME, 1, MB_TYPE_INTEGER, tag, MB_TAG_DENSE | MB_TAG_CREAT, &zero );
480 
481  AdaptiveKDTreeIter iter;
482  tool.get_tree_iterator( root, iter );
483 
484  std::vector< int > tag_vals;
485  do
486  {
487  range.clear();
488  moab->get_entities_by_handle( iter.handle(), range );
489  tag_vals.clear();
490  tag_vals.resize( range.size(), hash_handle( iter.handle() ) );
491  moab->tag_set_data( tag, range, &tag_vals[0] );
492  } while( MB_SUCCESS == iter.step() );
493 }
494 
496 {
497  EntityHandle root;
498  Range range;
499  AdaptiveKDTree tool( moab );
500 
501  tool.find_all_trees( range );
502  if( range.size() != 1 )
503  {
504  if( range.empty() )
505  std::cerr << "Internal error: Failed to retreive tree." << std::endl;
506  else
507  std::cerr << "Internal error: Multiple tree roots." << std::endl;
508  exit( 5 );
509  }
510 
511  root = *range.begin();
512  range.clear();
513 
514  Tag tag;
515  int zero = 0;
516  moab->tag_get_handle( TAG_NAME, 1, MB_TYPE_INTEGER, tag, MB_TAG_DENSE | MB_TAG_CREAT, &zero );
517 
518  AdaptiveKDTreeIter iter;
519  tool.get_tree_iterator( root, iter );
520 
521  do
522  {
523  range.clear();
524  moab->get_entities_by_handle( iter.handle(), range );
525 
526  int tag_val = hash_handle( iter.handle() );
527  Range verts;
528  moab->get_adjacencies( range, 0, false, verts, Interface::UNION );
529  for( Range::iterator i = verts.begin(); i != verts.end(); ++i )
530  {
531  CartVect coords;
532  moab->get_coords( &*i, 1, coords.array() );
533  if( GeomUtil::box_point_overlap( CartVect( iter.box_min() ), CartVect( iter.box_max() ), coords, 1e-7 ) )
534  moab->tag_set_data( tag, &*i, 1, &tag_val );
535  }
536  } while( MB_SUCCESS == iter.step() );
537 }
538 
539 void write_tree_blocks( Interface* interface, const char* file )
540 {
541  EntityHandle root;
542  Range range;
543  AdaptiveKDTree tool( interface );
544 
545  tool.find_all_trees( range );
546  if( range.size() != 1 )
547  {
548  if( range.empty() )
549  std::cerr << "Internal error: Failed to retreive tree." << std::endl;
550  else
551  std::cerr << "Internal error: Multiple tree roots." << std::endl;
552  exit( 5 );
553  }
554 
555  root = *range.begin();
556  range.clear();
557 
558  Core moab2;
559  Tag tag;
560  int zero = 0;
562 
563  AdaptiveKDTreeIter iter;
564  tool.get_tree_iterator( root, iter );
565 
566  do
567  {
568  double x1 = iter.box_min()[0];
569  double y1 = iter.box_min()[1];
570  double z1 = iter.box_min()[2];
571  double x2 = iter.box_max()[0];
572  double y2 = iter.box_max()[1];
573  double z2 = iter.box_max()[2];
574  double coords[24] = { x1, y1, z1, x2, y1, z1, x2, y2, z1, x1, y2, z1,
575  x1, y1, z2, x2, y1, z2, x2, y2, z2, x1, y2, z2 };
576  EntityHandle verts[8], elem;
577  for( int i = 0; i < 8; ++i )
578  moab2.create_vertex( coords + 3 * i, verts[i] );
579  moab2.create_element( MBHEX, verts, 8, elem );
580  int tag_val = hash_handle( iter.handle() );
581  moab2.tag_set_data( tag, &elem, 1, &tag_val );
582  } while( MB_SUCCESS == iter.step() );
583 
584  moab2.write_mesh( file );
585 }