![]() |
Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 #include
00002 #include
00003 #include
00004 #include
00005 #include
00006 #include
00007 #include
00008 #include
00009 #if !defined( _MSC_VER ) && !defined( __MINGW32__ )
00010 #include
00011 #include
00012 #include
00013 #endif
00014 #include
00015 #include "moab/Interface.hpp"
00016 #include "MBTagConventions.hpp"
00017 #include "moab/Core.hpp"
00018 #include "moab/Range.hpp"
00019 #include "moab/Skinner.hpp"
00020 #include "moab/AdaptiveKDTree.hpp"
00021 #include "moab/CN.hpp"
00022
00023 using namespace moab;
00024
00025 static void get_time_mem( double& tot_time, double& tot_mem );
00026
00027 // Different platforms follow different conventions for usage
00028 #if !defined( _MSC_VER ) && !defined( __MINGW32__ )
00029 #include
00030 #endif
00031 #ifdef SOLARIS
00032 extern "C" int getrusage( int, struct rusage* );
00033 #ifndef RUSAGE_SELF
00034 #include
00035 #endif
00036 #endif
00037
00038 const char DEFAULT_FIXED_TAG[] = "fixed";
00039 const int MIN_EDGE_LEN_DENOM = 4;
00040
00041 #define CHKERROR( A ) \
00042 do \
00043 { \
00044 if( MB_SUCCESS != ( A ) ) \
00045 { \
00046 std::cerr << "Internal error at line " << __LINE__ << std::endl; \
00047 return 3; \
00048 } \
00049 } while( false )
00050
00051 static ErrorCode merge_duplicate_vertices( Interface&, double epsilon );
00052 static ErrorCode min_edge_length( Interface&, double& result );
00053
00054 static void usage( const char* argv0, bool help = false )
00055 {
00056 std::ostream& str = help ? std::cout : std::cerr;
00057
00058 str << "Usage: " << argv0
00059 << " [-b [-b ...] ] [-l] [-m] [-M ] [-p] [-s ] [-S] [-t|-T "
00060 "] [-w] [-v|-V ]"
00061 << " []" << std::endl;
00062 str << "Help : " << argv0 << " -h" << std::endl;
00063 if( !help ) exit( 1 );
00064
00065 str << "Options: " << std::endl;
00066 str << "-a : Compute skin using vert-elem adjacencies (more memory, less time)." << std::endl;
00067 str << "-b : Compute skin only for material set/block ." << std::endl;
00068 str << "-p : Print cpu & memory performance." << std::endl;
00069 str << "-s : Put skin in neumann set/sideset ." << std::endl;
00070 str << "-S : Look for and use structured mesh information to speed up skinning." << std::endl;
00071 str << "-t : Set '" << DEFAULT_FIXED_TAG << "' tag on skin vertices." << std::endl;
00072 str << "-T : Create tag with specified name and set to 1 on skin vertices." << std::endl;
00073 str << "-w : Write out whole mesh (otherwise just writes skin)." << std::endl;
00074 str << "-m : consolidate duplicate vertices" << std::endl;
00075 str << "-M : consolidate duplicate vertices with specified tolerance. "
00076 "(Default: min_edge_length/"
00077 << MIN_EDGE_LEN_DENOM << ")" << std::endl;
00078 str << "-l : List total numbers of entities and vertices in skin." << std::endl;
00079 exit( 0 );
00080 }
00081
00082 int main( int argc, char* argv[] )
00083 {
00084 int i = 1;
00085 std::vector< int > matsets;
00086 int neuset_num = -1;
00087 bool write_tag = false, write_whole_mesh = false;
00088 bool print_perf = false;
00089 bool use_vert_elem_adjs = false;
00090 bool merge_vertices = false;
00091 double merge_epsilon = -1;
00092 bool list_skin = false;
00093 bool use_scd = false;
00094 const char* fixed_tag = DEFAULT_FIXED_TAG;
00095 const char *input_file = 0, *output_file = 0;
00096
00097 bool no_more_flags = false;
00098 char* endptr = 0;
00099 long block = 0; // initialize to eliminate compiler warning
00100 while( i < argc )
00101 {
00102 if( !no_more_flags && argv[i][0] == '-' )
00103 {
00104 const int f = i++;
00105 for( int j = 1; argv[f][j]; ++j )
00106 {
00107 switch( argv[f][j] )
00108 {
00109 case 'a':
00110 use_vert_elem_adjs = true;
00111 break;
00112 case 'p':
00113 print_perf = true;
00114 break;
00115 case 't':
00116 write_tag = true;
00117 break;
00118 case 'w':
00119 write_whole_mesh = true;
00120 break;
00121 case 'm':
00122 merge_vertices = true;
00123 break;
00124 case '-':
00125 no_more_flags = true;
00126 break;
00127 case 'h':
00128 usage( argv[0], true );
00129 break;
00130 case 'l':
00131 list_skin = true;
00132 break;
00133 case 'S':
00134 use_scd = true;
00135 break;
00136 case 'b':
00137 if( i == argc || 0 >= ( block = strtol( argv[i], &endptr, 0 ) ) || *endptr )
00138 {
00139 std::cerr << "Expected positive integer following '-b' flag" << std::endl;
00140 usage( argv[0] );
00141 }
00142 matsets.push_back( (int)block );
00143 ++i;
00144 break;
00145 case 's':
00146 if( i == argc || 0 >= ( neuset_num = strtol( argv[i], &endptr, 0 ) ) || *endptr )
00147 {
00148 std::cerr << "Expected positive integer following '-s' flag" << std::endl;
00149 usage( argv[0] );
00150 }
00151 ++i;
00152 break;
00153 case 'T':
00154 if( i == argc || argv[i][0] == '-' )
00155 {
00156 std::cerr << "Expected tag name following '-T' flag" << std::endl;
00157 usage( argv[0] );
00158 }
00159 fixed_tag = argv[i++];
00160 break;
00161 case 'M':
00162 if( i == argc || 0.0 > ( merge_epsilon = strtod( argv[i], &endptr ) ) || *endptr )
00163 {
00164 std::cerr << "Expected positive numeric value following '-M' flag" << std::endl;
00165 usage( argv[0] );
00166 }
00167 merge_vertices = true;
00168 ++i;
00169 break;
00170 default:
00171 std::cerr << "Unrecognized flag: '" << argv[f][j] << "'" << std::endl;
00172 usage( argv[0] );
00173 break;
00174 }
00175 }
00176 }
00177 else if( input_file && output_file )
00178 {
00179 std::cerr << "Extra argument: " << argv[i] << std::endl;
00180 usage( argv[0] );
00181 }
00182 else if( input_file )
00183 {
00184 output_file = argv[i++];
00185 }
00186 else
00187 {
00188 input_file = argv[i++];
00189 }
00190 }
00191
00192 if( !input_file )
00193 {
00194 std::cerr << "No input file specified" << std::endl;
00195 usage( argv[0] );
00196 }
00197
00198 ErrorCode result;
00199 Core mbimpl;
00200 Interface* iface = &mbimpl;
00201
00202 if( print_perf )
00203 {
00204 double tmp_time1, tmp_mem1;
00205 get_time_mem( tmp_time1, tmp_mem1 );
00206 std::cout << "Before reading: cpu time = " << tmp_time1 << ", memory = " << tmp_mem1 / 1.0e6 << "MB."
00207 << std::endl;
00208 }
00209
00210 // read input file
00211 result = iface->load_mesh( input_file );
00212 if( MB_SUCCESS != result )
00213 {
00214 std::cerr << "Failed to load \"" << input_file << "\"." << std::endl;
00215 return 2;
00216 }
00217 std::cerr << "Read \"" << input_file << "\"" << std::endl;
00218 if( print_perf )
00219 {
00220 double tmp_time2, tmp_mem2;
00221 get_time_mem( tmp_time2, tmp_mem2 );
00222 std::cout << "After reading: cpu time = " << tmp_time2 << ", memory = " << tmp_mem2 / 1.0e6 << "MB."
00223 << std::endl;
00224 }
00225
00226 if( merge_vertices )
00227 {
00228 if( merge_epsilon < 0.0 )
00229 {
00230 if( MB_SUCCESS != min_edge_length( *iface, merge_epsilon ) )
00231 {
00232 std::cerr << "Error determining minimum edge length" << std::endl;
00233 return 1;
00234 }
00235 merge_epsilon /= MIN_EDGE_LEN_DENOM;
00236 }
00237 if( MB_SUCCESS != merge_duplicate_vertices( *iface, merge_epsilon ) )
00238 {
00239 std::cerr << "Error merging duplicate vertices" << std::endl;
00240 return 1;
00241 }
00242 }
00243
00244 // get entities of largest dimension
00245 int dim = 4;
00246 Range entities;
00247 while( entities.empty() && dim > 1 )
00248 {
00249 dim--;
00250 result = iface->get_entities_by_dimension( 0, dim, entities );CHKERROR( result );
00251 }
00252
00253 Range skin_ents;
00254 Tag matset_tag = 0, neuset_tag = 0;
00255 result = iface->tag_get_handle( MATERIAL_SET_TAG_NAME, 1, MB_TYPE_INTEGER, matset_tag );
00256 if( MB_SUCCESS != result ) return 1;
00257 result = iface->tag_get_handle( NEUMANN_SET_TAG_NAME, 1, MB_TYPE_INTEGER, neuset_tag );
00258 if( MB_SUCCESS != result ) return 1;
00259
00260 if( matsets.empty() )
00261 skin_ents = entities;
00262 else
00263 {
00264 // get all entities in the specified blocks
00265 if( 0 == matset_tag )
00266 {
00267 std::cerr << "Couldn't find any material sets in this mesh." << std::endl;
00268 return 1;
00269 }
00270
00271 for( std::vector< int >::iterator vit = matsets.begin(); vit != matsets.end(); ++vit )
00272 {
00273 int this_matset = *vit;
00274 const void* this_matset_ptr = &this_matset;
00275 Range this_range, ent_range;
00276 result =
00277 iface->get_entities_by_type_and_tag( 0, MBENTITYSET, &matset_tag, &this_matset_ptr, 1, this_range );
00278 if( MB_SUCCESS != result )
00279 {
00280 std::cerr << "Trouble getting material set #" << *vit << std::endl;
00281 return 1;
00282 }
00283 else if( this_range.empty() )
00284 {
00285 std::cerr << "Warning: couldn't find material set " << *vit << std::endl;
00286 continue;
00287 }
00288
00289 result = iface->get_entities_by_dimension( *this_range.begin(), dim, ent_range, true );
00290 if( MB_SUCCESS != result ) continue;
00291 skin_ents.merge( ent_range );
00292 }
00293 }
00294
00295 if( skin_ents.empty() )
00296 {
00297 std::cerr << "No entities for which to compute skin; exiting." << std::endl;
00298 return 1;
00299 }
00300
00301 if( use_vert_elem_adjs )
00302 {
00303 // make a call which we know will generate vert-elem adjs
00304 Range dum_range;
00305 result = iface->get_adjacencies( &( *skin_ents.begin() ), 1, 1, false, dum_range );
00306 if( MB_SUCCESS != result ) return 1;
00307 }
00308
00309 double tmp_time = 0.0, tmp_mem = 0.0;
00310 if( print_perf )
00311 {
00312 get_time_mem( tmp_time, tmp_mem );
00313 std::cout << "Before skinning: cpu time = " << tmp_time << ", memory = " << tmp_mem / 1.0e6 << "MB."
00314 << std::endl;
00315 }
00316
00317 // skin the mesh
00318 Range forward_lower, reverse_lower;
00319 Skinner tool( iface );
00320 if( use_scd )
00321 result = tool.find_skin( 0, skin_ents, false, forward_lower, NULL, false, true, true );
00322 else
00323 result = tool.find_skin( 0, skin_ents, false, forward_lower, &reverse_lower );
00324 Range boundary;
00325 boundary.merge( forward_lower );
00326 boundary.merge( reverse_lower );
00327 if( MB_SUCCESS != result || boundary.empty() )
00328 {
00329 std::cerr << "Mesh skinning failed." << std::endl;
00330 return 3;
00331 }
00332
00333 if( list_skin )
00334 {
00335 Range skin_verts;
00336 result = iface->get_adjacencies( boundary, 0, true, skin_verts, Interface::UNION );
00337 std::cout << "Skin has ";
00338 if( skin_ents.num_of_dimension( 3 ) )
00339 std::cout << boundary.num_of_dimension( 2 ) << " faces and ";
00340 else if( skin_ents.num_of_dimension( 2 ) )
00341 std::cout << boundary.num_of_dimension( 1 ) << " edges and ";
00342 std::cout << skin_verts.size() << " vertices." << std::endl;
00343 }
00344 if( write_tag )
00345 {
00346 // get tag handle
00347 Tag tag;
00348 int zero = 0;
00349 result = iface->tag_get_handle( fixed_tag, 1, MB_TYPE_INTEGER, tag, MB_TAG_DENSE | MB_TAG_CREAT, &zero );CHKERROR( result );
00350
00351 // Set tags
00352 std::vector< int > ones;
00353 Range bverts;
00354 result = iface->get_adjacencies( boundary, 0, false, bverts, Interface::UNION );
00355 if( MB_SUCCESS != result )
00356 {
00357 std::cerr << "Trouble getting vertices on boundary." << std::endl;
00358 return 1;
00359 }
00360 ones.resize( bverts.size(), 1 );
00361 result = iface->tag_set_data( tag, bverts, &ones[0] );CHKERROR( result );
00362 }
00363
00364 if( -1 != neuset_num )
00365 {
00366 // create a neumann set with these entities
00367 if( 0 == neuset_tag )
00368 {
00369 result = iface->tag_get_handle( "NEUMANN_SET_TAG_NAME", 1, MB_TYPE_INTEGER, neuset_tag,
00370 MB_TAG_SPARSE | MB_TAG_CREAT );
00371 if( MB_SUCCESS != result || 0 == neuset_tag ) return 1;
00372 }
00373
00374 // always create a forward neumann set, assuming we have something in the set
00375 EntityHandle forward_neuset = 0;
00376 result = iface->create_meshset( MESHSET_SET, forward_neuset );
00377 if( MB_SUCCESS != result || 0 == forward_neuset ) return 1;
00378 result = iface->tag_set_data( neuset_tag, &forward_neuset, 1, &neuset_num );
00379 if( MB_SUCCESS != result ) return 1;
00380
00381 if( !forward_lower.empty() )
00382 {
00383 result = iface->add_entities( forward_neuset, forward_lower );
00384 if( MB_SUCCESS != result ) return 1;
00385 }
00386 if( !reverse_lower.empty() )
00387 {
00388 EntityHandle reverse_neuset = 1;
00389 result = iface->create_meshset( MESHSET_SET, reverse_neuset );
00390 if( MB_SUCCESS != result || 0 == forward_neuset ) return 1;
00391
00392 result = iface->add_entities( reverse_neuset, reverse_lower );
00393 if( MB_SUCCESS != result ) return 1;
00394 Tag sense_tag;
00395 int dum_sense = 0;
00396 result = iface->tag_get_handle( "SENSE", 1, MB_TYPE_INTEGER, sense_tag, MB_TAG_SPARSE | MB_TAG_CREAT,
00397 &dum_sense );
00398 if( result != MB_SUCCESS ) return 1;
00399 int sense_val = -1;
00400 result = iface->tag_set_data( neuset_tag, &reverse_neuset, 1, &sense_val );
00401 if( MB_SUCCESS != result ) return 0;
00402 result = iface->add_entities( forward_neuset, &reverse_neuset, 1 );
00403 if( MB_SUCCESS != result ) return 0;
00404 }
00405 }
00406
00407 if( NULL != output_file && write_whole_mesh )
00408 {
00409
00410 // write output file
00411 result = iface->write_mesh( output_file );
00412 if( MB_SUCCESS != result )
00413 {
00414 std::cerr << "Failed to write \"" << output_file << "\"." << std::endl;
00415 return 2;
00416 }
00417 std::cerr << "Wrote \"" << output_file << "\"" << std::endl;
00418 }
00419 else if( NULL != output_file )
00420 {
00421 // write only skin; write them as one set
00422 EntityHandle skin_set;
00423 result = iface->create_meshset( MESHSET_SET, skin_set );
00424 if( MB_SUCCESS != result ) return 1;
00425 result = iface->add_entities( skin_set, forward_lower );
00426 if( MB_SUCCESS != result ) return 1;
00427 result = iface->add_entities( skin_set, reverse_lower );
00428 if( MB_SUCCESS != result ) return 1;
00429
00430 int dum = 10000;
00431 result = iface->tag_set_data( matset_tag, &skin_set, 1, &dum );
00432 if( MB_SUCCESS != result ) return 1;
00433
00434 result = iface->write_mesh( output_file, &skin_set, 1 );
00435 if( MB_SUCCESS != result )
00436 {
00437 std::cerr << "Failed to write \"" << output_file << "\"." << std::endl;
00438 return 2;
00439 }
00440 std::cerr << "Wrote \"" << output_file << "\"" << std::endl;
00441 }
00442
00443 if( print_perf )
00444 {
00445 double tot_time, tot_mem;
00446 get_time_mem( tot_time, tot_mem );
00447 std::cout << "Total cpu time = " << tot_time << " seconds." << std::endl;
00448 std::cout << "Total skin cpu time = " << tot_time - tmp_time << " seconds." << std::endl;
00449 std::cout << "Total memory = " << tot_mem / 1024 << " MB." << std::endl;
00450 std::cout << "Total skin memory = " << ( tot_mem - tmp_mem ) / 1024 << " MB." << std::endl;
00451 std::cout << "Entities: " << std::endl;
00452 iface->list_entities( 0, 0 );
00453 }
00454
00455 return 0;
00456 }
00457
00458 #if defined( _MSC_VER ) || defined( __MINGW32__ )
00459 void get_time_mem( double& tot_time, double& tot_mem )
00460 {
00461 tot_time = (double)clock() / CLOCKS_PER_SEC;
00462 tot_mem = 0;
00463 }
00464 #else
00465 void get_time_mem( double& tot_time, double& tot_mem )
00466 {
00467 struct rusage r_usage;
00468 getrusage( RUSAGE_SELF, &r_usage );
00469 double utime = (double)r_usage.ru_utime.tv_sec + ( (double)r_usage.ru_utime.tv_usec / 1.e6 );
00470 double stime = (double)r_usage.ru_stime.tv_sec + ( (double)r_usage.ru_stime.tv_usec / 1.e6 );
00471 tot_time = utime + stime;
00472 tot_mem = 0;
00473 if( 0 != r_usage.ru_maxrss )
00474 {
00475 tot_mem = (double)r_usage.ru_maxrss;
00476 }
00477 else
00478 {
00479 // this machine doesn't return rss - try going to /proc
00480 // print the file name to open
00481 char file_str[4096], dum_str[4096];
00482 int file_ptr = open( "/proc/self/stat", O_RDONLY );
00483 int file_len = read( file_ptr, file_str, sizeof( file_str ) - 1 );
00484 if( file_len <= 0 )
00485 {
00486 close( file_ptr );
00487 return;
00488 }
00489
00490 close( file_ptr );
00491 file_str[file_len] = '\0';
00492 // read the preceding fields and the ones we really want...
00493 int dum_int;
00494 unsigned int dum_uint, vm_size, rss;
00495 int num_fields = sscanf( file_str,
00496 "%d " // pid
00497 "%s " // comm
00498 "%c " // state
00499 "%d %d %d %d %d " // ppid, pgrp, session, tty, tpgid
00500 "%u %u %u %u %u " // flags, minflt, cminflt, majflt, cmajflt
00501 "%d %d %d %d %d %d " // utime, stime, cutime, cstime, counter, priority
00502 "%u %u " // timeout, itrealvalue
00503 "%d " // starttime
00504 "%u %u", // vsize, rss
00505 &dum_int, dum_str, dum_str, &dum_int, &dum_int, &dum_int, &dum_int, &dum_int,
00506 &dum_uint, &dum_uint, &dum_uint, &dum_uint, &dum_uint, &dum_int, &dum_int, &dum_int,
00507 &dum_int, &dum_int, &dum_int, &dum_uint, &dum_uint, &dum_int, &vm_size, &rss );
00508 if( num_fields == 24 ) tot_mem = ( (double)vm_size );
00509 }
00510 }
00511 #endif
00512
00513 ErrorCode min_edge_length( Interface& moab, double& result )
00514 {
00515 double sqr_result = std::numeric_limits< double >::max();
00516
00517 ErrorCode rval;
00518 Range entities;
00519 rval = moab.get_entities_by_handle( 0, entities );
00520 if( MB_SUCCESS != rval ) return rval;
00521 Range::iterator i = entities.upper_bound( MBVERTEX );
00522 entities.erase( entities.begin(), i );
00523 i = entities.lower_bound( MBENTITYSET );
00524 entities.erase( i, entities.end() );
00525
00526 std::vector< EntityHandle > storage;
00527 for( i = entities.begin(); i != entities.end(); ++i )
00528 {
00529 EntityType t = moab.type_from_handle( *i );
00530 const EntityHandle* conn;
00531 int conn_len, indices[2];
00532 rval = moab.get_connectivity( *i, conn, conn_len, true, &storage );
00533 if( MB_SUCCESS != rval ) return rval;
00534
00535 int num_edges = CN::NumSubEntities( t, 1 );
00536 for( int j = 0; j < num_edges; ++j )
00537 {
00538 CN::SubEntityVertexIndices( t, 1, j, indices );
00539 EntityHandle v[2] = { conn[indices[0]], conn[indices[1]] };
00540 if( v[0] == v[1] ) continue;
00541
00542 double c[6];
00543 rval = moab.get_coords( v, 2, c );
00544 if( MB_SUCCESS != rval ) return rval;
00545
00546 c[0] -= c[3];
00547 c[1] -= c[4];
00548 c[2] -= c[5];
00549 double len_sqr = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
00550 if( len_sqr < sqr_result ) sqr_result = len_sqr;
00551 }
00552 }
00553
00554 result = sqrt( sqr_result );
00555 return MB_SUCCESS;
00556 }
00557
00558 ErrorCode merge_duplicate_vertices( Interface& moab, const double epsilon )
00559 {
00560 ErrorCode rval;
00561 Range verts;
00562 rval = moab.get_entities_by_type( 0, MBVERTEX, verts );
00563 if( MB_SUCCESS != rval ) return rval;
00564
00565 AdaptiveKDTree tree( &moab );
00566 EntityHandle root;
00567 rval = tree.build_tree( verts, &root );
00568 if( MB_SUCCESS != rval )
00569 {
00570 fprintf( stderr, "Failed to build kD-tree.\n" );
00571 return rval;
00572 }
00573
00574 std::set< EntityHandle > dead_verts;
00575 std::vector< EntityHandle > leaves;
00576 for( Range::iterator i = verts.begin(); i != verts.end(); ++i )
00577 {
00578 double coords[3];
00579 rval = moab.get_coords( &*i, 1, coords );
00580 if( MB_SUCCESS != rval ) return rval;
00581
00582 leaves.clear();
00583 ;
00584 rval = tree.distance_search( coords, epsilon, leaves, epsilon, epsilon );
00585 if( MB_SUCCESS != rval ) return rval;
00586
00587 Range near;
00588 for( std::vector< EntityHandle >::iterator j = leaves.begin(); j != leaves.end(); ++j )
00589 {
00590 Range tmp;
00591 rval = moab.get_entities_by_type( *j, MBVERTEX, tmp );
00592 if( MB_SUCCESS != rval ) return rval;
00593 near.merge( tmp.begin(), tmp.end() );
00594 }
00595
00596 Range::iterator v = near.find( *i );
00597 assert( v != near.end() );
00598 near.erase( v );
00599
00600 EntityHandle merge = 0;
00601 for( Range::iterator j = near.begin(); j != near.end(); ++j )
00602 {
00603 if( *j < *i && dead_verts.find( *j ) != dead_verts.end() ) continue;
00604
00605 double coords2[3];
00606 rval = moab.get_coords( &*j, 1, coords2 );
00607 if( MB_SUCCESS != rval ) return rval;
00608
00609 coords2[0] -= coords[0];
00610 coords2[1] -= coords[1];
00611 coords2[2] -= coords[2];
00612 double dsqr = coords2[0] * coords2[0] + coords2[1] * coords2[1] + coords2[2] * coords2[2];
00613 if( dsqr <= epsilon * epsilon )
00614 {
00615 merge = *j;
00616 break;
00617 }
00618 }
00619
00620 if( merge )
00621 {
00622 dead_verts.insert( *i );
00623 rval = moab.merge_entities( merge, *i, false, true );
00624 if( MB_SUCCESS != rval ) return rval;
00625 }
00626 }
00627
00628 if( dead_verts.empty() )
00629 std::cout << "No duplicate/coincident vertices." << std::endl;
00630 else
00631 std::cout << "Merged and deleted " << dead_verts.size() << " vertices "
00632 << "coincident within " << epsilon << std::endl;
00633
00634 return MB_SUCCESS;
00635 }