Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
WriteNC.cpp
Go to the documentation of this file.
1 #include "WriteNC.hpp"
2 #include "moab/CN.hpp"
3 #include "MBTagConventions.hpp"
5 #include "moab/Interface.hpp"
6 #include "moab/Range.hpp"
8 #include "moab/FileOptions.hpp"
9 #include "NCWriteHelper.hpp"
10 
11 #include <fstream>
12 #include <map>
13 #include <set>
14 
15 #include <iostream>
16 #include <sstream>
17 
18 #ifdef WIN32
19 #ifdef size_t
20 #undef size_t
21 #endif
22 #endif
23 
24 namespace moab
25 {
26 
28 {
29  return new WriteNC( iface );
30 }
31 
33  : mbImpl( impl ), dbgOut( stderr ),
34 #ifdef MOAB_HAVE_MPI
35  myPcomm( NULL ),
36 #endif
37  noMesh( false ), noVars( false ), append( false ), mGlobalIdTag( 0 ), isParallel( false ), myHelper( NULL )
38 {
39  assert( impl != NULL );
41 }
42 
44 {
46  if( myHelper != NULL ) delete myHelper;
47 }
48 
49 //! Writes out a file
50 ErrorCode WriteNC::write_file( const char* file_name,
51  const bool overwrite,
52  const FileOptions& options,
53  const EntityHandle* file_set,
54  const int num_set,
55  const std::vector< std::string >&,
56  const Tag*,
57  int,
58  int )
59 {
60  // See if opts has variable(s) specified
61  std::vector< std::string > var_names;
62  std::vector< std::string > desired_names;
63  std::vector< int > tstep_nums;
64  std::vector< double > tstep_vals;
65 
66  // Get and cache predefined tag handles
68 
69  // num set has to be 1, we will write only one set, the original file set used to load
70  if( num_set != 1 ) MB_SET_ERR( MB_FAILURE, "We should write only one set (the file set used to read data into)" );
71 
72  MB_CHK_SET_ERR( parse_options( options, var_names, desired_names, tstep_nums, tstep_vals ),
73  "Trouble parsing option string" );
74 
75  // Important to create some data that will be used to write the file; dimensions, variables, etc
76  // new variables still need to have some way of defining their dimensions
77  // maybe it will be passed as write options
78  MB_CHK_SET_ERR( process_conventional_tags( *file_set ), "Trouble processing conventional tags" );
79 
80  // Create or append the file
81  if( append )
82  dbgOut.tprintf( 1, "opening file %s for appending \n", file_name );
83  else
84  dbgOut.tprintf( 1, "creating file %s\n", file_name );
85  fileName = file_name;
86  int success;
87 
88  if( append )
89  {
90  int omode = NC_WRITE;
91 #ifdef MOAB_HAVE_PNETCDF
92  if( isParallel )
93  success = NCFUNC( open )( myPcomm->proc_config().proc_comm(), file_name, omode, MPI_INFO_NULL, &fileId );
94  else
95  success = NCFUNC( open )( MPI_COMM_SELF, file_name, omode, MPI_INFO_NULL, &fileId );
96 #else
97  // This is a regular netcdf file, open in write mode
98  success = NCFUNC( open )( file_name, omode, &fileId );
99 #endif
100  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble opening file " << file_name << " for appending" );
101  }
102  else
103  { // Case when the file is new, will be overwritten, most likely
104  int cmode = overwrite ? NC_CLOBBER : NC_NOCLOBBER;
105 #ifdef MOAB_HAVE_PNETCDF
106  if( isParallel )
107  success = NCFUNC( create )( myPcomm->proc_config().proc_comm(), file_name, cmode, MPI_INFO_NULL, &fileId );
108  else
109  success = NCFUNC( create )( MPI_COMM_SELF, file_name, cmode, MPI_INFO_NULL, &fileId );
110 #else
111  // This is a regular netcdf file
112  success = NCFUNC( create )( file_name, cmode, &fileId );
113 #endif
114  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble creating file " << file_name << " for writing" );
115  }
116 
117  if( NULL != myHelper ) delete myHelper;
118 
119  // Get appropriate helper instance for WriteNC class based on some info in the file set
120  myHelper = NCWriteHelper::get_nc_helper( this, fileId, options, *file_set );
121  if( NULL == myHelper )
122  {
123  MB_SET_ERR( MB_FAILURE, "Failed to get NCWriteHelper class instance" );
124  }
125 
126  MB_CHK_SET_ERR( myHelper->collect_mesh_info(), "Trouble collecting mesh information" );
127 
128  MB_CHK_SET_ERR( myHelper->collect_variable_data( var_names, tstep_nums ), "Trouble collecting variable data" );
129 
130  MB_CHK_SET_ERR( myHelper->init_file( var_names, desired_names, append ), "Trouble initializing file" );
131 
132  MB_CHK_SET_ERR( myHelper->write_values( var_names, tstep_nums ), "Trouble writing values to file" );
133 
134  success = NCFUNC( close )( fileId );
135  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble closing file" );
136 
137  return MB_SUCCESS;
138 }
139 
141  std::vector< std::string >& var_names,
142  std::vector< std::string >& desired_names,
143  std::vector< int >& tstep_nums,
144  std::vector< double >& tstep_vals )
145 {
146  int tmpval;
147  if( MB_SUCCESS == opts.get_int_option( "DEBUG_IO", 1, tmpval ) )
148  {
149  dbgOut.set_verbosity( tmpval );
150  dbgOut.set_prefix( "NCWrite" );
151  }
152 
153  ErrorCode rval = opts.get_strs_option( "VARIABLE", var_names );
154  if( MB_TYPE_OUT_OF_RANGE == rval )
155  noVars = true;
156  else
157  noVars = false;
158 
159  rval = opts.get_strs_option( "RENAME", desired_names );
160  if( MB_ENTITY_NOT_FOUND == rval )
161  {
162  if( !noVars )
163  {
164  desired_names.resize( var_names.size() );
165  std::copy( var_names.begin(), var_names.end(), desired_names.begin() );
166  }
167  }
168  // Either way
169  assert( desired_names.size() == var_names.size() );
170 
171  opts.get_ints_option( "TIMESTEP", tstep_nums );
172  opts.get_reals_option( "TIMEVAL", tstep_vals );
173  rval = opts.get_null_option( "NOMESH" );
174  if( MB_SUCCESS == rval ) noMesh = true;
175 
176  rval = opts.get_null_option( "APPEND" );
177  if( MB_SUCCESS == rval ) append = true;
178 
179  if( 2 <= dbgOut.get_verbosity() )
180  {
181  if( !var_names.empty() )
182  {
183  std::cerr << "Variables requested: ";
184  for( unsigned int i = 0; i < var_names.size(); i++ )
185  std::cerr << var_names[i];
186  std::cerr << std::endl;
187  }
188  if( !tstep_nums.empty() )
189  {
190  std::cerr << "Timesteps requested: ";
191  for( unsigned int i = 0; i < tstep_nums.size(); i++ )
192  std::cerr << tstep_nums[i];
193  std::cerr << std::endl;
194  }
195  if( !tstep_vals.empty() )
196  {
197  std::cerr << "Time vals requested: ";
198  for( unsigned int i = 0; i < tstep_vals.size(); i++ )
199  std::cerr << tstep_vals[i];
200  std::cerr << std::endl;
201  }
202  }
203 
204 // FIXME: copied from ReadNC, may need revise
205 #ifdef MOAB_HAVE_MPI
206  isParallel = ( opts.match_option( "PARALLEL", "WRITE_PART" ) != MB_ENTITY_NOT_FOUND );
207 
208  if( !isParallel )
209  // Return success here, since rval still has _NOT_FOUND from not finding option
210  // in this case, myPcomm will be NULL, so it can never be used; always check for isParallel
211  // before any use for myPcomm
212  return MB_SUCCESS;
213 
214  int pcomm_no = 0;
215  rval = opts.get_int_option( "PARALLEL_COMM", pcomm_no );
216  if( MB_TYPE_OUT_OF_RANGE == rval )
217  {
218  MB_SET_ERR( rval, "Invalid value for PARALLEL_COMM option" );
219  }
220 
221  myPcomm = ParallelComm::get_pcomm( mbImpl, pcomm_no );
222  if( 0 == myPcomm )
223  {
224  myPcomm = new ParallelComm( mbImpl, MPI_COMM_WORLD );
225  }
226 
227 #ifndef MOAB_HAVE_PNETCDF
228  const int procs = myPcomm->proc_config().proc_size();
229  if( procs > 1 )
230  {
231  MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Attempt to launch NC writer in parallel without pnetcdf support" );
232  }
233 #endif
234 
235  const int rank = myPcomm->proc_config().proc_rank();
236  dbgOut.set_rank( rank );
237 #endif
238 
239  return MB_SUCCESS;
240 }
241 
242 // This is the inverse process to create conventional tags
243 // Will look at <pargal_source>/src/core/fileinfo.cpp, init dim, vars, atts
245 {
246  ErrorCode rval;
247 
248  // Start copy
249  Tag dimNamesTag = 0;
250  std::string tag_name = "__DIM_NAMES";
251  const void* data = NULL;
252  int dimNamesSz = 0;
253  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, dimNamesTag, MB_TAG_ANY ),
254  "Trouble getting conventional tag " << tag_name );
255  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( dimNamesTag, &fileSet, 1, &data, &dimNamesSz ),
256  "Trouble getting data of conventional tag " << tag_name );
257  const char* p = static_cast< const char* >( data );
258  dbgOut.tprintf( 1, "__DIM_NAMES tag has string length %d\n", dimNamesSz );
259 
260  std::size_t start = 0;
261 
262  Tag dimLensTag = 0;
263  tag_name = "__DIM_LENS";
264  data = NULL;
265  int dimLensSz = 0;
266  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, dimLensTag, MB_TAG_ANY ),
267  "Trouble getting conventional tag " << tag_name );
268  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( dimLensTag, &fileSet, 1, &data, &dimLensSz ),
269  "Trouble getting data of conventional tag " << tag_name );
270  const int* int_p = static_cast< const int* >( data );
271  dbgOut.tprintf( 1, "__DIM_LENS tag has %d values\n", dimLensSz );
272 
273  int idxDim = 0;
274  // Dim names are separated by '\0' in the string of __DIM_NAMES tag
275  for( std::size_t i = 0; i != static_cast< std::size_t >( dimNamesSz ); i++ )
276  {
277  if( p[i] == '\0' )
278  {
279  std::string dim_name( &p[start], i - start );
280  int len = int_p[idxDim];
281  dimNames.push_back( dim_name );
282  dimLens.push_back( len );
283  dbgOut.tprintf( 2, "Dimension %s has length %d\n", dim_name.c_str(), len );
284  // FIXME: Need info from moab to set unlimited dimension
285  // Currently assume each file has the same number of time dimensions
286  /*if ((dim_name == "time") || (dim_name == "Time"))
287  insert(dim_name, *(new pcdim(dim_name, len * m_file_names.size(), true)));
288  else
289  insert(dim_name, *(new pcdim(dim_name, len)));*/
290  start = i + 1;
291  idxDim++;
292  }
293  }
294 
295  Tag meshTypeTag = 0;
296  tag_name = "__MESH_TYPE";
297  data = NULL;
298  int meshTypeSz = 0;
299  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, meshTypeTag, MB_TAG_ANY ),
300  "Trouble getting conventional tag " << tag_name );
301  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( meshTypeTag, &fileSet, 1, &data, &meshTypeSz ),
302  "Trouble getting data of conventional tag " << tag_name );
303  p = static_cast< const char* >( data );
304  grid_type = std::string( &p[0], meshTypeSz );
305  dbgOut.tprintf( 2, "Mesh type: %s\n", grid_type.c_str() );
306 
307  // Read <__VAR_NAMES_LOCATIONS> tag
308  Tag varNamesLocsTag = 0;
309  tag_name = "__VAR_NAMES_LOCATIONS";
310  data = NULL;
311  int varNamesLocsSz = 0;
312  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, varNamesLocsTag, MB_TAG_ANY ),
313  "Trouble getting conventional tag " << tag_name );
314  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( varNamesLocsTag, &fileSet, 1, &data, &varNamesLocsSz ),
315  "Trouble getting data of conventional tag " << tag_name );
316  int_p = static_cast< const int* >( data );
317  std::vector< int > varNamesLocs( varNamesLocsSz );
318  std::copy( int_p, int_p + varNamesLocsSz, varNamesLocs.begin() );
319 
320  Tag varNamesTag = 0;
321  tag_name = "__VAR_NAMES";
322  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, varNamesTag, MB_TAG_ANY ),
323  "Trouble getting conventional tag " << tag_name );
324  data = NULL;
325  int varNamesSz = 0;
326  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( varNamesTag, &fileSet, 1, &data, &varNamesSz ),
327  "Trouble getting data of conventional tag " << tag_name );
328  dbgOut.tprintf( 2, "__VAR_NAMES tag has string length %d\n", varNamesSz );
329  p = static_cast< const char* >( data );
330 
331  start = 0;
332  int idxVar = 0;
333  int sz;
334  // Var names are separated by '\0' in the string of __VAR_NAMES tag
335  for( std::size_t i = 0; i != static_cast< std::size_t >( varNamesSz ); i++ )
336  {
337  if( p[i] == '\0' )
338  {
339  std::string var_name( &p[start], i - start );
340 
341  dbgOut.tprintf( 2, "var name: %s index %d \n", var_name.c_str(), idxVar );
342  // Process var name:
343  // This will create/initiate map; we will populate variableDataStruct with info about
344  // dims, tags, etc reference & is important; otherwise variableDataStruct will go out of
345  // scope, and deleted :(
346  VarData& variableDataStruct = varInfo[var_name];
347  variableDataStruct.varName = var_name;
348  variableDataStruct.entLoc = varNamesLocs[idxVar];
349 
350  dbgOut.tprintf( 2, "at var name %s varInfo size %d \n", var_name.c_str(), (int)varInfo.size() );
351 
352  sz = 0;
353  Tag dims_tag = 0;
354  std::string dim_names = "__" + var_name + "_DIMS";
355  rval = mbImpl->tag_get_handle( dim_names.c_str(), 0, MB_TYPE_OPAQUE, dims_tag, MB_TAG_ANY );
356  if( MB_SUCCESS != rval )
357  {
358  if( MB_TAG_NOT_FOUND == rval )
359  {
360  dbgOut.tprintf( 2, "tag : %s not found, continue \n", dim_names.c_str() );
361  start = i + 1;
362  idxVar++;
363  continue;
364  }
365  MB_SET_ERR( rval, "Trouble getting conventional tag " << dim_names );
366  }
367  MB_CHK_SET_ERR( mbImpl->tag_get_length( dims_tag, sz ),
368  "Trouble getting size of dimensions for variable " << var_name );
369  sz /= sizeof( Tag ); // The type is MB_TYPE_OPAQUE, but it is a list of tags, so we
370  // need to divide by the size of Tag
371  // sz is used for number of dimension tags in this list
372  dbgOut.tprintf( 2, "var name: %s has %d dimensions \n", var_name.c_str(), sz );
373 
374  variableDataStruct.varDims.resize( sz );
375  const void* ptr = NULL;
376  rval = mbImpl->tag_get_by_ptr( dims_tag, &fileSet, 1, &ptr );
377 
378  const Tag* ptags = static_cast< const moab::Tag* >( ptr );
379  for( std::size_t j = 0; j != static_cast< std::size_t >( sz ); j++ )
380  {
381  std::string dim_name;
382  MB_CHK_SET_ERR( mbImpl->tag_get_name( ptags[j], dim_name ),
383  "Trouble getting dimension of variable " << var_name );
384  dbgOut.tprintf( 2, "var name: %s has %s as dimension \n", var_name.c_str(), dim_name.c_str() );
385  std::vector< std::string >::iterator vit = std::find( dimNames.begin(), dimNames.end(), dim_name );
386  if( vit == dimNames.end() )
387  MB_SET_ERR( MB_FAILURE, "Dimension " << dim_name << " not found for variable " << var_name );
388  variableDataStruct.varDims[j] = (int)( vit - dimNames.begin() ); // Will be used for writing
389  // This will have to change to actual file dimension, for writing
390  }
391 
392  // Attributes for this variable
393  std::stringstream ssTagName;
394  ssTagName << "__" << var_name << "_ATTRIBS";
395  tag_name = ssTagName.str();
396  Tag varAttTag = 0;
397  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, varAttTag,
399  "Trouble getting conventional tag " << tag_name );
400  const void* varAttPtr = NULL;
401  int varAttSz = 0;
402  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( varAttTag, &fileSet, 1, &varAttPtr, &varAttSz ),
403  "Trouble getting data of conventional tag " << tag_name );
404  if( MB_SUCCESS == rval ) dbgOut.tprintf( 2, "Tag retrieved for variable %s\n", tag_name.c_str() );
405 
406  std::string attribString( (char*)varAttPtr, (char*)varAttPtr + varAttSz );
407  if( attribString == "NO_ATTRIBS" )
408  {
409  // This variable has no attributes
410  variableDataStruct.numAtts = 0;
411  }
412  else if( attribString == "DUMMY_VAR" )
413  {
414  // This variable is a dummy coordinate variable
415  variableDataStruct.numAtts = 0;
416  dummyVarNames.insert( variableDataStruct.varName );
417  }
418  else
419  {
420  ssTagName << "_LEN";
421  tag_name = ssTagName.str();
422  Tag varAttLenTag = 0;
423  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, varAttLenTag,
424  MB_TAG_ANY ),
425  "Trouble getting conventional tag " << tag_name );
426  int varAttLenSz = 0;
427  MB_CHK_SET_ERR( mbImpl->tag_get_length( varAttLenTag, varAttLenSz ),
428  "Trouble getting length of conventional tag " << tag_name );
429  std::vector< int > varAttLen( varAttLenSz );
430  MB_CHK_SET_ERR( mbImpl->tag_get_data( varAttLenTag, &fileSet, 1, &varAttLen[0] ),
431  "Trouble getting data of conventional tag " << tag_name );
432 
433  MB_CHK_SET_ERR( process_concatenated_attribute( varAttPtr, varAttSz, varAttLen,
434  variableDataStruct.varAtts ),
435  "Trouble processing attributes of variable " << var_name );
436 
437  if( MB_SUCCESS == rval ) dbgOut.tprintf( 2, "Tag metadata for variable %s\n", tag_name.c_str() );
438  }
439  // End attribute
440 
441  start = i + 1;
442  idxVar++;
443  } // if (p[i] == '\0')
444  }
445 
446  // Global attributes
447  tag_name = "__GLOBAL_ATTRIBS";
448  Tag globalAttTag = 0;
449  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, globalAttTag,
451  "Trouble getting conventional tag " << tag_name );
452  std::vector< int > gattLen;
453 
454  const void* gattptr = NULL;
455  int globalAttSz = 0;
456  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( globalAttTag, &fileSet, 1, &gattptr, &globalAttSz ),
457  "Trouble getting data of conventional tag " << tag_name );
458 
459  if( MB_SUCCESS == rval ) dbgOut.tprintf( 2, "Tag value retrieved for %s size %d\n", tag_name.c_str(), globalAttSz );
460 
461  // <__GLOBAL_ATTRIBS_LEN>
462  tag_name = "__GLOBAL_ATTRIBS_LEN";
463  Tag globalAttLenTag = 0;
464 
465  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, globalAttLenTag, MB_TAG_ANY ),
466  "Trouble getting conventional tag " << tag_name );
467  int sizeGAtt = 0;
468  MB_CHK_SET_ERR( mbImpl->tag_get_length( globalAttLenTag, sizeGAtt ),
469  "Trouble getting length of conventional tag " << tag_name );
470  gattLen.resize( sizeGAtt );
471  MB_CHK_SET_ERR( mbImpl->tag_get_data( globalAttLenTag, &fileSet, 1, &gattLen[0] ),
472  "Trouble getting data of conventional tag " << tag_name );
473  if( MB_SUCCESS == rval ) dbgOut.tprintf( 2, "Tag retrieved for variable %s\n", tag_name.c_str() );
474 
475  MB_CHK_SET_ERR( process_concatenated_attribute( gattptr, globalAttSz, gattLen, globalAtts ),
476  "Trouble processing global attributes" );
477 
478  return MB_SUCCESS;
479 }
480 
481 // Reverse process from create_attrib_string
483  int attSz,
484  std::vector< int >& attLen,
485  std::map< std::string, AttData >& attributes )
486 {
487  std::size_t start = 0;
488  std::size_t att_counter = 0;
489  std::string concatString( (char*)attPtr, (char*)attPtr + attSz );
490 
491  for( std::size_t i = 0; i != (size_t)attSz; i++ )
492  {
493  if( concatString[i] == '\0' )
494  {
495  std::string att_name( &concatString[start], i - start );
496  start = i + 1;
497  while( concatString[i] != ';' )
498  ++i;
499  std::string data_type( &concatString[start], i - start );
500  ++i;
501  start = i;
502  i = attLen[att_counter];
503  if( concatString[i] != ';' ) MB_SET_ERR( MB_FAILURE, "Error parsing attributes" );
504 
505  std::string data_val( &concatString[start], i - start );
506  start = i + 1;
507 
508  AttData& attrib = attributes[att_name];
509  attrib.attValue = data_val;
510  attrib.attLen = data_val.size();
511 
512  if( data_type == "char" )
513  attrib.attDataType = NC_CHAR;
514  else if( data_type == "double" )
515  attrib.attDataType = NC_DOUBLE;
516  else if( data_type == "float" )
517  attrib.attDataType = NC_FLOAT;
518  else if( data_type == "int" )
519  attrib.attDataType = NC_INT;
520  else if( data_type == "short" )
521  attrib.attDataType = NC_SHORT;
522 
523  ++att_counter;
524  dbgOut.tprintf( 2, " Process attribute %s with value %s \n", att_name.c_str(), data_val.c_str() );
525  }
526  }
527 
528  return MB_SUCCESS;
529 }
530 
531 } // namespace moab