1 #include "cgm2moab.hpp"
2 #include "moab/ProgOptions.hpp"
3
4 #include "moab/Core.hpp"
5 #include "moab/Range.hpp"
6 #include "moab/CartVect.hpp"
7 #include "MBTagConventions.hpp"
8 #include "moab/GeomQueryTool.hpp"
9 #include "moab/GeomTopoTool.hpp"
10
11 #include <sstream>
12 #include <iomanip>
13 #include <cstdlib>
14 #include <algorithm>
15 #include <cstdio>
16
17 using namespace moab;
18
19 bool verbose = false;
20
21 void chkerr( Interface* mbi, ErrorCode code, int line, const char* file )
22 {
23
24 if( code != MB_SUCCESS )
25 {
26 std::cerr << "Error: " << mbi->get_error_string( code ) << " (" << code << ")" << std::endl;
27 std::string message;
28 if( MB_SUCCESS == mbi->get_last_error( message ) && !message.empty() )
29 std::cerr << "Error message: " << message << std::endl;
30 std::string fname = file;
31 size_t slash = fname.find_last_of( '/' );
32 if( slash != fname.npos )
33 {
34 fname = fname.substr( slash + 1 );
35 }
36 std::cerr << "At " << fname << " line: " << line << std::endl;
37 std::exit( EXIT_FAILURE );
38 }
39 }
40
41 void chkerr( Interface& mbi, ErrorCode code, int line, const char* file )
42 {
43 chkerr( &mbi, code, line, file );
44 }
45
46 void chkerr( GeomTopoTool& gtt, ErrorCode code, int line, const char* file )
47 {
48 chkerr( gtt.get_moab_instance(), code, line, file );
49 }
50
51
58 static ErrorCode get_signed_volume( Interface* MBI,
59 const EntityHandle surf_set,
60 const CartVect& offset,
61 double& signed_volume )
62 {
63 ErrorCode rval;
64 Range tris;
65 rval = MBI->get_entities_by_type( surf_set, MBTRI, tris );
66 if( MB_SUCCESS != rval ) return rval;
67
68 signed_volume = 0.0;
69 const EntityHandle* conn;
70 int len;
71 CartVect coords[3];
72 for( Range::iterator j = tris.begin(); j != tris.end(); ++j )
73 {
74 rval = MBI->get_connectivity( *j, conn, len, true );
75 if( MB_SUCCESS != rval ) return rval;
76 if( 3 != len ) return MB_INVALID_SIZE;
77 rval = MBI->get_coords( conn, 3, coords[0].array() );
78 if( MB_SUCCESS != rval ) return rval;
79
80
81
82 for( unsigned int k = 0; k < 3; ++k )
83 {
84 coords[k] += offset;
85 }
86
87 coords[1] -= coords[0];
88 coords[2] -= coords[0];
89 signed_volume += ( coords[0] % ( coords[1] * coords[2] ) );
90 }
91 return MB_SUCCESS;
92 }
93
94
98 static ErrorCode replace_surface( Interface* mbi,
99 EntityHandle old_surf,
100 EntityHandle old_file_set,
101 EntityHandle new_surf,
102 const Tag& senseTag )
103 {
104
105 ErrorCode rval;
106
107
108
109
110
111
112 CartVect offset;
113 const double min_vol = 0.1;
114
115 double old_vol = 0, new_vol = 0;
116
117 bool success = false;
118 int num_attempts = 100;
119
120 while( num_attempts-- > 0 )
121 {
122
123 rval = get_signed_volume( mbi, old_surf, offset, old_vol );
124 if( MB_SUCCESS != rval ) return rval;
125
126 rval = get_signed_volume( mbi, new_surf, offset, new_vol );
127 if( MB_SUCCESS != rval ) return rval;
128
129 if( std::fabs( old_vol ) >= min_vol && std::fabs( new_vol ) >= min_vol )
130 {
131 success = true;
132 break;
133 }
134
135
136 const int max_random = 10;
137 for( int i = 0; i < 3; ++i )
138 {
139 offset[i] = std::rand() % max_random;
140 }
141 }
142
143 if( !success )
144 {
145 std::cerr << "Error: could not calculate a surface volume" << std::endl;
146 return MB_FAILURE;
147 }
148
149
150
151 if( ( old_vol < 0 && new_vol > 0 ) || ( old_vol > 0 && new_vol < 0 ) )
152 {
153
154 EntityHandle old_surf_volumes[2];
155 rval = mbi->tag_get_data( senseTag, &old_surf, 1, old_surf_volumes );
156 if( MB_SUCCESS != rval ) return rval;
157
158 std::swap( old_surf_volumes[0], old_surf_volumes[1] );
159
160 rval = mbi->tag_set_data( senseTag, &old_surf, 1, old_surf_volumes );
161 if( MB_SUCCESS != rval ) return rval;
162 }
163
164 int num_old_tris, num_new_tris;
165
166
167
168 Range old_tris;
169 rval = mbi->get_entities_by_type( old_surf, MBTRI, old_tris );
170 if( MB_SUCCESS != rval ) return rval;
171 num_old_tris = old_tris.size();
172 rval = mbi->remove_entities( old_surf, old_tris );
173 if( MB_SUCCESS != rval ) return rval;
174 rval = mbi->remove_entities( old_file_set, old_tris );
175 if( MB_SUCCESS != rval ) return rval;
176 rval = mbi->delete_entities( old_tris );
177 if( MB_SUCCESS != rval ) return rval;
178
179
180 Range new_tris;
181 rval = mbi->get_entities_by_type( new_surf, MBTRI, new_tris );
182 if( MB_SUCCESS != rval ) return rval;
183 num_new_tris = new_tris.size();
184 rval = mbi->add_entities( old_surf, new_tris );
185 if( MB_SUCCESS != rval ) return rval;
186
187 if( verbose )
188 {
189 std::cout << num_old_tris << " tris -> " << num_new_tris << " tris" << std::endl;
190 }
191
192 return MB_SUCCESS;
193 }
194
195
199 static ErrorCode merge_input_surfs( Interface* mbi,
200 const EntityHandle old_file_set,
201 const EntityHandle new_file_set,
202 const Tag& idTag,
203 const Tag& dimTag,
204 const Tag& senseTag )
205 {
206 ErrorCode rval;
207
208 const int two = 2;
209 const Tag tags[2] = { dimTag, idTag };
210 const void* tag_vals[2] = { &two, NULL };
211
212 Range old_surfs;
213 rval = mbi->get_entities_by_type_and_tag( old_file_set, MBENTITYSET, &dimTag, tag_vals, 1, old_surfs );
214 if( MB_SUCCESS != rval ) return rval;
215
216 int count = 0;
217
218 for( Range::iterator i = old_surfs.begin(); i != old_surfs.end(); ++i )
219 {
220 EntityHandle old_surf = *i;
221
222 int surf_id;
223 rval = mbi->tag_get_data( idTag, &old_surf, 1, &surf_id );
224 if( MB_SUCCESS != rval ) return rval;
225
226 Range new_surf_range;
227 tag_vals[1] = &surf_id;
228 rval = mbi->get_entities_by_type_and_tag( new_file_set, MBENTITYSET, tags, tag_vals, 2, new_surf_range );
229 if( MB_SUCCESS != rval ) return rval;
230
231 if( new_surf_range.size() != 1 )
232 {
233 if( new_surf_range.size() > 1 )
234 {
235 std::cerr << "Warning: surface " << surf_id << " has more than one representation in new file"
236 << std::endl;
237 }
238 continue;
239 }
240
241
242 EntityHandle new_surf = new_surf_range[0];
243
244
245
246 if( verbose )
247 {
248 std::cout << "Surface " << surf_id << ": " << std::flush;
249 }
250 rval = replace_surface( mbi, old_surf, old_file_set, new_surf, senseTag );
251 if( MB_SUCCESS != rval ) return rval;
252 count++;
253 }
254
255 std::cout << "Replaced " << count << " surface" << ( count == 1 ? "." : "s." ) << std::endl;
256
257 return MB_SUCCESS;
258 }
259
260 int main( int argc, char* argv[] )
261 {
262
263 ProgOptions po( "cgm2moab: a tool for preprocessing CAD and mesh files for analysis" );
264
265 std::string input_file;
266 std::string output_file = "dagmc_preproc_out.h5m";
267 int grid = 50;
268
269 po.addOpt< void >( ",v", "Verbose output", &verbose );
270 po.addOpt< std::string >( "outmesh,o", "Specify output file name (default " + output_file + ")", &output_file );
271 po.addOpt< void >( "no-outmesh,", "Do not write an output mesh" );
272 po.addOpt< std::string >( ",m", "Specify alternate input mesh to override surfaces in input_file" );
273 po.addOpt< std::string >( "obb-vis,O", "Specify obb visualization output file (default none)" );
274 po.addOpt< int >( "obb-vis-divs", "Resolution of obb visualization grid (default 50)", &grid );
275 po.addOpt< void >( "obb-stats,S", "Print obb statistics. With -v, print verbose statistics." );
276 po.addOpt< std::vector< int > >( "vols,V",
277 "Specify a set of volumes (applies to --obb_vis and --obb_stats, default all)" );
278 po.addOptionHelpHeading( "Options for loading CAD files" );
279 po.addOpt< double >( "ftol,f", "Faceting distance tolerance", po.add_cancel_opt );
280 po.addOpt< double >( "ltol,l", "Faceting edge length tolerance", po.add_cancel_opt );
281 po.addOpt< int >( "atol,a", "Faceting normal angle tolerance (degrees)", po.add_cancel_opt );
282 po.addOpt< void >( "all-warnings", "Verbose warnings about attributes and curve tolerances" );
283 po.addOpt< void >( "no-attribs", "Do not actuate CGM attributes" );
284 po.addOpt< void >( "fatal_curves", "Fatal Error when curves cannot be faceted" );
285
286 po.addRequiredArg< std::string >( "input_file", "Path to input file for preprocessing", &input_file );
287
288 po.parseCommandLine( argc, argv );
289
290
291 bool obb_task = po.numOptSet( "obb-vis" ) || po.numOptSet( "obb-stats" );
292
293 if( po.numOptSet( "no-outmesh" ) && !obb_task )
294 {
295 po.error( "Nothing to do. Please specify an OBB-related option, or remove --no_outmesh." );
296 }
297
298
299 std::string options;
300 #define OPTION_APPEND( X ) \
301 { \
302 if( options.length() ) options += ";"; \
303 options += ( X ); \
304 }
305
306 if( po.numOptSet( "no-attribs" ) )
307 {
308 OPTION_APPEND( "CGM_ATTRIBS=no" );
309 }
310
311 if( po.numOptSet( "fatal_curves" ) )
312 {
313 OPTION_APPEND( "FATAL_ON_CURVES" );
314 }
315
316 if( po.numOptSet( "all-warnings" ) )
317 {
318 OPTION_APPEND( "VERBOSE_CGM_WARNINGS" );
319 }
320
321
322
323 double tol;
324 static const int tol_prec = 12;
325 if( po.getOpt( "ftol", &tol ) )
326 {
327 std::stringstream s;
328 s << "FACET_DISTANCE_TOLERANCE=" << std::setprecision( tol_prec ) << tol;
329 OPTION_APPEND( s.str() );
330 }
331
332 if( po.getOpt( "ltol", &tol ) )
333 {
334 std::stringstream s;
335 s << "MAX_FACET_EDGE_LENGTH=" << std::setprecision( tol_prec ) << tol;
336 OPTION_APPEND( s.str() );
337 }
338
339 int atol;
340 if( po.getOpt( "atol", &atol ) )
341 {
342 std::stringstream s;
343 s << "FACET_NORMAL_TOLERANCE=" << atol;
344 OPTION_APPEND( s.str() );
345 }
346
347 #undef OPTION_APPEND
348
349
350 if( verbose )
351 {
352 std::cout << "Loading file " << input_file << std::endl;
353 if( options.length() ) std::cout << "Option string: " << options << std::endl;
354 }
355
356 EntityHandle input_file_set;
357 ErrorCode ret;
358 Core mbi;
359
360 ret = mbi.create_meshset( 0, input_file_set );
361 CHECKERR( mbi, ret );
362
363 ret = mbi.load_file( input_file.c_str(), &input_file_set, options.c_str() );
364 if( ret == MB_UNHANDLED_OPTION )
365 {
366 std::cerr << "Warning: unhandled option while loading input_file, will proceed anyway" << std::endl;
367 }
368 else
369 {
370 CHECKERR( mbi, ret );
371 }
372
373
374
375 std::vector< std::string > m_list;
376 po.getOptAllArgs( ",m", m_list );
377
378 if( m_list.size() > 0 )
379 {
380
381 if( obb_task )
382 {
383 std::cerr << "Warning: using obb features in conjunction with -m may not work correctly!" << std::endl;
384 }
385
386
387 Tag dimTag, idTag, senseTag;
388 ret = mbi.tag_get_handle( GEOM_DIMENSION_TAG_NAME, 1, MB_TYPE_INTEGER, dimTag, MB_TAG_SPARSE | MB_TAG_CREAT );
389 CHECKERR( mbi, ret );
390
391 idTag = mbi.globalId_tag();
392
393 ret = mbi.tag_get_handle( "GEOM_SENSE_2", 2, MB_TYPE_HANDLE, senseTag, MB_TAG_SPARSE | MB_TAG_CREAT );
394 CHECKERR( mbi, ret );
395
396 for( std::vector< std::string >::iterator i = m_list.begin(); i != m_list.end(); ++i )
397 {
398 std::cout << "Loading alternate mesh file " << *i << std::endl;
399
400 EntityHandle alt_file_set;
401 ret = mbi.create_meshset( 0, alt_file_set );
402 CHECKERR( mbi, ret );
403
404 ret = mbi.load_file( ( *i ).c_str(), &alt_file_set );
405 CHECKERR( mbi, ret );
406
407 if( verbose ) std::cout << "Merging input surfaces..." << std::flush;
408
409 ret = merge_input_surfs( &mbi, input_file_set, alt_file_set, idTag, dimTag, senseTag );
410 CHECKERR( mbi, ret );
411
412 if( verbose ) std::cout << "done." << std::endl;
413 }
414 }
415
416
417
418 if( !po.numOptSet( "no-outmesh" ) )
419 {
420 if( verbose )
421 {
422 std::cout << "Writing " << output_file << std::endl;
423 }
424 ret = mbi.write_file( output_file.c_str(), NULL, NULL, &input_file_set, 1 );
425 CHECKERR( mbi, ret );
426 }
427
428
429 if( obb_task )
430 {
431
432 if( verbose )
433 {
434 std::cout << "Loading data into GeomTopoTool" << std::endl;
435 }
436 GeomTopoTool* gtt = new GeomTopoTool( &mbi, false );
437 ret = gtt->find_geomsets();
438 CHECKERR( *gtt, ret );
439 ret = gtt->construct_obb_trees();
440 CHECKERR( *gtt, ret );
441 }
442 return 0;
443 }