Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
NCWriteHelper.cpp
Go to the documentation of this file.
1 /*
2  * NCWriteHelper.cpp
3  *
4  * Created on: Mar 28, 2014
5  * Author: iulian
6  */
7 
8 #include "NCWriteHelper.hpp"
9 #include "NCWriteEuler.hpp"
10 #include "NCWriteFV.hpp"
11 #include "NCWriteHOMME.hpp"
12 #include "NCWriteMPAS.hpp"
13 #include "NCWriteGCRM.hpp"
14 #include "NCWriteScrip.hpp"
15 #include "NCWriteESMF.hpp"
16 #include "NCWriteDomain.hpp"
17 
18 #include "moab/WriteUtilIface.hpp"
19 #include "MBTagConventions.hpp"
20 
21 #include <sstream>
22 
23 #ifdef WIN32
24 #ifdef size_t
25 #undef size_t
26 #endif
27 #endif
28 
29 namespace moab
30 {
31 
32 //! Get appropriate helper instance for WriteNC class; based on some info in the file set
34  int fileId,
35  const FileOptions& opts,
36  EntityHandle fileSet )
37 {
38  std::string& grid_type = writeNC->grid_type;
39  if( grid_type == "CAM_EUL" )
40  return new( std::nothrow ) NCWriteEuler( writeNC, fileId, opts, fileSet );
41  else if( grid_type == "CAM_FV" )
42  return new( std::nothrow ) NCWriteFV( writeNC, fileId, opts, fileSet );
43  else if( grid_type == "CAM_SE" )
44  return new( std::nothrow ) NCWriteHOMME( writeNC, fileId, opts, fileSet );
45  else if( grid_type == "MPAS" )
46  return new( std::nothrow ) NCWriteMPAS( writeNC, fileId, opts, fileSet );
47  else if( grid_type == "GCRM" )
48  return new( std::nothrow ) NCWriteGCRM( writeNC, fileId, opts, fileSet );
49  else if( grid_type == "SCRIP" )
50  return new( std::nothrow ) NCWriteScrip( writeNC, fileId, opts, fileSet );
51  else if( grid_type == "ESMF" )
52  return new( std::nothrow ) NCWriteESMF( writeNC, fileId, opts, fileSet );
53  else if( grid_type == "DOMAIN" )
54  return new( std::nothrow ) NCWriteDomain( writeNC, fileId, opts, fileSet );
55 
56  // Unknown NetCDF grid
57  return NULL;
58 }
59 
60 ErrorCode NCWriteHelper::collect_variable_data( std::vector< std::string >& var_names, std::vector< int >& tstep_nums )
61 {
62  Interface*& mbImpl = _writeNC->mbImpl;
63  std::vector< std::string >& dimNames = _writeNC->dimNames;
64  std::vector< int >& dimLens = _writeNC->dimLens;
65  std::set< std::string >& usedCoordinates = _writeNC->usedCoordinates;
66  std::set< std::string >& dummyVarNames = _writeNC->dummyVarNames;
67  std::map< std::string, WriteNC::VarData >& varInfo = _writeNC->varInfo;
68  DebugOutput& dbgOut = _writeNC->dbgOut;
69 
70  usedCoordinates.clear();
71  if( tstep_nums.empty() && nTimeSteps > 0 )
72  {
73  // No timesteps input, get them all
74  for( int i = 0; i < nTimeSteps; i++ )
75  tstep_nums.push_back( i );
76  }
77 
78  for( size_t i = 0; i < var_names.size(); i++ )
79  {
80  std::string varname = var_names[i];
81  std::map< std::string, WriteNC::VarData >::iterator vit = varInfo.find( varname );
82  if( vit == varInfo.end() ) MB_SET_ERR( MB_FAILURE, "Can't find variable " << varname );
83 
84  WriteNC::VarData& currentVarData = vit->second;
85 
86  dbgOut.tprintf( 2, " for variable %s varDims.size %d \n", varname.c_str(),
87  (int)currentVarData.varDims.size() );
88  for( size_t j = 0; j < currentVarData.varDims.size(); j++ )
89  {
90  std::string dimName = dimNames[currentVarData.varDims[j]];
91  vit = varInfo.find( dimName );
92  if( vit == varInfo.end() ) MB_SET_ERR( MB_FAILURE, "Can't find coordinate variable " << dimName );
93 
94  usedCoordinates.insert( dimName ); // Collect those used, we will need to write them to the file
95  dbgOut.tprintf( 2, " for variable %s need dimension %s with length %d\n", varname.c_str(),
96  dimName.c_str(), dimLens[currentVarData.varDims[j]] );
97  }
98 
99  // Process coordinate variables later
100  if( usedCoordinates.find( varname ) != usedCoordinates.end() ) continue;
101 
102  // Default has_tsteps is false
103  if( std::find( currentVarData.varDims.begin(), currentVarData.varDims.end(), tDim ) !=
104  currentVarData.varDims.end() )
105  currentVarData.has_tsteps = true;
106 
107  // Default numLev is 0
108  if( ( std::find( currentVarData.varDims.begin(), currentVarData.varDims.end(), levDim ) !=
109  currentVarData.varDims.end() ) )
110  currentVarData.numLev = nLevels;
111 
112  // Process set variables
113  if( WriteNC::ENTLOCSET == currentVarData.entLoc )
114  {
115  if( currentVarData.has_tsteps )
116  {
117  // Set variables with timesteps, e.g. xtime(Time) or xtime(Time, StrLen)
118  // TBD
119  MB_SET_ERR( MB_NOT_IMPLEMENTED, "Writing set variables with timesteps is not implemented yet" );
120  }
121  else
122  {
123  // Get the tag with varname
124  Tag tag = 0;
125  MB_CHK_SET_ERR( mbImpl->tag_get_handle( varname.c_str(), tag ), "Can't find tag " << varname );
126  currentVarData.varTags.push_back( tag ); // Really, only one for these
127  const void* data;
128  int size;
129  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( tag, &_fileSet, 1, &data, &size ),
130  "Can't get data of tag " << varname );
131 
132  // Find the type of tag, and use it
133  DataType type;
134  MB_CHK_SET_ERR( mbImpl->tag_get_data_type( tag, type ), "Can't get data type of tag " << varname );
135 
136  currentVarData.varDataType = NC_DOUBLE;
137  if( MB_TYPE_INTEGER == type ) currentVarData.varDataType = NC_INT;
138 
139  assert( 0 == currentVarData.memoryHogs.size() ); // Nothing so far
140  currentVarData.memoryHogs.push_back( (void*)data );
141 
142  if( currentVarData.varDims.empty() )
143  {
144  // Scalar variable
145  currentVarData.writeStarts.push_back( 0 );
146  currentVarData.writeCounts.push_back( 1 );
147  }
148  else
149  {
150  for( size_t j = 0; j < currentVarData.varDims.size(); j++ )
151  {
152  currentVarData.writeStarts.push_back( 0 );
153  currentVarData.writeCounts.push_back( dimLens[currentVarData.varDims[j]] );
154  }
155  }
156 
157  // Get variable size
158  currentVarData.sz = 1;
159  for( std::size_t idx = 0; idx != currentVarData.writeCounts.size(); idx++ )
160  currentVarData.sz *= currentVarData.writeCounts[idx];
161  }
162  } // if (WriteNC::ENTLOCSET == currentVarData.entLoc)
163  // Process non-set variables
164  else
165  {
166  Tag indexedTag = 0;
167 
168  if( currentVarData.has_tsteps )
169  {
170  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
171  {
172  std::stringstream ssTagNameWithIndex;
173  ssTagNameWithIndex << varname << tstep_nums[t];
174  MB_CHK_SET_ERR( mbImpl->tag_get_handle( ssTagNameWithIndex.str().c_str(), indexedTag ),
175  "Can't find tag " << ssTagNameWithIndex.str() );
176  dbgOut.tprintf( 2, " found indexed tag %d with name %s\n", tstep_nums[t],
177  ssTagNameWithIndex.str().c_str() );
178  currentVarData.varTags.push_back( indexedTag );
179  }
180  }
181  else
182  {
183  // This should be a user-created non-set variable without timesteps
184  // Treat it like having one, 0th, timestep
185  std::stringstream ssTagNameWithIndex;
186  ssTagNameWithIndex << varname << 0;
187  MB_CHK_SET_ERR( mbImpl->tag_get_handle( ssTagNameWithIndex.str().c_str(), indexedTag ),
188  "Can't find tag " << ssTagNameWithIndex.str() << " for a user-created variable" );
189  dbgOut.tprintf( 2, " found indexed tag 0 with name %s\n", ssTagNameWithIndex.str().c_str() );
190  currentVarData.varTags.push_back( indexedTag );
191  }
192 
193  // The type of the tag is fixed though
194  DataType type;
195  MB_CHK_SET_ERR( mbImpl->tag_get_data_type( indexedTag, type ), "Can't get data type of tag " << varname );
196 
197  currentVarData.varDataType = NC_DOUBLE;
198  if( MB_TYPE_INTEGER == type ) currentVarData.varDataType = NC_INT;
199  }
200  } // for (size_t i = 0; i < var_names.size(); i++)
201 
202  // Process coordinate variables here
203  // Check that for used coordinates we have found the tags
204  for( std::set< std::string >::iterator setIt = usedCoordinates.begin(); setIt != usedCoordinates.end(); ++setIt )
205  {
206  const std::string& coordName = *setIt;
207 
208  std::map< std::string, WriteNC::VarData >::iterator vit = varInfo.find( coordName );
209  if( vit == varInfo.end() ) MB_SET_ERR( MB_FAILURE, "Can't find coordinate variable " << coordName );
210 
211  WriteNC::VarData& varCoordData = vit->second;
212  Tag coordTag = 0;
213  MB_CHK_SET_ERR( mbImpl->tag_get_handle( coordName.c_str(), coordTag ), "Can't find tag " << coordName );
214  varCoordData.varTags.push_back( coordTag ); // Really, only one for these
215 
216  const void* data;
217  int sizeCoordinate;
218  MB_CHK_SET_ERR( mbImpl->tag_get_by_ptr( coordTag, &_fileSet, 1, &data, &sizeCoordinate ),
219  "Can't get coordinate values of " << coordName );
220  dbgOut.tprintf( 2, " found coordinate tag with name %s and length %d\n", coordName.c_str(), sizeCoordinate );
221 
222  // Find the type of tag, and use it
223  DataType type;
224  MB_CHK_SET_ERR( mbImpl->tag_get_data_type( coordTag, type ), "Can't get data type of tag " << coordName );
225  varCoordData.varDataType = NC_DOUBLE;
226  if( MB_TYPE_INTEGER == type ) varCoordData.varDataType = NC_INT;
227 
228  // Get dimension length (the only dimension of this coordinate variable, with the same name)
229  assert( 1 == varCoordData.varDims.size() );
230  int coordDimLen = dimLens[varCoordData.varDims[0]];
231 
232  if( dummyVarNames.find( coordName ) != dummyVarNames.end() )
233  {
234  // For a dummy coordinate variable, the tag size is always 1
235  // The number of coordinates should be set to dimension length, instead of 1
236  assert( 1 == sizeCoordinate );
237  sizeCoordinate = coordDimLen;
238 
239  // No variable data to write
240  data = NULL;
241  }
242  else
243  {
244  // The number of coordinates should be exactly the same as dimension length
245  // However, if timesteps spread across files and time tag has been updated,
246  // sizeCoordinate will be larger
247  if( varCoordData.varDims[0] != tDim ) assert( sizeCoordinate == coordDimLen );
248  }
249 
250  // For time, the actual output size and values are determined by tstep_nums
251  if( varCoordData.varDims[0] == tDim )
252  {
253  // Does not apply to dummy time tag (e.g. 'Time' tag of MPAS), when timesteps
254  // spread across files
255  if( NULL != data ) assert( tstep_nums.size() > 0 && tstep_nums.size() <= (size_t)sizeCoordinate );
256 
257  sizeCoordinate = tstep_nums.size();
258 
259  if( NULL != data )
260  {
261  assert( NC_DOUBLE == varCoordData.varDataType );
262  timeStepVals.resize( sizeCoordinate );
263  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
264  timeStepVals[t] = ( (double*)data )[tstep_nums[t]];
265 
266  data = &timeStepVals[0];
267  }
268  }
269 
270  // This is the length
271  varCoordData.sz = sizeCoordinate;
272  varCoordData.writeStarts.resize( 1 );
273  varCoordData.writeStarts[0] = 0;
274  varCoordData.writeCounts.resize( 1 );
275  varCoordData.writeCounts[0] = sizeCoordinate;
276 
277  assert( 0 == varCoordData.memoryHogs.size() ); // Nothing so far
278  varCoordData.memoryHogs.push_back( (void*)data );
279  } // for (std::set<std::string>::iterator setIt ...
280 
281  return MB_SUCCESS;
282 }
283 
284 ErrorCode NCWriteHelper::init_file( std::vector< std::string >& var_names,
285  std::vector< std::string >& desired_names,
286  bool append )
287 {
288  std::vector< std::string >& dimNames = _writeNC->dimNames;
289  std::set< std::string >& usedCoordinates = _writeNC->usedCoordinates;
290  std::set< std::string >& dummyVarNames = _writeNC->dummyVarNames;
291  std::map< std::string, WriteNC::VarData >& varInfo = _writeNC->varInfo;
292  std::map< std::string, WriteNC::AttData >& globalAtts = _writeNC->globalAtts;
293  DebugOutput& dbgOut = _writeNC->dbgOut;
294 
295  int tDim_in_dimNames = tDim;
296  int levDim_in_dimNames = levDim;
297 
298  // If append mode, make sure we are in define mode; a simple open will not allow creation of new
299  // variables
300  if( append )
301  {
302  int errcode = NCFUNC( redef )( _fileId );
303  if( errcode != NC_NOERR ) MB_SET_ERR( MB_FAILURE, "Can't open file in redefine mode" );
304  }
305 
306  // First initialize all coordinates, then fill VarData for actual variables (and dimensions)
307  // Check that for used coordinates we have found the tags
308  for( std::set< std::string >::iterator setIt = usedCoordinates.begin(); setIt != usedCoordinates.end(); ++setIt )
309  {
310  const std::string& coordName = *setIt;
311 
312  std::map< std::string, WriteNC::VarData >::iterator vit = varInfo.find( coordName );
313  if( vit == varInfo.end() ) MB_SET_ERR( MB_FAILURE, "Can't find coordinate variable " << coordName );
314 
315  WriteNC::VarData& varCoordData = vit->second;
316  varCoordData.varDims.resize( 1 );
317 
318  // If not append, create it for sure
319  // If append, we might already have it, including the tag / variable with the same name
320  /*
321  * int ncmpi_inq_dimid(int ncid, const char *name, int *idp);
322  */
323  if( append )
324  {
325  int dimId;
326  if( NCFUNC( inq_dimid )( _fileId, coordName.c_str(), &dimId ) == NC_NOERR )
327  { // If not found, create it later
328  varCoordData.varDims[0] = dimId;
329  dbgOut.tprintf( 2, " file already has coordName %s dim id is %d \n", coordName.c_str(),
330  (int)varCoordData.varDims[0] );
331 
332  // Update tDim and levDim to actual dimension id
333  if( coordName == dimNames[tDim_in_dimNames] )
334  tDim = varCoordData.varDims[0];
335  else if( coordName == dimNames[levDim_in_dimNames] )
336  levDim = varCoordData.varDims[0];
337 
338  // Skip dummy coordinate variables (e.g. ncol)
339  if( dummyVarNames.find( coordName ) != dummyVarNames.end() ) continue;
340 
341  // Check that the coordinate is a variable too
342  // Inquire for a variable with the same name
343  int varId;
344  if( NCFUNC( inq_varid )( _fileId, coordName.c_str(), &varId ) != NC_NOERR )
345  MB_SET_ERR( MB_FAILURE, "We do not have a variable with the same name " << coordName );
346  // We should also check that this variable has one dimension, and it is dimId
347  varCoordData.varId = varId;
348  dbgOut.tprintf( 2, " file already has coordinate %s and varId is %d \n", coordName.c_str(), varId );
349 
350  continue; // Maybe more checks are needed here
351  }
352  }
353 
354  /* int nc_def_dim (int ncid, const char *name, size_t len, int *dimidp);
355  * example: status = nc_def_dim(fileId, "lat", 18L, &latid);
356  */
357 
358  // Actually define a dimension
359  if( NCFUNC( def_dim )( _fileId, coordName.c_str(), (size_t)varCoordData.sz, &varCoordData.varDims[0] ) !=
360  NC_NOERR )
361  MB_SET_ERR( MB_FAILURE, "Failed to generate dimension " << coordName );
362  dbgOut.tprintf( 2, " for coordName %s dim id is %d \n", coordName.c_str(), (int)varCoordData.varDims[0] );
363 
364  // Update tDim and levDim to actual dimension id
365  if( coordName == dimNames[tDim_in_dimNames] )
366  tDim = varCoordData.varDims[0];
367  else if( coordName == dimNames[levDim_in_dimNames] )
368  levDim = varCoordData.varDims[0];
369 
370  // Create a variable with the same name, and its only dimension the one we just defined
371  /*
372  * int nc_def_var (int ncid, const char *name, nc_type xtype,
373  int ndims, const int dimids[], int *varidp);
374  example:
375  http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-c/nc_005fdef_005fvar.html#nc_005fdef_005fvar
376  */
377 
378  // Skip dummy coordinate variables (e.g. ncol)
379  if( dummyVarNames.find( coordName ) != dummyVarNames.end() ) continue;
380 
381  // Define a coordinate variable
382  if( NCFUNC( def_var )( _fileId, coordName.c_str(), varCoordData.varDataType, 1, &( varCoordData.varDims[0] ),
383  &varCoordData.varId ) != NC_NOERR )
384  MB_SET_ERR( MB_FAILURE, "Failed to create coordinate variable " << coordName );
385 
386  dbgOut.tprintf( 2, " for coordName %s variable id is %d \n", coordName.c_str(), varCoordData.varId );
387  }
388 
389  // Now look at requested variables, and update from the index in dimNames to the actual
390  // dimension id
391  for( size_t i = 0; i < var_names.size(); i++ )
392  {
393  std::map< std::string, WriteNC::VarData >::iterator vit = varInfo.find( var_names[i] );
394  if( vit == varInfo.end() ) MB_SET_ERR( MB_FAILURE, "Can't find requested variable " << var_names[i] );
395 
396  // Skip coordinate variables
397  if( usedCoordinates.find( var_names[i] ) != usedCoordinates.end() ) continue;
398 
399  WriteNC::VarData& variableData = vit->second;
400 
401  // The index is for dimNames; we need to find out the actual dimension id (from above)
402  int numDims = (int)variableData.varDims.size();
403  for( int j = 0; j < numDims; j++ )
404  {
405  std::string dimName = dimNames[variableData.varDims[j]];
406  std::map< std::string, WriteNC::VarData >::iterator vit2 = varInfo.find( dimName );
407  if( vit2 == varInfo.end() )
408  MB_SET_ERR( MB_FAILURE, "Can't find requested coordinate variable " << dimName );
409 
410  WriteNC::VarData& coordData = vit2->second;
411  // Index in dimNames to actual dimension id
412  variableData.varDims[j] = coordData.varDims[0]; // This one, being a coordinate, is the only one
413  dbgOut.tprintf( 2, " dimension with index %d name %s has ID %d \n", j, dimName.c_str(),
414  variableData.varDims[j] );
415  }
416 
417  // Define the variable now:
418  int errCode =
419  NCFUNC( def_var )( _fileId, desired_names[i].c_str(), variableData.varDataType,
420  (int)variableData.varDims.size(), &( variableData.varDims[0] ), &variableData.varId );
421  if( errCode != NC_NOERR ) MB_SET_ERR( MB_FAILURE, "Failed to create requested variable " << desired_names[i] );
422 
423  dbgOut.tprintf( 2, " for variable %s with desired name %s variable id is %d \n", var_names[i].c_str(),
424  desired_names[i].c_str(), variableData.varId );
425  // Now define the variable, with all dimensions
426  }
427 
428  // Define global attributes (exactly copied from the original file for the time being)
429  // Should we modify some of them (e.g. revision_Id) later?
430  std::map< std::string, WriteNC::AttData >::iterator attIt;
431  for( attIt = globalAtts.begin(); attIt != globalAtts.end(); ++attIt )
432  {
433  const std::string& attName = attIt->first;
434  WriteNC::AttData& attData = attIt->second;
435  NCDF_SIZE& attLen = attData.attLen;
436  nc_type& attDataType = attData.attDataType;
437  const std::string& attValue = attData.attValue;
438 
439  switch( attDataType )
440  {
441  case NC_BYTE:
442  case NC_CHAR:
443  if( NC_NOERR !=
444  NCFUNC( put_att_text )( _fileId, NC_GLOBAL, attName.c_str(), attLen, attValue.c_str() ) )
445  MB_SET_ERR( MB_FAILURE, "Failed to define text type attribute" );
446  break;
447  case NC_DOUBLE:
448  if( NC_NOERR != NCFUNC( put_att_double )( _fileId, NC_GLOBAL, attName.c_str(), NC_DOUBLE, 1,
449  (double*)attValue.c_str() ) )
450  MB_SET_ERR( MB_FAILURE, "Failed to define double type attribute" );
451  break;
452  case NC_FLOAT:
453  if( NC_NOERR != NCFUNC( put_att_float )( _fileId, NC_GLOBAL, attName.c_str(), NC_FLOAT, 1,
454  (float*)attValue.c_str() ) )
455  MB_SET_ERR( MB_FAILURE, "Failed to define float type attribute" );
456  break;
457  case NC_INT:
458  if( NC_NOERR !=
459  NCFUNC( put_att_int )( _fileId, NC_GLOBAL, attName.c_str(), NC_INT, 1, (int*)attValue.c_str() ) )
460  MB_SET_ERR( MB_FAILURE, "Failed to define int type attribute" );
461  break;
462  case NC_SHORT:
463  if( NC_NOERR != NCFUNC( put_att_short )( _fileId, NC_GLOBAL, attName.c_str(), NC_SHORT, 1,
464  (short*)attValue.c_str() ) )
465  MB_SET_ERR( MB_FAILURE, "Failed to define short type attribute" );
466  break;
467  default:
468  MB_SET_ERR( MB_FAILURE, "Unknown attribute data type" );
469  }
470  }
471 
472  // Take it out of define mode
473  if( NC_NOERR != NCFUNC( enddef )( _fileId ) ) MB_SET_ERR( MB_FAILURE, "Failed to close define mode" );
474 
475  return MB_SUCCESS;
476 }
477 
478 ErrorCode NCWriteHelper::write_values( std::vector< std::string >& var_names, std::vector< int >& tstep_nums )
479 {
480  std::set< std::string >& usedCoordinates = _writeNC->usedCoordinates;
481  std::set< std::string >& dummyVarNames = _writeNC->dummyVarNames;
482  std::map< std::string, WriteNC::VarData >& varInfo = _writeNC->varInfo;
483 
484  std::vector< WriteNC::VarData > vdatas;
485  std::vector< WriteNC::VarData > vsetdatas;
486 
487  // For set variables, include coordinates used by requested var_names
488  for( std::set< std::string >::iterator setIt = usedCoordinates.begin(); setIt != usedCoordinates.end(); ++setIt )
489  {
490  const std::string& coordName = *setIt;
491 
492  // Skip dummy coordinate variables (if any)
493  if( dummyVarNames.find( coordName ) != dummyVarNames.end() ) continue;
494 
495  std::map< std::string, WriteNC::VarData >::iterator vit = varInfo.find( coordName );
496  if( vit == varInfo.end() )
497  {
498  MB_SET_ERR( MB_FAILURE, "Can't find coordinate variable " << coordName );
499  }
500 
501  vsetdatas.push_back( vit->second );
502  }
503 
504  // Collect non-set and set variables from requested var_names
505  for( unsigned int i = 0; i < var_names.size(); i++ )
506  {
507  std::map< std::string, WriteNC::VarData >::iterator vit = varInfo.find( var_names[i] );
508  if( vit == varInfo.end() )
509  {
510  MB_SET_ERR( MB_FAILURE, "Can't find requested variable " << var_names[i] );
511  }
512 
513  WriteNC::VarData& variableData = vit->second;
514  if( WriteNC::ENTLOCSET == variableData.entLoc )
515  {
516  // Used coordinates has all ready been included
517  if( usedCoordinates.find( var_names[i] ) != usedCoordinates.end() ) continue;
518 
519  vsetdatas.push_back( variableData );
520  }
521  else
522  vdatas.push_back( variableData );
523  }
524 
525  // Assume that the data ranges do not overlap across processors
526  // While overlapped writing might still work, we should better not take that risk
527  write_nonset_variables( vdatas, tstep_nums );
528 
529  // Use independent I/O mode put, since this write is only for the root processor
530  write_set_variables( vsetdatas, tstep_nums );
531 
532  return MB_SUCCESS;
533 }
534 
535 ErrorCode NCWriteHelper::write_set_variables( std::vector< WriteNC::VarData >& vsetdatas,
536  std::vector< int >& /* tstep_nums */ )
537 {
538  int success;
539 
540  // CAUTION: if the NetCDF ID is from a previous call to ncmpi_create rather than ncmpi_open,
541  // all processors need to call ncmpi_begin_indep_data(). If only the root processor does so,
542  // ncmpi_begin_indep_data() call will be blocked forever :(
543 #ifdef MOAB_HAVE_PNETCDF
544  // Enter independent I/O mode
545  success = NCFUNC( begin_indep_data )( _fileId );
546  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to begin independent I/O mode" );
547 #endif
548 
549  int rank = 0;
550 #ifdef MOAB_HAVE_MPI
551  bool& isParallel = _writeNC->isParallel;
552  if( isParallel )
553  {
554  ParallelComm*& myPcomm = _writeNC->myPcomm;
555  rank = myPcomm->proc_config().proc_rank();
556  }
557 #endif
558  if( 0 == rank )
559  {
560  for( unsigned int i = 0; i < vsetdatas.size(); i++ )
561  {
562  WriteNC::VarData& variableData = vsetdatas[i];
563 
564  // Set variables with timesteps, e.g. xtime(Time) or xtime(Time, StrLen)
565  if( variableData.has_tsteps )
566  {
567  MB_SET_ERR( MB_NOT_IMPLEMENTED, "Writing set variables with timesteps is not implemented yet" );
568  }
569 
570  switch( variableData.varDataType )
571  {
572  case NC_DOUBLE:
573  // Independent I/O mode put
574  success = NCFUNCP( _vara_double )( _fileId, variableData.varId, &variableData.writeStarts[0],
575  &variableData.writeCounts[0],
576  (double*)( variableData.memoryHogs[0] ) );
577  if( success )
578  MB_SET_ERR( MB_FAILURE, "Failed to write double data for variable " << variableData.varName );
579  break;
580  case NC_INT:
581  // Independent I/O mode put
582  success =
583  NCFUNCP( _vara_int )( _fileId, variableData.varId, &variableData.writeStarts[0],
584  &variableData.writeCounts[0], (int*)( variableData.memoryHogs[0] ) );
585  if( success )
586  MB_SET_ERR( MB_FAILURE, "Failed to write int data for variable " << variableData.varName );
587  break;
588  default:
589  MB_SET_ERR( MB_NOT_IMPLEMENTED, "Writing non-double or non-int data is not implemented yet" );
590  }
591  }
592  }
593 
594 #ifdef MOAB_HAVE_PNETCDF
595  // End independent I/O mode
596  success = NCFUNC( end_indep_data )( _fileId );
597  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to end independent I/O mode" );
598 #endif
599 
600  return MB_SUCCESS;
601 }
602 
604 {
605  Interface*& mbImpl = _writeNC->mbImpl;
606  std::vector< std::string >& dimNames = _writeNC->dimNames;
607  std::vector< int >& dimLens = _writeNC->dimLens;
608 
609  // Look for time dimension
610  std::vector< std::string >::iterator vecIt;
611  if( ( vecIt = std::find( dimNames.begin(), dimNames.end(), "time" ) ) != dimNames.end() )
612  tDim = vecIt - dimNames.begin();
613  else if( ( vecIt = std::find( dimNames.begin(), dimNames.end(), "t" ) ) != dimNames.end() )
614  tDim = vecIt - dimNames.begin();
615  else
616  {
617  MB_SET_ERR( MB_FAILURE, "Couldn't find 'time' or 't' dimension" );
618  }
619  nTimeSteps = dimLens[tDim];
620 
621  // Get number of levels
622  if( ( vecIt = std::find( dimNames.begin(), dimNames.end(), "lev" ) ) != dimNames.end() )
623  levDim = vecIt - dimNames.begin();
624  else if( ( vecIt = std::find( dimNames.begin(), dimNames.end(), "ilev" ) ) != dimNames.end() )
625  levDim = vecIt - dimNames.begin();
626  else
627  {
628  MB_SET_ERR( MB_FAILURE, "Couldn't find 'lev' or 'ilev' dimension" );
629  }
630  nLevels = dimLens[levDim];
631 
632  // __<dim_name>_LOC_MINMAX (for slon, slat, lon and lat)
633  Tag convTag = 0;
634  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "__slon_LOC_MINMAX", 0, MB_TYPE_INTEGER, convTag, MB_TAG_ANY ),
635  "Trouble getting conventional tag __slon_LOC_MINMAX" );
636  int val[2];
637  MB_CHK_SET_ERR( mbImpl->tag_get_data( convTag, &_fileSet, 1, val ),
638  "Trouble getting data of conventional tag __slon_LOC_MINMAX" );
639  lDims[0] = val[0];
640  lDims[3] = val[1];
641 
642  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "__slat_LOC_MINMAX", 0, MB_TYPE_INTEGER, convTag, MB_TAG_ANY ),
643  "Trouble getting conventional tag __slat_LOC_MINMAX" );
644  MB_CHK_SET_ERR( mbImpl->tag_get_data( convTag, &_fileSet, 1, val ),
645  "Trouble getting data of conventional tag __slat_LOC_MINMAX" );
646  lDims[1] = val[0];
647  lDims[4] = val[1];
648 
649  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "__lon_LOC_MINMAX", 0, MB_TYPE_INTEGER, convTag, MB_TAG_ANY ),
650  "Trouble getting conventional tag __lon_LOC_MINMAX" );
651  MB_CHK_SET_ERR( mbImpl->tag_get_data( convTag, &_fileSet, 1, val ),
652  "Trouble getting data of conventional tag __lon_LOC_MINMAX" );
653  lCDims[0] = val[0];
654  lCDims[3] = val[1];
655 
656  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "__lat_LOC_MINMAX", 0, MB_TYPE_INTEGER, convTag, MB_TAG_ANY ),
657  "Trouble getting conventional tag __lat_LOC_MINMAX" );
658  MB_CHK_SET_ERR( mbImpl->tag_get_data( convTag, &_fileSet, 1, val ),
659  "Trouble getting data of conventional tag __lat_LOC_MINMAX" );
660  lCDims[1] = val[0];
661  lCDims[4] = val[1];
662 
663  // Get local faces
665  "Trouble getting local faces in current file set" );
666  assert( !localCellsOwned.empty() );
667 
668 #ifdef MOAB_HAVE_MPI
669  bool& isParallel = _writeNC->isParallel;
670  if( isParallel )
671  {
672  ParallelComm*& myPcomm = _writeNC->myPcomm;
673  int procs = myPcomm->proc_config().proc_size();
674  if( procs > 1 )
675  {
677  "Trouble getting owned faces in current file set" );
678  }
679  }
680 #endif
681 
682  return MB_SUCCESS;
683 }
684 
685 ErrorCode ScdNCWriteHelper::collect_variable_data( std::vector< std::string >& var_names,
686  std::vector< int >& tstep_nums )
687 {
688  NCWriteHelper::collect_variable_data( var_names, tstep_nums );
689 
690  std::map< std::string, WriteNC::VarData >& varInfo = _writeNC->varInfo;
691 
692  for( size_t i = 0; i < var_names.size(); i++ )
693  {
694  std::string varname = var_names[i];
695  std::map< std::string, WriteNC::VarData >::iterator vit = varInfo.find( varname );
696  if( vit == varInfo.end() ) MB_SET_ERR( MB_FAILURE, "Can't find variable " << varname );
697 
698  WriteNC::VarData& currentVarData = vit->second;
699 #ifndef NDEBUG
700  std::vector< int >& varDims = currentVarData.varDims;
701 #endif
702 
703  // Skip set variables, which were already processed in
704  // NCWriteHelper::collect_variable_data()
705  if( WriteNC::ENTLOCSET == currentVarData.entLoc ) continue;
706 
707  // Set up writeStarts and writeCounts (maximum number of dimensions is 4)
708  currentVarData.writeStarts.resize( 4 );
709  currentVarData.writeCounts.resize( 4 );
710  unsigned int dim_idx = 0;
711 
712  // First: time
713  if( currentVarData.has_tsteps )
714  {
715  // Non-set variables with timesteps
716  // 4 dimensions like (time, lev, lat, lon)
717  // 3 dimensions like (time, lat, lon)
718  assert( 4 == varDims.size() || 3 == varDims.size() );
719 
720  // Time should be the first dimension
721  assert( tDim == varDims[0] );
722 
723  currentVarData.writeStarts[dim_idx] = 0; // This value is timestep dependent, will be set later
724  currentVarData.writeCounts[dim_idx] = 1;
725  dim_idx++;
726  }
727  else
728  {
729  // Non-set variables without timesteps
730  // 3 dimensions like (lev, lat, lon)
731  // 2 dimensions like (lat, lon)
732  assert( 3 == varDims.size() || 2 == varDims.size() );
733  }
734 
735  // Next: lev
736  if( currentVarData.numLev > 0 )
737  {
738  // Non-set variables with levels
739  // 4 dimensions like (time, lev, lat, lon)
740  // 3 dimensions like (lev, lat, lon)
741  assert( 4 == varDims.size() || 3 == varDims.size() );
742 
743  currentVarData.writeStarts[dim_idx] = 0;
744  currentVarData.writeCounts[dim_idx] = currentVarData.numLev;
745  dim_idx++;
746  }
747  else
748  {
749  // Non-set variables without levels
750  // 3 dimensions like (time, lat, lon)
751  // 2 dimensions like (lat, lon)
752  assert( 3 == varDims.size() || 2 == varDims.size() );
753  }
754 
755  // Finally: lat and lon
756  switch( currentVarData.entLoc )
757  {
758  case WriteNC::ENTLOCFACE:
759  // Faces
760  currentVarData.writeStarts[dim_idx] = lCDims[1];
761  currentVarData.writeCounts[dim_idx] = lCDims[4] - lCDims[1] + 1;
762  currentVarData.writeStarts[dim_idx + 1] = lCDims[0];
763  currentVarData.writeCounts[dim_idx + 1] = lCDims[3] - lCDims[0] + 1;
764  break;
765  default:
766  MB_SET_ERR( MB_FAILURE, "Unexpected entity location type for variable " << varname );
767  }
768  dim_idx += 2;
769 
770  // Get variable size
771  currentVarData.sz = 1;
772  for( std::size_t idx = 0; idx < dim_idx; idx++ )
773  currentVarData.sz *= currentVarData.writeCounts[idx];
774  } // for (size_t i = 0; i < var_names.size(); i++)
775 
776  return MB_SUCCESS;
777 }
778 
779 // Write CAM-EUL and CAM-FV non-set variables on non-shared quads (e.g. T)
780 // We assume that there are no variables on vertices and we do not support
781 // variables on edges (e.g. US in CAM-FV) for the time being
782 ErrorCode ScdNCWriteHelper::write_nonset_variables( std::vector< WriteNC::VarData >& vdatas,
783  std::vector< int >& tstep_nums )
784 {
785  Interface*& mbImpl = _writeNC->mbImpl;
786 
787  int success;
788 
789  // For each indexed variable tag, write a time step data
790  for( unsigned int i = 0; i < vdatas.size(); i++ )
791  {
792  WriteNC::VarData& variableData = vdatas[i];
793 
794  // Assume this variable is on faces for the time being
795  switch( variableData.entLoc )
796  {
797  case WriteNC::ENTLOCFACE:
798  // Faces
799  break;
800  default:
801  MB_SET_ERR( MB_FAILURE, "Unexpected entity location type for variable " << variableData.varName );
802  }
803 
804  unsigned int num_timesteps;
805  unsigned int lat_idx = 0;
806  unsigned int lon_idx = 1;
807  if( variableData.has_tsteps )
808  {
809  // Non-set variables with timesteps
810  // 4 dimensions like (time, lev, lat, lon)
811  // 3 dimensions like (time, lat, lon)
812  num_timesteps = tstep_nums.size();
813  lat_idx++;
814  lon_idx++;
815  }
816  else
817  {
818  // Non-set variables without timesteps
819  // 3 dimensions like (lev, lat, lon)
820  // 2 dimensions like (lat, lon)
821  num_timesteps = 1;
822  }
823 
824  unsigned int num_lev;
825  if( variableData.numLev > 0 )
826  {
827  // Non-set variables with levels
828  // 4 dimensions like (time, lev, lat, lon)
829  // 3 dimensions like (lev, lat, lon)
830  num_lev = variableData.numLev;
831  lat_idx++;
832  lon_idx++;
833  }
834  else
835  {
836  // Non-set variables without levels
837  // 3 dimensions like (time, lat, lon)
838  // 2 dimensions like (lat, lon)
839  num_lev = 1;
840  }
841 
842  size_t ni = variableData.writeCounts[lon_idx]; // lon
843  size_t nj = variableData.writeCounts[lat_idx]; // lat
844 
845  // At each timestep, we need to transpose tag format (lat, lon, lev) back
846  // to NC format (lev, lat, lon) for writing
847  for( unsigned int t = 0; t < num_timesteps; t++ )
848  {
849  // We will write one time step, and count will be one; start will be different
850  // Use tag_iterate to get tag data (assume that localCellsOwned is contiguous)
851  // We should also transpose for level so that means deep copy for transpose
852  if( tDim == variableData.varDims[0] ) variableData.writeStarts[0] = t; // This is start for time
853  int count;
854  void* dataptr;
855  MB_CHK_SET_ERR( mbImpl->tag_iterate( variableData.varTags[t], localCellsOwned.begin(),
856  localCellsOwned.end(), count, dataptr ),
857  "Failed to iterate tag on owned faces" );
858  assert( count == (int)localCellsOwned.size() );
859 
860  // Now transpose and write tag data
861  // Use collective I/O mode put (synchronous write) for the time being, we can try
862  // nonblocking put (request aggregation) later
863  switch( variableData.varDataType )
864  {
865  case NC_DOUBLE: {
866  std::vector< double > tmpdoubledata( ni * nj * num_lev );
867  if( num_lev > 1 )
868  // Transpose (lat, lon, lev) back to (lev, lat, lon)
869  jik_to_kji( ni, nj, num_lev, &tmpdoubledata[0], (double*)( dataptr ) );
870  success = NCFUNCAP( _vara_double )( _fileId, variableData.varId, &variableData.writeStarts[0],
871  &variableData.writeCounts[0], &tmpdoubledata[0] );
872  if( success )
873  MB_SET_ERR( MB_FAILURE, "Failed to write double data for variable " << variableData.varName );
874  break;
875  }
876  default:
877  MB_SET_ERR( MB_NOT_IMPLEMENTED, "Writing non-double data is not implemented yet" );
878  }
879  }
880  }
881 
882  return MB_SUCCESS;
883 }
884 
885 } /* namespace moab */