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  // Process the climate-style conventional tags only if the user did NOT
79  // override grid_type via WRITE_FORMAT. Grid-output writers (NCWriteScrip,
80  // NCWriteESMF, NCWriteDomain) synthesize their schema from mesh state
81  // and don't need the prior-read __VAR_NAMES / __MESH_TYPE tag chain;
82  // running process_conventional_tags would also fail on meshes that
83  // weren't loaded via ReadNC (h5m, exodus, ...).
84  if( grid_type.empty() )
85  {
86  MB_CHK_SET_ERR( process_conventional_tags( *file_set ), "Trouble processing conventional tags" );
87  }
88  else
89  {
90  dbgOut.tprintf( 2, "Skipping process_conventional_tags; grid_type already set by WRITE_FORMAT: %s\n",
91  grid_type.c_str() );
92  }
93 
94  // Create or append the file
95  if( append )
96  dbgOut.tprintf( 1, "opening file %s for appending \n", file_name );
97  else
98  dbgOut.tprintf( 1, "creating file %s\n", file_name );
99  fileName = file_name;
100  int success;
101 
102  // Format-aware open/create via the runtime dispatch layer. For append
103  // mode the format is detected from the existing file. For create mode
104  // we pick CLASSIC by default (preserves the behavior of PNetCDF builds
105  // which write CDF-5 via ncmpi_create) — a NetCDF-4 write path can be
106  // added later behind an explicit WriteNC option.
107 #ifdef MOAB_HAVE_MPI
108  const int write_mpi_size = isParallel ? myPcomm->proc_config().proc_size() : 1;
109 #else
110  const int write_mpi_size = 1;
111 #endif
112 
113  if( append )
114  {
115  int omode = NC_WRITE;
116 
117  int existingFormat = NCFMT_UNKNOWN;
118 #ifdef MOAB_HAVE_MPI
119  if( isParallel )
120  {
121  int rank = myPcomm->proc_config().proc_rank();
122  if( rank == 0 ) existingFormat = mbnc_detect_format( file_name );
123  MPI_Bcast( &existingFormat, 1, MPI_INT, 0, myPcomm->proc_config().proc_comm() );
124  }
125  else
126 #endif
127  {
128  existingFormat = mbnc_detect_format( file_name );
129  }
130 
131  const NcBackend backend = mbnc_choose_backend_for_write( existingFormat, write_mpi_size );
132  if( backend == NCB_NONE )
133  {
134  MB_SET_ERR( MB_FAILURE, "Cannot find a compatible parallel writer for appending to '"
135  << file_name << "' (file format not recognized or no compatible backend)" );
136  }
137 
138 #ifdef MOAB_HAVE_MPI
139  if( backend == NCB_NETCDF_SERIAL || write_mpi_size == 1 )
140  {
141  success = mbnc_open( file_name, omode, &fileId );
142  }
143  else
144  {
145  success =
146  mbnc_open_par( backend, myPcomm->proc_config().proc_comm(), MPI_INFO_NULL, file_name, omode, &fileId );
147  }
148 #else
149  success = mbnc_open( file_name, omode, &fileId );
150 #endif
151  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble opening file " << file_name << " for appending" );
152  }
153  else
154  {
155  // Case when the file is new — choose classic format to match the
156  // long-standing default of the PNetCDF-built path (ncmpi_create
157  // defaults to CDF-5). Future: expose a "FORMAT" option on the
158  // WriteNC option string to let the caller request NetCDF-4.
159  int cmode = overwrite ? NC_CLOBBER : NC_NOCLOBBER;
160 
161  const NcBackend backend = mbnc_choose_backend_for_write( NCFMT_CLASSIC, write_mpi_size );
162  if( backend == NCB_NONE )
163  {
164  MB_SET_ERR( MB_FAILURE, "Cannot find a compatible parallel writer for creating '" << file_name << "'" );
165  }
166 
167 #ifdef MOAB_HAVE_MPI
168  if( backend == NCB_NETCDF_SERIAL || write_mpi_size == 1 )
169  {
170  success = mbnc_create( file_name, cmode, &fileId );
171  }
172  else
173  {
174  success =
175  mbnc_create_par( backend, myPcomm->proc_config().proc_comm(), MPI_INFO_NULL, file_name, cmode, &fileId );
176  }
177 #else
178  success = mbnc_create( file_name, cmode, &fileId );
179 #endif
180  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble creating file " << file_name << " for writing" );
181  }
182 
183  if( NULL != myHelper ) delete myHelper;
184 
185  // Get appropriate helper instance for WriteNC class based on some info in the file set
186  myHelper = NCWriteHelper::get_nc_helper( this, fileId, options, *file_set );
187  if( NULL == myHelper )
188  {
189  MB_SET_ERR( MB_FAILURE, "Failed to get NCWriteHelper class instance" );
190  }
191 
192  MB_CHK_SET_ERR( myHelper->collect_mesh_info(), "Trouble collecting mesh information" );
193 
194  MB_CHK_SET_ERR( myHelper->collect_variable_data( var_names, tstep_nums ), "Trouble collecting variable data" );
195 
196  MB_CHK_SET_ERR( myHelper->init_file( var_names, desired_names, append ), "Trouble initializing file" );
197 
198  MB_CHK_SET_ERR( myHelper->write_values( var_names, tstep_nums ), "Trouble writing values to file" );
199 
200  success = NCFUNC( close )( fileId );
201  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble closing file" );
202 
203  return MB_SUCCESS;
204 }
205 
207  std::vector< std::string >& var_names,
208  std::vector< std::string >& desired_names,
209  std::vector< int >& tstep_nums,
210  std::vector< double >& tstep_vals )
211 {
212  int tmpval;
213  if( MB_SUCCESS == opts.get_int_option( "DEBUG_IO", 1, tmpval ) )
214  {
215  dbgOut.set_verbosity( tmpval );
216  dbgOut.set_prefix( "NCWrite" );
217  }
218 
219  ErrorCode rval = opts.get_strs_option( "VARIABLE", var_names );
220  if( MB_TYPE_OUT_OF_RANGE == rval )
221  noVars = true;
222  else
223  noVars = false;
224 
225  rval = opts.get_strs_option( "RENAME", desired_names );
226  if( MB_ENTITY_NOT_FOUND == rval )
227  {
228  if( !noVars )
229  {
230  desired_names.resize( var_names.size() );
231  std::copy( var_names.begin(), var_names.end(), desired_names.begin() );
232  }
233  }
234  // Either way
235  assert( desired_names.size() == var_names.size() );
236 
237  opts.get_ints_option( "TIMESTEP", tstep_nums );
238  opts.get_reals_option( "TIMEVAL", tstep_vals );
239  rval = opts.get_null_option( "NOMESH" );
240  if( MB_SUCCESS == rval ) noMesh = true;
241 
242  rval = opts.get_null_option( "APPEND" );
243  if( MB_SUCCESS == rval ) append = true;
244 
245  // WRITE_FORMAT: optional explicit output grid_type. Set this when you
246  // want to convert FROM one grid type TO another (e.g. MPAS → SCRIP),
247  // or when writing from a mesh that doesn't carry the climate
248  // __MESH_TYPE / __VAR_NAMES tags that process_conventional_tags
249  // expects. Valid values match the strings returned by the readers'
250  // get_mesh_type_name(): "CAM_EUL", "CAM_FV", "CAM_SE", "MPAS",
251  // "GCRM", "SCRIP", "ESMF", "DOMAIN".
252  std::string requestedFormat;
253  rval = opts.get_str_option( "WRITE_FORMAT", requestedFormat );
254  if( MB_SUCCESS == rval && !requestedFormat.empty() )
255  {
256  grid_type = requestedFormat;
257  dbgOut.tprintf( 1, "WRITE_FORMAT override: grid_type = %s\n", grid_type.c_str() );
258  }
259 
260  if( 2 <= dbgOut.get_verbosity() )
261  {
262  if( !var_names.empty() )
263  {
264  std::cerr << "Variables requested: ";
265  for( unsigned int i = 0; i < var_names.size(); i++ )
266  std::cerr << var_names[i];
267  std::cerr << std::endl;
268  }
269  if( !tstep_nums.empty() )
270  {
271  std::cerr << "Timesteps requested: ";
272  for( unsigned int i = 0; i < tstep_nums.size(); i++ )
273  std::cerr << tstep_nums[i];
274  std::cerr << std::endl;
275  }
276  if( !tstep_vals.empty() )
277  {
278  std::cerr << "Time vals requested: ";
279  for( unsigned int i = 0; i < tstep_vals.size(); i++ )
280  std::cerr << tstep_vals[i];
281  std::cerr << std::endl;
282  }
283  }
284 
285 // FIXME: copied from ReadNC, may need revise
286 #ifdef MOAB_HAVE_MPI
287  isParallel = ( opts.match_option( "PARALLEL", "WRITE_PART" ) != MB_ENTITY_NOT_FOUND );
288 
289  if( !isParallel )
290  // Return success here, since rval still has _NOT_FOUND from not finding option
291  // in this case, myPcomm will be NULL, so it can never be used; always check for isParallel
292  // before any use for myPcomm
293  return MB_SUCCESS;
294 
295  int pcomm_no = 0;
296  rval = opts.get_int_option( "PARALLEL_COMM", pcomm_no );
297  if( MB_TYPE_OUT_OF_RANGE == rval )
298  {
299  MB_SET_ERR( rval, "Invalid value for PARALLEL_COMM option" );
300  }
301 
302  myPcomm = ParallelComm::get_pcomm( mbImpl, pcomm_no );
303  if( 0 == myPcomm )
304  {
305  myPcomm = new ParallelComm( mbImpl, MPI_COMM_WORLD );
306  }
307 
308 #ifndef MOAB_HAVE_PNETCDF
309  const int procs = myPcomm->proc_config().proc_size();
310  if( procs > 1 )
311  {
312  MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Attempt to launch NC writer in parallel without pnetcdf support" );
313  }
314 #endif
315 
316  const int rank = myPcomm->proc_config().proc_rank();
317  dbgOut.set_rank( rank );
318 #endif
319 
320  return MB_SUCCESS;
321 }
322 
323 // This is the inverse process to create conventional tags
324 // Will look at <pargal_source>/src/core/fileinfo.cpp, init dim, vars, atts
326 {
327  ErrorCode rval;
328 
329  // Start copy
330  Tag dimNamesTag = 0;
331  std::string tag_name = "__DIM_NAMES";
332  const void* data = NULL;
333  int dimNamesSz = 0;
334  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, dimNamesTag, MB_TAG_ANY ),
335  "Trouble getting conventional tag " << tag_name );
336  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( dimNamesTag, &fileSet, 1, &data, &dimNamesSz ),
337  "Trouble getting data of conventional tag " << tag_name );
338  const char* p = static_cast< const char* >( data );
339  dbgOut.tprintf( 1, "__DIM_NAMES tag has string length %d\n", dimNamesSz );
340 
341  std::size_t start = 0;
342 
343  Tag dimLensTag = 0;
344  tag_name = "__DIM_LENS";
345  data = NULL;
346  int dimLensSz = 0;
347  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, dimLensTag, MB_TAG_ANY ),
348  "Trouble getting conventional tag " << tag_name );
349  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( dimLensTag, &fileSet, 1, &data, &dimLensSz ),
350  "Trouble getting data of conventional tag " << tag_name );
351  const int* int_p = static_cast< const int* >( data );
352  dbgOut.tprintf( 1, "__DIM_LENS tag has %d values\n", dimLensSz );
353 
354  int idxDim = 0;
355  // Dim names are separated by '\0' in the string of __DIM_NAMES tag
356  for( std::size_t i = 0; i != static_cast< std::size_t >( dimNamesSz ); i++ )
357  {
358  if( p[i] == '\0' )
359  {
360  std::string dim_name( &p[start], i - start );
361  int len = int_p[idxDim];
362  dimNames.push_back( dim_name );
363  dimLens.push_back( len );
364  dbgOut.tprintf( 2, "Dimension %s has length %d\n", dim_name.c_str(), len );
365  // FIXME: Need info from moab to set unlimited dimension
366  // Currently assume each file has the same number of time dimensions
367  /*if ((dim_name == "time") || (dim_name == "Time"))
368  insert(dim_name, *(new pcdim(dim_name, len * m_file_names.size(), true)));
369  else
370  insert(dim_name, *(new pcdim(dim_name, len)));*/
371  start = i + 1;
372  idxDim++;
373  }
374  }
375 
376  Tag meshTypeTag = 0;
377  tag_name = "__MESH_TYPE";
378  data = NULL;
379  int meshTypeSz = 0;
380  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, meshTypeTag, MB_TAG_ANY ),
381  "Trouble getting conventional tag " << tag_name );
382  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( meshTypeTag, &fileSet, 1, &data, &meshTypeSz ),
383  "Trouble getting data of conventional tag " << tag_name );
384  p = static_cast< const char* >( data );
385  grid_type = std::string( &p[0], meshTypeSz );
386  dbgOut.tprintf( 2, "Mesh type: %s\n", grid_type.c_str() );
387 
388  // Read <__VAR_NAMES_LOCATIONS> tag
389  Tag varNamesLocsTag = 0;
390  tag_name = "__VAR_NAMES_LOCATIONS";
391  data = NULL;
392  int varNamesLocsSz = 0;
393  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, varNamesLocsTag, MB_TAG_ANY ),
394  "Trouble getting conventional tag " << tag_name );
395  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( varNamesLocsTag, &fileSet, 1, &data, &varNamesLocsSz ),
396  "Trouble getting data of conventional tag " << tag_name );
397  int_p = static_cast< const int* >( data );
398  std::vector< int > varNamesLocs( varNamesLocsSz );
399  std::copy( int_p, int_p + varNamesLocsSz, varNamesLocs.begin() );
400 
401  Tag varNamesTag = 0;
402  tag_name = "__VAR_NAMES";
403  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, varNamesTag, MB_TAG_ANY ),
404  "Trouble getting conventional tag " << tag_name );
405  data = NULL;
406  int varNamesSz = 0;
407  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( varNamesTag, &fileSet, 1, &data, &varNamesSz ),
408  "Trouble getting data of conventional tag " << tag_name );
409  dbgOut.tprintf( 2, "__VAR_NAMES tag has string length %d\n", varNamesSz );
410  p = static_cast< const char* >( data );
411 
412  start = 0;
413  int idxVar = 0;
414  int sz;
415  // Var names are separated by '\0' in the string of __VAR_NAMES tag
416  for( std::size_t i = 0; i != static_cast< std::size_t >( varNamesSz ); i++ )
417  {
418  if( p[i] == '\0' )
419  {
420  std::string var_name( &p[start], i - start );
421 
422  dbgOut.tprintf( 2, "var name: %s index %d \n", var_name.c_str(), idxVar );
423  // Process var name:
424  // This will create/initiate map; we will populate variableDataStruct with info about
425  // dims, tags, etc reference & is important; otherwise variableDataStruct will go out of
426  // scope, and deleted :(
427  VarData& variableDataStruct = varInfo[var_name];
428  variableDataStruct.varName = var_name;
429  variableDataStruct.entLoc = varNamesLocs[idxVar];
430 
431  dbgOut.tprintf( 2, "at var name %s varInfo size %d \n", var_name.c_str(), (int)varInfo.size() );
432 
433  sz = 0;
434  Tag dims_tag = 0;
435  std::string dim_names = "__" + var_name + "_DIMS";
436  rval = mbImpl->tag_get_handle( dim_names.c_str(), 0, MB_TYPE_OPAQUE, dims_tag, MB_TAG_ANY );
437  if( MB_SUCCESS != rval )
438  {
439  if( MB_TAG_NOT_FOUND == rval )
440  {
441  dbgOut.tprintf( 2, "tag : %s not found, continue \n", dim_names.c_str() );
442  start = i + 1;
443  idxVar++;
444  continue;
445  }
446  MB_SET_ERR( rval, "Trouble getting conventional tag " << dim_names );
447  }
448  MB_CHK_SET_ERR( mbImpl->tag_get_length( dims_tag, sz ),
449  "Trouble getting size of dimensions for variable " << var_name );
450  sz /= sizeof( Tag ); // The type is MB_TYPE_OPAQUE, but it is a list of tags, so we
451  // need to divide by the size of Tag
452  // sz is used for number of dimension tags in this list
453  dbgOut.tprintf( 2, "var name: %s has %d dimensions \n", var_name.c_str(), sz );
454 
455  variableDataStruct.varDims.resize( sz );
456  const void* ptr = NULL;
457  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( dims_tag, &fileSet, 1, &ptr ),
458  "Could not get tag by pointer" );
459 
460  const Tag* ptags = static_cast< const moab::Tag* >( ptr );
461  for( std::size_t j = 0; j != static_cast< std::size_t >( sz ); j++ )
462  {
463  std::string dim_name;
464  MB_CHK_SET_ERR( mbImpl->tag_get_name( ptags[j], dim_name ),
465  "Trouble getting dimension of variable " << var_name );
466  dbgOut.tprintf( 2, "var name: %s has %s as dimension \n", var_name.c_str(), dim_name.c_str() );
467  std::vector< std::string >::iterator vit = std::find( dimNames.begin(), dimNames.end(), dim_name );
468  if( vit == dimNames.end() )
469  MB_SET_ERR( MB_FAILURE, "Dimension " << dim_name << " not found for variable " << var_name );
470  variableDataStruct.varDims[j] = (int)( vit - dimNames.begin() ); // Will be used for writing
471  // This will have to change to actual file dimension, for writing
472  }
473 
474  // Attributes for this variable
475  std::stringstream ssTagName;
476  ssTagName << "__" << var_name << "_ATTRIBS";
477  tag_name = ssTagName.str();
478  Tag varAttTag = 0;
479  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, varAttTag,
481  "Trouble getting conventional tag " << tag_name );
482  const void* varAttPtr = NULL;
483  int varAttSz = 0;
484  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( varAttTag, &fileSet, 1, &varAttPtr, &varAttSz ),
485  "Trouble getting data of conventional tag " << tag_name );
486  dbgOut.tprintf( 2, "Tag retrieved for variable %s\n", tag_name.c_str() );
487 
488  std::string attribString( (char*)varAttPtr, (char*)varAttPtr + varAttSz );
489  if( attribString == "NO_ATTRIBS" )
490  {
491  // This variable has no attributes
492  variableDataStruct.numAtts = 0;
493  }
494  else if( attribString == "DUMMY_VAR" )
495  {
496  // This variable is a dummy coordinate variable
497  variableDataStruct.numAtts = 0;
498  dummyVarNames.insert( variableDataStruct.varName );
499  }
500  else
501  {
502  ssTagName << "_LEN";
503  tag_name = ssTagName.str();
504  Tag varAttLenTag = 0;
505  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, varAttLenTag,
506  MB_TAG_ANY ),
507  "Trouble getting conventional tag " << tag_name );
508  int varAttLenSz = 0;
509  MB_CHK_SET_ERR( mbImpl->tag_get_length( varAttLenTag, varAttLenSz ),
510  "Trouble getting length of conventional tag " << tag_name );
511  std::vector< int > varAttLen( varAttLenSz );
512  MB_CHK_SET_ERR( mbImpl->tag_get_data( varAttLenTag, &fileSet, 1, &varAttLen[0] ),
513  "Trouble getting data of conventional tag " << tag_name );
514 
515  MB_CHK_SET_ERR( process_concatenated_attribute( varAttPtr, varAttSz, varAttLen,
516  variableDataStruct.varAtts ),
517  "Trouble processing attributes of variable " << var_name );
518 
519  dbgOut.tprintf( 2, "Tag metadata for variable %s\n", tag_name.c_str() );
520  }
521  // End attribute
522 
523  start = i + 1;
524  idxVar++;
525  } // if (p[i] == '\0')
526  }
527 
528  // Global attributes
529  tag_name = "__GLOBAL_ATTRIBS";
530  Tag globalAttTag = 0;
531  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, globalAttTag,
533  "Trouble getting conventional tag " << tag_name );
534  std::vector< int > gattLen;
535 
536  const void* gattptr = NULL;
537  int globalAttSz = 0;
538  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( globalAttTag, &fileSet, 1, &gattptr, &globalAttSz ),
539  "Trouble getting data of conventional tag " << tag_name );
540 
541  dbgOut.tprintf( 2, "Tag value retrieved for %s size %d\n", tag_name.c_str(), globalAttSz );
542 
543  // <__GLOBAL_ATTRIBS_LEN>
544  tag_name = "__GLOBAL_ATTRIBS_LEN";
545  Tag globalAttLenTag = 0;
546 
547  MB_CHK_SET_ERR( mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, globalAttLenTag, MB_TAG_ANY ),
548  "Trouble getting conventional tag " << tag_name );
549  int sizeGAtt = 0;
550  MB_CHK_SET_ERR( mbImpl->tag_get_length( globalAttLenTag, sizeGAtt ),
551  "Trouble getting length of conventional tag " << tag_name );
552 
553  gattLen.resize( sizeGAtt );
554  MB_CHK_SET_ERR( mbImpl->tag_get_data( globalAttLenTag, &fileSet, 1, &gattLen[0] ),
555  "Trouble getting data of conventional tag " << tag_name );
556 
557  dbgOut.tprintf( 2, "Tag retrieved for variable %s\n", tag_name.c_str() );
558 
559  MB_CHK_SET_ERR( process_concatenated_attribute( gattptr, globalAttSz, gattLen, globalAtts ),
560  "Trouble processing global attributes" );
561 
562  return MB_SUCCESS;
563 }
564 
565 // Reverse process from create_attrib_string
567  int attSz,
568  std::vector< int >& attLen,
569  std::map< std::string, AttData >& attributes )
570 {
571  std::size_t start = 0;
572  std::size_t att_counter = 0;
573  std::string concatString( (char*)attPtr, (char*)attPtr + attSz );
574 
575  for( std::size_t i = 0; i != (size_t)attSz; i++ )
576  {
577  if( concatString[i] == '\0' )
578  {
579  std::string att_name( &concatString[start], i - start );
580  start = i + 1;
581  while( concatString[i] != ';' )
582  ++i;
583  std::string data_type( &concatString[start], i - start );
584  ++i;
585  start = i;
586  i = attLen[att_counter];
587  if( concatString[i] != ';' ) MB_SET_ERR( MB_FAILURE, "Error parsing attributes" );
588 
589  std::string data_val( &concatString[start], i - start );
590  start = i + 1;
591 
592  AttData& attrib = attributes[att_name];
593  attrib.attValue = data_val;
594  attrib.attLen = data_val.size();
595 
596  if( data_type == "char" )
597  attrib.attDataType = NC_CHAR;
598  else if( data_type == "double" )
599  attrib.attDataType = NC_DOUBLE;
600  else if( data_type == "float" )
601  attrib.attDataType = NC_FLOAT;
602  else if( data_type == "int" )
603  attrib.attDataType = NC_INT;
604  else if( data_type == "short" )
605  attrib.attDataType = NC_SHORT;
606 
607  ++att_counter;
608  dbgOut.tprintf( 2, " Process attribute %s with value %s \n", att_name.c_str(), data_val.c_str() );
609  }
610  }
611 
612  return MB_SUCCESS;
613 }
614 
615 } // namespace moab