Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
ReadNC.cpp
Go to the documentation of this file.
1 #include "ReadNC.hpp"
2 #include "NCHelper.hpp"
3 
4 #include "moab/ReadUtilIface.hpp"
5 #include "MBTagConventions.hpp"
6 #include "moab/FileOptions.hpp"
7 
8 namespace moab
9 {
10 
12 {
13  return new ReadNC( iface );
14 }
15 
17  : mbImpl( impl ), fileId( -1 ), mGlobalIdTag( 0 ), mpFileIdTag( NULL ), dbgOut( stderr ), isParallel( false ),
18  partMethod( ScdParData::ALLJORKORI ), scdi( NULL ),
19 #ifdef MOAB_HAVE_MPI
20  myPcomm( NULL ),
21 #endif
22  noMesh( false ), noVars( false ), spectralMesh( false ), noMixedElements( false ), noEdges( false ),
23  culling( true ), repartition( false ), gatherSetRank( -1 ), tStepBase( -1 ), trivialPartitionShift( 0 ),
24  myHelper( NULL )
25 {
26  assert( impl != NULL );
28 }
29 
31 {
33  if( myHelper != NULL ) delete myHelper;
34 }
35 
36 ErrorCode ReadNC::load_file( const char* file_name,
37  const EntityHandle* file_set,
38  const FileOptions& opts,
39  const ReaderIface::SubsetList* /*subset_list*/,
40  const Tag* file_id_tag )
41 {
42  // See if opts has variable(s) specified
43  std::vector< std::string > var_names;
44  std::vector< int > tstep_nums;
45  std::vector< double > tstep_vals;
46 
47  // Get and cache predefined tag handles
49  // Store the pointer to the tag; if not null, set when global id tag
50  // is set too, with the same data, duplicated
51  mpFileIdTag = file_id_tag;
52 
53  MB_CHK_SET_ERR( parse_options( opts, var_names, tstep_nums, tstep_vals ), "Trouble parsing option string" );
54 
55  // Open the file
56  dbgOut.tprintf( 1, "Opening file %s\n", file_name );
57  fileName = std::string( file_name );
58  int success;
59 
60 #ifdef MOAB_HAVE_PNETCDF
61  if( isParallel )
62  success = NCFUNC( open )( myPcomm->proc_config().proc_comm(), file_name, 0, MPI_INFO_NULL, &fileId );
63  else
64  success = NCFUNC( open )( MPI_COMM_SELF, file_name, 0, MPI_INFO_NULL, &fileId );
65 #else
66  success = NCFUNC( open )( file_name, 0, &fileId );
67 #endif
68  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble opening file " << file_name );
69 
70  // Read the header (num dimensions, dimensions, num variables, global attribs)
71  MB_CHK_SET_ERR( read_header(), "Trouble reading file header" );
72 
73  // Make sure there's a file set to put things in
74  EntityHandle tmp_set;
75  if( noMesh && !file_set )
76  {
77  MB_SET_ERR( MB_FAILURE, "NOMESH option requires non-NULL file set on input" );
78  }
79  else if( !file_set || ( file_set && *file_set == 0 ) )
80  {
81  MB_CHK_SET_ERR( mbImpl->create_meshset( MESHSET_SET, tmp_set ), "Trouble creating file set" );
82  }
83  else
84  tmp_set = *file_set;
85 
86  // Get the scd interface
87  scdi = nullptr;
88  MB_CHK_SET_ERR( mbImpl->query_interface( scdi ), "failed to get SCD interface from query" );
89  if( nullptr == scdi ) return MB_FAILURE;
90 
91  if( nullptr != myHelper ) delete myHelper;
92 
93  // Get appropriate NC helper instance based on information read from the header
94  myHelper = NCHelper::get_nc_helper( this, fileId, opts, tmp_set );
95  if( nullptr == myHelper )
96  {
97  MB_SET_ERR( MB_FAILURE, "Failed to get NCHelper class instance" );
98  }
99 
100  // Initialize mesh values
101  MB_CHK_SET_ERR( myHelper->init_mesh_vals(), "Trouble initializing mesh values" );
102 
103  // Check existing mesh from last read
104  if( noMesh && !noVars )
105  {
106  MB_CHK_SET_ERR( myHelper->check_existing_mesh(), "Trouble checking mesh from last read" );
107  }
108 
109  // Create some conventional tags, e.g. __NUM_DIMS
110  // For multiple reads to a specified file set, we assume a single file, or a series of
111  // files with separated timesteps. Keep a flag on the file set to prevent conventional
112  // tags from being created again on a second read
113  Tag convTagsCreated = 0;
114  int def_val = 0;
115  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "__CONV_TAGS_CREATED", 1, MB_TYPE_INTEGER, convTagsCreated,
116  MB_TAG_SPARSE | MB_TAG_CREAT, &def_val ),
117  "Trouble getting _CONV_TAGS_CREATED tag" );
118  int create_conv_tags_flag = 0;
119  MB_CHK_SET_ERR( mbImpl->tag_get_data( convTagsCreated, &tmp_set, 1, &create_conv_tags_flag ),
120  "failed to get conventional tags" );
121  // The first read to the file set
122  if( 0 == create_conv_tags_flag )
123  {
124  // Read dimensions (coordinate variables) by default to create tags like __<var_name>_DIMS
125  // This is done only once (assume that all files read to the file set have the same
126  // dimensions)
127  MB_CHK_SET_ERR( myHelper->read_variables( dimNames, tstep_nums ), "Trouble reading dimensions" );
128 
129  MB_CHK_SET_ERR( myHelper->create_conventional_tags( tstep_nums ), "Trouble creating NC conventional tags" );
130 
131  create_conv_tags_flag = 1;
132  MB_CHK_SET_ERR( mbImpl->tag_set_data( convTagsCreated, &tmp_set, 1, &create_conv_tags_flag ),
133  "Trouble setting data to _CONV_TAGS_CREATED tag" );
134  }
135  else // Another read to the file set
136  {
137  if( tStepBase > -1 )
138  {
139  // If timesteps spread across files, merge time values read
140  // from current file to existing time tag
141  MB_CHK_SET_ERR( myHelper->update_time_tag_vals(), "Trouble updating time tag values" );
142  }
143  }
144 
145  // Create mesh vertex/edge/face sequences
146  Range faces;
147  if( !noMesh )
148  {
149  MB_CHK_SET_ERR( myHelper->create_mesh( faces ), "Trouble creating mesh" );
150  }
151 
152  // Read specified variables onto grid
153  if( !noVars )
154  {
155  if( var_names.empty() )
156  {
157  // If VARIABLE option is missing, read all variables
158  MB_CHK_SET_ERR( myHelper->read_variables( var_names, tstep_nums ), "Trouble reading all variables" );
159  }
160  else
161  {
162  // Exclude dimensions that are read to the file set by default
163  std::vector< std::string > non_dim_var_names;
164  for( unsigned int i = 0; i < var_names.size(); i++ )
165  {
166  if( std::find( dimNames.begin(), dimNames.end(), var_names[i] ) == dimNames.end() )
167  non_dim_var_names.push_back( var_names[i] );
168  }
169 
170  if( !non_dim_var_names.empty() )
171  {
172  MB_CHK_SET_ERR( myHelper->read_variables( non_dim_var_names, tstep_nums ),
173  "Trouble reading specified variables" );
174  }
175  }
176  }
177 
178 #ifdef MOAB_HAVE_MPI
179  // Create partition set, and populate with elements
180  if( isParallel )
181  {
182  // Write partition tag name on partition set
183  Tag part_tag = myPcomm->partition_tag();
184  int dum_rank = myPcomm->proc_config().proc_rank();
185  // the tmp_set is the file_set
186  MB_CHK_SET_ERR( mbImpl->tag_set_data( part_tag, &tmp_set, 1, &dum_rank ),
187  "Trouble writing partition tag name on partition set" );
188  }
189 #endif
190 
192  scdi = NULL;
193 
194  // Close the file
195  success = NCFUNC( close )( fileId );
196  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble closing file" );
197 
198  return MB_SUCCESS;
199 }
200 
202  std::vector< std::string >& var_names,
203  std::vector< int >& tstep_nums,
204  std::vector< double >& tstep_vals )
205 {
206  int tmpval;
207  if( MB_SUCCESS == opts.get_int_option( "DEBUG_IO", 1, tmpval ) )
208  {
209  dbgOut.set_verbosity( tmpval );
210  dbgOut.set_prefix( "NC " );
211  }
212 
213  ErrorCode rval = opts.get_strs_option( "VARIABLE", var_names );
214  if( MB_TYPE_OUT_OF_RANGE == rval )
215  noVars = true;
216  else
217  noVars = false;
218 
219  opts.get_ints_option( "TIMESTEP", tstep_nums );
220  opts.get_reals_option( "TIMEVAL", tstep_vals );
221 
222  rval = opts.get_null_option( "NOMESH" );
223  if( MB_SUCCESS == rval ) noMesh = true;
224 
225  rval = opts.get_null_option( "SPECTRAL_MESH" );
226  if( MB_SUCCESS == rval ) spectralMesh = true;
227 
228  rval = opts.get_null_option( "NO_MIXED_ELEMENTS" );
229  if( MB_SUCCESS == rval ) noMixedElements = true;
230 
231  rval = opts.get_null_option( "NO_EDGES" );
232  if( MB_SUCCESS == rval ) noEdges = true;
233 
234  rval = opts.get_null_option( "NO_CULLING" ); // used now only for domain nc convention
235  if( MB_SUCCESS == rval ) culling = false;
236 
237  rval = opts.get_null_option( "REPARTITION" ); // used now only for domain nc, to repartition with zoltan
238  if( MB_SUCCESS == rval ) repartition = true;
239 
240  if( 2 <= dbgOut.get_verbosity() )
241  {
242  if( !var_names.empty() )
243  {
244  std::cerr << "Variables requested: ";
245  for( unsigned int i = 0; i < var_names.size(); i++ )
246  std::cerr << var_names[i];
247  std::cerr << std::endl;
248  }
249 
250  if( !tstep_nums.empty() )
251  {
252  std::cerr << "Timesteps requested: ";
253  for( unsigned int i = 0; i < tstep_nums.size(); i++ )
254  std::cerr << tstep_nums[i];
255  std::cerr << std::endl;
256  }
257 
258  if( !tstep_vals.empty() )
259  {
260  std::cerr << "Time vals requested: ";
261  for( unsigned int i = 0; i < tstep_vals.size(); i++ )
262  std::cerr << tstep_vals[i];
263  std::cerr << std::endl;
264  }
265  }
266 
267  rval = opts.get_int_option( "GATHER_SET", 0, gatherSetRank );
268  if( MB_TYPE_OUT_OF_RANGE == rval )
269  {
270  MB_SET_ERR( rval, "Invalid value for GATHER_SET option" );
271  }
272 
273  rval = opts.get_int_option( "TIMESTEPBASE", 0, tStepBase );
274  if( MB_TYPE_OUT_OF_RANGE == rval )
275  {
276  MB_SET_ERR( rval, "Invalid value for TIMESTEPBASE option" );
277  }
278 
279  rval = opts.get_int_option( "TRIVIAL_PARTITION_SHIFT", 1, trivialPartitionShift );
280  if( MB_TYPE_OUT_OF_RANGE == rval )
281  {
282  MB_SET_ERR( rval, "Invalid value for TRIVIAL_PARTITION_SHIFT option" );
283  }
284 
285 #ifdef MOAB_HAVE_MPI
286  isParallel = ( opts.match_option( "PARALLEL", "READ_PART" ) != MB_ENTITY_NOT_FOUND );
287 
288  if( !isParallel )
289  // Return success here, since rval still has _NOT_FOUND from not finding option
290  // in this case, myPcomm will be NULL, so it can never be used; always check for isParallel
291  // before any use for myPcomm
292  return MB_SUCCESS;
293 
294  int pcomm_no = 0;
295  rval = opts.get_int_option( "PARALLEL_COMM", pcomm_no );
296  if( MB_TYPE_OUT_OF_RANGE == rval )
297  {
298  MB_SET_ERR( rval, "Invalid value for PARALLEL_COMM option" );
299  }
300  myPcomm = ParallelComm::get_pcomm( mbImpl, pcomm_no );
301  if( 0 == myPcomm )
302  {
303  myPcomm = new ParallelComm( mbImpl, MPI_COMM_WORLD );
304  }
305  const int rank = myPcomm->proc_config().proc_rank();
306  dbgOut.set_rank( rank );
307 
308  int dum;
309  rval = opts.match_option( "PARTITION_METHOD", ScdParData::PartitionMethodNames, dum );
310  if( MB_FAILURE == rval )
311  {
312  MB_SET_ERR( rval, "Unknown partition method specified" );
313  }
314  else if( MB_ENTITY_NOT_FOUND == rval )
316  else
317  partMethod = dum;
318 #endif
319 
320  return MB_SUCCESS;
321 }
322 
324 {
325  dbgOut.tprint( 1, "Reading header...\n" );
326 
327  // Get the global attributes
328  int numgatts;
329  int success;
330  success = NCFUNC( inq_natts )( fileId, &numgatts );
331  if( success ) MB_SET_ERR( MB_FAILURE, "Couldn't get number of global attributes" );
332 
333  // Read attributes into globalAtts
334  ErrorCode result = get_attributes( NC_GLOBAL, numgatts, globalAtts );
335  MB_CHK_SET_ERR( result, "Trouble getting global attributes" );
336  dbgOut.tprintf( 1, "Read %u attributes\n", (unsigned int)globalAtts.size() );
337 
338  // Read in dimensions into dimNames and dimLens
339  result = get_dimensions( fileId, dimNames, dimLens );
340  MB_CHK_SET_ERR( result, "Trouble getting dimensions" );
341  dbgOut.tprintf( 1, "Read %u dimensions\n", (unsigned int)dimNames.size() );
342 
343  // Read in variables into varInfo
344  result = get_variables();
345  MB_CHK_SET_ERR( result, "Trouble getting variables" );
346  dbgOut.tprintf( 1, "Read %u variables\n", (unsigned int)varInfo.size() );
347 
348  return MB_SUCCESS;
349 }
350 
351 ErrorCode ReadNC::get_attributes( int var_id, int num_atts, std::map< std::string, AttData >& atts, const char* prefix )
352 {
353  char dum_name[120];
354 
355  for( int i = 0; i < num_atts; i++ )
356  {
357  // Get the name
358  int success = NCFUNC( inq_attname )( fileId, var_id, i, dum_name );
359  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting attribute name" );
360 
361  AttData& data = atts[std::string( dum_name )];
362  data.attName = std::string( dum_name );
363  success = NCFUNC( inq_att )( fileId, var_id, dum_name, &data.attDataType, &data.attLen );
364  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting info for attribute " << data.attName );
365  data.attVarId = var_id;
366 
367  dbgOut.tprintf( 2, "%sAttribute %s: length=%u, varId=%d, type=%d\n", ( prefix ? prefix : "" ),
368  data.attName.c_str(), (unsigned int)data.attLen, data.attVarId, data.attDataType );
369  }
370 
371  return MB_SUCCESS;
372 }
373 
374 ErrorCode ReadNC::get_dimensions( int file_id, std::vector< std::string >& dim_names, std::vector< int >& dim_lens )
375 {
376  // Get the number of dimensions
377  int num_dims;
378  int success = NCFUNC( inq_ndims )( file_id, &num_dims );
379  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting number of dimensions" );
380 
381  if( num_dims > NC_MAX_DIMS )
382  {
383  MB_SET_ERR( MB_FAILURE,
384  "ReadNC: File contains " << num_dims << " dims but NetCDF library supports only " << NC_MAX_DIMS );
385  }
386 
387  char dim_name[NC_MAX_NAME + 1];
388  NCDF_SIZE dim_len;
389  dim_names.resize( num_dims );
390  dim_lens.resize( num_dims );
391 
392  for( int i = 0; i < num_dims; i++ )
393  {
394  success = NCFUNC( inq_dim )( file_id, i, dim_name, &dim_len );
395  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting dimension info" );
396 
397  dim_names[i] = std::string( dim_name );
398  dim_lens[i] = dim_len;
399 
400  dbgOut.tprintf( 2, "Dimension %s, length=%u\n", dim_name, (unsigned int)dim_len );
401  }
402 
403  return MB_SUCCESS;
404 }
405 
407 {
408  // First cache the number of time steps
409  std::vector< std::string >::iterator vit = std::find( dimNames.begin(), dimNames.end(), "time" );
410  if( vit == dimNames.end() ) vit = std::find( dimNames.begin(), dimNames.end(), "t" );
411 
412  int ntimes = 0;
413  if( vit != dimNames.end() ) ntimes = dimLens[vit - dimNames.begin()];
414  if( !ntimes ) ntimes = 1;
415 
416  // Get the number of variables
417  int num_vars;
418  int success = NCFUNC( inq_nvars )( fileId, &num_vars );
419  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting number of variables" );
420 
421  if( num_vars > NC_MAX_VARS )
422  {
423  MB_SET_ERR( MB_FAILURE,
424  "ReadNC: File contains " << num_vars << " vars but NetCDF library supports only " << NC_MAX_VARS );
425  }
426 
427  char var_name[NC_MAX_NAME + 1];
428  int var_ndims;
429 
430  for( int i = 0; i < num_vars; i++ )
431  {
432  // Get the name first, so we can allocate a map iterate for this var
433  success = NCFUNC( inq_varname )( fileId, i, var_name );
434  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting variable name" );
435  VarData& data = varInfo[std::string( var_name )];
436  data.varName = std::string( var_name );
437  data.varId = i;
438  data.varTags.resize( ntimes, 0 );
439 
440  // Get the data type
441  success = NCFUNC( inq_vartype )( fileId, i, &data.varDataType );
442  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting data type for variable " << data.varName );
443 
444  // Get the number of dimensions, then the dimensions
445  success = NCFUNC( inq_varndims )( fileId, i, &var_ndims );
446  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting number of dims for variable " << data.varName );
447  data.varDims.resize( var_ndims );
448 
449  success = NCFUNC( inq_vardimid )( fileId, i, &data.varDims[0] );
450  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting dimensions for variable " << data.varName );
451 
452  // Finally, get the number of attributes, then the attributes
453  success = NCFUNC( inq_varnatts )( fileId, i, &data.numAtts );
454  if( success ) MB_SET_ERR( MB_FAILURE, "Trouble getting number of dims for variable " << data.varName );
455 
456  // Print debug info here so attribute info comes afterwards
457  dbgOut.tprintf( 2, "Variable %s: Id=%d, numAtts=%d, datatype=%d, num_dims=%u\n", data.varName.c_str(),
458  data.varId, data.numAtts, data.varDataType, (unsigned int)data.varDims.size() );
459 
460  MB_CHK_SET_ERR( get_attributes( i, data.numAtts, data.varAtts, " " ),
461  "Trouble getting attributes for variable " << data.varName );
462  }
463 
464  return MB_SUCCESS;
465 }
466 
468  const char*,
469  const FileOptions&,
470  std::vector< int >&,
471  const SubsetList* )
472 {
473  return MB_FAILURE;
474 }
475 
476 } // namespace moab