Mesh Oriented datABase  (version 5.5.0)
An array-based unstructured mesh library
cub2h5m.cpp
Go to the documentation of this file.
1 #include "moab/GeomQueryTool.hpp"
2 #include "moab/GeomTopoTool.hpp"
3 #include "InitCGMA.hpp"
4 #include "CGMApp.hpp"
5 #include "moab/CN.hpp"
6 #include "moab/Core.hpp"
7 #include "moab/CartVect.hpp"
8 #include "moab/FileOptions.hpp"
9 #include "moab/Skinner.hpp"
10 #include "quads_to_tris.hpp"
11 #include <limits>
12 #include <cstdlib>
13 #include <sstream>
14 #include <ctime>
15 
16 #define GF_CUBIT_FILE_TYPE "CUBIT"
17 #define GF_STEP_FILE_TYPE "STEP"
18 #define GF_IGES_FILE_TYPE "IGES"
19 #define GF_ACIS_TXT_FILE_TYPE "ACIS_SAT"
20 #define GF_ACIS_BIN_FILE_TYPE "ACIS_SAB"
21 #define GF_OCC_BREP_FILE_TYPE "OCC"
22 
23 using namespace moab;
24 
25 void tokenize( const std::string& str, std::vector< std::string >& tokens, const char* delimiters )
26 {
27  std::string::size_type last = str.find_first_not_of( delimiters, 0 );
28  std::string::size_type pos = str.find_first_of( delimiters, last );
29  if( std::string::npos == pos )
30  tokens.push_back( str );
31  else
32  while( std::string::npos != pos && std::string::npos != last )
33  {
34  tokens.push_back( str.substr( last, pos - last ) );
35  last = str.find_first_not_of( delimiters, pos );
36  pos = str.find_first_of( delimiters, last );
37  if( std::string::npos == pos ) pos = str.size();
38  }
39 }
40 
42  const EntityHandle group_set,
43  const Tag nameTag,
44  std::vector< std::string >& grp_names )
45 {
46  // get names
47  char name0[NAME_TAG_SIZE];
48  std::fill( name0, name0 + NAME_TAG_SIZE, '\0' );
49  ErrorCode result = MBI->tag_get_data( nameTag, &group_set, 1, &name0 );
50  if( MB_SUCCESS != result && MB_TAG_NOT_FOUND != result ) return MB_FAILURE;
51 
52  if( MB_TAG_NOT_FOUND != result ) grp_names.push_back( std::string( name0 ) );
53 
54  return MB_SUCCESS;
55 }
56 
57 // For each material, sum the volume. If the coordinates were updated for
58 // deformation, summarize the volume change.
60  const EntityHandle cgm_file_set,
61  const Tag categoryTag,
62  const Tag dimTag,
63  const Tag sizeTag,
64  const Tag nameTag,
65  const Tag idTag,
66  const bool conserve_mass,
67  const bool debug )
68 {
69  // get groups
70  ErrorCode rval;
71  const char group_category[CATEGORY_TAG_SIZE] = { "Group\0" };
72  const void* const group_val[] = { &group_category };
73  Range groups;
74  rval = MBI->get_entities_by_type_and_tag( 0, MBENTITYSET, &categoryTag, group_val, 1, groups );
75  if( MB_SUCCESS != rval ) return rval;
76 
77  // Get the maximum group id. This is so that new groups do not have
78  // duplicate ids.
79  int max_grp_id = -1;
80  for( Range::const_iterator i = groups.begin(); i != groups.end(); ++i )
81  {
82  int grp_id;
83  rval = MBI->tag_get_data( idTag, &( *i ), 1, &grp_id );
84  if( MB_SUCCESS != rval ) return rval;
85  if( max_grp_id < grp_id ) max_grp_id = grp_id;
86  }
87  if( conserve_mass )
88  {
89  std::cout << " Altering group densities to conserve mass for each volume." << std::endl;
90  std::cout << " maximum group id=" << max_grp_id << std::endl;
91  }
92 
93  for( Range::const_iterator i = groups.begin(); i != groups.end(); ++i )
94  {
95  // get group names
96  std::vector< std::string > grp_names;
97  rval = get_group_names( MBI, *i, nameTag, grp_names );
98  if( MB_SUCCESS != rval ) return MB_FAILURE;
99 
100  // determine if it is a material group
101  bool material_grp = false;
102  int mat_id = -1;
103  double rho = 0;
104  for( std::vector< std::string >::const_iterator j = grp_names.begin(); j != grp_names.end(); ++j )
105  {
106  if( std::string::npos != ( *j ).find( "mat" ) && std::string::npos != ( *j ).find( "rho" ) )
107  {
108  material_grp = true;
109  std::cout << " material group: " << *j << std::endl;
110 
111  // get the density and material id
112  std::vector< std::string > tokens;
113  tokenize( *j, tokens, "_" );
114  mat_id = atoi( tokens[1].c_str() );
115  rho = atof( tokens[3].c_str() );
116  }
117  }
118  if( !material_grp ) continue;
119 
120  // get the volume sets of the material group
121  const int three = 3;
122  const void* const three_val[] = { &three };
123  Range vols;
124  rval = MBI->get_entities_by_type_and_tag( *i, MBENTITYSET, &dimTag, three_val, 1, vols );
125  if( MB_SUCCESS != rval ) return rval;
126 
127  // for each volume, sum predeformed and deformed volume
128  double orig_grp_volume = 0, defo_grp_volume = 0;
129 
130  moab::GeomTopoTool gtt = moab::GeomTopoTool( MBI, false );
132  for( Range::const_iterator j = vols.begin(); j != vols.end(); ++j )
133  {
134  double defo_size = 0, orig_size = 0;
135  rval = gqt.measure_volume( *j, defo_size );
136  if( MB_SUCCESS != rval ) return rval;
137  defo_grp_volume += defo_size;
138  rval = MBI->tag_get_data( sizeTag, &( *j ), 1, &orig_size );
139  if( MB_SUCCESS != rval ) return rval;
140  orig_grp_volume += orig_size;
141 
142  // calculate a new density to conserve mass through the deformation
143  if( !conserve_mass ) continue;
144  double new_rho = rho * orig_size / defo_size;
145 
146  // create a group for the volume with modified density
147  EntityHandle new_grp;
148  rval = MBI->create_meshset( MESHSET_SET, new_grp );
149  if( MB_SUCCESS != rval ) return rval;
150  std::stringstream new_name_ss;
151  new_name_ss << "mat_" << mat_id << "_rho_" << new_rho << "\0";
152  std::string new_name;
153  new_name_ss >> new_name;
154  rval = MBI->tag_set_data( nameTag, &new_grp, 1, new_name.c_str() );
155  if( MB_SUCCESS != rval ) return rval;
156  max_grp_id++;
157  rval = MBI->tag_set_data( idTag, &new_grp, 1, &max_grp_id );
158  if( MB_SUCCESS != rval ) return rval;
159  const char group_category2[CATEGORY_TAG_SIZE] = "Group\0";
160  rval = MBI->tag_set_data( categoryTag, &new_grp, 1, group_category2 );
161  if( MB_SUCCESS != rval ) return rval;
162 
163  // add the volume to the new group
164  rval = MBI->add_entities( new_grp, &( *j ), 1 );
165  if( MB_SUCCESS != rval ) return rval;
166 
167  // add the new grp to the cgm_file_set
168  rval = MBI->add_entities( cgm_file_set, &new_grp, 1 );
169  if( MB_SUCCESS != rval ) return rval;
170 
171  // remove the volume from the old group
172  rval = MBI->remove_entities( *i, &( *j ), 1 );
173  if( MB_SUCCESS != rval ) return rval;
174  if( debug ) std::cout << " new group: " << new_name << " id=" << max_grp_id << std::endl;
175  }
176 
177  std::cout << " orig_volume=" << orig_grp_volume << " defo_volume=" << defo_grp_volume
178  << " defo/orig=" << defo_grp_volume / orig_grp_volume << std::endl;
179  }
180 
181  return MB_SUCCESS;
182 }
183 
184 // We cannot build an OBB tree if all of a volume's surfaces have no facets.
185 // To prevent this, remove the cgm surface set if the cub surface set exists,
186 // but had its faced removed (due to dead elements). Remember that the cgm_file_set
187 // is not TRACKING.
189  const EntityHandle cgm_file_set,
190  const Tag idTag,
191  const Tag dimTag,
192  const bool /*debug */ )
193 {
194 
195  ErrorCode result;
196  const int two = 2;
197  const void* const two_val[] = { &two };
198  Range cgm_surfs;
199  result = MBI->get_entities_by_type_and_tag( cgm_file_set, MBENTITYSET, &dimTag, two_val, 1, cgm_surfs );
200  if( MB_SUCCESS != result ) return result;
201 
202  for( Range::iterator i = cgm_surfs.begin(); i != cgm_surfs.end(); ++i )
203  {
204  int n_tris;
205  result = MBI->get_number_entities_by_type( *i, MBTRI, n_tris );
206  if( MB_SUCCESS != result ) return result;
207 
208  if( 0 == n_tris )
209  {
210  int surf_id;
211  result = MBI->tag_get_data( idTag, &( *i ), 1, &surf_id );
212  assert( MB_SUCCESS == result );
213 
214  Range parent_vols;
215  result = MBI->get_parent_meshsets( *i, parent_vols );
216  assert( MB_SUCCESS == result );
217  for( Range::iterator j = parent_vols.begin(); j != parent_vols.end(); ++j )
218  {
219  result = MBI->remove_parent_child( *j, *i );
220  assert( MB_SUCCESS == result );
221  }
222  Range child_curves;
223  result = MBI->get_child_meshsets( *i, child_curves );
224  assert( MB_SUCCESS == result );
225  for( Range::iterator j = child_curves.begin(); j != child_curves.end(); ++j )
226  {
227  result = MBI->remove_parent_child( *i, *j );
228  assert( MB_SUCCESS == result );
229  }
230  result = MBI->remove_entities( cgm_file_set, &( *i ), 1 );
231  assert( MB_SUCCESS == result );
232 
233  // Is the set contained anywhere else? If the surface is in a CUBIT group,
234  // such as "unmerged_surfs" it will cause write_mesh to fail. This should
235  // be a MOAB bug.
236  Range all_sets;
237  result = MBI->get_entities_by_type( 0, MBENTITYSET, all_sets );
238  assert( MB_SUCCESS == result );
239  for( Range::iterator j = all_sets.begin(); j != all_sets.end(); ++j )
240  {
241  if( MBI->contains_entities( *j, &( *i ), 1 ) )
242  {
243  result = MBI->remove_entities( *j, &( *i ), 1 );
244  assert( MB_SUCCESS == result );
245  }
246  }
247 
248  result = MBI->delete_entities( &( *i ), 1 );
249  assert( MB_SUCCESS == result );
250  std::cout << " Surface " << surf_id << ": removed because all of its mesh faces have been removed"
251  << std::endl;
252  }
253  }
254 
255  // get all volumes
256  const int three = 3;
257  const void* const three_val[] = { &three };
258  Range cgm_vols;
259  result = MBI->get_entities_by_type_and_tag( cgm_file_set, MBENTITYSET, &dimTag, three_val, 1, cgm_vols );
260  if( MB_SUCCESS != result ) return result;
261 
262  for( Range::iterator i = cgm_vols.begin(); i != cgm_vols.end(); ++i )
263  {
264  // get the volume's number of surfaces
265  int n_surfs;
266  result = MBI->num_child_meshsets( *i, &n_surfs );
267  assert( MB_SUCCESS == result );
268 
269  // if no surfaces remain, remove the volume
270  if( 0 == n_surfs )
271  {
272  int vol_id;
273  result = MBI->tag_get_data( idTag, &( *i ), 1, &vol_id );
274  assert( MB_SUCCESS == result );
275  // Is the set contained anywhere else? If the surface is in a CUBIT group,
276  // such as "unmerged_surfs" it will cause write_mesh to fail. This should
277  // be a MOAB bug.
278  Range all_sets;
279  result = MBI->get_entities_by_type( 0, MBENTITYSET, all_sets );
280  assert( MB_SUCCESS == result );
281  for( Range::iterator j = all_sets.begin(); j != all_sets.end(); ++j )
282  {
283  if( MBI->contains_entities( *j, &( *i ), 1 ) )
284  {
285  result = MBI->remove_entities( *j, &( *i ), 1 );
286  assert( MB_SUCCESS == result );
287  }
288  }
289  result = MBI->delete_entities( &( *i ), 1 );
290  assert( MB_SUCCESS == result );
291  std::cout << " Volume " << vol_id << ": removed because all of its surfaces have been removed"
292  << std::endl;
293  }
294  }
295  return MB_SUCCESS;
296 }
297 
298 // Given parent volume senses, an id, and a set handle, this function creates a
299 // new surface set with dimension, geometry category, id, and sense tags.
301  EntityHandle& new_surf,
302  const EntityHandle forward_parent_vol,
303  const EntityHandle reverse_parent_vol,
304  const int new_surf_id,
305  const Tag dimTag,
306  const Tag idTag,
307  const Tag categoryTag,
308  const Tag senseTag )
309 {
310 
311  ErrorCode result;
312  result = MBI->create_meshset( 0, new_surf );
313  if( MB_SUCCESS != result ) return result;
314  if( 0 != forward_parent_vol )
315  {
316  result = MBI->add_parent_child( forward_parent_vol, new_surf );
317  if( MB_SUCCESS != result ) return result;
318  }
319  if( 0 != reverse_parent_vol )
320  {
321  result = MBI->add_parent_child( reverse_parent_vol, new_surf );
322  if( MB_SUCCESS != result ) return result;
323  }
324  const int two = 2;
325  result = MBI->tag_set_data( dimTag, &new_surf, 1, &two );
326  if( MB_SUCCESS != result ) return result;
327  result = MBI->tag_set_data( idTag, &new_surf, 1, &new_surf_id );
328  if( MB_SUCCESS != result ) return result;
329  const char geom_category[CATEGORY_TAG_SIZE] = { "Surface\0" };
330  result = MBI->tag_set_data( categoryTag, &new_surf, 1, &geom_category );
331  if( MB_SUCCESS != result ) return result;
332  EntityHandle vols[2] = { forward_parent_vol, reverse_parent_vol };
333  result = MBI->tag_set_data( senseTag, &new_surf, 1, vols );
334  if( MB_SUCCESS != result ) return result;
335 
336  return MB_SUCCESS;
337 }
338 
339 // Given a face, orient it outward wrt its adjacent mesh element.
340 // Each face must be adjacent to exactly one mesh element.
341 ErrorCode orient_faces_outward( Interface* MBI, const Range& faces, const bool /*debug*/ )
342 {
343 
344  ErrorCode result;
345  for( Range::const_iterator i = faces.begin(); i != faces.end(); ++i )
346  {
347  Range adj_elem;
348  result = MBI->get_adjacencies( &( *i ), 1, 3, false, adj_elem );
349  if( MB_SUCCESS != result ) return result;
350  if( 1 != adj_elem.size() ) return MB_INVALID_SIZE;
351 
352  // get connectivity for element and face
353  const EntityHandle* elem_conn;
354  int elem_n_nodes;
355  result = MBI->get_connectivity( adj_elem.front(), elem_conn, elem_n_nodes );
356  if( MB_SUCCESS != result ) return result;
357  const EntityHandle* face_conn;
358  int face_n_nodes;
359  result = MBI->get_connectivity( *i, face_conn, face_n_nodes );
360  if( MB_SUCCESS != result ) return result;
361 
362  // Get the sense of the face wrt the element
363  EntityType elem_type = MBI->type_from_handle( adj_elem.front() );
364  EntityType face_type = MBI->type_from_handle( *i );
365  int face_num, offset;
366  int sense = 0;
367  const int face_dim = CN::Dimension( face_type );
368  int rval = CN::SideNumber( elem_type, elem_conn, face_conn, face_n_nodes, face_dim, face_num, sense, offset );
369  if( 0 != rval ) return MB_FAILURE;
370 
371  // If the face is not oriented outward wrt the element, reverse it
372  if( -1 == sense )
373  {
374  EntityHandle new_face_conn[4] = { face_conn[3], face_conn[2], face_conn[1], face_conn[0] };
375  result = MBI->set_connectivity( *i, new_face_conn, 4 );
376  if( MB_SUCCESS != result ) return result;
377  }
378  }
379  return MB_SUCCESS;
380 }
381 
382 /* Isolate dead code in a preproc-killed block - sjackson 11/22/10 */
383 #if 0
384 
385 /* qsort int comparison function */
386 int handle_compare(const void *a, const void *b)
387 {
388  const EntityHandle *ia = (const EntityHandle *)a; // casting pointer types
389  const EntityHandle *ib = (const EntityHandle *)b;
390  return *ia - *ib;
391  /* integer comparison: returns negative if b > a
392  and positive if a > b */
393 }
394 
395 // qsort face comparison function. assume each face has 4 nodes
396 int compare_face(const void *a, const void *b)
397 {
398  EntityHandle *ia = (EntityHandle *)a;
399  EntityHandle *ib = (EntityHandle *)b;
400  if(*ia == *ib)
401  {
402  if(*(ia+1) == *(ib+1))
403  {
404  if(*(ia+2) == *(ib+2))
405  {
406  return (int)(*(ia+3) - *(ib+3));
407  }
408  else
409  {
410  return (int)(*(ia+2) - *(ib+2));
411  }
412  }
413  else
414  {
415  return (int)(*(ia+1) - *(ib+1));
416  }
417  }
418  else
419  {
420  return (int)(*ia - *ib);
421  }
422 }
423 
424 // Use this to get quad faces from hex elems.
425 ErrorCode skin_hex_elems(Interface *MBI, Range elems, const int dim,
426  Range &faces )
427 {
428  // get faces of each hex
429  const int nodes_per_face = 4;
430  const unsigned int faces_per_elem = 6;
431  unsigned int n_faces = faces_per_elem*elems.size();
432  EntityHandle f[n_faces][nodes_per_face];
433  ErrorCode result;
434  int counter = 0;
435  for(Range::iterator i=elems.begin(); i!=elems.end(); ++i)
436  {
437  Range elem_faces;
438  result = MBI->get_adjacencies( &(*i), 1, 2, true, elem_faces );
439  if(MB_SUCCESS != result) return result;
440  if(faces_per_elem != elem_faces.size()) return MB_INVALID_SIZE;
441  for(Range::iterator j=elem_faces.begin(); j!=elem_faces.end(); ++j)
442  {
443  const EntityHandle *conn;
444  int n_nodes;
445  ErrorCode result = MBI->get_connectivity( *j, conn, n_nodes );
446  if(MB_SUCCESS != result) return result;
447  if(nodes_per_face != n_nodes) return MB_INVALID_SIZE;
448  // Sort the node handles of the face
449  for(int k=0; k<nodes_per_face; ++k) f[counter][k] = conn[k];
450  qsort( &f[counter][0], nodes_per_face, sizeof(EntityHandle), handle_compare );
451  ++counter;
452  }
453  }
454 
455  // Sort the faces by the first node handle, then second node, then third node...
456  qsort( &f[0][0], n_faces, nodes_per_face*sizeof(EntityHandle), compare_face );
457 
458  // if a face has 1 or more duplicates, it is not on the skin
459  faces.clear();
460  for(unsigned int i=0; i<n_faces; ++i)
461  {
462  // if the last face is tested, it must be on the skin
463  if(n_faces-1 == i)
464  {
465  Range face_handle;
466  result = MBI->get_adjacencies( &(f[i][0]), nodes_per_face, 2, false, face_handle );
467  if(MB_SUCCESS != result) return result;
468  if(1 != face_handle.size()) return MB_INVALID_SIZE;
469  faces.insert( face_handle.front() );
470  // Due to sort, if a duplicate exists it must be next
471  }
472  else if( f[i][0]==f[i+1][0] && f[i][1]==f[i+1][1] &&
473  f[i][2]==f[i+1][2] && f[i][3]==f[i+1][3] )
474  {
475  ++i;
476  while( f[i][0]==f[i+1][0] && f[i][1]==f[i+1][1] &&
477  f[i][2]==f[i+1][2] && f[i][3]==f[i+1][3] )
478  {
479  std::cout << " skin WARNING: non-manifold face" << std::endl;
480  ++i;
481  }
482  // otherwise it is on the skin
483  }
484  else
485  {
486  Range face_handle;
487  result = MBI->get_adjacencies( &(f[i][0]), nodes_per_face, 2, false, face_handle );
488  if(MB_SUCCESS != result) return result;
489  if(1 != face_handle.size()) return MB_INVALID_SIZE;
490  faces.insert( face_handle.front() );
491  }
492  }
493 
494  return MB_SUCCESS;
495 }
496 
497 #endif // dead code isolation
498 // Given a 1D array of data, axis labels, title, and number of bins, create a
499 // histogram.
500 void plot_histogram( const std::string& title,
501  const std::string& x_axis_label,
502  const std::string& y_axis_label,
503  const int n_bins,
504  const double data[],
505  const int n_data )
506 {
507  // find max and min
508  double min = std::numeric_limits< double >::max();
509  double max = -std::numeric_limits< double >::max();
510  for( int i = 0; i < n_data; ++i )
511  {
512  if( min > data[i] ) min = data[i];
513  if( max < data[i] ) max = data[i];
514  }
515 
516  // make bins for histogram
517  double bin_width = ( max - min ) / n_bins;
518  std::vector< int > bins( n_bins );
519  for( int i = 0; i < n_bins; ++i )
520  bins[i] = 0;
521 
522  // fill the bins
523  for( int i = 0; i < n_data; ++i )
524  {
525  double diff = data[i] - min;
526  int bin = diff / bin_width;
527  if( 9 < bin ) bin = 9; // cheap fix for numerical precision error
528  if( 0 > bin ) bin = 0; // cheap fix for numerical precision error
529  ++bins[bin];
530  }
531 
532  // create bars
533  int max_bin = 0;
534  for( int i = 0; i < n_bins; ++i )
535  if( max_bin < bins[i] ) max_bin = bins[i];
536  int bar_height;
537  int max_bar_chars = 72;
538  std::vector< std::string > bars( n_bins );
539  for( int i = 0; i < n_bins; ++i )
540  {
541  bar_height = ( max_bar_chars * bins[i] ) / max_bin;
542  for( int j = 0; j < bar_height; ++j )
543  bars[i] += "*";
544  }
545 
546  // print histogram header
547  std::cout << std::endl;
548  std::cout << " " << title << std::endl;
549 
550  // print results
551  std::cout.width( 15 );
552  std::cout << min << std::endl;
553  for( int i = 0; i < n_bins; ++i )
554  {
555  std::cout.width( 15 );
556  std::cout << min + ( ( i + 1 ) * bin_width );
557  std::cout.width( max_bar_chars );
558  std::cout << bars[i] << bins[i] << std::endl;
559  }
560 
561  // print histogram footer
562  std::cout.width( 15 );
563  std::cout << y_axis_label;
564  std::cout.width( max_bar_chars );
565  std::cout << " " << x_axis_label << std::endl;
566  std::cout << std::endl;
567 }
568 
569 // This is a helper function that creates data and labels for histograms.
570 void generate_plots( const double orig[], const double defo[], const int n_elems, const std::string& time_step )
571 {
572 
573  // find volume ratio then max and min
574  double* ratio = new double[n_elems];
575  for( int i = 0; i < n_elems; ++i )
576  ratio[i] = ( defo[i] - orig[i] ) / orig[i];
577 
578  plot_histogram( "Predeformed Element Volume", "Num_Elems", "Volume [cc]", 10, orig, n_elems );
579  std::string title = "Element Volume Change Ratio at Time Step " + time_step;
580  plot_histogram( title, "Num_Elems", "Volume Ratio", 10, ratio, n_elems );
581  delete[] ratio;
582 }
583 
584 // Given four nodes, calculate the tet volume.
585 inline static double tet_volume( const CartVect& v0, const CartVect& v1, const CartVect& v2, const CartVect& v3 )
586 {
587  return 1. / 6. * ( ( ( v1 - v0 ) * ( v2 - v0 ) ) % ( v3 - v0 ) );
588 }
589 
590 // Measure and tet volume are taken from measure.cpp
591 double measure( Interface* MBI, const EntityHandle element )
592 {
593  EntityType type = MBI->type_from_handle( element );
594 
595  const EntityHandle* conn;
596  int num_vertices;
597  ErrorCode result = MBI->get_connectivity( element, conn, num_vertices );
598  if( MB_SUCCESS != result ) return result;
599 
600  std::vector< CartVect > coords( num_vertices );
601  result = MBI->get_coords( conn, num_vertices, coords[0].array() );
602  if( MB_SUCCESS != result ) return result;
603 
604  switch( type )
605  {
606  case MBEDGE:
607  return ( coords[0] - coords[1] ).length();
608  case MBTRI:
609  return 0.5 * ( ( coords[1] - coords[0] ) * ( coords[2] - coords[0] ) ).length();
610  case MBQUAD:
611  num_vertices = 4;
612  case MBPOLYGON: {
613  CartVect mid( 0, 0, 0 );
614  for( int i = 0; i < num_vertices; ++i )
615  mid += coords[i];
616  mid /= num_vertices;
617 
618  double sum = 0.0;
619  for( int i = 0; i < num_vertices; ++i )
620  {
621  int j = ( i + 1 ) % num_vertices;
622  sum += ( ( mid - coords[i] ) * ( mid - coords[j] ) ).length();
623  }
624  return 0.5 * sum;
625  }
626  case MBTET:
627  return tet_volume( coords[0], coords[1], coords[2], coords[3] );
628  case MBPYRAMID:
629  return tet_volume( coords[0], coords[1], coords[2], coords[4] ) +
630  tet_volume( coords[0], coords[2], coords[3], coords[4] );
631  case MBPRISM:
632  return tet_volume( coords[0], coords[1], coords[2], coords[5] ) +
633  tet_volume( coords[3], coords[5], coords[4], coords[0] ) +
634  tet_volume( coords[1], coords[4], coords[5], coords[0] );
635  case MBHEX:
636  return tet_volume( coords[0], coords[1], coords[3], coords[4] ) +
637  tet_volume( coords[7], coords[3], coords[6], coords[4] ) +
638  tet_volume( coords[4], coords[5], coords[1], coords[6] ) +
639  tet_volume( coords[1], coords[6], coords[3], coords[4] ) +
640  tet_volume( coords[2], coords[6], coords[3], coords[1] );
641  default:
642  return 0.0;
643  }
644 }
645 
646 /* Calculate the signed volumes beneath the surface (x 6.0). Use the triangle's
647  canonical sense. Do not take sense tags into account. Code taken from
648  DagMC::measure_volume.
649 
650  Special Case: If the surface is planar, and the plane includes the origin,
651  the signed volume will be ~0. If the signed volume is ~0 then offset everything
652  by a random amount and try again. */
654  const EntityHandle surf_set,
655  const CartVect& offset,
656  double& signed_volume )
657 {
658  ErrorCode rval;
659  Range tris;
660  rval = MBI->get_entities_by_type( surf_set, MBTRI, tris );
661  if( MB_SUCCESS != rval ) return rval;
662  signed_volume = 0.0;
663  const EntityHandle* conn;
664  int len;
665  CartVect coords[3];
666  for( Range::iterator j = tris.begin(); j != tris.end(); ++j )
667  {
668  rval = MBI->get_connectivity( *j, conn, len, true );
669  if( MB_SUCCESS != rval ) return rval;
670  if( 3 != len ) return MB_INVALID_SIZE;
671  rval = MBI->get_coords( conn, 3, coords[0].array() );
672  if( MB_SUCCESS != rval ) return rval;
673 
674  // Apply offset to avoid calculating 0 for cases when the origin is in the
675  // plane of the surface.
676  for( unsigned int k = 0; k < 3; ++k )
677  {
678  coords[k][0] += offset[0];
679  coords[k][1] += offset[1];
680  coords[k][2] += offset[2];
681  }
682 
683  coords[1] -= coords[0];
684  coords[2] -= coords[0];
685  signed_volume += ( coords[0] % ( coords[1] * coords[2] ) );
686  }
687  return MB_SUCCESS;
688 }
689 
690 // The cgm and cub surfaces may not have the same sense. Create triangles that
691 // represent the quads in the cub surface. Calculate the signed volume of both
692 // the cgm and cub surface. If they are different, change the cgm sense so that
693 // it matches the sense of the cub surface.
695  const EntityHandle cgm_file_set,
696  const EntityHandle cub_file_set,
697  const Tag idTag,
698  const Tag dimTag,
699  const Tag senseTag,
700  const bool debug )
701 {
702  ErrorCode result;
703  const int two = 2;
704  const void* const two_val[] = { &two };
705  Range cgm_surfs;
706  result = MBI->get_entities_by_type_and_tag( cgm_file_set, MBENTITYSET, &dimTag, two_val, 1, cgm_surfs );
707  if( MB_SUCCESS != result ) return result;
708  for( Range::iterator i = cgm_surfs.begin(); i != cgm_surfs.end(); ++i )
709  {
710  int surf_id;
711  result = MBI->tag_get_data( idTag, &( *i ), 1, &surf_id );
712  if( MB_SUCCESS != result ) return result;
713 
714  // Find the meshed surface set with the same id
715  Range cub_surf;
716  const Tag tags[] = { idTag, dimTag };
717  const void* const tag_vals[] = { &surf_id, &two };
718  result = MBI->get_entities_by_type_and_tag( cub_file_set, MBENTITYSET, tags, tag_vals, 2, cub_surf );
719  if( MB_SUCCESS != result ) return result;
720  if( 1 != cub_surf.size() )
721  {
722  std::cout << " Surface " << surf_id << ": no meshed representation found, using CAD representation instead"
723  << std::endl;
724  continue;
725  }
726 
727  // Get tris that represent the quads of the cub surf
728  Range quads;
729  result = MBI->get_entities_by_type( cub_surf.front(), MBQUAD, quads );
730  if( MB_SUCCESS != result ) return result;
731  Range cub_tris;
732  result = make_tris_from_quads( MBI, quads, cub_tris );
733  if( MB_SUCCESS != result ) return result;
734 
735  // Add the tris to the same surface meshset as the quads are inside.
736  result = MBI->add_entities( cub_surf.front(), cub_tris );
737  if( MB_SUCCESS != result ) return result;
738 
739  // get the signed volume for each surface representation. Keep trying until
740  // The signed volumes are much greater than numerical precision. Planar
741  // surfaces will have a signed volume of zero if the plane goes through the
742  // origin, unless we apply an offset.
743  const int n_attempts = 100;
744  const int max_random = 10;
745  const double min_signed_vol = 0.1;
746  double cgm_signed_vol, cub_signed_vol;
747  for( int j = 0; j < n_attempts; ++j )
748  {
749  cgm_signed_vol = 0;
750  cub_signed_vol = 0;
751  CartVect offset( std::rand() % max_random, std::rand() % max_random, std::rand() % max_random );
752  result = get_signed_volume( MBI, *i, offset, cgm_signed_vol );
753  if( MB_SUCCESS != result ) return result;
754  result = get_signed_volume( MBI, cub_surf.front(), offset, cub_signed_vol );
755  if( MB_SUCCESS != result ) return result;
756  if( debug )
757  std::cout << " surf_id=" << surf_id << " cgm_signed_vol=" << cgm_signed_vol
758  << " cub_signed_vol=" << cub_signed_vol << std::endl;
759  if( fabs( cgm_signed_vol ) > min_signed_vol && fabs( cub_signed_vol ) > min_signed_vol ) break;
760  if( n_attempts == j + 1 )
761  {
762  std::cout << "error: signed volume could not be calculated unambiguously" << std::endl;
763  return MB_FAILURE;
764  }
765  }
766 
767  // If the sign is different, reverse the cgm senses so that both
768  // representations have the same signed volume.
769  if( ( cgm_signed_vol < 0 && cub_signed_vol > 0 ) || ( cgm_signed_vol > 0 && cub_signed_vol < 0 ) )
770  {
771  EntityHandle cgm_surf_volumes[2], reversed_cgm_surf_volumes[2];
772  result = MBI->tag_get_data( senseTag, &( *i ), 1, cgm_surf_volumes );
773  if( MB_SUCCESS != result ) return result;
774  if( MB_SUCCESS != result ) return result;
775 
776  reversed_cgm_surf_volumes[0] = cgm_surf_volumes[1];
777  reversed_cgm_surf_volumes[1] = cgm_surf_volumes[0];
778 
779  result = MBI->tag_set_data( senseTag, &( *i ), 1, reversed_cgm_surf_volumes );
780  if( MB_SUCCESS != result ) return result;
781  }
782  }
783 
784  return MB_SUCCESS;
785 }
786 
787 // The quads in the cub_file_set have been updated for dead elements. For each
788 // cgm_surf, if there exists a cub_surf with the same id, replace the cgm tris
789 // with cub_tris (created from the quads). Note the a surface that is not
790 // meshed (in cub file) will not be effected.
792  const EntityHandle cgm_file_set,
793  const EntityHandle cub_file_set,
794  const Tag idTag,
795  const Tag dimTag,
796  const bool debug )
797 {
798  ErrorCode result;
799  const int two = 2;
800  const void* const two_val[] = { &two };
801  Range cgm_surfs;
802  result = MBI->get_entities_by_type_and_tag( cgm_file_set, MBENTITYSET, &dimTag, two_val, 1, cgm_surfs );
803  if( MB_SUCCESS != result ) return result;
804 
805  for( Range::iterator i = cgm_surfs.begin(); i != cgm_surfs.end(); ++i )
806  {
807  int surf_id;
808  result = MBI->tag_get_data( idTag, &( *i ), 1, &surf_id );
809  if( MB_SUCCESS != result ) return result;
810  if( debug ) std::cout << "surf_id=" << surf_id << std::endl;
811 
812  // Find the meshed surface set with the same id
813  Range cub_surf;
814  const Tag tags[] = { idTag, dimTag };
815  const void* const tag_vals[] = { &surf_id, &two };
816  result = MBI->get_entities_by_type_and_tag( cub_file_set, MBENTITYSET, tags, tag_vals, 2, cub_surf );
817  if( MB_SUCCESS != result ) return result;
818  if( 1 != cub_surf.size() )
819  {
820  std::cout << " Surface " << surf_id << ": no meshed representation found, using CAD representation instead"
821  << std::endl;
822  continue;
823  }
824 
825  // Get tris that represent the quads of the cub surf
826  Range quads;
827  result = MBI->get_entities_by_type( cub_surf.front(), MBQUAD, quads );
828  if( MB_SUCCESS != result ) return result;
829 
830  Range cub_tris;
831  result = make_tris_from_quads( MBI, quads, cub_tris );
832  if( MB_SUCCESS != result ) return result;
833 
834  // Remove the tris from the cgm surf. Don't forget to remove them from the
835  // cgm_file_set because it is not TRACKING.
836  Range cgm_tris;
837  result = MBI->get_entities_by_type( *i, MBTRI, cgm_tris );
838  if( MB_SUCCESS != result ) return result;
839  result = MBI->remove_entities( *i, cgm_tris );
840  if( MB_SUCCESS != result ) return result;
841  result = MBI->remove_entities( cgm_file_set, cgm_tris );
842  if( MB_SUCCESS != result ) return result;
843  result = MBI->delete_entities( cgm_tris );
844  if( MB_SUCCESS != result ) return result;
845 
846  // Add the cub_tris to the cgm_surf
847  result = MBI->add_entities( *i, cub_tris );
848  if( MB_SUCCESS != result ) return result;
849  }
850 
851  return MB_SUCCESS;
852 }
853 
854 // Dead elements need removed from the simulation. This is done by removing them
855 // from their volume set and adding them to the implicit complement. New surfaces
856 // must be created for this.
857 // IF MODIFYING THIS CODE, BE AWARE THAT DEAD ELEMENTS CAN BE ADJACENT TO MORE
858 // THAN ONE SURFACE, MAKING THE ASSOCIATION BETWEEN NEWLY EXPOSED AND EXISTING
859 // SURFACES AMBIGUOUS.
861  const EntityHandle cgm_file_set,
862  const EntityHandle cub_file_set,
863  const Tag idTag,
864  const Tag dimTag,
865  const Tag categoryTag,
866  const Tag senseTag,
867  const bool debug )
868 {
869 
870  // Get the cgm surfaces
871  ErrorCode result;
872  const int two = 2;
873  const void* const two_val[] = { &two };
874  Range cgm_surfs;
875  result = MBI->get_entities_by_type_and_tag( cgm_file_set, MBENTITYSET, &dimTag, two_val, 1, cgm_surfs );
876  if( MB_SUCCESS != result ) return result;
877 
878  // Get the maximum surface id. This is so that new surfaces do not have
879  // duplicate ids.
880  int max_surf_id = -1;
881  for( Range::const_iterator i = cgm_surfs.begin(); i != cgm_surfs.end(); ++i )
882  {
883  int surf_id;
884  result = MBI->tag_get_data( idTag, &( *i ), 1, &surf_id );
885  if( MB_SUCCESS != result ) return result;
886  if( max_surf_id < surf_id ) max_surf_id = surf_id;
887  }
888  std::cout << " Maximum surface id=" << max_surf_id << std::endl;
889 
890  // For each cgm volume, does a cub volume with the same id exist?
891  const int three = 3;
892  const void* const three_val[] = { &three };
893  Range cgm_vols;
894  result = MBI->get_entities_by_type_and_tag( cgm_file_set, MBENTITYSET, &dimTag, three_val, 1, cgm_vols );
895  if( MB_SUCCESS != result ) return result;
896 
897  // get the corresponding cub volume
898  for( Range::iterator i = cgm_vols.begin(); i != cgm_vols.end(); ++i )
899  {
900  int vol_id;
901  result = MBI->tag_get_data( idTag, &( *i ), 1, &vol_id );
902  assert( MB_SUCCESS == result );
903  if( MB_SUCCESS != result ) return result;
904  std::cout << " Volume " << vol_id;
905 
906  // Find the meshed vol set with the same id
907  Range cub_vol;
908  const Tag tags[] = { idTag, dimTag };
909  const void* const tag_vals[] = { &vol_id, &three };
910  result = MBI->get_entities_by_type_and_tag( cub_file_set, MBENTITYSET, tags, tag_vals, 2, cub_vol );
911  assert( MB_SUCCESS == result );
912  if( MB_SUCCESS != result ) return result;
913  if( 1 != cub_vol.size() )
914  {
915  std::cout << ": no meshed representation found" << std::endl;
916  continue;
917  }
918  else
919  {
920  std::cout << std::endl;
921  }
922 
923  // get the mesh elements of the volume.
924  Range elems;
925  result = MBI->get_entities_by_type( cub_vol.front(), MBHEX, elems );
926  assert( MB_SUCCESS == result );
927  if( MB_SUCCESS != result ) return result;
928  if( debug ) std::cout << " found " << elems.size() << " hex elems" << std::endl;
929 
930  // skin the volumes
931  Skinner tool( MBI );
932  Range skin_faces;
933  result = tool.find_skin( 0, elems, 2, skin_faces, true );
934  assert( MB_SUCCESS == result );
935  if( MB_SUCCESS != result ) return result;
936 
937  // Reconcile the difference between faces of the cub file surfaces and skin
938  // faces of the 3D mesh. The difference exists because dead elements have been
939  // removed. Faces are divided into:
940  // cub_faces (in) - the faces in the cub file surface
941  // skin_faces (in) - the faces of the 3D mesh elements
942  // common_faces (out)- the faces common to both the cub file surface and 3D mesh
943  // they are still adjacent to this volume
944  // old_faces (out)- the faces of the cub surface not on the 3D mesh skin
945  // they are no longer adjacent to this vol
946  // new_faces (out)- the faces of the 3D mesh skin not on the cub surface
947  // they are now adjacent to this volume
948 
949  // get cub child surfaces.
950  Range cub_surfs;
951  result = MBI->get_child_meshsets( cub_vol.front(), cub_surfs );
952  assert( MB_SUCCESS == result );
953  if( MB_SUCCESS != result ) return result;
954  for( Range::iterator j = cub_surfs.begin(); j != cub_surfs.end(); ++j )
955  {
956  int surf_id;
957  result = MBI->tag_get_data( idTag, &( *j ), 1, &surf_id );
958  assert( MB_SUCCESS == result );
959  if( MB_SUCCESS != result ) return result;
960  // get the quads on each surface
961  Range cub_faces;
962  result = MBI->get_entities_by_type( *j, MBQUAD, cub_faces );
963  assert( MB_SUCCESS == result );
964  if( MB_SUCCESS != result ) return result;
965  // Meshed volumes must have meshed surfaces
966  if( cub_faces.empty() )
967  {
968  std::cout << " Surface " << surf_id << ": contains no meshed faces" << std::endl;
969  // return MB_ENTITY_NOT_FOUND;
970  }
971  // get the faces common to both the skin and this surface
972  Range common_faces = intersect( cub_faces, skin_faces );
973  // find the surface faces not on the skin - these are old and need removed
974  Range old_faces = subtract( cub_faces, common_faces );
975  result = MBI->remove_entities( *j, old_faces );
976  assert( MB_SUCCESS == result );
977  if( MB_SUCCESS != result ) return result;
978 
979  // remove the common faces from the skin faces
980  skin_faces = subtract( skin_faces, common_faces );
981  // If no old faces exist we are done
982  if( old_faces.empty() ) continue;
983  std::cout << " Surface " << surf_id << ": " << old_faces.size() << " old faces removed" << std::endl;
984  // Place the old faces in a new surface, because they may still be adjacent
985  // to 3D mesh in another volume. Get the parent vols of the surface.
986  Range cgm_surf;
987  const Tag tags2[] = { idTag, dimTag };
988  const void* const tag_vals2[] = { &surf_id, &two };
989  result = MBI->get_entities_by_type_and_tag( cgm_file_set, MBENTITYSET, tags2, tag_vals2, 2, cgm_surf );
990  assert( MB_SUCCESS == result );
991  if( MB_SUCCESS != result ) return result;
992  if( 1 != cgm_surf.size() )
993  {
994  std::cout << "invalid size" << std::endl;
995  return MB_INVALID_SIZE;
996  }
997  EntityHandle cgm_vols2[2], cub_vols[2];
998  result = MBI->tag_get_data( senseTag, cgm_surf, &cgm_vols2 );
999  assert( MB_SUCCESS == result );
1000  if( MB_SUCCESS != result ) return result;
1001  result = MBI->tag_get_data( senseTag, &( *j ), 1, &cub_vols );
1002  assert( MB_SUCCESS == result );
1003  if( MB_SUCCESS != result ) return result;
1004  // for the new surf, replace the current volume with the impl compl vol.
1005  // This is because the faces that no longer exist will become adjacent to
1006  // the impl compl
1007  if( *i == cgm_vols2[0] )
1008  {
1009  cgm_vols2[0] = 0;
1010  cub_vols[0] = 0;
1011  }
1012  if( *i == cgm_vols2[1] )
1013  {
1014  cgm_vols2[1] = 0;
1015  cub_vols[1] = 0;
1016  }
1017  // If both sides of the surface are the impl comp, do not create the surface.
1018  if( 0 == cgm_vols2[0] && 0 == cgm_vols2[1] )
1019  {
1020  std::cout << " New surface was not created for old faces because both parents "
1021  "are impl_compl volume "
1022  << std::endl;
1023  continue;
1024  }
1025  // build the new surface.
1026  EntityHandle new_cgm_surf, new_cub_surf;
1027  ++max_surf_id;
1028  result = build_new_surface( MBI, new_cgm_surf, cgm_vols2[0], cgm_vols2[1], max_surf_id, dimTag, idTag,
1029  categoryTag, senseTag );
1030  assert( MB_SUCCESS == result );
1031  if( MB_SUCCESS != result ) return result;
1032  result = build_new_surface( MBI, new_cub_surf, cub_vols[0], cub_vols[1], max_surf_id, dimTag, idTag,
1033  categoryTag, senseTag );
1034  assert( MB_SUCCESS == result );
1035  if( MB_SUCCESS != result ) return result;
1036  // add the new surface to the file set and populate it with the old faces
1037  result = MBI->add_entities( cgm_file_set, &new_cgm_surf, 1 );
1038  assert( MB_SUCCESS == result );
1039  if( MB_SUCCESS != result ) return result;
1040  assert( MB_SUCCESS == result );
1041  result = MBI->add_entities( cub_file_set, &new_cub_surf, 1 );
1042  if( MB_SUCCESS != result ) return result;
1043  assert( MB_SUCCESS == result );
1044  result = MBI->add_entities( new_cub_surf, old_faces );
1045  assert( MB_SUCCESS == result );
1046  if( MB_SUCCESS != result ) return result;
1047 
1048  std::cout << " Surface " << max_surf_id << ": created for " << old_faces.size() << " old faces"
1049  << std::endl;
1050  }
1051 
1052  // the remaining skin faces are newly exposed faces
1053  Range new_faces = skin_faces;
1054 
1055  // new skin faces must be assigned to a surface
1056  if( new_faces.empty() ) continue;
1057  std::cout << " Surface " << max_surf_id + 1 << ": created for " << new_faces.size() << " new faces"
1058  << std::endl;
1059 
1060  // Ensure that faces are oriented outwards
1061  result = orient_faces_outward( MBI, new_faces, debug );
1062  assert( MB_SUCCESS == result );
1063  if( MB_SUCCESS != result ) return result;
1064 
1065  // Create the new surface.
1066  EntityHandle new_cgm_surf, new_cub_surf;
1067  ++max_surf_id;
1068  result = build_new_surface( MBI, new_cgm_surf, *i, 0, max_surf_id, dimTag, idTag, categoryTag, senseTag );
1069  assert( MB_SUCCESS == result );
1070  if( MB_SUCCESS != result ) return result;
1071  result = build_new_surface( MBI, new_cub_surf, cub_vol.front(), 0, max_surf_id, dimTag, idTag, categoryTag,
1072  senseTag );
1073  assert( MB_SUCCESS == result );
1074  if( MB_SUCCESS != result ) return result;
1075  // Insert the new surf into file sets and populate it with faces.
1076  result = MBI->add_entities( cgm_file_set, &new_cgm_surf, 1 );
1077  assert( MB_SUCCESS == result );
1078  if( MB_SUCCESS != result ) return result;
1079  result = MBI->add_entities( cub_file_set, &new_cub_surf, 1 );
1080  assert( MB_SUCCESS == result );
1081  if( MB_SUCCESS != result ) return result;
1082  result = MBI->add_entities( new_cub_surf, new_faces );
1083  assert( MB_SUCCESS == result );
1084  if( MB_SUCCESS != result ) return result;
1085  }
1086 
1087  return MB_SUCCESS;
1088 }
1089 
1090 /* Get the type of a file.
1091  Return value is one of the above constants
1092  */
1093 const char* get_geom_file_type( const char* filename );
1094 const char* get_geom_fptr_type( FILE* file );
1095 
1096 int is_cubit_file( FILE* file );
1097 int is_step_file( FILE* file );
1098 int is_iges_file( FILE* file );
1099 int is_acis_txt_file( FILE* file );
1100 int is_acis_bin_file( FILE* file );
1101 int is_occ_brep_file( FILE* file );
1102 
1103 double DEFAULT_DISTANCE = 0.001;
1104 double DEFAULT_LEN = 0.0;
1106 
1107 // load cub file
1108 // load cgm file
1109 // for each surface
1110 // convert cub surf quads to tris
1111 // get signed volume from cgm and cub surf MUST COME BEFORE COORD UPDATE, NEEDS MBTRIS
1112 // reverse cgm surface sense if needed
1113 // replace cgm surf tris with cub surf tris
1114 // measure volume of predeformed cub elements
1115 // convert cub volumes sets to tracking so that dead elems are removed from vol sets
1116 // update coordinates and delete dead elems
1117 // measure volume of deformed cub elems
1118 // print histogram of volume change
1119 // for each cub volume
1120 // skin volume elems to get faces
1121 // for each child cub surface
1122 // assign old skin faces to a new surface in case they are adjacent to another volume
1123 // orient each skin face outward
1124 // assign new skin faces to a new surface
1125 // for each surface
1126 // remove existing tris (from before the update)
1127 // convert quads to tris
1128 // remove empty surfaces and volumes due to dead elements
1129 int main( int argc, char* argv[] )
1130 {
1131  clock_t start_time = clock();
1132 
1133  const bool debug = false;
1134  const char* file_type = NULL;
1135 
1136  const char* cub_name = 0;
1137  const char* exo_name = 0;
1138  const char* out_name = 0;
1139  const char* time_step = 0;
1140  const char* sat_name = 0;
1141  double dist_tol = 0.001, len_tol = 0.0;
1142  int norm_tol = 5;
1143 
1144  if( 6 != argc && 9 != argc )
1145  {
1146  std::cerr << "To read meshed geometry for MOAB:" << std::endl;
1147  std::cerr << "$> <cub_file.cub> <acis_file.sat> <facet_tol> <output_file.h5m> conserve_mass<bool>" << std::endl;
1148  std::cerr << "To read meshed geometry for MOAB and update node coordinates:" << std::endl;
1149  std::cerr << "$> <cub_file.cub> <acis_file.sat> <facet_tol> <output_file.h5m> "
1150  "<deformed_exo_file.e> time_step<int> check_vol_change<bool> conserve_mass<bool>"
1151  << std::endl;
1152  exit( 4 );
1153  }
1154 
1155  // check filenames for proper suffix
1156  std::string temp;
1157  cub_name = argv[1];
1158  temp.assign( cub_name );
1159  if( std::string::npos == temp.find( ".cub" ) )
1160  {
1161  std::cerr << "cub_file does not have correct suffix" << std::endl;
1162  return 1;
1163  }
1164  sat_name = argv[2]; // needed because the cub file's embedded sat file does not have groups
1165  temp.assign( sat_name );
1166  if( std::string::npos == temp.find( ".sat" ) )
1167  {
1168  std::cerr << "sat_file does not have correct suffix" << std::endl;
1169  return 1;
1170  }
1171  out_name = argv[4];
1172  temp.assign( out_name );
1173  if( std::string::npos == temp.find( ".h5m" ) )
1174  {
1175  std::cerr << "out_file does not have correct suffix" << std::endl;
1176  return 1;
1177  }
1178 
1179  // get the facet tolerance
1180  dist_tol = atof( argv[3] );
1181  if( 0 > dist_tol || 1 < dist_tol )
1182  {
1183  std::cout << "error: facet_tolerance=" << dist_tol << std::endl;
1184  return 1;
1185  }
1186 
1187  // Should the nodes be updated?
1188  bool update_coords = false;
1189  if( 9 == argc )
1190  {
1191  exo_name = argv[5];
1192  temp.assign( exo_name );
1193  if( std::string::npos == temp.find( ".e" ) )
1194  {
1195  std::cerr << "e_file does not have correct suffix" << std::endl;
1196  return 1;
1197  }
1198  time_step = argv[6];
1199  update_coords = true;
1200  }
1201 
1202  // Should the volume change be determined?
1203  bool determine_volume_change = false;
1204  if( 9 == argc )
1205  {
1206  temp.assign( argv[7] );
1207  if( std::string::npos != temp.find( "true" ) ) determine_volume_change = true;
1208  }
1209 
1210  // Should densities be changed to conserve mass?
1211  bool conserve_mass = false;
1212  if( 9 == argc )
1213  {
1214  temp.assign( argv[8] );
1215  if( std::string::npos != temp.find( "true" ) ) conserve_mass = true;
1216  }
1217  else if( 6 == argc )
1218  {
1219  temp.assign( argv[5] );
1220  if( std::string::npos != temp.find( "true" ) ) conserve_mass = true;
1221  }
1222 
1223  // Get CGM file type
1224  if( !file_type )
1225  {
1226  file_type = get_geom_file_type( cub_name );
1227  if( !file_type )
1228  {
1229  std::cerr << cub_name << " : unknown file type, try '-t'" << std::endl;
1230  exit( 1 );
1231  }
1232  }
1233 
1234  // Read the mesh from the cub file with Tqcdfr
1235  Core* MBI = new Core();
1236  ErrorCode result;
1237  EntityHandle cub_file_set;
1238  result = MBI->create_meshset( 0, cub_file_set );
1239  if( MB_SUCCESS != result ) return result;
1240  // Do not ignore the Cubit file version. In testing, a cub file from Cubit12
1241  // did not work.
1242  // char cub_options[256] = "120";
1243  // char cub_options[256] = "IGNORE_VERSION";
1244  // result = MBI->load_file(cub_name, &cub_file_set, cub_options, NULL, 0, 0);
1245  result = MBI->load_file( cub_name, &cub_file_set, 0, NULL, 0, 0 );
1246  if( MB_SUCCESS != result )
1247  {
1248  std::cout << "error: problem reading cub file" << std::endl;
1249  return result;
1250  }
1251  std::cout << "Mesh file read." << std::endl;
1252 
1253  // Read the ACIS file with ReadCGM
1254  char cgm_options[256];
1255  std::cout << " facet tolerance=" << dist_tol << std::endl;
1256  sprintf( cgm_options,
1257  "CGM_ATTRIBS=yes;FACET_DISTANCE_TOLERANCE=%g;FACET_NORMAL_TOLERANCE=%d;MAX_FACET_EDGE_"
1258  "LENGTH=%g;",
1259  dist_tol, norm_tol, len_tol );
1260  EntityHandle cgm_file_set;
1261  result = MBI->create_meshset( 0, cgm_file_set );
1262  if( MB_SUCCESS != result ) return result;
1263  result = MBI->load_file( sat_name, &cgm_file_set, cgm_options, NULL, 0, 0 );
1264  if( MB_SUCCESS != result )
1265  {
1266  std::cout << "error: problem reading sat file" << std::endl;
1267  return result;
1268  }
1269  std::cout << "CAD file read." << std::endl;
1270 
1271  // Create tags
1272  Tag dimTag, idTag, categoryTag, senseTag, sizeTag, nameTag;
1273  result = MBI->tag_get_handle( GEOM_DIMENSION_TAG_NAME, 1, MB_TYPE_INTEGER, dimTag, MB_TAG_SPARSE | MB_TAG_CREAT );
1274  if( MB_SUCCESS != result ) return result;
1275  idTag = MBI->globalId_tag();
1276  result = MBI->tag_get_handle( CATEGORY_TAG_NAME, CATEGORY_TAG_SIZE, MB_TYPE_OPAQUE, categoryTag,
1278  if( MB_SUCCESS != result ) return result;
1279  result = MBI->tag_get_handle( "GEOM_SENSE_2", 2, MB_TYPE_HANDLE, senseTag, MB_TAG_SPARSE | MB_TAG_CREAT );
1280  if( MB_SUCCESS != result ) return result;
1281  result = MBI->tag_get_handle( "GEOM_SIZE", 1, MB_TYPE_DOUBLE, sizeTag, MB_TAG_DENSE | MB_TAG_CREAT );
1282  if( MB_SUCCESS != result ) return result;
1284  if( MB_SUCCESS != result ) return result;
1285 
1286  // Create triangles from the quads of the cub surface sets and add them to the
1287  // cub surface sets. Get the signed volume of each surface for both cgm and
1288  // cub representations. Change the sense of the cgm representation to match
1289  // the cub representation.
1290  result = fix_surface_senses( MBI, cgm_file_set, cub_file_set, idTag, dimTag, senseTag, debug );
1291  if( MB_SUCCESS != result ) return result;
1292  std::cout << "Fixed CAD surface senses to match meshed surface senses." << std::endl;
1293 
1294  // Get the 3D elements in the cub file and measure their volume.
1295  Range orig_elems;
1296  std::vector< double > orig_size;
1297  if( determine_volume_change )
1298  {
1299  result = MBI->get_entities_by_dimension( 0, 3, orig_elems );
1300  if( MB_SUCCESS != result ) return result;
1301  orig_size.resize( orig_elems.size() );
1302  for( unsigned int i = 0; i < orig_elems.size(); ++i )
1303  {
1304  orig_size[i] = measure( MBI, orig_elems[i] );
1305  }
1306  }
1307 
1308  // Before updating the nodes and removing dead elements, force the cub volume
1309  // sets to track ownership so that dead elements will be deleted from the sets.
1310  const int three = 3;
1311  const void* const three_val[] = { &three };
1312  Range cub_vols;
1313  result = MBI->get_entities_by_type_and_tag( cub_file_set, MBENTITYSET, &dimTag, three_val, 1, cub_vols );
1314  if( MB_SUCCESS != result ) return result;
1315  for( Range::const_iterator i = cub_vols.begin(); i != cub_vols.end(); ++i )
1316  {
1317  result = MBI->set_meshset_options( *i, MESHSET_TRACK_OWNER );
1318  if( MB_SUCCESS != result ) return result;
1319  }
1320 
1321  // Tag volume sets with the undeformed size of each volume.
1322  Range vol_sets;
1323  result = MBI->get_entities_by_type_and_tag( cgm_file_set, MBENTITYSET, &dimTag, three_val, 1, vol_sets );
1324  if( MB_SUCCESS != result ) return result;
1325 
1326  moab::GeomTopoTool gtt = moab::GeomTopoTool( MBI, false );
1328 
1329  for( Range::const_iterator i = vol_sets.begin(); i != vol_sets.end(); ++i )
1330  {
1331  double size;
1332  result = gqt.measure_volume( *i, size );
1333  if( MB_SUCCESS != result ) return result;
1334  result = MBI->tag_set_data( sizeTag, &( *i ), 1, &size );
1335  if( MB_SUCCESS != result ) return result;
1336  }
1337 
1338  // Update the coordinates if needed. Do not do this before checking surface
1339  // sense, because the coordinate update could deform the surfaces too much
1340  // to make an accurate comparison.
1341  // The cub node ids are unique because cgm vertex ids are tagged on the vertex
1342  // meshset and not the vertex itself.
1343  // result = MBI->delete_entities( &cub_file_set, 1 );
1344  // if(MB_SUCCESS != result) return result;
1345  // Assume dead elements exist until I think of something better.
1346  bool dead_elements_exist = true;
1347  if( update_coords )
1348  {
1349  // ReadNCDF my_ex_reader(MBI);
1350  char exo_options[120] = "tdata=coord,";
1351  strcat( exo_options, time_step );
1352  strcat( exo_options, ",set" );
1353  // FileOptions exo_opts(exo_options) ;
1354  // opts = "tdata=coord, 100, sum, temp.exo";
1355  // result = my_ex_reader.load_file(exo_name, cgm_file_set, exo_opts, NULL, 0 , 0);
1356  // result = my_ex_reader.load_file(exo_name, cub_file_set, exo_opts, NULL, 0 , 0);
1357  // result = my_ex_reader.load_file(exo_name, &cub_file_set, exo_opts, NULL, 0 , 0);
1358  MBI->load_file( exo_name, &cub_file_set, exo_options );
1359  if( MB_SUCCESS != result )
1360  {
1361  std::string last_error;
1362  MBI->get_last_error( last_error );
1363  std::cout << "coordinate update failed, " << last_error << std::endl;
1364  return result;
1365  }
1366  std::cout << "Updated mesh nodes with deformed coordinates from exodus file." << std::endl;
1367  }
1368 
1369  if( determine_volume_change )
1370  {
1371  // Dead elements have been removed by the deformation. Get the elements that
1372  // still exist.
1373  Range defo_elems;
1374  result = MBI->get_entities_by_dimension( 0, 3, defo_elems );
1375  if( MB_SUCCESS != result ) return result;
1376 
1377  // Determine the volume of the elements now that a deformation has been
1378  // applied. Condense the original array by removing dead elements.
1379  double* orig_size_condensed = new double[defo_elems.size()];
1380  double* defo_size_condensed = new double[defo_elems.size()];
1381  int j = 0;
1382  for( unsigned int i = 0; i < orig_elems.size(); ++i )
1383  {
1384  if( orig_elems[i] == defo_elems[j] )
1385  {
1386  orig_size_condensed[j] = orig_size[i];
1387  defo_size_condensed[j] = measure( MBI, defo_elems[j] );
1388  ++j;
1389  }
1390  }
1391  generate_plots( orig_size_condensed, defo_size_condensed, defo_elems.size(), std::string( time_step ) );
1392  delete[] orig_size_condensed; // can't use the same delete[] for both
1393  delete[] defo_size_condensed;
1394  }
1395 
1396  // Deal with dead elements. For now, add them to the impl_compl volume.
1397  // Extra surfaces are created to do this.
1398  if( update_coords && dead_elements_exist )
1399  {
1400  result = add_dead_elems_to_impl_compl( MBI, cgm_file_set, cub_file_set, idTag, dimTag, categoryTag, senseTag,
1401  debug );
1402  if( MB_SUCCESS != result ) return result;
1403  std::cout << "Placed dead elements in implicit complement volume and added required surfaces." << std::endl;
1404  }
1405 
1406  // The quads in the cub_file_set have been updated for dead elements. For each
1407  // cgm_surf, if there exists a cub_surf with the same id, replace the cgm tris
1408  // with cub_tris (created from the quads). Note the a surface that is not
1409  // meshed (in cub file) will not be effected.
1410  result = replace_faceted_cgm_surfs( MBI, cgm_file_set, cub_file_set, idTag, dimTag, debug );
1411  if( MB_SUCCESS != result ) return result;
1412  std::cout << "Replaced faceted CAD surfaces with meshed surfaces of triangles." << std::endl;
1413 
1414  result = remove_empty_cgm_surfs_and_vols( MBI, cgm_file_set, idTag, dimTag, debug );
1415  if( MB_SUCCESS != result ) return result;
1416  std::cout << "Removed surfaces and volumes that no longer have any triangles." << std::endl;
1417 
1418  // For each material, sum the volume. If the coordinates were updated for
1419  // deformation, summarize the volume change.
1420  result = summarize_cell_volume_change( MBI, cgm_file_set, categoryTag, dimTag, sizeTag, nameTag, idTag,
1421  conserve_mass, debug );
1422  if( MB_SUCCESS != result ) return result;
1423  std::cout << "Summarized the volume change of each material, with respect to the solid model." << std::endl;
1424 
1425  result = MBI->write_mesh( out_name, &cgm_file_set, 1 );
1426  if( MB_SUCCESS != result )
1427  {
1428  std::cout << "write mesh failed" << std::endl;
1429  return result;
1430  }
1431  std::cout << "Saved output file for mesh-based analysis." << std::endl;
1432 
1433  clock_t end_time = clock();
1434  std::cout << " " << (double)( end_time - start_time ) / CLOCKS_PER_SEC << " seconds" << std::endl;
1435  std::cout << std::endl;
1436 
1437  return 0;
1438 }
1439 
1440 const char* get_geom_file_type( const char* name )
1441 {
1442  FILE* file;
1443  const char* result = 0;
1444 
1445  file = fopen( name, "r" );
1446  if( file )
1447  {
1448  result = get_geom_fptr_type( file );
1449  fclose( file );
1450  }
1451 
1452  return result;
1453 }
1454 
1455 const char* get_geom_fptr_type( FILE* file )
1456 {
1457  static const char* CUBIT_NAME = GF_CUBIT_FILE_TYPE;
1458  static const char* STEP_NAME = GF_STEP_FILE_TYPE;
1459  static const char* IGES_NAME = GF_IGES_FILE_TYPE;
1460  static const char* SAT_NAME = GF_ACIS_TXT_FILE_TYPE;
1461  static const char* SAB_NAME = GF_ACIS_BIN_FILE_TYPE;
1462  static const char* BREP_NAME = GF_OCC_BREP_FILE_TYPE;
1463 
1464  if( is_cubit_file( file ) )
1465  return CUBIT_NAME;
1466  else if( is_step_file( file ) )
1467  return STEP_NAME;
1468  else if( is_iges_file( file ) )
1469  return IGES_NAME;
1470  else if( is_acis_bin_file( file ) )
1471  return SAB_NAME;
1472  else if( is_acis_txt_file( file ) )
1473  return SAT_NAME;
1474  else if( is_occ_brep_file( file ) )
1475  return BREP_NAME;
1476  else
1477  return 0;
1478 }
1479 
1480 int is_cubit_file( FILE* file )
1481 {
1482  unsigned char buffer[4];
1483  return !fseek( file, 0, SEEK_SET ) && fread( buffer, 4, 1, file ) && !memcmp( buffer, "CUBE", 4 );
1484 }
1485 
1486 int is_step_file( FILE* file )
1487 {
1488  unsigned char buffer[9];
1489  return !fseek( file, 0, SEEK_SET ) && fread( buffer, 9, 1, file ) && !memcmp( buffer, "ISO-10303", 9 );
1490 }
1491 
1492 int is_iges_file( FILE* file )
1493 {
1494  unsigned char buffer[10];
1495  return !fseek( file, 72, SEEK_SET ) && fread( buffer, 10, 1, file ) && !memcmp( buffer, "S 1\r\n", 10 );
1496 }
1497 
1498 int is_acis_bin_file( FILE* file )
1499 {
1500  char buffer[15];
1501  return !fseek( file, 0, SEEK_SET ) && fread( buffer, 15, 1, file ) && !memcmp( buffer, "ACIS BinaryFile", 9 );
1502 }
1503 
1504 int is_acis_txt_file( FILE* file )
1505 {
1506  char buffer[5];
1507  int version, length;
1508 
1509  if( fseek( file, 0, SEEK_SET ) || 2 != fscanf( file, "%d %*d %*d %*d %d ", &version, &length ) ) return 0;
1510 
1511  if( version < 1 || version > 0xFFFF ) return 0;
1512 
1513  // Skip application name
1514  if( fseek( file, length, SEEK_CUR ) ) return 0;
1515 
1516  // Read length of version string followed by first 5 characters
1517  if( 2 != fscanf( file, "%d %4s", &length, buffer ) ) return 0;
1518 
1519  return !strcmp( buffer, "ACIS" );
1520 }
1521 
1522 int is_occ_brep_file( FILE* file )
1523 {
1524  unsigned char buffer[6];
1525  return !fseek( file, 0, SEEK_SET ) && fread( buffer, 6, 1, file ) && !memcmp( buffer, "DBRep_", 6 );
1526 }