MOAB: Mesh Oriented datABase  (version 5.5.0)
perftool.cpp
Go to the documentation of this file.
1 /**
2  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3  * storing and accessing finite element mesh data.
4  *
5  * Copyright 2004 Sandia Corporation. Under the terms of Contract
6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7  * retains certain rights in this software.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  */
15 
16 // MOAB performance tests building mapped mesh with nodes and
17 // hexes created one at a time. This also creates the node to hex adjacencies.
18 
19 #include <cmath>
20 #include <iostream>
21 #include <iomanip>
22 #include <ctime>
23 #include <cassert>
24 #include <list>
25 #include "moab/Core.hpp"
26 #include "moab/Skinner.hpp"
27 #include "moab/ReadUtilIface.hpp"
28 
29 using namespace moab;
30 
31 double LENGTH = 1.0;
32 const int DEFAULT_INTERVALS = 50;
33 
34 void create_regular_mesh( int interval, int dimension );
35 void skin_common( int interval, int dim, int blocks, bool use_adj );
36 void skin( int intervals, int dim, int num )
37 {
38  std::cout << "Skinning w/out adjacencies:" << std::endl;
39  skin_common( intervals, dim, num, false );
40 }
41 void skin_adj( int intervals, int dim, int num )
42 {
43  std::cout << "Skinning with adjacencies:" << std::endl;
44  skin_common( intervals, dim, num, true );
45 }
46 
47 void tag_time( TagType storage, bool direct, int intervals, int dim, int blocks );
48 
49 void dense_tag( int intervals, int dim, int blocks )
50 {
51  std::cout << "Dense Tag Time:";
52  tag_time( MB_TAG_DENSE, false, intervals, dim, blocks );
53 }
54 void sparse_tag( int intervals, int dim, int blocks )
55 {
56  std::cout << "Sparse Tag Time:";
57  tag_time( MB_TAG_SPARSE, false, intervals, dim, blocks );
58 }
59 void direct_tag( int intervals, int dim, int blocks )
60 {
61  std::cout << "Direct Tag Time:";
62  tag_time( MB_TAG_DENSE, true, intervals, dim, blocks );
63 }
64 
65 typedef void ( *test_func_t )( int, int, int );
66 const struct
67 {
68  std::string testName;
70  std::string testDesc;
71 } TestList[] = {
72  { "skin", &skin, "Test time to get skin mesh w/out adjacencies" },
73  { "skin_adj", &skin_adj, "Test time to get skin mesh with adjacencies" },
74  { "sparse", &sparse_tag, "Sparse tag data manipulation" },
75  { "dense", &dense_tag, "Dense tag data manipulation" },
76  { "direct", &direct_tag, "Dense tag data manipulation using direct data access" },
77 };
78 const int TestListSize = sizeof( TestList ) / sizeof( TestList[0] );
79 
80 void usage( const char* argv0, bool error = true )
81 {
82  std::ostream& str = error ? std::cerr : std::cout;
83  str << "Usage: " << argv0
84  << " [-i <ints_per_side>] [-d <dimension>] [-n <test_specifc_int>] <test_name> "
85  "[<test_name2> ...]"
86  << std::endl;
87  str << " " << argv0 << " [-h|-l]" << std::endl;
88  if( error ) return;
89  str << " -i : specify interverals per side (num hex = ints^3, default: " << DEFAULT_INTERVALS << std::endl;
90  str << " -d : specify element dimension, default: 3" << std::endl;
91  str << " -n : specify an integer value that for which the meaning is test-specific" << std::endl;
92  str << " -h : print this help text." << std::endl;
93  str << " -l : list available tests" << std::endl;
94 }
95 
96 void list_tests()
97 {
98  unsigned max_test_name = 0, max_test_desc = 0;
99  for( int i = 0; i < TestListSize; ++i )
100  {
101  if( TestList[i].testName.size() > max_test_name ) max_test_name = TestList[i].testName.size();
102  if( TestList[i].testDesc.size() > max_test_desc ) max_test_desc = TestList[i].testDesc.size();
103  }
104  std::cout << std::setw( max_test_name ) << "NAME"
105  << " " << std::setw( max_test_desc ) << std::left << "DESCRIPTION" << std::endl
106  << std::setfill( '-' ) << std::setw( max_test_name ) << ""
107  << " " << std::setfill( '-' ) << std::setw( max_test_desc ) << "" << std::setfill( ' ' ) << std::endl;
108  for( int i = 0; i < TestListSize; ++i )
109  std::cout << std::setw( max_test_name ) << TestList[i].testName << " : " << std::setw( max_test_desc )
110  << std::left << TestList[i].testDesc << std::endl;
111 }
112 
113 int main( int argc, char* argv[] )
114 {
115  int intervals = DEFAULT_INTERVALS;
116  int dimension = 3;
117  int number = 0;
118  std::vector< test_func_t > test_list;
119  std::list< int* > expected_list;
120  bool did_help = false;
121  for( int i = 1; i < argc; ++i )
122  {
123  if( !expected_list.empty() )
124  {
125  int* ptr = expected_list.front();
126  expected_list.pop_front();
127  char* endptr;
128  *ptr = strtol( argv[i], &endptr, 0 );
129  if( !endptr )
130  {
131  usage( argv[0] );
132  std::cerr << "Expected integer value, got \"" << argv[i] << '"' << std::endl;
133  return 1;
134  }
135  }
136  else if( *argv[i] == '-' )
137  { // flag
138  for( int j = 1; argv[i][j]; ++j )
139  {
140  switch( argv[i][j] )
141  {
142  case 'i':
143  expected_list.push_back( &intervals );
144  break;
145  case 'd':
146  expected_list.push_back( &dimension );
147  break;
148  case 'n':
149  expected_list.push_back( &number );
150  break;
151  case 'h':
152  did_help = true;
153  usage( argv[0], false );
154  break;
155  case 'l':
156  did_help = true;
157  list_tests();
158  break;
159  default:
160  usage( argv[0] );
161  std::cerr << "Invalid option: -" << argv[i][j] << std::endl;
162  return 1;
163  }
164  }
165  }
166  else
167  {
168  int j = -1;
169  do
170  {
171  ++j;
172  if( j >= TestListSize )
173  {
174  usage( argv[0] );
175  std::cerr << "Invalid test name: " << argv[i] << std::endl;
176  return 1;
177  }
178  } while( TestList[j].testName != argv[i] );
179  test_list.push_back( TestList[j].testFunc );
180  }
181  }
182 
183  if( !expected_list.empty() )
184  {
185  usage( argv[0] );
186  std::cerr << "Missing final argument" << std::endl;
187  return 1;
188  }
189 
190  // error if no input
191  if( test_list.empty() && !did_help )
192  {
193  usage( argv[0] );
194  std::cerr << "No tests specified" << std::endl;
195  return 1;
196  }
197 
198  if( intervals < 1 )
199  {
200  std::cerr << "Invalid interval count: " << intervals << std::endl;
201  return 1;
202  }
203 
204  if( dimension < 1 || dimension > 3 )
205  {
206  std::cerr << "Invalid dimension: " << dimension << std::endl;
207  return 1;
208  }
209 
210  // now run the tests
211  for( std::vector< test_func_t >::iterator i = test_list.begin(); i != test_list.end(); ++i )
212  {
213  test_func_t fptr = *i;
214  fptr( intervals, dimension, number );
215  }
216 
217  return 0;
218 }
219 
220 void create_regular_mesh( Interface* gMB, int interval, int dim )
221 {
222  if( dim < 1 || dim > 3 || interval < 1 )
223  {
224  std::cerr << "Invalid arguments" << std::endl;
225  exit( 1 );
226  }
227 
228  const int nvi = interval + 1;
229  const int dims[3] = { nvi, dim > 1 ? nvi : 1, dim > 2 ? nvi : 1 };
230  int num_vert = dims[0] * dims[1] * dims[2];
231 
232  ReadUtilIface* readMeshIface;
233  gMB->query_interface( readMeshIface );
234 
235  EntityHandle vstart;
236  std::vector< double* > arrays;
237  ErrorCode rval = readMeshIface->get_node_coords( 3, num_vert, 1, vstart, arrays );
238  if( MB_SUCCESS != rval || arrays.size() < 3 )
239  {
240  std::cerr << "Vertex creation failed" << std::endl;
241  exit( 2 );
242  }
243  double *x = arrays[0], *y = arrays[1], *z = arrays[2];
244 
245  // Calculate vertex coordinates
246  for( int k = 0; k < dims[2]; ++k )
247  for( int j = 0; j < dims[1]; ++j )
248  for( int i = 0; i < dims[0]; ++i )
249  {
250  *x = i;
251  ++x;
252  *y = j;
253  ++y;
254  *z = k;
255  ++z;
256  }
257 
258  const long vert_per_elem = 1 << dim; // 2^dim
259  const long intervals[3] = { interval, dim > 1 ? interval : 1, dim > 2 ? interval : 1 };
260  const long num_elem = intervals[0] * intervals[1] * intervals[2];
261  const EntityType type = ( dim == 1 ) ? MBEDGE : ( dim == 2 ) ? MBQUAD : MBHEX;
262 
263  EntityHandle estart, *conn = 0;
264  rval = readMeshIface->get_element_connect( num_elem, vert_per_elem, type, 0, estart, conn );
265  if( MB_SUCCESS != rval || !conn )
266  {
267  std::cerr << "Element creation failed" << std::endl;
268  exit( 2 );
269  }
270 
271  // Offsets of element vertices in grid relative to corner closest to origin
272  long c = dims[0] * dims[1];
273  const long corners[8] = { 0, 1, 1 + dims[0], dims[0], c, c + 1, c + 1 + dims[0], c + dims[0] };
274 
275  // Populate element list
276  EntityHandle* iter = conn;
277  for( long z1 = 0; z1 < intervals[2]; ++z1 )
278  for( long y1 = 0; y1 < intervals[1]; ++y1 )
279  for( long x1 = 0; x1 < intervals[0]; ++x1 )
280  {
281  const long index = x1 + y1 * dims[0] + z1 * ( dims[0] * dims[1] );
282  for( long j = 0; j < vert_per_elem; ++j, ++iter )
283  *iter = index + corners[j] + vstart;
284  }
285 
286  // notify MOAB of the new elements
287  rval = readMeshIface->update_adjacencies( estart, num_elem, vert_per_elem, conn );
288  if( MB_SUCCESS != rval )
289  {
290  std::cerr << "Element update failed" << std::endl;
291  exit( 2 );
292  }
293 }
294 
295 void skin_common( int interval, int dim, int num, bool use_adj )
296 {
297  Core moab;
298  Interface* gMB = &moab;
299  ErrorCode rval;
300  double d;
301  clock_t t, tt;
302 
303  create_regular_mesh( gMB, interval, dim );
304 
305  Range skin, verts, elems;
306  rval = gMB->get_entities_by_dimension( 0, dim, elems );
307  assert( MB_SUCCESS == rval );
308  assert( !elems.empty() );
309 
310  Skinner tool( gMB );
311 
312  t = clock();
313  rval = tool.find_skin( 0, elems, true, verts, 0, use_adj, false );
314  t = clock() - t;
315  if( MB_SUCCESS != rval )
316  {
317  std::cerr << "Search for skin vertices failed" << std::endl;
318  exit( 2 );
319  }
320  d = ( (double)t ) / CLOCKS_PER_SEC;
321  std::cout << "Got " << verts.size() << " skin vertices in " << d << " seconds." << std::endl;
322 
323  t = 0;
324  if( num < 1 ) num = 1000;
325  long blocksize = elems.size() / num;
326  if( !blocksize ) blocksize = 1;
327  long numblocks = elems.size() / blocksize;
328  Range::iterator it = elems.begin();
329  for( long i = 0; i < numblocks; ++i )
330  {
331  verts.clear();
332  Range::iterator end = it + blocksize;
333  Range blockelems;
334  blockelems.merge( it, end );
335  it = end;
336  tt = clock();
337  rval = tool.find_skin( 0, blockelems, true, verts, 0, use_adj, false );
338  t += clock() - tt;
339  if( MB_SUCCESS != rval )
340  {
341  std::cerr << "Search for skin vertices failed" << std::endl;
342  exit( 2 );
343  }
344  }
345  d = ( (double)t ) / CLOCKS_PER_SEC;
346  std::cout << "Got skin vertices for " << numblocks << " blocks of " << blocksize << " elements in " << d
347  << " seconds." << std::endl;
348 
349  for( int e = 0; e < 2; ++e )
350  { // do this twice
351  if( e == 1 )
352  {
353  // create all interior faces
354  skin.clear();
355  t = clock();
356  gMB->get_adjacencies( elems, dim - 1, true, skin, Interface::UNION );
357  t = clock() - t;
358  d = ( (double)t ) / CLOCKS_PER_SEC;
359  std::cout << "Created " << skin.size() << " entities of dimension-1 in " << d << " seconds" << std::endl;
360  }
361 
362  skin.clear();
363  t = clock();
364  rval = tool.find_skin( 0, elems, false, skin, 0, use_adj, true );
365  t = clock() - t;
366  if( MB_SUCCESS != rval )
367  {
368  std::cerr << "Search for skin vertices failed" << std::endl;
369  exit( 2 );
370  }
371  d = ( (double)t ) / CLOCKS_PER_SEC;
372  std::cout << "Got " << skin.size() << " skin elements in " << d << " seconds." << std::endl;
373 
374  t = 0;
375  it = elems.begin();
376  for( long i = 0; i < numblocks; ++i )
377  {
378  skin.clear();
379  Range::iterator end = it + blocksize;
380  Range blockelems;
381  blockelems.merge( it, end );
382  it = end;
383  tt = clock();
384  rval = tool.find_skin( 0, blockelems, false, skin, 0, use_adj, true );
385  t += clock() - tt;
386  if( MB_SUCCESS != rval )
387  {
388  std::cerr << "Search for skin elements failed" << std::endl;
389  exit( 2 );
390  }
391  }
392  d = ( (double)t ) / CLOCKS_PER_SEC;
393  std::cout << "Got skin elements for " << numblocks << " blocks of " << blocksize << " elements in " << d
394  << " seconds." << std::endl;
395  }
396 }
397 
398 void tag_time( TagType storage, bool direct, int intervals, int dim, int blocks )
399 {
400  Core moab;
401  Interface& mb = moab;
402  create_regular_mesh( &mb, intervals, dim );
403 
404  // Create tag in which to store data
405  Tag tag;
406  mb.tag_get_handle( "data", 1, MB_TYPE_DOUBLE, tag, storage | MB_TAG_CREAT );
407 
408  // Make up some arbitrary iterative calculation for timing purposes:
409  // set each value v_n = (V + v_n)/2 until all values are within
410  // epsilon of V.
411  std::vector< double > data;
412  Range verts;
413  mb.get_entities_by_type( 0, MBVERTEX, verts );
414 
415  clock_t t = clock();
416 
417  // initialize
418  if( direct )
419  {
420  Range::iterator i, j = verts.begin();
421  void* ptr;
422  int count;
423  while( j != verts.end() )
424  {
425  mb.tag_iterate( tag, i, verts.end(), count, ptr );
426  double* arr = reinterpret_cast< double* >( ptr );
427  for( j = i + count; i != j; ++i, ++arr )
428  *arr = ( 11.0 * *i + 7.0 ) / ( *i );
429  }
430  }
431  else
432  {
433  data.resize( verts.size() );
434  double* arr = &data[0];
435  for( Range::iterator i = verts.begin(); i != verts.end(); ++i, ++arr )
436  *arr = ( 11.0 * *i + 7.0 ) / ( *i );
437  mb.tag_set_data( tag, verts, &data[0] );
438  }
439 
440  // iterate
441  const double v0 = acos( -1.0 ); // pi
442  size_t iter_count = 0;
443  double max_diff;
444  const size_t num_verts = verts.size();
445  do
446  {
447  if( direct )
448  {
449  max_diff = 0.0;
450  Range::iterator i, j = verts.begin();
451  void* ptr;
452  while( j != verts.end() )
453  {
454  int count;
455  mb.tag_iterate( tag, i, verts.end(), count, ptr );
456  double* arr = reinterpret_cast< double* >( ptr );
457 
458  for( j = i + count; i != j; ++i, ++arr )
459  {
460  *arr = 0.5 * ( *arr + v0 );
461  double diff = fabs( *arr - v0 );
462  if( diff > max_diff ) max_diff = diff;
463  }
464  }
465  }
466  else if( blocks < 1 )
467  {
468  max_diff = 0.0;
469  mb.tag_get_data( tag, verts, &data[0] );
470  for( size_t i = 0; i < data.size(); ++i )
471  {
472  data[i] = 0.5 * ( v0 + data[i] );
473  double diff = fabs( data[i] - v0 );
474  if( diff > max_diff ) max_diff = diff;
475  }
476  mb.tag_set_data( tag, verts, &data[0] );
477  }
478  else
479  {
480  max_diff = 0.0;
481  Range r;
482  Range::iterator it = verts.begin();
483  size_t step = num_verts / blocks;
484  for( int j = 0; j < blocks - 1; ++j )
485  {
486  Range::iterator nx = it;
487  nx += step;
488  r.clear();
489  r.merge( it, nx );
490  mb.tag_get_data( tag, r, &data[0] );
491  it = nx;
492 
493  for( size_t i = 0; i < step; ++i )
494  {
495  data[i] = 0.5 * ( v0 + data[i] );
496  double diff = fabs( data[i] - v0 );
497  if( diff > max_diff ) max_diff = diff;
498  }
499  mb.tag_set_data( tag, r, &data[0] );
500  }
501 
502  r.clear();
503  r.merge( it, verts.end() );
504  mb.tag_get_data( tag, r, &data[0] );
505  for( size_t i = 0; i < ( num_verts - ( blocks - 1 ) * step ); ++i )
506  {
507  data[i] = 0.5 * ( v0 + data[i] );
508  double diff = fabs( data[i] - v0 );
509  if( diff > max_diff ) max_diff = diff;
510  }
511  mb.tag_set_data( tag, r, &data[0] );
512  }
513  ++iter_count;
514  // std::cout << iter_count << " " << max_diff << std::endl;
515  } while( max_diff > 1e-6 );
516 
517  double secs = ( clock() - t ) / (double)CLOCKS_PER_SEC;
518  std::cout << " " << iter_count << " iterations in " << secs << " seconds" << std::endl;
519 }