Mesh Oriented datABase  (version 5.5.0)
An array-based unstructured mesh library
WriteNCDF.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 #ifdef WIN32
17 #ifdef _DEBUG
18 // turn off warnings that say they debugging identifier has been truncated
19 // this warning comes up when using some STL containers
20 #pragma warning( disable : 4786 )
21 #endif
22 #endif
23 
24 #include "WriteNCDF.hpp"
25 
26 #include "netcdf.h"
27 #include <utility>
28 #include <algorithm>
29 #include <ctime>
30 #include <string>
31 #include <vector>
32 #include <cstdio>
33 #include <cstring>
34 #include <cassert>
35 
36 #include "moab/Interface.hpp"
37 #include "moab/Range.hpp"
38 #include "moab/CN.hpp"
39 #include "moab/FileOptions.hpp"
40 #include "MBTagConventions.hpp"
41 #include "Internals.hpp"
42 #include "ExoIIUtil.hpp"
43 #include "moab/WriteUtilIface.hpp"
44 #include "exodus_order.h"
45 
46 #ifndef MOAB_HAVE_NETCDF
47 #error Attempt to compile WriteNCDF with NetCDF support disabled
48 #endif
49 
50 namespace moab
51 {
52 
53 const int TIME_STR_LEN = 11;
54 
55 #define INS_ID( stringvar, prefix, id ) sprintf( stringvar, prefix, id )
56 
57 #define GET_DIM( ncdim, name, val ) \
58  { \
59  int gdfail = nc_inq_dimid( ncFile, name, &( ncdim ) ); \
60  if( NC_NOERR == gdfail ) \
61  { \
62  size_t tmp_val; \
63  gdfail = nc_inq_dimlen( ncFile, ncdim, &tmp_val ); \
64  if( NC_NOERR != gdfail ) \
65  { \
66  MB_SET_ERR( MB_FAILURE, "WriteNCDF:: couldn't get dimension length" ); \
67  } \
68  else \
69  ( val ) = tmp_val; \
70  } \
71  else \
72  ( val ) = 0; \
73  }
74 
75 #define GET_DIMB( ncdim, name, varname, id, val ) \
76  INS_ID( name, varname, id ); \
77  GET_DIM( ncdim, name, val );
78 
79 #define GET_VAR( name, id, dims ) \
80  { \
81  ( id ) = -1; \
82  int gvfail = nc_inq_varid( ncFile, name, &( id ) ); \
83  if( NC_NOERR == gvfail ) \
84  { \
85  int ndims; \
86  gvfail = nc_inq_varndims( ncFile, id, &ndims ); \
87  if( NC_NOERR == gvfail ) \
88  { \
89  ( dims ).resize( ndims ); \
90  gvfail = nc_inq_vardimid( ncFile, id, &( dims )[0] ); \
91  } \
92  } \
93  }
94 
96 {
97  return new WriteNCDF( iface );
98 }
99 
101  : mdbImpl( impl ), ncFile( 0 ), mCurrentMeshHandle( 0 ), mGeomDimensionTag( 0 ), repeat_face_blocks( 0 )
102 {
103  assert( impl != NULL );
104 
105  impl->query_interface( mWriteIface );
106 
107  // Initialize in case tag_get_handle fails below
108  //! Get and cache predefined tag handles
109  int negone = -1;
111  &negone );
112 
114  &negone );
115 
117  &negone );
118 
119  mGlobalIdTag = impl->globalId_tag();
120 
121  int dum_val_array[] = { -1, -1, -1, -1 };
123  dum_val_array );
124 
125  impl->tag_get_handle( "distFactor", 0, MB_TYPE_DOUBLE, mDistFactorTag,
127 
129 
130  impl->tag_get_handle( "WriteNCDF element mark", 1, MB_TYPE_BIT, mEntityMark, MB_TAG_CREAT );
131 }
132 
134 {
136 
138 
139  if( 0 != ncFile ) ncFile = 0;
140 }
141 
142 void WriteNCDF::reset_block( std::vector< MaterialSetData >& block_info )
143 {
144  std::vector< MaterialSetData >::iterator iter;
145 
146  for( iter = block_info.begin(); iter != block_info.end(); ++iter )
147  {
148  iter->elements.clear();
149  }
150 }
151 
152 void WriteNCDF::time_and_date( char* time_string, char* date_string )
153 {
154  struct tm* local_time;
155  time_t calendar_time;
156 
157  calendar_time = time( NULL );
158  local_time = localtime( &calendar_time );
159 
160  assert( NULL != time_string && NULL != date_string );
161 
162  strftime( time_string, TIME_STR_LEN, "%H:%M:%S", local_time );
163  strftime( date_string, TIME_STR_LEN, "%m/%d/%Y", local_time );
164 
165  // Terminate with NULL character
166  time_string[10] = '\0';
167  date_string[10] = '\0';
168 }
169 
170 ErrorCode WriteNCDF::write_file( const char* exodus_file_name,
171  const bool overwrite,
172  const FileOptions& opts,
173  const EntityHandle* ent_handles,
174  const int num_sets,
175  const std::vector< std::string >& qa_records,
176  const Tag*,
177  int,
178  int user_dimension )
179 {
180  assert( 0 != mMaterialSetTag && 0 != mNeumannSetTag && 0 != mDirichletSetTag );
181 
182  if( user_dimension == 0 ) mdbImpl->get_dimension( user_dimension );
183 
184  if( opts.get_null_option( "REPEAT_FACE_BLOCKS" ) == MB_SUCCESS ) repeat_face_blocks = 1;
185 
186  std::vector< EntityHandle > blocks, nodesets, sidesets, entities;
187 
188  // Separate into blocks, nodesets, sidesets
189 
190  if( num_sets == 0 )
191  {
192  // Default to all defined block, nodeset and sideset-type sets
193  Range this_range;
194  mdbImpl->get_entities_by_type_and_tag( 0, MBENTITYSET, &mMaterialSetTag, NULL, 1, this_range );
195  std::copy( this_range.begin(), this_range.end(), std::back_inserter( blocks ) );
196  this_range.clear();
198  std::copy( this_range.begin(), this_range.end(), std::back_inserter( nodesets ) );
199  this_range.clear();
200  mdbImpl->get_entities_by_type_and_tag( 0, MBENTITYSET, &mNeumannSetTag, NULL, 1, this_range );
201  std::copy( this_range.begin(), this_range.end(), std::back_inserter( sidesets ) );
202 
203  // If there is nothing to write, write everything as one block.
204  if( blocks.empty() && nodesets.empty() && sidesets.empty() )
205  {
206  this_range.clear();
207  for( int d = user_dimension; d > 0 && this_range.empty(); --d )
208  mdbImpl->get_entities_by_dimension( 0, d, this_range, false );
209  if( this_range.empty() ) return MB_FILE_WRITE_ERROR;
210 
211  EntityHandle block_handle;
212  int block_id = 1;
213  mdbImpl->create_meshset( MESHSET_SET, block_handle );
214  mdbImpl->tag_set_data( mMaterialSetTag, &block_handle, 1, &block_id );
215  mdbImpl->add_entities( block_handle, this_range );
216  blocks.push_back( block_handle );
217  }
218  }
219  else
220  {
221  int dummy;
222  for( const EntityHandle* iter = ent_handles; iter < ent_handles + num_sets; ++iter )
223  {
224  if( MB_SUCCESS == mdbImpl->tag_get_data( mMaterialSetTag, &( *iter ), 1, &dummy ) && -1 != dummy )
225  blocks.push_back( *iter );
226  else if( MB_SUCCESS == mdbImpl->tag_get_data( mDirichletSetTag, &( *iter ), 1, &dummy ) && -1 != dummy )
227  nodesets.push_back( *iter );
228  else if( MB_SUCCESS == mdbImpl->tag_get_data( mNeumannSetTag, &( *iter ), 1, &dummy ) && -1 != dummy )
229  sidesets.push_back( *iter );
230  }
231  }
232 
233  // If there is nothing to write just return.
234  if( blocks.empty() && nodesets.empty() && sidesets.empty() ) return MB_FILE_WRITE_ERROR;
235 
236  // Try to get mesh information
237  ExodusMeshInfo mesh_info;
238 
239  std::vector< MaterialSetData > block_info;
240  std::vector< NeumannSetData > sideset_info;
241  std::vector< DirichletSetData > nodeset_info;
242 
243  mesh_info.num_dim = user_dimension;
244 
245  if( qa_records.empty() )
246  {
247  // qa records are empty - initialize some MB-standard ones
248  mesh_info.qaRecords.push_back( "MB" );
249  mesh_info.qaRecords.push_back( "0.99" );
250  char string1[80], string2[80];
251  time_and_date( string2, string1 );
252  mesh_info.qaRecords.push_back( string2 );
253  mesh_info.qaRecords.push_back( string1 );
254  }
255  else
256  {
257  // Constrained to multiples of 4 qa records
258  assert( qa_records.size() % 4 == 0 );
259 
260  std::copy( qa_records.begin(), qa_records.end(), std::back_inserter( mesh_info.qaRecords ) );
261  }
262 
263  block_info.clear();
264  if( gather_mesh_information( mesh_info, block_info, sideset_info, nodeset_info, blocks, sidesets, nodesets ) !=
265  MB_SUCCESS )
266  {
267  reset_block( block_info );
268  return MB_FAILURE;
269  }
270 
271  // Try to open the file after gather mesh info succeeds
272  int fail = nc_create( exodus_file_name, overwrite ? NC_CLOBBER : NC_NOCLOBBER, &ncFile );
273  if( NC_NOERR != fail )
274  {
275  reset_block( block_info );
276  return MB_FAILURE;
277  }
278 
279  if( write_header( mesh_info, block_info, sideset_info, nodeset_info, exodus_file_name ) != MB_SUCCESS )
280  {
281  reset_block( block_info );
282  return MB_FAILURE;
283  }
284 
285  {
286  // write dummy time_whole
287  double timev = 0.0; // dummy, to make paraview happy
288  size_t start = 0, count = 1;
289  int nc_var;
290  std::vector< int > dims;
291  GET_VAR( "time_whole", nc_var, dims );
292  fail = nc_put_vara_double( ncFile, nc_var, &start, &count, &timev );
293  if( NC_NOERR != fail )
294  {
295  MB_SET_ERR( MB_FAILURE, "Failed writing dist factor array" );
296  }
297  }
298 
299  if( write_nodes( mesh_info.num_nodes, mesh_info.nodes, mesh_info.num_dim ) != MB_SUCCESS )
300  {
301  reset_block( block_info );
302  return MB_FAILURE;
303  }
304 
305  if( !mesh_info.polyhedronFaces.empty() )
306  {
307  if( write_poly_faces( mesh_info ) != MB_SUCCESS )
308  {
309  reset_block( block_info );
310  return MB_FAILURE;
311  }
312  }
313 
314  if( write_elementblocks( mesh_info, block_info ) )
315  {
316  reset_block( block_info );
317  return MB_FAILURE;
318  }
319 
320  // Write the three maps
321  if( write_global_node_order_map( mesh_info.num_nodes, mesh_info.nodes ) != MB_SUCCESS )
322  {
323  reset_block( block_info );
324  return MB_FAILURE;
325  }
326 
328  {
329  reset_block( block_info );
330  return MB_FAILURE;
331  }
332 
333  if( write_element_order_map( mesh_info.num_elements ) != MB_SUCCESS )
334  {
335  reset_block( block_info );
336  return MB_FAILURE;
337  }
338 
339  /*
340  if (write_elementmap(mesh_info) != MB_SUCCESS)
341  return MB_FAILURE;
342  */
343 
344  if( write_BCs( sideset_info, nodeset_info ) != MB_SUCCESS )
345  {
346  reset_block( block_info );
347  return MB_FAILURE;
348  }
349 
350  if( write_qa_records( mesh_info.qaRecords ) != MB_SUCCESS ) return MB_FAILURE;
351 
352  // Copy the qa records into the argument
353  // mesh_info.qaRecords.swap(qa_records);
354  // Close the file
355  fail = nc_close( ncFile );
356  if( NC_NOERR != fail )
357  {
358  MB_SET_ERR( MB_FAILURE, "Trouble closing file" );
359  }
360 
361  return MB_SUCCESS;
362 }
363 
365  std::vector< MaterialSetData >& block_info,
366  std::vector< NeumannSetData >& sideset_info,
367  std::vector< DirichletSetData >& nodeset_info,
368  std::vector< EntityHandle >& blocks,
369  std::vector< EntityHandle >& sidesets,
370  std::vector< EntityHandle >& nodesets )
371 {
372  ErrorCode rval;
373  std::vector< EntityHandle >::iterator vector_iter, end_vector_iter;
374 
375  mesh_info.num_nodes = 0;
376  mesh_info.num_elements = 0;
377  mesh_info.num_elementblocks = 0;
378  mesh_info.num_polyhedra_blocks = 0;
379 
380  int id = 0;
381 
382  vector_iter = blocks.begin();
383  end_vector_iter = blocks.end();
384 
385  std::vector< EntityHandle > parent_meshsets;
386 
387  // Clean out the bits for the element mark
388  rval = mdbImpl->tag_delete( mEntityMark );
389  if( MB_SUCCESS != rval ) return rval;
390  rval = mdbImpl->tag_get_handle( "WriteNCDF element mark", 1, MB_TYPE_BIT, mEntityMark, MB_TAG_CREAT );
391  if( MB_SUCCESS != rval ) return rval;
392 
393  int highest_dimension_of_element_blocks = 0;
394 
395  for( vector_iter = blocks.begin(); vector_iter != blocks.end(); ++vector_iter )
396  {
397  MaterialSetData block_data;
398 
399  // For the purpose of qa records, get the parents of these blocks
400  if( mdbImpl->get_parent_meshsets( *vector_iter, parent_meshsets ) != MB_SUCCESS ) return MB_FAILURE;
401 
402  // Get all Entity Handles in the mesh set
403  Range dummy_range;
404  rval = mdbImpl->get_entities_by_handle( *vector_iter, dummy_range, true );
405  if( MB_SUCCESS != rval ) return rval;
406 
407  // Skip empty blocks
408  if( dummy_range.empty() ) continue;
409 
410  // Get the block's id
411  if( mdbImpl->tag_get_data( mMaterialSetTag, &( *vector_iter ), 1, &id ) != MB_SUCCESS )
412  {
413  MB_SET_ERR( MB_FAILURE, "Couldn't get block id from a tag for an element block" );
414  }
415 
416  block_data.id = id;
417  block_data.number_attributes = 0;
418 
419  // Wait a minute, we are doing some filtering here that doesn't make sense at this level CJS
420 
421  // Find the dimension of the last entity in this range
422  int this_dim = CN::Dimension( TYPE_FROM_HANDLE( dummy_range.back() ) );
423  if( this_dim > 3 )
424  {
425  MB_SET_ERR( MB_TYPE_OUT_OF_RANGE, "Block " << id << " contains entity sets" );
426  }
427  block_data.elements = dummy_range.subset_by_dimension( this_dim );
428 
429  // End of -- wait a minute, we are doing some filtering here that doesn't make sense at this
430  // level CJS
431 
432  // Get the entity type for this block, verifying that it's the same for all elements
433  EntityType entity_type = TYPE_FROM_HANDLE( block_data.elements.front() );
434  if( !block_data.elements.all_of_type( entity_type ) )
435  {
436  MB_SET_ERR( MB_FAILURE, "Entities in block " << id << " not of common type" );
437  }
438 
439  int dimension = -1;
440  if( entity_type == MBQUAD || entity_type == MBTRI )
441  dimension = 2; // Output shells by default
442  else if( entity_type == MBEDGE )
443  dimension = 1;
444  else
445  dimension = CN::Dimension( entity_type );
446 
447  if( dimension > highest_dimension_of_element_blocks ) highest_dimension_of_element_blocks = dimension;
448 
449  std::vector< EntityHandle > tmp_conn;
450  rval = mdbImpl->get_connectivity( &( block_data.elements.front() ), 1, tmp_conn );
451  if( MB_SUCCESS != rval ) return rval;
452  block_data.element_type = ExoIIUtil::get_element_type_from_num_verts( tmp_conn.size(), entity_type, dimension );
453 
454  if( block_data.element_type == EXOII_MAX_ELEM_TYPE )
455  {
456  MB_SET_ERR( MB_FAILURE, "Element type in block " << id << " didn't get set correctly" );
457  }
458 
459  if( block_data.element_type == EXOII_POLYGON )
460  {
461  // get all poly connectivity
462  int numconn = 0;
463  for( Range::iterator eit = block_data.elements.begin(); eit != block_data.elements.end(); eit++ )
464  {
465  EntityHandle polg = *eit;
466  int nnodes = 0;
467  const EntityHandle* conn = NULL;
468  rval = mdbImpl->get_connectivity( polg, conn, nnodes );MB_CHK_ERR( rval );
469  numconn += nnodes;
470  }
471  block_data.number_nodes_per_element = numconn;
472  }
473  else
475 
476  // Number of nodes for this block
477  block_data.number_elements = block_data.elements.size();
478 
479  // Total number of elements
480  mesh_info.num_elements += block_data.number_elements;
481 
482  // Get the nodes for the elements
483  rval = mWriteIface->gather_nodes_from_elements( block_data.elements, mEntityMark, mesh_info.nodes );
484  if( MB_SUCCESS != rval ) return rval;
485 
486  // if polyhedra block
487  if( EXOII_POLYHEDRON == block_data.element_type )
488  {
489  rval = mdbImpl->get_connectivity( block_data.elements, mesh_info.polyhedronFaces );MB_CHK_ERR( rval );
490  mesh_info.num_polyhedra_blocks++;
491  }
492 
493  if( !sidesets.empty() )
494  {
495  // If there are sidesets, keep track of which elements are being written out
496  for( Range::iterator iter = block_data.elements.begin(); iter != block_data.elements.end(); ++iter )
497  {
498  unsigned char bit = 0x1;
499  rval = mdbImpl->tag_set_data( mEntityMark, &( *iter ), 1, &bit );
500  if( MB_SUCCESS != rval ) return rval;
501  }
502  }
503 
504  block_info.push_back( block_data );
505 
506  const void* data = NULL;
507  int size = 0;
508  if( MB_SUCCESS == mdbImpl->tag_get_by_ptr( mQaRecordTag, &( *vector_iter ), 1, &data, &size ) && NULL != data )
509  {
510  // There are qa records on this block - copy over to mesh qa records
511  const char* qa_rec = static_cast< const char* >( data );
512  int start = 0;
513  int count = 0;
514  for( int i = 0; i < size; i++ )
515  {
516  if( qa_rec[i] == '\0' )
517  {
518  std::string qa_string( &qa_rec[start], i - start );
519  mesh_info.qaRecords.push_back( qa_string );
520  start = i + 1;
521  count++;
522  }
523  }
524 
525  // Constrained to multiples of 4 qa records
526  if( count > 0 ) assert( count % 4 == 0 );
527  }
528  }
529 
530  mesh_info.num_elementblocks = block_info.size();
531 
532  for( std::vector< MaterialSetData >::iterator blit = block_info.begin(); blit != block_info.end(); blit++ )
533  {
534  MaterialSetData& block = *blit;
535  if( block.element_type != EXOII_POLYHEDRON )
536  {
537  mesh_info.polyhedronFaces = subtract( mesh_info.polyhedronFaces, block.elements );
538  }
539  }
540 
541  // If user hasn't entered dimension, we figure it out
542  if( mesh_info.num_dim == 0 )
543  {
544  // Never want 1 or zero dimensions
545  if( highest_dimension_of_element_blocks < 2 )
546  mesh_info.num_dim = 3;
547  else
548  mesh_info.num_dim = highest_dimension_of_element_blocks;
549  }
550 
551  Range::iterator range_iter, end_range_iter;
552  range_iter = mesh_info.nodes.begin();
553  end_range_iter = mesh_info.nodes.end();
554 
555  mesh_info.num_nodes = mesh_info.nodes.size();
556 
557  //------nodesets--------
558 
559  vector_iter = nodesets.begin();
560  end_vector_iter = nodesets.end();
561 
562  for( ; vector_iter != end_vector_iter; ++vector_iter )
563  {
564  DirichletSetData nodeset_data;
565  nodeset_data.id = 0;
566  nodeset_data.number_nodes = 0;
567 
568  // Get the nodeset's id
569  if( mdbImpl->tag_get_data( mDirichletSetTag, &( *vector_iter ), 1, &id ) != MB_SUCCESS )
570  {
571  MB_SET_ERR( MB_FAILURE, "Couldn't get id tag for nodeset " << id );
572  }
573 
574  nodeset_data.id = id;
575 
576  std::vector< EntityHandle > node_vector;
577  // Get the nodes of the nodeset that are in mesh_info.nodes
578  if( mdbImpl->get_entities_by_handle( *vector_iter, node_vector, true ) != MB_SUCCESS )
579  {
580  MB_SET_ERR( MB_FAILURE, "Couldn't get nodes in nodeset " << id );
581  }
582 
583  // Get the tag for distribution factors
584  const double* dist_factor_vector;
585  int dist_factor_size;
586  const void* ptr = 0;
587 
588  int has_dist_factors = 0;
589  if( mdbImpl->tag_get_by_ptr( mDistFactorTag, &( *vector_iter ), 1, &ptr, &dist_factor_size ) == MB_SUCCESS &&
590  dist_factor_size )
591  has_dist_factors = 1;
592  dist_factor_size /= sizeof( double );
593  dist_factor_vector = reinterpret_cast< const double* >( ptr );
594  std::vector< EntityHandle >::iterator iter, end_iter;
595  iter = node_vector.begin();
596  end_iter = node_vector.end();
597 
598  int j = 0;
599  unsigned char node_marked = 0;
600  ErrorCode result;
601  for( ; iter != end_iter; ++iter )
602  {
603  if( TYPE_FROM_HANDLE( *iter ) != MBVERTEX ) continue;
604  result = mdbImpl->tag_get_data( mEntityMark, &( *iter ), 1, &node_marked );MB_CHK_SET_ERR( result, "Couldn't get mark data" );
605 
606  if( 0x1 == node_marked )
607  {
608  nodeset_data.nodes.push_back( *iter );
609  if( 0 != has_dist_factors )
610  nodeset_data.node_dist_factors.push_back( dist_factor_vector[j] );
611  else
612  nodeset_data.node_dist_factors.push_back( 1.0 );
613  }
614  j++;
615  }
616 
617  nodeset_data.number_nodes = nodeset_data.nodes.size();
618  nodeset_info.push_back( nodeset_data );
619  }
620 
621  //------sidesets--------
622  vector_iter = sidesets.begin();
623  end_vector_iter = sidesets.end();
624 
625  for( ; vector_iter != end_vector_iter; ++vector_iter )
626  {
627  NeumannSetData sideset_data;
628 
629  // Get the sideset's id
630  if( mdbImpl->tag_get_data( mNeumannSetTag, &( *vector_iter ), 1, &id ) != MB_SUCCESS ) return MB_FAILURE;
631 
632  sideset_data.id = id;
633  sideset_data.mesh_set_handle = *vector_iter;
634 
635  // Get the sides in two lists, one forward the other reverse; starts with forward sense
636  // by convention
637  Range forward_elems, reverse_elems;
638  if( get_sideset_elems( *vector_iter, 0, forward_elems, reverse_elems ) == MB_FAILURE ) return MB_FAILURE;
639 
640  ErrorCode result = get_valid_sides( forward_elems, mesh_info, 1, sideset_data );MB_CHK_SET_ERR( result, "Couldn't get valid sides data" );
641  result = get_valid_sides( reverse_elems, mesh_info, -1, sideset_data );MB_CHK_SET_ERR( result, "Couldn't get valid sides data" );
642 
643  sideset_data.number_elements = sideset_data.elements.size();
644  sideset_info.push_back( sideset_data );
645  }
646 
647  return MB_SUCCESS;
648 }
649 
651  ExodusMeshInfo& /*mesh_info*/,
652  const int sense,
653  NeumannSetData& sideset_data )
654 {
655  // This is where we see if underlying element of side set element is included in output
656 
657  // Get the sideset-based info for distribution factors
658  const double* dist_factor_vector = 0;
659  int dist_factor_size = 0;
660 
661  // Initialize dist_fac_iter to get rid of compiler warning
662  const double* dist_fac_iter = 0;
663  const void* ptr = 0;
664  bool has_dist_factors = false;
665  if( mdbImpl->tag_get_by_ptr( mDistFactorTag, &( sideset_data.mesh_set_handle ), 1, &ptr, &dist_factor_size ) ==
666  MB_SUCCESS &&
667  dist_factor_size )
668  {
669  has_dist_factors = true;
670  dist_factor_vector = reinterpret_cast< const double* >( ptr );
671  dist_fac_iter = dist_factor_vector;
672  dist_factor_size /= sizeof( double );
673  }
674 
675  unsigned char element_marked = 0;
676  ErrorCode result;
677  for( Range::iterator iter = elems.begin(); iter != elems.end(); ++iter )
678  {
679  // Should insert here if "side" is a quad/tri on a quad/tri mesh
680  result = mdbImpl->tag_get_data( mEntityMark, &( *iter ), 1, &element_marked );MB_CHK_SET_ERR( result, "Couldn't get mark data" );
681 
682  if( 0x1 == element_marked )
683  {
684  sideset_data.elements.push_back( *iter );
685 
686  // TJT TODO: the sense should really be # edges + 1or2
687  sideset_data.side_numbers.push_back( ( sense == 1 ? 1 : 2 ) );
688  }
689  else
690  { // Then "side" is probably a quad/tri on a hex/tet mesh
691  std::vector< EntityHandle > parents;
692  int dimension = CN::Dimension( TYPE_FROM_HANDLE( *iter ) );
693 
694  // Get the adjacent parent element of "side"
695  if( mdbImpl->get_adjacencies( &( *iter ), 1, dimension + 1, false, parents ) != MB_SUCCESS )
696  {
697 #if 0
698  // This is not treated as an error, print warning messages for
699  // debugging only
700  fprintf(stderr, "[Warning]: Couldn't get adjacencies for sideset.\n");
701 #endif
702  }
703 
704  if( !parents.empty() )
705  {
706  // Make sure the adjacent parent element will be output
707  for( unsigned int k = 0; k < parents.size(); k++ )
708  {
709  result = mdbImpl->tag_get_data( mEntityMark, &( parents[k] ), 1, &element_marked );MB_CHK_SET_ERR( result, "Couldn't get mark data" );
710 
711  int side_no, this_sense, this_offset;
712  if( 0x1 == element_marked &&
713  mdbImpl->side_number( parents[k], *iter, side_no, this_sense, this_offset ) == MB_SUCCESS &&
714  this_sense == sense )
715  {
716  sideset_data.elements.push_back( parents[k] );
717  sideset_data.side_numbers.push_back( side_no + 1 );
718  break;
719  }
720  }
721  }
722  else
723  {
724 #if 0
725  // This is not treated as an error, print warning messages for
726  // debugging only
727  fprintf(stderr, "[Warning]: No parent element exists for element in sideset %i\n", sideset_data.id);
728 #endif
729  }
730  }
731 
732  if( sideset_data.elements.size() != 0 )
733  {
734  // Distribution factors
735  int num_nodes = CN::VerticesPerEntity( TYPE_FROM_HANDLE( *iter ) );
736  // put some dummy dist factors for polygons; why do we need them?
737  if( TYPE_FROM_HANDLE( *iter ) == MBPOLYGON ) num_nodes = 1; // dummy
738  if( has_dist_factors )
739  {
740  std::copy( dist_fac_iter, dist_fac_iter + num_nodes,
741  std::back_inserter( sideset_data.ss_dist_factors ) );
742  dist_fac_iter += num_nodes;
743  }
744  else
745  {
746  for( int j = 0; j < num_nodes; j++ )
747  sideset_data.ss_dist_factors.push_back( 1.0 );
748  }
749  }
750  }
751 
752  return MB_SUCCESS;
753 }
754 
755 ErrorCode WriteNCDF::write_qa_records( std::vector< std::string >& qa_record_list )
756 {
757  int i = 0;
758 
759  for( std::vector< std::string >::iterator string_it = qa_record_list.begin(); string_it != qa_record_list.end(); )
760  {
761  for( int j = 0; j < 4; j++ )
762  write_qa_string( ( *string_it++ ).c_str(), i, j );
763  i++;
764  }
765 
766  return MB_SUCCESS;
767 }
768 
769 ErrorCode WriteNCDF::write_qa_string( const char* string, int record_number, int record_position )
770 {
771  // Get the variable id in the exodus file
772 
773  std::vector< int > dims;
774  int temp_var = -1;
775  GET_VAR( "qa_records", temp_var, dims );
776  if( -1 == temp_var )
777  {
778  MB_SET_ERR( MB_FAILURE, "WriteNCDF:: Problem getting qa record variable" );
779  }
780  size_t count[3], start[3];
781 
782  // Write out the record
783  start[0] = record_number;
784  start[1] = record_position;
785  start[2] = 0;
786 
787  count[0] = 1;
788  count[1] = 1;
789  count[2] = (long)strlen( string ) + 1;
790  int fail = nc_put_vara_text( ncFile, temp_var, start, count, string );
791  if( NC_NOERR != fail )
792  {
793  MB_SET_ERR( MB_FAILURE, "Failed to position qa string variable" );
794  }
795 
796  return MB_SUCCESS;
797 }
798 
799 ErrorCode WriteNCDF::write_nodes( int num_nodes, Range& nodes, int dimension )
800 {
801  // Write coordinates names
802  int nc_var = -1;
803  std::vector< int > dims;
804  GET_VAR( "coor_names", nc_var, dims );
805  if( -1 == nc_var )
806  {
807  MB_SET_ERR( MB_FAILURE, "Trouble getting coordinate name variable" );
808  }
809 
810  size_t start[2] = { 0, 0 }, count[2] = { 1, ExoIIInterface::MAX_STR_LENGTH };
811  char dum_str[ExoIIInterface::MAX_STR_LENGTH];
812  strcpy( dum_str, "x" );
813  int fail = nc_put_vara_text( ncFile, nc_var, start, count, dum_str );
814  if( NC_NOERR != fail )
815  {
816  MB_SET_ERR( MB_FAILURE, "Trouble adding x coordinate name; netcdf message: " << nc_strerror( fail ) );
817  }
818 
819  start[0] = 1;
820  strcpy( dum_str, "y" );
821  fail = nc_put_vara_text( ncFile, nc_var, start, count, dum_str );
822  if( NC_NOERR != fail )
823  {
824  MB_SET_ERR( MB_FAILURE, "Trouble adding y coordinate name; netcdf message: " << nc_strerror( fail ) );
825  }
826 
827  start[0] = 2;
828  strcpy( dum_str, "z" );
829  fail = nc_put_vara_text( ncFile, nc_var, start, count, dum_str );
830  if( NC_NOERR != fail )
831  {
832  MB_SET_ERR( MB_FAILURE, "Trouble adding z coordinate name; netcdf message: " << nc_strerror( fail ) );
833  }
834 
835  // See if should transform coordinates
836  ErrorCode result;
837  Tag trans_tag;
838  result = mdbImpl->tag_get_handle( MESH_TRANSFORM_TAG_NAME, 16, MB_TYPE_DOUBLE, trans_tag );
839  bool transform_needed = true;
840  if( result == MB_TAG_NOT_FOUND ) transform_needed = false;
841 
842  int num_coords_to_fill = transform_needed ? 3 : dimension;
843 
844  std::vector< double* > coord_arrays( 3 );
845  coord_arrays[0] = new double[num_nodes];
846  coord_arrays[1] = new double[num_nodes];
847  coord_arrays[2] = NULL;
848 
849  if( num_coords_to_fill == 3 ) coord_arrays[2] = new double[num_nodes];
850 
851  result = mWriteIface->get_node_coords( dimension, num_nodes, nodes, mGlobalIdTag, 1, coord_arrays );
852  if( result != MB_SUCCESS )
853  {
854  delete[] coord_arrays[0];
855  delete[] coord_arrays[1];
856  if( coord_arrays[2] ) delete[] coord_arrays[2];
857  return result;
858  }
859 
860  if( transform_needed )
861  {
862  double trans_matrix[16];
863  const EntityHandle mesh = 0;
864  result = mdbImpl->tag_get_data( trans_tag, &mesh, 0, trans_matrix );MB_CHK_SET_ERR( result, "Couldn't get transform data" );
865 
866  for( int i = 0; i < num_nodes; i++ )
867  {
868  double vec1[3];
869  double vec2[3];
870 
871  vec2[0] = coord_arrays[0][i];
872  vec2[1] = coord_arrays[1][i];
873  vec2[2] = coord_arrays[2][i];
874 
875  for( int row = 0; row < 3; row++ )
876  {
877  vec1[row] = 0.0;
878  for( int col = 0; col < 3; col++ )
879  vec1[row] += ( trans_matrix[( row * 4 ) + col] * vec2[col] );
880  }
881 
882  coord_arrays[0][i] = vec1[0];
883  coord_arrays[1][i] = vec1[1];
884  coord_arrays[2][i] = vec1[2];
885  }
886  }
887 
888  // Write the nodes
889  nc_var = -1;
890  GET_VAR( "coord", nc_var, dims );
891  if( -1 == nc_var )
892  {
893  MB_SET_ERR( MB_FAILURE, "Trouble getting coordinate variable" );
894  }
895  start[0] = 0;
896  count[1] = num_nodes;
897  fail = nc_put_vara_double( ncFile, nc_var, start, count, &( coord_arrays[0][0] ) );
898  if( NC_NOERR != fail )
899  {
900  MB_SET_ERR( MB_FAILURE, "Trouble writing x coordinate" );
901  }
902 
903  start[0] = 1;
904  fail = nc_put_vara_double( ncFile, nc_var, start, count, &( coord_arrays[1][0] ) );
905  if( NC_NOERR != fail )
906  {
907  MB_SET_ERR( MB_FAILURE, "Trouble writing y coordinate" );
908  }
909 
910  start[0] = 2;
911  fail = nc_put_vara_double( ncFile, nc_var, start, count, &( coord_arrays[2][0] ) );
912  if( NC_NOERR != fail )
913  {
914  MB_SET_ERR( MB_FAILURE, "Trouble writing z coordinate" );
915  }
916 
917  delete[] coord_arrays[0];
918  delete[] coord_arrays[1];
919  if( coord_arrays[2] ) delete[] coord_arrays[2];
920 
921  return MB_SUCCESS;
922 }
923 
925 {
926  // write all polygons that are not in another element block;
927  // usually they are nowhere else, but be sure, write in this block only ones that are not in the
928  // other blocks
929  Range pfaces = mesh_info.polyhedronFaces;
930 
931  /*
932  * int fbconn1(num_nod_per_fa1) ;
933  fbconn1:elem_type = "nsided" ;
934  int fbepecnt1(num_fa_in_blk1) ;
935  fbepecnt1:entity_type1 = "NODE" ;
936  fbepecnt1:entity_type2 = "FACE" ;
937  */
938  if( pfaces.empty() ) return MB_SUCCESS;
939  char wname[80];
940  int nc_var = -1;
941  std::vector< int > dims;
942 
943  // write one for each element block, to make paraview and visit happy
944  int num_faces_in_block = (int)pfaces.size();
945  for( unsigned int bl = 0; bl < mesh_info.num_polyhedra_blocks; bl++ )
946  {
947  INS_ID( wname, "fbconn%u", bl + 1 ); // it is the first block
948  GET_VAR( wname, nc_var, dims ); // fbconn# variable, 1 dimensional
949 
950  INS_ID( wname, "num_nod_per_fa%u", bl + 1 );
951  int ncdim, num_nod_per_face;
952  GET_DIM( ncdim, wname, num_nod_per_face );
953  int* connectivity = new int[num_nod_per_face];
954  int ixcon = 0, j = 0;
955  std::vector< int > fbepe( num_faces_in_block ); // fbepecnt1
956  for( Range::iterator eit = pfaces.begin(); eit != pfaces.end(); eit++ )
957  {
958  EntityHandle polyg = *eit;
959  int nnodes = 0;
960  const EntityHandle* conn = NULL;
961  ErrorCode rval = mdbImpl->get_connectivity( polyg, conn, nnodes );MB_CHK_ERR( rval );
962  for( int k = 0; k < nnodes; k++ )
963  connectivity[ixcon++] = conn[k];
964  fbepe[j++] = nnodes;
965  }
966  size_t start[1] = { 0 }, count[1] = { 0 };
967  count[0] = ixcon;
968  int fail = nc_put_vara_int( ncFile, nc_var, start, count, connectivity );
969  if( NC_NOERR != fail )
970  {
971  delete[] connectivity;
972  MB_SET_ERR( MB_FAILURE, "Couldn't write fbconn variable" );
973  }
974 
975  INS_ID( wname, "fbepecnt%u", bl + 1 );
976  GET_VAR( wname, nc_var, dims ); // fbconn# variable, 1 dimensional
977  count[0] = num_faces_in_block;
978 
979  fail = nc_put_vara_int( ncFile, nc_var, start, count, &fbepe[0] );
980  if( NC_NOERR != fail )
981  {
982  delete[] connectivity;
983  MB_SET_ERR( MB_FAILURE, "Couldn't write fbepecnt variable" );
984  }
985 
986  int id = bl + 1;
987  if( write_exodus_integer_variable( "fa_prop1", &id, bl, 1 ) != MB_SUCCESS )
988  {
989  MB_SET_ERR_CONT( "Problem writing element block id " << id );
990  }
991 
992  int status = 1;
993  if( write_exodus_integer_variable( "fa_status", &status, bl, 1 ) != MB_SUCCESS )
994  {
995  MB_SET_ERR( MB_FAILURE, "Problem writing face block status" );
996  }
997 
998  delete[] connectivity;
999  if( 0 == repeat_face_blocks ) break; // do not repeat face blocks
1000  }
1001 
1002  return MB_SUCCESS;
1003 }
1005  std::vector< MaterialSetData >& block_info,
1006  std::vector< NeumannSetData >& sideset_info,
1007  std::vector< DirichletSetData >& nodeset_info,
1008  const char* filename )
1009 {
1010  // Get the date and time
1011  char time[TIME_STR_LEN];
1012  char date[TIME_STR_LEN];
1013  time_and_date( time, date );
1014 
1015  std::string title_string = "MOAB";
1016  title_string.append( "(" );
1017  title_string.append( filename );
1018  title_string.append( "): " );
1019  title_string.append( date );
1020  title_string.append( ": " );
1021  title_string.append( "time " );
1022 
1023  if( title_string.length() > ExoIIInterface::MAX_LINE_LENGTH )
1024  title_string.resize( ExoIIInterface::MAX_LINE_LENGTH );
1025 
1026  // Initialize the exodus file
1027 
1028  int result = initialize_exodus_file( mesh_info, block_info, sideset_info, nodeset_info, title_string.c_str() );
1029 
1030  if( result == MB_FAILURE ) return MB_FAILURE;
1031 
1032  return MB_SUCCESS;
1033 }
1034 
1035 ErrorCode WriteNCDF::write_elementblocks( ExodusMeshInfo& mesh_info, std::vector< MaterialSetData >& block_data )
1036 {
1037  unsigned int i;
1038  int block_index = 0; // Index into block list, may != 1 if there are inactive blocks
1039  int exodus_id = 1;
1040 
1041  for( i = 0; i < block_data.size(); i++ )
1042  {
1043  MaterialSetData& block = block_data[i];
1044 
1045  unsigned int num_nodes_per_elem = block.number_nodes_per_element;
1046 
1047  // Write out the id for the block
1048 
1049  int id = block.id;
1050  int num_values = 1;
1051 
1052  if( write_exodus_integer_variable( "eb_prop1", &id, block_index, num_values ) != MB_SUCCESS )
1053  {
1054  MB_SET_ERR_CONT( "Problem writing element block id " << id );
1055  }
1056 
1057  // Write out the block status
1058 
1059  int status = 1;
1060  if( 0 == block.number_elements )
1061  {
1062  MB_SET_ERR( MB_FAILURE, "No elements in block " << id );
1063  }
1064 
1065  if( write_exodus_integer_variable( "eb_status", &status, block_index, num_values ) != MB_SUCCESS )
1066  {
1067  MB_SET_ERR( MB_FAILURE, "Problem writing element block status" );
1068  }
1069 
1070  //
1071  // Map the connectivity to the new nodes
1072  const unsigned int num_elem = block.number_elements;
1073  unsigned int num_nodes = num_nodes_per_elem * num_elem;
1074  if( EXOII_POLYGON == block.element_type || EXOII_POLYHEDRON == block.element_type )
1075  {
1076  num_nodes = num_nodes_per_elem;
1077  }
1078  int* connectivity = new int[num_nodes];
1079 
1080  ErrorCode result = MB_SUCCESS;
1081  if( block.element_type != EXOII_POLYHEDRON )
1082  mWriteIface->get_element_connect( num_elem, num_nodes_per_elem, mGlobalIdTag, block.elements, mGlobalIdTag,
1083  exodus_id, connectivity );
1084  if( result != MB_SUCCESS )
1085  {
1086  delete[] connectivity;
1087  MB_SET_ERR( result, "Couldn't get element array to write from" );
1088  }
1089 
1090  // If necessary, convert from EXODUS to CN node order
1091  const EntityType elem_type = ExoIIUtil::ExoIIElementMBEntity[block.element_type];
1092  assert( block.elements.all_of_type( elem_type ) );
1093  const int* reorder = 0;
1094  if( block.element_type != EXOII_POLYHEDRON && block.element_type != EXOII_POLYGON )
1095  reorder = exodus_elem_order_map[elem_type][block.number_nodes_per_element];
1096  if( reorder )
1097  WriteUtilIface::reorder( reorder, connectivity, block.number_elements, block.number_nodes_per_element );
1098 
1099  char wname[80];
1100  int nc_var = -1;
1101  std::vector< int > dims;
1102  if( block.element_type != EXOII_POLYHEDRON )
1103  {
1104  exodus_id += num_elem;
1105  INS_ID( wname, "connect%u", i + 1 );
1106 
1107  GET_VAR( wname, nc_var, dims );
1108  if( -1 == nc_var )
1109  {
1110  delete[] connectivity;
1111  MB_SET_ERR( MB_FAILURE, "Couldn't get connectivity variable" );
1112  }
1113  }
1114 
1115  if( EXOII_POLYGON == block.element_type )
1116  {
1117  size_t start[1] = { 0 }, count[1] = { num_nodes_per_elem };
1118  int fail = nc_put_vara_int( ncFile, nc_var, start, count, connectivity );
1119  if( NC_NOERR != fail )
1120  {
1121  delete[] connectivity;
1122  MB_SET_ERR( MB_FAILURE, "Couldn't write connectivity variable" );
1123  }
1124  // now put also number ebepecnt1
1125  INS_ID( wname, "ebepecnt%u", i + 1 );
1126  GET_VAR( wname, nc_var, dims );
1127  count[0] = block.number_elements;
1128  start[0] = 0;
1129  // reuse connectivity array, to not allocate another one
1130  int j = 0;
1131  for( Range::iterator eit = block.elements.begin(); eit != block.elements.end(); j++, eit++ )
1132  {
1133  EntityHandle polg = *eit;
1134  int nnodes = 0;
1135  const EntityHandle* conn = NULL;
1136  ErrorCode rval = mdbImpl->get_connectivity( polg, conn, nnodes );MB_CHK_ERR( rval );
1137  connectivity[j] = nnodes;
1138  }
1139  fail = nc_put_vara_int( ncFile, nc_var, start, count, connectivity );
1140  if( NC_NOERR != fail )
1141  {
1142  delete[] connectivity;
1143  MB_SET_ERR( MB_FAILURE, "Couldn't write ebepecnt variable" );
1144  }
1145  }
1146  else if( block.element_type != EXOII_POLYHEDRON )
1147  {
1148  size_t start[2] = { 0, 0 }, count[2] = { num_elem, num_nodes_per_elem };
1149  int fail = nc_put_vara_int( ncFile, nc_var, start, count, connectivity );
1150  if( NC_NOERR != fail )
1151  {
1152  delete[] connectivity;
1153  MB_SET_ERR( MB_FAILURE, "Couldn't write connectivity variable" );
1154  }
1155  }
1156  else // if (block.element_type == EXOII_POLYHEDRON)
1157  {
1158  /* write a lot of stuff // faconn
1159  num_fa_in_blk1 = 15 ;
1160  num_nod_per_fa1 = 58 ;
1161  num_el_in_blk1 = 3 ;
1162  num_fac_per_el1 = 17 ;
1163  int fbconn1(num_nod_per_fa1) ;
1164  fbconn1:elem_type = "nsided" ;
1165  int fbepecnt1(num_fa_in_blk1) ;
1166  fbepecnt1:entity_type1 = "NODE" ;
1167  fbepecnt1:entity_type2 = "FACE" ;
1168  int facconn1(num_fac_per_el1) ;
1169  facconn1:elem_type = "NFACED" ;
1170  int ebepecnt1(num_el_in_blk1) ;
1171  ebepecnt1:entity_type1 = "FACE" ;
1172  ebepecnt1:entity_type2 = "ELEM" ;
1173 
1174  */
1175  Range& block_faces = mesh_info.polyhedronFaces;
1176  // ErrorCode rval = mdbImpl->get_connectivity(block.elements, block_faces);
1177  // MB_CHK_ERR(rval);
1178 
1179  // reuse now connectivity for facconn1
1180  INS_ID( wname, "facconn%u", i + 1 );
1181  GET_VAR( wname, nc_var, dims ); // fbconn# variable, 1 dimensional
1182 
1183  std::vector< int > ebepe( block.elements.size() ); // ebepecnt1
1184  int ixcon = 0, j = 0;
1185  size_t start[1] = { 0 }, count[1] = { 0 };
1186 
1187  for( Range::iterator eit = block.elements.begin(); eit != block.elements.end(); eit++ )
1188  {
1189  EntityHandle polyh = *eit;
1190  int nfaces = 0;
1191  const EntityHandle* conn = NULL;
1192  ErrorCode rval = mdbImpl->get_connectivity( polyh, conn, nfaces );MB_CHK_ERR( rval );
1193  for( int k = 0; k < nfaces; k++ )
1194  {
1195  int index = block_faces.index( conn[k] );
1196  if( index == -1 ) MB_SET_ERR( MB_FAILURE, "Couldn't find face in polyhedron" );
1197  connectivity[ixcon++] = index + 1;
1198  }
1199  ebepe[j++] = nfaces;
1200  // num_faces+=nfaces;
1201  }
1202  count[0] = ixcon; // facconn1
1203  int fail = nc_put_vara_int( ncFile, nc_var, start, count, connectivity );
1204  if( NC_NOERR != fail )
1205  {
1206  delete[] connectivity;
1207  MB_SET_ERR( MB_FAILURE, "Couldn't write fbconn variable" );
1208  }
1209 
1210  INS_ID( wname, "ebepecnt%u", i + 1 );
1211  GET_VAR( wname, nc_var, dims ); // ebepecnt# variable, 1 dimensional
1212  count[0] = block.elements.size();
1213 
1214  fail = nc_put_vara_int( ncFile, nc_var, start, count, &ebepe[0] );
1215  if( NC_NOERR != fail )
1216  {
1217  delete[] connectivity;
1218  MB_SET_ERR( MB_FAILURE, "Couldn't write fbepecnt variable" );
1219  }
1220  }
1221  block_index++;
1222  delete[] connectivity;
1223  }
1224 
1225  return MB_SUCCESS;
1226 }
1227 
1229 {
1230  // Note: this routine bypasses the standard exodusII interface for efficiency!
1231 
1232  // Node order map
1233  int* map = new int[num_nodes];
1234 
1235  // For now, output a dummy map!
1236 
1237  Range::iterator range_iter, end_iter;
1238  range_iter = nodes.begin();
1239  end_iter = nodes.end();
1240 
1241  int i = 0;
1242 
1243  for( ; range_iter != end_iter; ++range_iter )
1244  {
1245  // TODO -- do we really want to cast this to an int?
1246  map[i++] = (int)ID_FROM_HANDLE( *range_iter );
1247  }
1248 
1249  // Output array and cleanup
1250 
1251  int error = write_exodus_integer_variable( "node_num_map", map, 0, num_nodes );
1252 
1253  if( map ) delete[] map;
1254 
1255  if( error < 0 )
1256  {
1257  MB_SET_ERR( MB_FAILURE, "Failed writing global node order map" );
1258  }
1259 
1260  return MB_SUCCESS;
1261 }
1262 
1264 {
1265  // Allocate map array
1266  int* map = new int[num_elements];
1267 
1268  // Many Sandia codes assume this map is unique, and CUBIT does not currently
1269  // have unique ids for all elements. Therefore, to make sure nothing crashes,
1270  // insert a dummy map...
1271 
1272  for( int i = 0; i < num_elements; i++ )
1273  map[i] = i + 1;
1274 
1275  // Output array and cleanup
1276 
1277  int error = write_exodus_integer_variable( "elem_num_map", map, 0, num_elements );
1278 
1279  if( map ) delete[] map;
1280 
1281  if( error < 0 )
1282  {
1283  MB_SET_ERR( MB_FAILURE, "Failed writing global element order map" );
1284  }
1285 
1286  return MB_SUCCESS;
1287 }
1288 
1290 {
1291  // Note: this routine bypasses the standard exodusII interface for efficiency!
1292 
1293  // Element order map
1294  int* map = new int[num_elements];
1295 
1296  // For now, output a dummy map!
1297 
1298  for( int i = 0; i < num_elements; i++ )
1299  {
1300  map[i] = i + 1;
1301  }
1302 
1303  // Output array and cleanup
1304 
1305  int error = write_exodus_integer_variable( "elem_map", map, 0, num_elements );
1306 
1307  if( map ) delete[] map;
1308 
1309  if( error < 0 )
1310  {
1311  MB_SET_ERR( MB_FAILURE, "Failed writing element map" );
1312  }
1313 
1314  return MB_SUCCESS;
1315 }
1316 
1318  int* variable_array,
1319  int start_position,
1320  int number_values )
1321 {
1322  // Note: this routine bypasses the standard exodusII interface for efficiency!
1323 
1324  // Write directly to netcdf interface for efficiency
1325 
1326  // Get the variable id of the element map
1327  int nc_var = -1;
1328  std::vector< int > dims;
1329  GET_VAR( variable_name, nc_var, dims );
1330  if( -1 == nc_var )
1331  {
1332  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to locate variable " << variable_name << " in file" );
1333  }
1334  // This contortion is necessary because netCDF is expecting nclongs;
1335  // fortunately it's necessary only when ints and nclongs aren't the same size
1336 
1337  size_t start[1], count[1];
1338  start[0] = start_position;
1339  count[0] = number_values;
1340 
1341  int fail = NC_NOERR;
1342  if( sizeof( int ) == sizeof( long ) )
1343  {
1344  fail = nc_put_vara_int( ncFile, nc_var, start, count, variable_array );
1345  }
1346  else
1347  {
1348  long* lptr = new long[number_values];
1349  for( int jj = 0; jj < number_values; jj++ )
1350  lptr[jj] = variable_array[jj];
1351  fail = nc_put_vara_long( ncFile, nc_var, start, count, lptr );
1352  delete[] lptr;
1353  }
1354 
1355  if( NC_NOERR != fail )
1356  {
1357  MB_SET_ERR( MB_FAILURE, "Failed to store variable " << variable_name );
1358  }
1359 
1360  return MB_SUCCESS;
1361 }
1362 
1363 ErrorCode WriteNCDF::write_BCs( std::vector< NeumannSetData >& sidesets, std::vector< DirichletSetData >& nodesets )
1364 {
1365  unsigned int i, j;
1366  int id;
1367  int ns_index = -1;
1368 
1369  for( std::vector< DirichletSetData >::iterator ns_it = nodesets.begin(); ns_it != nodesets.end(); ++ns_it )
1370  {
1371  // Get number of nodes in set
1372  int number_nodes = ( *ns_it ).number_nodes;
1373  if( 0 == number_nodes ) continue;
1374 
1375  // If we're here, we have a non-empty nodeset; increment the index
1376  ns_index++;
1377 
1378  // Get the node set id
1379  id = ( *ns_it ).id;
1380 
1381  // Build new array to old exodus ids
1382  int* exodus_id_array = new int[number_nodes];
1383  double* dist_factor_array = new double[number_nodes];
1384 
1385  std::vector< EntityHandle >::iterator begin_iter, end_iter;
1386  std::vector< double >::iterator other_iter;
1387  begin_iter = ( *ns_it ).nodes.begin();
1388  end_iter = ( *ns_it ).nodes.end();
1389  other_iter = ( *ns_it ).node_dist_factors.begin();
1390 
1391  j = 0;
1392  int exodus_id;
1393  ErrorCode result;
1394  // Fill up node array and dist. factor array at the same time
1395  for( ; begin_iter != end_iter; ++begin_iter )
1396  {
1397  result = mdbImpl->tag_get_data( mGlobalIdTag, &( *begin_iter ), 1, &exodus_id );MB_CHK_SET_ERR( result, "Problem getting id tag data" );
1398 
1399  exodus_id_array[j] = exodus_id;
1400  dist_factor_array[j] = *( other_iter );
1401  ++other_iter;
1402  j++;
1403  }
1404 
1405  // Write out the id for the nodeset
1406 
1407  int num_values = 1;
1408 
1409  result = write_exodus_integer_variable( "ns_prop1", &id, ns_index, num_values );MB_CHK_SET_ERR_RET_VAL( result, "Problem writing node set id " << id, MB_FAILURE );
1410 
1411  // Write out the nodeset status
1412 
1413  int status = 1;
1414  if( !number_nodes ) status = 0;
1415 
1416  result = write_exodus_integer_variable( "ns_status", &status, ns_index, num_values );MB_CHK_SET_ERR_RET_VAL( result, "Problem writing node set status", MB_FAILURE );
1417 
1418  // Write it out
1419  char wname[80];
1420  int nc_var = -1;
1421  std::vector< int > dims;
1422  INS_ID( wname, "node_ns%d", ns_index + 1 );
1423  GET_VAR( wname, nc_var, dims );
1424  if( -1 == nc_var )
1425  {
1426  MB_SET_ERR( MB_FAILURE, "Failed to get node_ns variable" );
1427  }
1428 
1429  size_t start = 0, count = number_nodes;
1430  int fail = nc_put_vara_int( ncFile, nc_var, &start, &count, exodus_id_array );
1431  if( NC_NOERR != fail )
1432  {
1433  MB_SET_ERR( MB_FAILURE, "Failed writing exodus id array" );
1434  }
1435 
1436  // Write out nodeset distribution factors
1437  INS_ID( wname, "dist_fact_ns%d", ns_index + 1 );
1438  nc_var = -1;
1439  GET_VAR( wname, nc_var, dims );
1440  if( -1 == nc_var )
1441  {
1442  MB_SET_ERR( MB_FAILURE, "Failed to get dist_fact variable" );
1443  }
1444  fail = nc_put_vara_double( ncFile, nc_var, &start, &count, dist_factor_array );
1445  if( NC_NOERR != fail )
1446  {
1447  MB_SET_ERR( MB_FAILURE, "Failed writing dist factor array" );
1448  }
1449 
1450  delete[] dist_factor_array;
1451  delete[] exodus_id_array;
1452  }
1453 
1454  // Now do sidesets
1455  int ss_index = 0; // Index of sideset - not the same as 'i' because
1456  // only writing non-empty side sets
1457  for( i = 0; i < sidesets.size(); i++ )
1458  {
1459  NeumannSetData sideset_data = sidesets[i];
1460 
1461  // Get the side set id
1462  int side_set_id = sideset_data.id;
1463 
1464  // Get number of elements in set
1465  int number_elements = sideset_data.number_elements;
1466  if( 0 == number_elements ) continue;
1467 
1468  // Build new array to old exodus ids
1469  int* output_element_ids = new int[number_elements];
1470  int* output_element_side_numbers = new int[number_elements];
1471 
1472  std::vector< EntityHandle >::iterator begin_iter, end_iter;
1473  begin_iter = sideset_data.elements.begin();
1474  end_iter = sideset_data.elements.end();
1475  std::vector< int >::iterator side_iter = sideset_data.side_numbers.begin();
1476 
1477  // Get the tag handle
1478  j = 0;
1479  int exodus_id;
1480 
1481  // For each "side"
1482  for( ; begin_iter != end_iter; ++begin_iter, ++side_iter )
1483  {
1484  ErrorCode result = mdbImpl->tag_get_data( mGlobalIdTag, &( *begin_iter ), 1, &exodus_id );MB_CHK_SET_ERR( result, "Problem getting exodus id for sideset element "
1485  << (long unsigned int)ID_FROM_HANDLE( *begin_iter ) );
1486 
1487  output_element_ids[j] = exodus_id;
1488  output_element_side_numbers[j++] = *side_iter;
1489  }
1490 
1491  if( 0 != number_elements )
1492  {
1493  // Write out the id for the nodeset
1494 
1495  int num_values = 1;
1496 
1497  // ss_prop1[ss_index] = side_set_id
1498  ErrorCode result = write_exodus_integer_variable( "ss_prop1", &side_set_id, ss_index, num_values );MB_CHK_SET_ERR_RET_VAL( result, "Problem writing node set id " << id, MB_FAILURE );
1499 
1500  // FIXME : Something seems wrong here. The we are within a block
1501  // started with if (0 != number_elements), so this condition is always
1502  // false. This code seems to indicate that we want to write all
1503  // sidesets, not just empty ones. But the code both here and in
1504  // initialize_exodus_file() skip empty side sets.
1505  // - j.k. 2007-03-09
1506  int status = 1;
1507  if( 0 == number_elements ) status = 0;
1508 
1509  // ss_status[ss_index] = status
1510  result = write_exodus_integer_variable( "ss_status", &status, ss_index, num_values );MB_CHK_SET_ERR_RET_VAL( result, "Problem writing side set status", MB_FAILURE );
1511 
1512  // Increment ss_index now because we want a) we need to
1513  // increment it somewhere within the if (0 != number_elements)
1514  // block and b) the above calls need a zero-based index
1515  // while the following use a one-based index.
1516  ++ss_index;
1517 
1518  char wname[80];
1519  int nc_var;
1520  std::vector< int > dims;
1521  INS_ID( wname, "elem_ss%d", ss_index );
1522  GET_VAR( wname, nc_var, dims );
1523  if( -1 == nc_var )
1524  {
1525  MB_SET_ERR( MB_FAILURE, "Failed to get elem_ss variable" );
1526  }
1527  size_t start = 0, count = number_elements;
1528  int fail = nc_put_vara_int( ncFile, nc_var, &start, &count, output_element_ids );
1529  if( NC_NOERR != fail )
1530  {
1531  MB_SET_ERR( MB_FAILURE, "Failed writing sideset element array" );
1532  }
1533 
1534  INS_ID( wname, "side_ss%d", ss_index );
1535  nc_var = -1;
1536  GET_VAR( wname, nc_var, dims );
1537  if( -1 == nc_var )
1538  {
1539  MB_SET_ERR( MB_FAILURE, "Failed to get side_ss variable" );
1540  }
1541  fail = nc_put_vara_int( ncFile, nc_var, &start, &count, output_element_side_numbers );
1542  if( NC_NOERR != fail )
1543  {
1544  MB_SET_ERR( MB_FAILURE, "Failed writing sideset side array" );
1545  }
1546 
1547  INS_ID( wname, "dist_fact_ss%d", ss_index );
1548  nc_var = -1;
1549  GET_VAR( wname, nc_var, dims );
1550  if( -1 == nc_var )
1551  {
1552  MB_SET_ERR( MB_FAILURE, "Failed to get sideset dist factors variable" );
1553  }
1554  count = sideset_data.ss_dist_factors.size();
1555  fail = nc_put_vara_double( ncFile, nc_var, &start, &count, &( sideset_data.ss_dist_factors[0] ) );
1556  if( NC_NOERR != fail )
1557  {
1558  MB_SET_ERR( MB_FAILURE, "Failed writing sideset dist factors array" );
1559  }
1560  }
1561 
1562  delete[] output_element_ids;
1563  delete[] output_element_side_numbers;
1564  }
1565 
1566  return MB_SUCCESS;
1567 }
1568 
1570  std::vector< MaterialSetData >& block_data,
1571  std::vector< NeumannSetData >& sideset_data,
1572  std::vector< DirichletSetData >& nodeset_data,
1573  const char* title_string,
1574  bool write_maps,
1575  bool /* write_sideset_distribution_factors */ )
1576 {
1577  // This routine takes the place of the exodusii routine ex_put_init,
1578  // and additionally pre-defines variables such as qa, element blocks,
1579  // sidesets and nodesets in a single pass.
1580  //
1581  // This is necessary because of the way exodusII works. Each time the
1582  // netcdf routine endef is called to take the file out of define mode,
1583  // the entire file is copied before the new information is added.
1584  //
1585  // With very large files, this is simply not workable. This routine takes
1586  // the definition portions of all applicable exodus routines and puts them
1587  // in a single definition, preventing repeated copying of the file.
1588  //
1589  // Most of the code is copied directly from the applicable exodus routine,
1590  // and thus this routine may not seem very consistent in usage or variable
1591  // naming, etc.
1592 
1593  // Perform the initializations
1594 
1595  int element_block_index;
1596 
1597  // Inquire on defined string dimension and general dimension for qa
1598 
1599  int dim_str, dim_four, dim_line, dim_time;
1600  if( nc_def_dim( ncFile, "len_string", ExoIIInterface::MAX_STR_LENGTH, &dim_str ) != NC_NOERR )
1601  {
1602  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to get string length in file" );
1603  }
1604 
1605  if( nc_def_dim( ncFile, "len_line", ExoIIInterface::MAX_STR_LENGTH, &dim_line ) != NC_NOERR )
1606  {
1607  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to get line length in file" );
1608  }
1609 
1610  if( nc_def_dim( ncFile, "four", 4, &dim_four ) != NC_NOERR )
1611  {
1612  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to locate four in file" );
1613  }
1614 
1615  if( nc_def_dim( ncFile, "time_step", 1, &dim_time ) != NC_NOERR )
1616  {
1617  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to locate time step in file" );
1618  }
1619  // some whole_time dummy :(
1620  int dtime;
1621  if( NC_NOERR != nc_def_var( ncFile, "time_whole", NC_DOUBLE, 1, &dim_time, &dtime ) )
1622  {
1623  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define time whole array" );
1624  }
1625 
1626  /* Put file into define mode */
1627 
1628  // It is possible that an NT filename using backslashes is in the title string
1629  // this can give fits to unix codes where the backslash is assumed to be an escape
1630  // sequence. For the exodus file, just flip the backslash to a slash to prevent
1631  // this problem
1632 
1633  // Get a working copy of the title_string;
1634 
1635  char working_title[80];
1636  strncpy( working_title, title_string, 79 );
1637 
1638  int length = strlen( working_title );
1639  for( int pos = 0; pos < length; pos++ )
1640  {
1641  if( working_title[pos] == '\\' ) working_title[pos] = '/';
1642  }
1643 
1644  if( NC_NOERR != nc_put_att_text( ncFile, NC_GLOBAL, "title", length, working_title ) )
1645  {
1646  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define title attribute" );
1647  }
1648 
1649  // Add other attributes while we're at it
1650  float dum_vers = 6.28F;
1651  if( NC_NOERR != nc_put_att_float( ncFile, NC_GLOBAL, "api_version", NC_FLOAT, 1, &dum_vers ) )
1652  {
1653  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define api_version attribute" );
1654  }
1655  dum_vers = 6.28F;
1656  if( NC_NOERR != nc_put_att_float( ncFile, NC_GLOBAL, "version", NC_FLOAT, 1, &dum_vers ) )
1657  {
1658  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define version attribute" );
1659  }
1660  int dum_siz = sizeof( double );
1661  if( NC_NOERR != nc_put_att_int( ncFile, NC_GLOBAL, "floating_point_word_size", NC_INT, 1, &dum_siz ) )
1662  {
1663  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define floating pt word size attribute" );
1664  }
1665 
1666  // Set up number of dimensions
1667 
1668  int num_el_blk, num_elem, num_nodes, num_dim, num_fa_blk, num_faces;
1669  if( nc_def_dim( ncFile, "num_dim", (size_t)mesh_info.num_dim, &num_dim ) != NC_NOERR )
1670  {
1671  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of dimensions" );
1672  }
1673 
1674  if( nc_def_dim( ncFile, "num_nodes", mesh_info.num_nodes, &num_nodes ) != NC_NOERR )
1675  {
1676  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of nodes" );
1677  }
1678 
1679  int num_nod_per_fa; // it is needed for polyhedron only; need to compute it (connectivity of
1680  // faces of polyhedra)
1681  if( mesh_info.polyhedronFaces.size() > 0 )
1682  if( nc_def_dim( ncFile, "num_faces", (int)mesh_info.polyhedronFaces.size(), &num_faces ) != NC_NOERR )
1683  {
1684  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of nodes" );
1685  }
1686 
1687  if( nc_def_dim( ncFile, "num_elem", mesh_info.num_elements, &num_elem ) != NC_NOERR )
1688  {
1689  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of elements" );
1690  }
1691 
1692  if( nc_def_dim( ncFile, "num_el_blk", mesh_info.num_elementblocks, &num_el_blk ) != NC_NOERR )
1693  {
1694  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of element blocks" );
1695  }
1696 
1697  /* ...and some variables */
1698 
1699  /* Element block id status array */
1700  int idstat = -1;
1701  if( NC_NOERR != nc_def_var( ncFile, "eb_status", NC_LONG, 1, &num_el_blk, &idstat ) )
1702  {
1703  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define element block status array" );
1704  }
1705 
1706  /* Element block id array */
1707 
1708  int idarr = -1;
1709  if( NC_NOERR != nc_def_var( ncFile, "eb_prop1", NC_LONG, 1, &num_el_blk, &idarr ) )
1710  {
1711  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define element block id array" );
1712  }
1713 
1714  /* store property name as attribute of property array variable */
1715  if( NC_NOERR != nc_put_att_text( ncFile, idarr, "name", strlen( "ID" ), "ID" ) )
1716  {
1717  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store element block property name ID" );
1718  }
1719 
1720  // count how many are polyhedron blocks
1721  int num_fa_blocks = 0, num_polyh_blocks = 0;
1722  for( unsigned int i = 0; i < block_data.size(); i++ )
1723  {
1724  MaterialSetData& block = block_data[i];
1725  if( EXOII_POLYHEDRON == block.element_type )
1726  {
1727  num_fa_blocks++;
1728  num_polyh_blocks++;
1729  }
1730  }
1731  if( 0 == this->repeat_face_blocks && num_fa_blocks > 1 ) num_fa_blocks = 1;
1732  char wname[80];
1733 
1734  if( num_fa_blocks > 0 )
1735  {
1736  /* face block id status array */
1737  if( nc_def_dim( ncFile, "num_fa_blk", num_fa_blocks, &num_fa_blk ) != NC_NOERR )
1738  {
1739  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of face blocks" );
1740  }
1741 
1742  int idstatf = -1;
1743  if( NC_NOERR != nc_def_var( ncFile, "fa_status", NC_LONG, 1, &num_fa_blk, &idstatf ) )
1744  {
1745  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define face block status array" );
1746  }
1747 
1748  /* Element block id array */
1749 
1750  int idarrf = -1;
1751  if( NC_NOERR != nc_def_var( ncFile, "fa_prop1", NC_LONG, 1, &num_fa_blk, &idarrf ) )
1752  {
1753  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define face block id array" );
1754  }
1755 
1756  /* store property name as attribute of property array variable */
1757  if( NC_NOERR != nc_put_att_text( ncFile, idarrf, "name", strlen( "ID" ), "ID" ) )
1758  {
1759  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store face block property name ID" );
1760  }
1761  // determine the number of num_nod_per_face
1762  /*
1763  num_fa_in_blk1 = 15 ;
1764  num_nod_per_fa1 = 58 ;
1765 
1766  int fbconn1(num_nod_per_fa1) ;
1767  fbconn1:elem_type = "nsided" ;
1768  int fbepecnt1(num_fa_in_blk1) ;
1769  fbepecnt1:entity_type1 = "NODE" ;
1770  fbepecnt1:entity_type2 = "FACE" ;
1771  */
1772 
1773  int num_nodes_per_face = 0;
1774 
1775  int dims[1]; // maybe 1 is enough here
1776  for( Range::iterator eit = mesh_info.polyhedronFaces.begin(); eit != mesh_info.polyhedronFaces.end(); eit++ )
1777  {
1778  EntityHandle polyg = *eit;
1779  int nnodes = 0;
1780  const EntityHandle* conn = NULL;
1781  ErrorCode rval = mdbImpl->get_connectivity( polyg, conn, nnodes );MB_CHK_ERR( rval );
1782  num_nodes_per_face += nnodes;
1783  }
1784 
1785  // duplicate if needed; default is not duplicate
1786  for( int j = 1; j <= num_fa_blocks; j++ )
1787  {
1788  INS_ID( wname, "num_nod_per_fa%d", j );
1789  if( nc_def_dim( ncFile, wname, (size_t)num_nodes_per_face, &num_nod_per_fa ) != NC_NOERR )
1790  {
1791  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of nodes for face block " );
1792  }
1793  dims[0] = num_nod_per_fa;
1794  INS_ID( wname, "fbconn%d", j ); // first one, or more
1795  int fbconn;
1796  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, dims, &fbconn ) )
1797  {
1798  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create connectivity array for face block " << 1 );
1799  }
1800  std::string element_type_string( "nsided" );
1801  if( NC_NOERR != nc_put_att_text( ncFile, fbconn, "elem_type", element_type_string.length(),
1802  element_type_string.c_str() ) )
1803  {
1804  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store element type nsided " );
1805  }
1806 
1807  INS_ID( wname, "num_fa_in_blk%d", j ); // first one, or more
1808  int num_fa_in_blk;
1809  if( nc_def_dim( ncFile, wname, (size_t)mesh_info.polyhedronFaces.size(), &num_fa_in_blk ) != NC_NOERR )
1810  {
1811  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of nodes for face block " );
1812  }
1813 
1814  // fbepecnt
1815  INS_ID( wname, "fbepecnt%d", j ); // first one, or more
1816  int fbepecnt;
1817  dims[0] = num_fa_in_blk;
1818  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, dims, &fbepecnt ) )
1819  {
1820  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create fbepecnt array for block " << 1 );
1821  }
1822  std::string enttype1( "NODE" );
1823  if( NC_NOERR != nc_put_att_text( ncFile, fbepecnt, "entity_type1", enttype1.length(), enttype1.c_str() ) )
1824  {
1825  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store entity type 1 " );
1826  }
1827  std::string enttype2( "FACE" );
1828  if( NC_NOERR != nc_put_att_text( ncFile, fbepecnt, "entity_type2", enttype2.length(), enttype2.c_str() ) )
1829  {
1830  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store entity type 2 " );
1831  }
1832  }
1833  }
1834 
1835  // Define element blocks
1836 
1837  for( unsigned int i = 0; i < block_data.size(); i++ )
1838  {
1839  MaterialSetData& block = block_data[i];
1840 
1841  element_block_index = i + 1;
1842  int num_el_in_blk = -1, num_att_in_blk = -1;
1843  int blk_attrib, connect;
1844 
1845  /* Define number of elements in this block */
1846 
1847  INS_ID( wname, "num_el_in_blk%d", element_block_index );
1848  if( nc_def_dim( ncFile, wname, (size_t)block.number_elements, &num_el_in_blk ) != NC_NOERR )
1849  {
1850  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of elements/block for block " << i + 1 );
1851  }
1852 
1853  /* Define number of nodes per element for this block */
1854  INS_ID( wname, "num_nod_per_el%d", element_block_index );
1855  int num_nod_per_el = -1;
1856  if( EXOII_POLYHEDRON != block.element_type )
1857  if( nc_def_dim( ncFile, wname, (size_t)block.number_nodes_per_element, &num_nod_per_el ) != NC_NOERR )
1858  {
1859  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of nodes/element for block " << block.id );
1860  }
1861 
1862  /* Define element attribute array for this block */
1863  int dims[3];
1864  if( block.number_attributes > 0 )
1865  {
1866  INS_ID( wname, "num_att_in_blk%d", element_block_index );
1867  if( nc_def_dim( ncFile, wname, (size_t)block.number_attributes, &num_att_in_blk ) != NC_NOERR )
1868  {
1869  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of attributes in block " << block.id );
1870  }
1871 
1872  INS_ID( wname, "attrib%d", element_block_index );
1873  dims[0] = num_el_in_blk;
1874  dims[1] = num_att_in_blk;
1875  if( NC_NOERR != nc_def_var( ncFile, wname, NC_DOUBLE, 2, dims, &blk_attrib ) )
1876  {
1877  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define attributes for element block " << block.id );
1878  }
1879  }
1880 
1881  /* Define element connectivity array for this block */
1882 
1883  if( EXOII_POLYGON != block.element_type && EXOII_POLYHEDRON != block.element_type )
1884  {
1885  INS_ID( wname, "connect%d", element_block_index );
1886  dims[0] = num_el_in_blk;
1887  dims[1] = num_nod_per_el;
1888  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 2, dims, &connect ) )
1889  {
1890  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create connectivity array for block " << i + 1 );
1891  }
1892 
1893  /* Store element type as attribute of connectivity variable */
1894 
1895  std::string element_type_string( ExoIIUtil::ElementTypeNames[block.element_type] );
1896  if( NC_NOERR != nc_put_att_text( ncFile, connect, "elem_type", element_type_string.length(),
1897  element_type_string.c_str() ) )
1898  {
1899  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store element type name " << (int)block.element_type );
1900  }
1901  }
1902  else if( EXOII_POLYGON == block.element_type )
1903  {
1904  INS_ID( wname, "connect%d", element_block_index );
1905  // need to define num_nod_per_el as total number of nodes
1906  // ebepecnt1 as number of nodes per polygon
1907  /*
1908  * int connect1(num_nod_per_el1) ;
1909  connect1:elem_type = "nsided" ;
1910  int ebepecnt1(num_el_in_blk1) ;
1911  ebepecnt1:entity_type1 = "NODE" ;
1912  ebepecnt1:entity_type2 = "ELEM" ;*/
1913  dims[0] = num_nod_per_el;
1914  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, dims, &connect ) )
1915  {
1916  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create connectivity array for block " << i + 1 );
1917  }
1918  std::string element_type_string( "nsided" );
1919  if( NC_NOERR != nc_put_att_text( ncFile, connect, "elem_type", element_type_string.length(),
1920  element_type_string.c_str() ) )
1921  {
1922  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store element type name " << (int)block.element_type );
1923  }
1924  INS_ID( wname, "ebepecnt%d", element_block_index );
1925  int ebepecnt;
1926  dims[0] = num_el_in_blk;
1927  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, dims, &ebepecnt ) )
1928  {
1929  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create ebepecnt array for block " << i + 1 );
1930  }
1931  std::string etype1( "NODE" );
1932  if( NC_NOERR != nc_put_att_text( ncFile, ebepecnt, "entity_type1", etype1.length(), etype1.c_str() ) )
1933  {
1934  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store entity type1 " << (int)block.element_type );
1935  }
1936  std::string etype2( "ELEM" );
1937  if( NC_NOERR != nc_put_att_text( ncFile, ebepecnt, "entity_type2", etype2.length(), etype2.c_str() ) )
1938  {
1939  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store entity type2 " << (int)block.element_type );
1940  }
1941  }
1942  else if( EXOII_POLYHEDRON == block.element_type )
1943  {
1944  // INS_ID(wname, "connect%d", element_block_index);
1945  /*
1946  testn face example: 3 polyh, 15 total faces, 2 shared; 15+2 = 17
1947  num_elem = 3 ;
1948  num_face = 15 ; // not needed?
1949  num_el_blk = 1 ;
1950 
1951  num_el_in_blk1 = 3 ;
1952  num_fac_per_el1 = 17 ;
1953 
1954  * num faces will be total face conn
1955  * num_faces_in_block will be number of faces (non repeated)
1956  * num_nodes_per_face will have total face connectivity
1957  */
1958  int num_faces2 = 0;
1959 
1960  for( Range::iterator eit = block.elements.begin(); eit != block.elements.end(); eit++ )
1961  {
1962  EntityHandle polyh = *eit;
1963  int nfaces = 0;
1964  const EntityHandle* conn = NULL;
1965  ErrorCode rval = mdbImpl->get_connectivity( polyh, conn, nfaces );MB_CHK_ERR( rval );
1966  num_faces2 += nfaces;
1967  }
1968 
1969  int num_fac_per_el;
1970 
1971  INS_ID( wname, "num_fac_per_el%d", element_block_index );
1972  if( nc_def_dim( ncFile, wname, (size_t)num_faces2, &num_fac_per_el ) != NC_NOERR )
1973  {
1974  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of faces per block " << block.id );
1975  }
1976 
1977  /*
1978  int facconn1(num_fac_per_el1) ;
1979  facconn1:elem_type = "NFACED" ;
1980  int ebepecnt1(num_el_in_blk1) ;
1981  ebepecnt1:entity_type1 = "FACE" ;
1982  ebepecnt1:entity_type2 = "ELEM" ;
1983  */
1984 
1985  // facconn
1986  INS_ID( wname, "facconn%d", element_block_index );
1987  int facconn;
1988  dims[0] = num_fac_per_el;
1989  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, dims, &facconn ) )
1990  {
1991  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create facconn array for block " << i + 1 );
1992  }
1993  std::string etype( "NFACED" );
1994  if( NC_NOERR != nc_put_att_text( ncFile, facconn, "elem_type", etype.length(), etype.c_str() ) )
1995  {
1996  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store elem type " << (int)block.element_type );
1997  }
1998 
1999  // ebepecnt
2000  INS_ID( wname, "ebepecnt%d", element_block_index );
2001  int ebepecnt;
2002  dims[0] = num_el_in_blk;
2003  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, dims, &ebepecnt ) )
2004  {
2005  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create ebepecnt array for block " << i + 1 );
2006  }
2007  std::string etype1( "FACE" );
2008  if( NC_NOERR != nc_put_att_text( ncFile, ebepecnt, "entity_type1", etype1.length(), etype1.c_str() ) )
2009  {
2010  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store entity type1 " << (int)block.element_type );
2011  }
2012  std::string etype2( "ELEM" );
2013  if( NC_NOERR != nc_put_att_text( ncFile, ebepecnt, "entity_type2", etype2.length(), etype2.c_str() ) )
2014  {
2015  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store entity type2 " << (int)block.element_type );
2016  }
2017 
2018  block.number_nodes_per_element = num_faces2; // connectivity for all polyhedra in block
2019  }
2020  }
2021 
2022  /* Node set id array: */
2023 
2024  int non_empty_nss = 0;
2025  // Need to go through nodesets to compute # nodesets, some might be empty
2026  std::vector< DirichletSetData >::iterator ns_it;
2027  for( ns_it = nodeset_data.begin(); ns_it != nodeset_data.end(); ++ns_it )
2028  {
2029  if( 0 != ( *ns_it ).number_nodes ) non_empty_nss++;
2030  }
2031 
2032  int num_ns = -1;
2033  int ns_idstat = -1, ns_idarr = -1;
2034  if( non_empty_nss > 0 )
2035  {
2036  if( nc_def_dim( ncFile, "num_node_sets", (size_t)( non_empty_nss ), &num_ns ) != NC_NOERR )
2037  {
2038  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of node sets" );
2039  }
2040 
2041  /* Node set id status array: */
2042 
2043  if( NC_NOERR != nc_def_var( ncFile, "ns_status", NC_LONG, 1, &num_ns, &ns_idstat ) )
2044  {
2045  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create node sets status array" );
2046  }
2047 
2048  /* Node set id array: */
2049  if( NC_NOERR != nc_def_var( ncFile, "ns_prop1", NC_LONG, 1, &num_ns, &ns_idarr ) )
2050  {
2051  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create node sets property array" );
2052  }
2053 
2054  /* Store property name as attribute of property array variable */
2055  if( NC_NOERR != nc_put_att_text( ncFile, NC_GLOBAL, "name", strlen( "ID" ), "ID" ) )
2056  {
2057  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store node set property name ID" );
2058  }
2059 
2060  // Now, define the arrays needed for each node set
2061 
2062  int index = 0;
2063 
2064  for( unsigned i = 0; i < nodeset_data.size(); i++ )
2065  {
2066  DirichletSetData node_set = nodeset_data[i];
2067 
2068  if( 0 == node_set.number_nodes )
2069  {
2070  MB_SET_ERR_CONT( "WriteNCDF: empty nodeset " << node_set.id );
2071  continue;
2072  }
2073  index++;
2074 
2075  int num_nod_ns = -1;
2076  INS_ID( wname, "num_nod_ns%d", index );
2077  if( nc_def_dim( ncFile, wname, (size_t)node_set.number_nodes, &num_nod_ns ) != NC_NOERR )
2078  {
2079  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of nodes for set " << node_set.id );
2080  }
2081 
2082  /* Create variable array in which to store the node set node list */
2083  int node_ns = -1;
2084  INS_ID( wname, "node_ns%d", index );
2085  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, &num_nod_ns, &node_ns ) )
2086  {
2087  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create node set " << node_set.id << " node list" );
2088  }
2089 
2090  // Create distribution factor array
2091  int fact_ns = -1;
2092  INS_ID( wname, "dist_fact_ns%d", index );
2093  if( NC_NOERR != nc_def_var( ncFile, wname, NC_DOUBLE, 1, &num_nod_ns, &fact_ns ) )
2094  {
2095  MB_SET_ERR( MB_FAILURE,
2096  "WriteNCDF: failed to create node set " << node_set.id << " distribution factor list" );
2097  }
2098  }
2099  }
2100 
2101  /* Side set id array: */
2102 
2103  long non_empty_ss = 0;
2104  // Need to go through nodesets to compute # nodesets, some might be empty
2105  std::vector< NeumannSetData >::iterator ss_it;
2106  for( ss_it = sideset_data.begin(); ss_it != sideset_data.end(); ++ss_it )
2107  {
2108  if( 0 != ( *ss_it ).number_elements ) non_empty_ss++;
2109  }
2110 
2111  if( non_empty_ss > 0 )
2112  {
2113  int num_ss = -1;
2114  if( nc_def_dim( ncFile, "num_side_sets", non_empty_ss, &num_ss ) != NC_NOERR )
2115  {
2116  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of side sets" );
2117  }
2118 
2119  /* Side set id status array: */
2120  int ss_idstat = -1, ss_idarr = -1;
2121  if( NC_NOERR != nc_def_var( ncFile, "ss_status", NC_LONG, 1, &num_ss, &ss_idstat ) )
2122  {
2123  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define side set status" );
2124  }
2125 
2126  /* Side set id array: */
2127  if( NC_NOERR != nc_def_var( ncFile, "ss_prop1", NC_LONG, 1, &num_ss, &ss_idarr ) )
2128  {
2129  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define side set property" );
2130  }
2131 
2132  /* Store property name as attribute of property array variable */
2133  if( NC_NOERR != nc_put_att_text( ncFile, ss_idarr, "name", strlen( "ID" ), "ID" ) )
2134  {
2135  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to store side set property name ID" );
2136  }
2137 
2138  // Now, define the arrays needed for each side set
2139 
2140  int index = 0;
2141  for( unsigned int i = 0; i < sideset_data.size(); i++ )
2142  {
2143  NeumannSetData side_set = sideset_data[i];
2144 
2145  // Don't define an empty set
2146  if( 0 == side_set.number_elements ) continue;
2147 
2148  index++;
2149 
2150  int num_side_ss = -1;
2151  int elem_ss = -1, side_ss = -1;
2152  INS_ID( wname, "num_side_ss%d", index );
2153  if( nc_def_dim( ncFile, wname, (size_t)side_set.number_elements, &num_side_ss ) != NC_NOERR )
2154  {
2155  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of sides in side set " << side_set.id );
2156  }
2157 
2158  INS_ID( wname, "elem_ss%d", index );
2159  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, &num_side_ss, &elem_ss ) )
2160  {
2161  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create element list for side set "
2162  << side_set.id ); /* Exit define mode and return */
2163  }
2164  INS_ID( wname, "side_ss%d", index );
2165  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, &num_side_ss, &side_ss ) )
2166  {
2167  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create side list for side set "
2168  << side_set.id ); /* Exit define mode and return */
2169  }
2170 
2171  // sideset distribution factors
2172  int num_df_ss = -1;
2173  INS_ID( wname, "num_df_ss%d", index );
2174  if( nc_def_dim( ncFile, wname, (size_t)side_set.ss_dist_factors.size(), &num_df_ss ) != NC_NOERR )
2175  {
2176  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define number of dist factors in side set "
2177  << side_set.id ); /* Exit define mode and return */
2178  }
2179 
2180  /* Create variable array in which to store the side set distribution factors */
2181 
2182  int fact_ss = -1;
2183  INS_ID( wname, "dist_fact_ss%d", index );
2184  if( NC_NOERR != nc_def_var( ncFile, wname, NC_LONG, 1, &num_df_ss, &fact_ss ) )
2185  {
2186  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create dist factors list for side set "
2187  << side_set.id ); /* Exit define mode and return */
2188  }
2189  }
2190  }
2191 
2192  /* Node coordinate arrays: */
2193 
2194  int coord, name_coord, dims[3];
2195  dims[0] = num_dim;
2196  dims[1] = num_nodes;
2197  if( NC_NOERR != nc_def_var( ncFile, "coord", NC_DOUBLE, 2, dims, &coord ) )
2198  {
2199  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define node coordinate array" );
2200  }
2201 
2202  /* Coordinate names array */
2203 
2204  dims[0] = num_dim;
2205  dims[1] = dim_str;
2206  if( NC_NOERR != nc_def_var( ncFile, "coor_names", NC_CHAR, 2, dims, &name_coord ) )
2207  {
2208  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define coordinate name array" );
2209  }
2210 
2211  // Define genesis maps if required
2212 
2213  if( write_maps )
2214  {
2215  // Element map
2216  int elem_map = -1, elem_map2 = -1, node_map = -1;
2217  if( NC_NOERR != nc_def_var( ncFile, "elem_map", NC_LONG, 1, &num_elem, &elem_map ) )
2218  {
2219  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create element map array" ); /* Exit define mode and return */
2220  }
2221 
2222  // Create the element number map
2223  if( NC_NOERR != nc_def_var( ncFile, "elem_num_map", NC_LONG, 1, &num_elem, &elem_map2 ) )
2224  {
2225  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create element numbering map" ); /* Exit define mode
2226  and return */
2227  }
2228 
2229  // Create node number map
2230  if( NC_NOERR != nc_def_var( ncFile, "node_num_map", NC_LONG, 1, &num_nodes, &node_map ) )
2231  {
2232  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to create node numbering map array" ); /* Exit define mode and
2233  return */
2234  }
2235  }
2236 
2237  // Define qa records to be used
2238 
2239  int num_qa_rec = mesh_info.qaRecords.size() / 4;
2240  int num_qa = -1;
2241 
2242  if( nc_def_dim( ncFile, "num_qa_rec", (long)num_qa_rec, &num_qa ) != NC_NOERR )
2243  {
2244  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define qa record array size" );
2245  }
2246 
2247  // Define qa array
2248  int qa_title;
2249  dims[0] = num_qa;
2250  dims[1] = dim_four;
2251  dims[2] = dim_str;
2252  if( NC_NOERR != nc_def_var( ncFile, "qa_records", NC_CHAR, 3, dims, &qa_title ) )
2253  {
2254  MB_SET_ERR( MB_FAILURE, "WriteNCDF: failed to define qa record array" );
2255  }
2256 
2257  // Take it out of define mode
2258  if( NC_NOERR != nc_enddef( ncFile ) )
2259  {
2260  MB_SET_ERR( MB_FAILURE, "WriteNCDF: Trouble leaving define mode" );
2261  }
2262 
2263  return MB_SUCCESS;
2264 }
2265 
2266 ErrorCode WriteNCDF::open_file( const char* filename )
2267 {
2268  // Not a valid filename
2269  if( strlen( (const char*)filename ) == 0 )
2270  {
2271  MB_SET_ERR( MB_FAILURE, "Output Exodus filename not specified" );
2272  }
2273 
2274  int fail = nc_create( filename, NC_CLOBBER, &ncFile );
2275 
2276  // File couldn't be opened
2277  if( NC_NOERR != fail )
2278  {
2279  MB_SET_ERR( MB_FAILURE, "Cannot open " << filename );
2280  }
2281 
2282  return MB_SUCCESS;
2283 }
2284 
2286  int current_sense,
2287  Range& forward_elems,
2288  Range& reverse_elems )
2289 {
2290  Range ss_elems, ss_meshsets;
2291 
2292  // Get the sense tag; don't need to check return, might be an error if the tag
2293  // hasn't been created yet
2294  Tag sense_tag = 0;
2295  mdbImpl->tag_get_handle( "SENSE", 1, MB_TYPE_INTEGER, sense_tag );
2296 
2297  // Get the entities in this set
2298  ErrorCode result = mdbImpl->get_entities_by_handle( sideset, ss_elems, true );
2299  if( MB_FAILURE == result ) return result;
2300 
2301  // Now remove the meshsets into the ss_meshsets; first find the first meshset,
2302  Range::iterator range_iter = ss_elems.begin();
2303  while( TYPE_FROM_HANDLE( *range_iter ) != MBENTITYSET && range_iter != ss_elems.end() )
2304  ++range_iter;
2305 
2306  // Then, if there are some, copy them into ss_meshsets and erase from ss_elems
2307  if( range_iter != ss_elems.end() )
2308  {
2309  std::copy( range_iter, ss_elems.end(), range_inserter( ss_meshsets ) );
2310  ss_elems.erase( range_iter, ss_elems.end() );
2311  }
2312 
2313  // OK, for the elements, check the sense of this set and copy into the right range
2314  // (if the sense is 0, copy into both ranges)
2315 
2316  // Need to step forward on list until we reach the right dimension
2317  Range::iterator dum_it = ss_elems.end();
2318  --dum_it;
2319  int target_dim = CN::Dimension( TYPE_FROM_HANDLE( *dum_it ) );
2320  dum_it = ss_elems.begin();
2321  while( target_dim != CN::Dimension( TYPE_FROM_HANDLE( *dum_it ) ) && dum_it != ss_elems.end() )
2322  ++dum_it;
2323 
2324  if( current_sense == 1 || current_sense == 0 ) std::copy( dum_it, ss_elems.end(), range_inserter( forward_elems ) );
2325  if( current_sense == -1 || current_sense == 0 )
2326  std::copy( dum_it, ss_elems.end(), range_inserter( reverse_elems ) );
2327 
2328  // Now loop over the contained meshsets, getting the sense of those and calling this
2329  // function recursively
2330  for( range_iter = ss_meshsets.begin(); range_iter != ss_meshsets.end(); ++range_iter )
2331  {
2332  // First get the sense; if it's not there, by convention it's forward
2333  int this_sense;
2334  if( 0 == sense_tag || MB_FAILURE == mdbImpl->tag_get_data( sense_tag, &( *range_iter ), 1, &this_sense ) )
2335  this_sense = 1;
2336 
2337  // Now get all the entities on this meshset, with the proper (possibly reversed) sense
2338  get_sideset_elems( *range_iter, this_sense * current_sense, forward_elems, reverse_elems );
2339  }
2340 
2341  return result;
2342 }
2343 
2344 } // namespace moab