Mesh Oriented datABase  (version 5.5.1)
An array-based unstructured mesh library
NCHelper.cpp
Go to the documentation of this file.
1 #include "NCHelper.hpp"
2 #include "NCHelperEuler.hpp"
3 #include "NCHelperFV.hpp"
4 #include "NCHelperDomain.hpp"
5 #include "NCHelperScrip.hpp"
6 #include "NCHelperHOMME.hpp"
7 #include "NCHelperMPAS.hpp"
8 #include "NCHelperGCRM.hpp"
9 #include "NCHelperESMF.hpp"
10 
11 #include <sstream>
12 
13 #include "MBTagConventions.hpp"
14 
15 #ifdef WIN32
16 #ifdef size_t
17 #undef size_t
18 #endif
19 #endif
20 
21 namespace moab
22 {
23 
25 {
26  // Check if CF convention is being followed
27  bool is_CF = false;
28 
29  std::map< std::string, ReadNC::AttData >& globalAtts = readNC->globalAtts;
30  std::map< std::string, ReadNC::AttData >::iterator attIt = globalAtts.find( "conventions" );
31  if( attIt == globalAtts.end() ) attIt = globalAtts.find( "Conventions" );
32 
33  if( attIt != globalAtts.end() )
34  {
35  unsigned int sz = attIt->second.attLen;
36  std::string att_data;
37  att_data.resize( sz + 1 );
38  att_data[sz] = '\000';
39  int success =
40  NCFUNC( get_att_text )( fileId, attIt->second.attVarId, attIt->second.attName.c_str(), &att_data[0] );
41  if( 0 == success && att_data.find( "CF" ) != std::string::npos ) is_CF = true;
42  }
43 
44  // Depending on the format, update FileOptions as well
45  if( NCHelperMPAS::can_read_file( readNC ) )
47  else if( NCHelperScrip::can_read_file( readNC, fileId ) )
49  else if( NCHelperESMF::can_read_file( readNC ) )
51  else if( NCHelperDomain::can_read_file( readNC, fileId ) ) // && is_CF )
53  else if( NCHelperHOMME::can_read_file( readNC, fileId ) )
55  else if( NCHelperEuler::can_read_file( readNC, fileId ) && is_CF )
57  else if( NCHelperGCRM::can_read_file( readNC ) )
59  else if( NCHelperFV::can_read_file( readNC, fileId ) && is_CF )
60  return ReadNC::NC_FORMAT_FV;
61  else // Unknown NetCDF grid (will fill this in later for POP, CICE and CLM)
63 }
64 
65 //! Get appropriate file read options depending on the format of the NC file
67 {
68  switch( format )
69  {
70  case moab::ReadNC::NC_FORMAT_GCRM: // GCRM format reader
71  case moab::ReadNC::NC_FORMAT_MPAS: // MPAS format reader
72  return "PARALLEL=READ_PART;PARTITION_METHOD=RCBZOLTAN;"
73  "PARALLEL_RESOLVE_SHARED_ENTS;NO_EDGES;NO_MIXED_ELEMENTS;VARIABLE=;";
74  case moab::ReadNC::NC_FORMAT_SCRIP: // SCRIP format reader
75  return "PARALLEL=READ_PART;PARTITION_METHOD=RCBZOLTAN;";
76  case moab::ReadNC::NC_FORMAT_ESMF: // ESMF unstructured format reader
77  return "PARALLEL=READ_PART;PARTITION_METHOD=RCBZOLTAN;PARALLEL_RESOLVE_SHARED_ENTS;VARIABLE=;";
78  case moab::ReadNC::NC_FORMAT_DOMAIN: // Climate Domain reader
79  return "PARALLEL=READ_PART;PARTITION_METHOD=SQIJ;VARIABLE=;";
80  case moab::ReadNC::NC_FORMAT_HOMME: // HOMME format reader
81  return "PARALLEL=READ_PART;PARTITION_METHOD=TRIVIAL;PARALLEL_RESOLVE_SHARED_ENTS;";
82  case moab::ReadNC::NC_FORMAT_EULER: // Euler format reader
83  case moab::ReadNC::NC_FORMAT_FV: // FV climate format reader
84  return "PARALLEL=READ_PART;PARTITION;PARALLEL_RESOLVE_SHARED_ENTS;"
85  "PARTITION_METHOD=SQIJ;VARIABLE=;";
86  default:
87  return "PARALLEL=READ_PART;PARTITION_METHOD=RCBZOLTAN;PARALLEL_RESOLVE_SHARED_ENTS;";
88  }
89 }
90 
91 NCHelper* NCHelper::get_nc_helper( ReadNC* readNC, int fileId, const FileOptions& opts, EntityHandle fileSet )
92 {
93  ReadNC::NCFormatType nctype = NCHelper::get_nc_format( readNC, fileId );
94 
95  switch( nctype )
96  {
97  case ReadNC::NC_FORMAT_MPAS: // MPAS format reader
98  return new( std::nothrow ) NCHelperMPAS( readNC, fileId, opts, fileSet );
99  case ReadNC::NC_FORMAT_SCRIP: // SCRIP format reader
100  // SCRIP can be CF or non CF, if it comes from MPAS :)
101  return new( std::nothrow ) NCHelperScrip( readNC, fileId, opts, fileSet );
102  case ReadNC::NC_FORMAT_ESMF: // ESMF unstructured format reader
103  return new( std::nothrow ) NCHelperESMF( readNC, fileId, opts, fileSet );
104  case ReadNC::NC_FORMAT_DOMAIN: // Climate Domain reader
105  return new( std::nothrow ) NCHelperDomain( readNC, fileId, opts, fileSet );
106  case ReadNC::NC_FORMAT_HOMME: // HOMME format reader
107  // For a HOMME connectivity file, there might be no CF convention
108  return new( std::nothrow ) NCHelperHOMME( readNC, fileId, opts, fileSet );
109  case ReadNC::NC_FORMAT_GCRM: // GCRM format reader
110  return new( std::nothrow ) NCHelperGCRM( readNC, fileId, opts, fileSet );
111  case ReadNC::NC_FORMAT_EULER: // Euler format reader
112  return new( std::nothrow ) NCHelperEuler( readNC, fileId, opts, fileSet );
113  case ReadNC::NC_FORMAT_FV: // FV climate format reader
114  return new( std::nothrow ) NCHelperFV( readNC, fileId, opts, fileSet );
115  default: // Unknown NetCDF grid (will fill this in later for POP, CICE and CLM)
116  return nullptr;
117  }
118 }
119 
120 ErrorCode NCHelper::create_conventional_tags( const std::vector< int >& tstep_nums )
121 {
122  Interface*& mbImpl = _readNC->mbImpl;
123  std::vector< std::string >& dimNames = _readNC->dimNames;
124  std::vector< int >& dimLens = _readNC->dimLens;
125  std::map< std::string, ReadNC::AttData >& globalAtts = _readNC->globalAtts;
126  std::map< std::string, ReadNC::VarData >& varInfo = _readNC->varInfo;
127  DebugOutput& dbgOut = _readNC->dbgOut;
128  int& partMethod = _readNC->partMethod;
129  ScdInterface* scdi = _readNC->scdi;
130 
131  ErrorCode rval;
132  std::string tag_name;
133 
134  // <__NUM_DIMS>
135  Tag numDimsTag = 0;
136  tag_name = "__NUM_DIMS";
137  int numDims = dimNames.size();
138  rval = mbImpl->tag_get_handle( tag_name.c_str(), 1, MB_TYPE_INTEGER, numDimsTag, MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
139  rval = mbImpl->tag_set_data( numDimsTag, &_fileSet, 1, &numDims );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
140  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
141 
142  // <__NUM_VARS>
143  Tag numVarsTag = 0;
144  tag_name = "__NUM_VARS";
145  int numVars = varInfo.size();
146  rval = mbImpl->tag_get_handle( tag_name.c_str(), 1, MB_TYPE_INTEGER, numVarsTag, MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
147  rval = mbImpl->tag_set_data( numVarsTag, &_fileSet, 1, &numVars );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
148  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
149 
150  // <__DIM_NAMES>
151  Tag dimNamesTag = 0;
152  tag_name = "__DIM_NAMES";
153  std::string dimnames;
154  unsigned int dimNamesSz = dimNames.size();
155  for( unsigned int i = 0; i != dimNamesSz; i++ )
156  {
157  dimnames.append( dimNames[i] );
158  dimnames.push_back( '\0' );
159  }
160  int dimnamesSz = dimnames.size();
161  rval = mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, dimNamesTag,
162  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
163  const void* ptr = dimnames.c_str();
164  rval = mbImpl->tag_set_by_ptr( dimNamesTag, &_fileSet, 1, &ptr, &dimnamesSz );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
165  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
166 
167  // <__DIM_LENS>
168  Tag dimLensTag = 0;
169  tag_name = "__DIM_LENS";
170  int dimLensSz = dimLens.size();
171  rval = mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_INTEGER, dimLensTag,
172  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
173  ptr = &( dimLens[0] );
174  rval = mbImpl->tag_set_by_ptr( dimLensTag, &_fileSet, 1, &ptr, &dimLensSz );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
175  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
176 
177  // <__VAR_NAMES>
178  Tag varNamesTag = 0;
179  tag_name = "__VAR_NAMES";
180  std::string varnames;
181  std::map< std::string, ReadNC::VarData >::iterator mapIter;
182  for( mapIter = varInfo.begin(); mapIter != varInfo.end(); ++mapIter )
183  {
184  varnames.append( mapIter->first );
185  varnames.push_back( '\0' );
186  }
187  int varnamesSz = varnames.size();
188  rval = mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, varNamesTag,
189  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
190  ptr = varnames.c_str();
191  rval = mbImpl->tag_set_by_ptr( varNamesTag, &_fileSet, 1, &ptr, &varnamesSz );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
192  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
193 
194  // __<dim_name>_LOC_MINMAX (for time)
195  for( unsigned int i = 0; i != dimNamesSz; i++ )
196  {
197  if( dimNames[i] == "time" || dimNames[i] == "Time" || dimNames[i] == "t" )
198  {
199  // some files might have Time dimension as 0, it will not appear in any
200  // variables, so skip it
201  if( nTimeSteps == 0 ) continue;
202  std::stringstream ss_tag_name;
203  ss_tag_name << "__" << dimNames[i] << "_LOC_MINMAX";
204  tag_name = ss_tag_name.str();
205  Tag tagh = 0;
206  std::vector< int > val( 2, 0 );
207  val[0] = 0;
208  val[1] = nTimeSteps - 1;
209  rval = mbImpl->tag_get_handle( tag_name.c_str(), 2, MB_TYPE_INTEGER, tagh, MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
210  rval = mbImpl->tag_set_data( tagh, &_fileSet, 1, &val[0] );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
211  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
212  }
213  }
214 
215  // __<dim_name>_LOC_VALS (for time)
216  for( unsigned int i = 0; i != dimNamesSz; i++ )
217  {
218  if( dimNames[i] == "time" || dimNames[i] == "Time" || dimNames[i] == "t" )
219  {
220  std::vector< int > val;
221  if( !tstep_nums.empty() )
222  val = tstep_nums;
223  else
224  {
225  // some files might have Time dimension as 0, it will not appear in any
226  // variables, so skip it
227  if( tVals.empty() ) continue;
228  val.resize( tVals.size() );
229  for( unsigned int j = 0; j != tVals.size(); j++ )
230  val[j] = j;
231  }
232  Tag tagh = 0;
233  std::stringstream ss_tag_name;
234  ss_tag_name << "__" << dimNames[i] << "_LOC_VALS";
235  tag_name = ss_tag_name.str();
236  rval = mbImpl->tag_get_handle( tag_name.c_str(), val.size(), MB_TYPE_INTEGER, tagh,
237  MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
238  rval = mbImpl->tag_set_data( tagh, &_fileSet, 1, &val[0] );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
239  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
240  }
241  }
242 
243  // __<var_name>_DIMS
244  for( mapIter = varInfo.begin(); mapIter != varInfo.end(); ++mapIter )
245  {
246  Tag varNamesDimsTag = 0;
247  std::stringstream ss_tag_name;
248  ss_tag_name << "__" << mapIter->first << "_DIMS";
249  tag_name = ss_tag_name.str();
250  unsigned int varDimSz = varInfo[mapIter->first].varDims.size();
251  if( varDimSz == 0 ) continue;
252  std::vector< Tag > varDimTags( varDimSz );
253  for( unsigned int i = 0; i != varDimSz; i++ )
254  {
255  Tag tmptag = 0;
256  std::string tmptagname = dimNames[varInfo[mapIter->first].varDims[i]];
257  rval = mbImpl->tag_get_handle( tmptagname.c_str(), 0, MB_TYPE_OPAQUE, tmptag, MB_TAG_ANY );MB_CHK_SET_ERR( rval, "Trouble getting tag " << tmptagname );
258  varDimTags[i] = tmptag;
259  }
260  // rval = mbImpl->tag_get_handle(tag_name.c_str(), varDimSz, MB_TYPE_HANDLE,
261  // varNamesDimsTag, MB_TAG_SPARSE | MB_TAG_CREAT); We should not use MB_TYPE_HANDLE for Tag
262  // here. Tag is a pointer, which is 4 bytes on 32 bit machines and 8 bytes on 64 bit
263  // machines. Normally, entity handle is 8 bytes on 64 bit machines, but it can also be
264  // configured to 4 bytes.
265  rval = mbImpl->tag_get_handle( tag_name.c_str(), varDimSz * sizeof( Tag ), MB_TYPE_OPAQUE, varNamesDimsTag,
266  MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
267  rval = mbImpl->tag_set_data( varNamesDimsTag, &_fileSet, 1, &( varDimTags[0] ) );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
268  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
269  }
270 
271  // <PARTITION_METHOD>
272  Tag part_tag = scdi->part_method_tag();
273  if( !part_tag ) MB_SET_ERR( MB_FAILURE, "Trouble getting PARTITION_METHOD tag" );
274  rval = mbImpl->tag_set_data( part_tag, &_fileSet, 1, &partMethod );MB_CHK_SET_ERR( rval, "Trouble setting data to PARTITION_METHOD tag" );
275  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
276 
277  // <__GLOBAL_ATTRIBS>
278  tag_name = "__GLOBAL_ATTRIBS";
279  Tag globalAttTag = 0;
280  rval = mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, globalAttTag,
281  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
282  std::string gattVal;
283  std::vector< int > gattLen;
284  rval = create_attrib_string( globalAtts, gattVal, gattLen );MB_CHK_SET_ERR( rval, "Trouble creating global attribute string" );
285  const void* gattptr = gattVal.c_str();
286  int globalAttSz = gattVal.size();
287  rval = mbImpl->tag_set_by_ptr( globalAttTag, &_fileSet, 1, &gattptr, &globalAttSz );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
288  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
289 
290  // <__GLOBAL_ATTRIBS_LEN>
291  tag_name = "__GLOBAL_ATTRIBS_LEN";
292  Tag globalAttLenTag = 0;
293  if( gattLen.size() == 0 ) gattLen.push_back( 0 );
294  rval = mbImpl->tag_get_handle( tag_name.c_str(), gattLen.size(), MB_TYPE_INTEGER, globalAttLenTag,
295  MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
296  rval = mbImpl->tag_set_data( globalAttLenTag, &_fileSet, 1, &gattLen[0] );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
297  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
298 
299  // __<var_name>_ATTRIBS and __<var_name>_ATTRIBS_LEN
300  for( mapIter = varInfo.begin(); mapIter != varInfo.end(); ++mapIter )
301  {
302  std::stringstream ssTagName;
303  ssTagName << "__" << mapIter->first << "_ATTRIBS";
304  tag_name = ssTagName.str();
305  Tag varAttTag = 0;
306  rval = mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, varAttTag,
307  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
308 
309  std::string varAttVal;
310  std::vector< int > varAttLen;
311  if( mapIter->second.numAtts < 1 )
312  {
313  if( dummyVarNames.find( mapIter->first ) != dummyVarNames.end() )
314  {
315  // This variable is a dummy coordinate variable
316  varAttVal = "DUMMY_VAR";
317  }
318  else
319  {
320  // This variable has no attributes
321  varAttVal = "NO_ATTRIBS";
322  }
323  }
324  else
325  {
326  rval = create_attrib_string( mapIter->second.varAtts, varAttVal, varAttLen );MB_CHK_SET_ERR( rval, "Trouble creating attribute string for variable " << mapIter->first );
327  }
328  const void* varAttPtr = varAttVal.c_str();
329  int varAttSz = varAttVal.size();
330  if( 0 == varAttSz ) varAttSz = 1;
331  rval = mbImpl->tag_set_by_ptr( varAttTag, &_fileSet, 1, &varAttPtr, &varAttSz );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
332  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
333 
334  ssTagName << "_LEN";
335  tag_name = ssTagName.str();
336  Tag varAttLenTag = 0;
337  if( 0 == varAttLen.size() ) varAttLen.push_back( 0 );
338  rval = mbImpl->tag_get_handle( tag_name.c_str(), varAttLen.size(), MB_TYPE_INTEGER, varAttLenTag,
339  MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
340  rval = mbImpl->tag_set_data( varAttLenTag, &_fileSet, 1, &varAttLen[0] );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
341  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
342  }
343 
344  // <__VAR_NAMES_LOCATIONS>
345  tag_name = "__VAR_NAMES_LOCATIONS";
346  Tag varNamesLocsTag = 0;
347  std::vector< int > varNamesLocs( varInfo.size() );
348  rval = mbImpl->tag_get_handle( tag_name.c_str(), varNamesLocs.size(), MB_TYPE_INTEGER, varNamesLocsTag,
349  MB_TAG_CREAT | MB_TAG_SPARSE );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
350  for( mapIter = varInfo.begin(); mapIter != varInfo.end(); ++mapIter )
351  {
352  varNamesLocs[std::distance( varInfo.begin(), mapIter )] = mapIter->second.entLoc;
353  }
354  rval = mbImpl->tag_set_data( varNamesLocsTag, &_fileSet, 1, &varNamesLocs[0] );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
355  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
356 
357  // <__MESH_TYPE>
358  Tag meshTypeTag = 0;
359  tag_name = "__MESH_TYPE";
360  std::string meshTypeName = get_mesh_type_name();
361 
362  rval = mbImpl->tag_get_handle( tag_name.c_str(), 0, MB_TYPE_OPAQUE, meshTypeTag,
363  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating conventional tag " << tag_name );
364  ptr = meshTypeName.c_str();
365  int leng = meshTypeName.size();
366  rval = mbImpl->tag_set_by_ptr( meshTypeTag, &_fileSet, 1, &ptr, &leng );MB_CHK_SET_ERR( rval, "Trouble setting data to conventional tag " << tag_name );
367  dbgOut.tprintf( 2, "Conventional tag %s created\n", tag_name.c_str() );
368 
369  return MB_SUCCESS;
370 }
371 
373 {
374  Interface*& mbImpl = _readNC->mbImpl;
375  std::vector< std::string >& dimNames = _readNC->dimNames;
376 
377  ErrorCode rval;
378 
379  // The time tag might be a dummy one (e.g. 'Time' for MPAS)
380  std::string time_tag_name = dimNames[tDim];
381  if( dummyVarNames.find( time_tag_name ) != dummyVarNames.end() ) return MB_SUCCESS;
382 
383  Tag time_tag = 0;
384  const void* data = NULL;
385  int time_tag_size = 0;
386  rval = mbImpl->tag_get_handle( time_tag_name.c_str(), 0, MB_TYPE_DOUBLE, time_tag, MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble getting tag " << time_tag_name );
387  rval = mbImpl->tag_get_by_ptr( time_tag, &_fileSet, 1, &data, &time_tag_size );MB_CHK_SET_ERR( rval, "Trouble getting data of tag " << time_tag_name );
388  const double* time_tag_vals = static_cast< const double* >( data );
389 
390  // Merge tVals (read from current file) to existing time tag
391  // Assume that time_tag_vals and tVals are both sorted
392  std::vector< double > merged_time_vals;
393  merged_time_vals.reserve( time_tag_size + nTimeSteps );
394  int i = 0;
395  int j = 0;
396 
397  // Merge time values from time_tag_vals and tVals
398  while( i < time_tag_size && j < nTimeSteps )
399  {
400  if( time_tag_vals[i] < tVals[j] )
401  merged_time_vals.push_back( time_tag_vals[i++] );
402  else
403  merged_time_vals.push_back( tVals[j++] );
404  }
405 
406  // Append remaining time values of time_tag_vals (if any)
407  while( i < time_tag_size )
408  merged_time_vals.push_back( time_tag_vals[i++] );
409 
410  // Append remaining time values of tVals (if any)
411  while( j < nTimeSteps )
412  merged_time_vals.push_back( tVals[j++] );
413 
414  data = &merged_time_vals[0];
415  time_tag_size = merged_time_vals.size();
416  rval = mbImpl->tag_set_by_ptr( time_tag, &_fileSet, 1, &data, &time_tag_size );MB_CHK_SET_ERR( rval, "Trouble setting data to tag " << time_tag_name );
417 
418  return MB_SUCCESS;
419 }
420 
421 ErrorCode NCHelper::read_variables_setup( std::vector< std::string >& var_names,
422  std::vector< int >& tstep_nums,
423  std::vector< ReadNC::VarData >& vdatas,
424  std::vector< ReadNC::VarData >& vsetdatas )
425 {
426  std::map< std::string, ReadNC::VarData >& varInfo = _readNC->varInfo;
427  std::vector< std::string >& dimNames = _readNC->dimNames;
428 
429  std::map< std::string, ReadNC::VarData >::iterator mit;
430 
431  // If empty read them all (except ignored variables)
432  if( var_names.empty() )
433  {
434  for( mit = varInfo.begin(); mit != varInfo.end(); ++mit )
435  {
436  ReadNC::VarData vd = ( *mit ).second;
437 
438  // If read all variables at once, skip ignored ones
439  if( ignoredVarNames.find( vd.varName ) != ignoredVarNames.end() ) continue;
440 
441  // Coordinate variables (include dummy ones) were read to the file set by default
442  if( std::find( dimNames.begin(), dimNames.end(), vd.varName ) != dimNames.end() ) continue;
443 
444  if( vd.entLoc == ReadNC::ENTLOCSET )
445  vsetdatas.push_back( vd );
446  else
447  vdatas.push_back( vd );
448  }
449  }
450  else
451  {
452  // Read specified variables (might include ignored ones)
453  for( unsigned int i = 0; i < var_names.size(); i++ )
454  {
455  mit = varInfo.find( var_names[i] );
456  if( mit != varInfo.end() )
457  {
458  ReadNC::VarData vd = ( *mit ).second;
459 
460  // Upon creation of dummy coordinate variables, tag values have already been set
461  if( dummyVarNames.find( vd.varName ) != dummyVarNames.end() ) continue;
462 
463  if( vd.entLoc == ReadNC::ENTLOCSET )
464  vsetdatas.push_back( vd );
465  else
466  vdatas.push_back( vd );
467  }
468  else
469  {
470  MB_SET_ERR( MB_FAILURE, "Couldn't find specified variable " << var_names[i] );
471  }
472  }
473  }
474 
475  if( tstep_nums.empty() && nTimeSteps > 0 )
476  {
477  // No timesteps input, get them all
478  for( int i = 0; i < nTimeSteps; i++ )
479  tstep_nums.push_back( i );
480  }
481 
482  if( !tstep_nums.empty() )
483  {
484  for( unsigned int i = 0; i < vdatas.size(); i++ )
485  {
486  vdatas[i].varTags.resize( tstep_nums.size(), 0 );
487  vdatas[i].varDatas.resize( tstep_nums.size() );
488  // NC reader assumes that non-set variables always have timesteps
489  assert( std::find( vdatas[i].varDims.begin(), vdatas[i].varDims.end(), tDim ) != vdatas[i].varDims.end() );
490  vdatas[i].has_tsteps = true;
491  }
492 
493  for( unsigned int i = 0; i < vsetdatas.size(); i++ )
494  {
495  if( ( std::find( vsetdatas[i].varDims.begin(), vsetdatas[i].varDims.end(), tDim ) !=
496  vsetdatas[i].varDims.end() ) &&
497  ( vsetdatas[i].varName != dimNames[tDim] ) )
498  {
499  // Set variables with timesteps: e.g. xtime(Time) or xtime(Time, StrLen)
500  vsetdatas[i].varTags.resize( tstep_nums.size(), 0 );
501  vsetdatas[i].varDatas.resize( tstep_nums.size() );
502  vsetdatas[i].has_tsteps = true;
503  }
504  else
505  {
506  // Set variables without timesteps: no time dimension, or time itself
507  vsetdatas[i].varTags.resize( 1, 0 );
508  vsetdatas[i].varDatas.resize( 1 );
509  vsetdatas[i].has_tsteps = false;
510  }
511  }
512  }
513 
514  return MB_SUCCESS;
515 }
516 
517 ErrorCode NCHelper::read_variables_to_set( std::vector< ReadNC::VarData >& vdatas, std::vector< int >& tstep_nums )
518 {
519  Interface*& mbImpl = _readNC->mbImpl;
520  DebugOutput& dbgOut = _readNC->dbgOut;
521 
522  ErrorCode rval = read_variables_to_set_allocate( vdatas, tstep_nums );MB_CHK_SET_ERR( rval, "Trouble allocating space to read set variables" );
523 
524  // Finally, read into that space
525  int success;
526  for( unsigned int i = 0; i < vdatas.size(); i++ )
527  {
528  // Note, for set variables without timesteps, loop one time and then break
529  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
530  {
531  void* data = vdatas[i].varDatas[t];
532 
533  // Set variables with timesteps, e.g. xtime(Time) or xtime(Time, StrLen)
534  if( vdatas[i].has_tsteps )
535  {
536  // Set readStart for each timestep along time dimension
537  vdatas[i].readStarts[0] = tstep_nums[t];
538  }
539 
540  switch( vdatas[i].varDataType )
541  {
542  case NC_BYTE:
543  case NC_CHAR:
544  success = NCFUNCAG( _vara_text )( _fileId, vdatas[i].varId, &vdatas[i].readStarts[0],
545  &vdatas[i].readCounts[0], (char*)data );
546  if( success )
547  MB_SET_ERR( MB_FAILURE, "Failed to read byte/char data for variable " << vdatas[i].varName );
548  break;
549  case NC_SHORT:
550  case NC_INT:
551  success = NCFUNCAG( _vara_int )( _fileId, vdatas[i].varId, &vdatas[i].readStarts[0],
552  &vdatas[i].readCounts[0], (int*)data );
553  if( success )
554  MB_SET_ERR( MB_FAILURE, "Failed to read short/int data for variable " << vdatas[i].varName );
555  break;
556  case NC_INT64:
557  success = NCFUNCAG( _vara_long )( _fileId, vdatas[i].varId, &vdatas[i].readStarts[0],
558  &vdatas[i].readCounts[0], (long*)data );
559  if( success )
560  MB_SET_ERR( MB_FAILURE, "Failed to read long data for variable " << vdatas[i].varName );
561  break;
562  case NC_FLOAT:
563  case NC_DOUBLE:
564  success = NCFUNCAG( _vara_double )( _fileId, vdatas[i].varId, &vdatas[i].readStarts[0],
565  &vdatas[i].readCounts[0], (double*)data );
566  if( success )
567  MB_SET_ERR( MB_FAILURE, "Failed to read float/double data for variable " << vdatas[i].varName );
568  break;
569  default:
570  MB_SET_ERR( MB_FAILURE, "Unexpected data type for variable " << vdatas[i].varName );
571  }
572 
573  dbgOut.tprintf( 2, "Setting data for variable %s, time step %d\n", vdatas[i].varName.c_str(),
574  tstep_nums[t] );
575  rval = mbImpl->tag_set_by_ptr( vdatas[i].varTags[t], &_fileSet, 1, &data, &vdatas[i].sz );MB_CHK_SET_ERR( rval, "Trouble setting tag data for variable " << vdatas[i].varName );
576 
577  // Memory pointed by pointer data can be deleted, as tag_set_by_ptr() has already copied
578  // the tag values
579  switch( vdatas[i].varDataType )
580  {
581  case NC_BYTE:
582  case NC_CHAR:
583  delete[](char*)data;
584  break;
585  case NC_SHORT:
586  case NC_INT:
587  delete[](int*)data;
588  break;
589  case NC_INT64:
590  delete[](long*)data;
591  break;
592  case NC_FLOAT:
593  case NC_DOUBLE:
594  delete[](double*)data;
595  break;
596  default:
597  break;
598  }
599  vdatas[i].varDatas[t] = NULL;
600 
601  // Loop continues only for set variables with timesteps, e.g. xtime(Time) or xtime(Time,
602  // StrLen)
603  if( !vdatas[i].has_tsteps ) break;
604  }
605  }
606 
607  // Debug output, if requested
608  if( 1 == dbgOut.get_verbosity() )
609  {
610  dbgOut.printf( 1, "Read variables: %s", vdatas.begin()->varName.c_str() );
611  for( unsigned int i = 1; i < vdatas.size(); i++ )
612  dbgOut.printf( 1, ", %s ", vdatas[i].varName.c_str() );
613  dbgOut.tprintf( 1, "\n" );
614  }
615 
616  return rval;
617 }
618 
619 ErrorCode NCHelper::read_coordinate( const char* var_name, int lmin, int lmax, std::vector< double >& cvals )
620 {
621  std::map< std::string, ReadNC::VarData >& varInfo = _readNC->varInfo;
622  std::map< std::string, ReadNC::VarData >::iterator vmit = varInfo.find( var_name );
623  if( varInfo.end() == vmit ) MB_SET_ERR( MB_FAILURE, "Couldn't find variable " << var_name );
624 
625  assert( lmin >= 0 && lmax >= lmin );
626  NCDF_SIZE tstart = lmin;
627  NCDF_SIZE tcount = lmax - lmin + 1;
628  NCDF_DIFF dum_stride = 1;
629  int success;
630 
631  // Check size
632  if( (std::size_t)tcount != cvals.size() ) cvals.resize( tcount );
633 
634  // Check to make sure it's a float or double
635  switch( ( *vmit ).second.varDataType )
636  {
637  case NC_FLOAT:
638  case NC_DOUBLE:
639  // Read float as double
640  success =
641  NCFUNCAG( _vars_double )( _fileId, ( *vmit ).second.varId, &tstart, &tcount, &dum_stride, &cvals[0] );
642  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to read float/double data for variable " << var_name );
643  break;
644  default:
645  MB_SET_ERR( MB_FAILURE, "Unexpected data type for variable " << var_name );
646  }
647 
648  return MB_SUCCESS;
649 }
650 
651 ErrorCode NCHelper::get_tag_to_set( ReadNC::VarData& var_data, int tstep_num, Tag& tagh )
652 {
653  Interface*& mbImpl = _readNC->mbImpl;
654  DebugOutput& dbgOut = _readNC->dbgOut;
655  int& tStepBase = _readNC->tStepBase;
656 
657  if( tStepBase > 0 ) tstep_num += tStepBase;
658 
659  std::ostringstream tag_name;
660  if( var_data.has_tsteps )
661  tag_name << var_data.varName << tstep_num;
662  else
663  tag_name << var_data.varName;
664 
665  ErrorCode rval = MB_SUCCESS;
666  tagh = 0;
667  switch( var_data.varDataType )
668  {
669  case NC_BYTE:
670  case NC_CHAR:
671  rval = mbImpl->tag_get_handle( tag_name.str().c_str(), 0, MB_TYPE_OPAQUE, tagh,
672  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating tag " << tag_name.str() );
673  break;
674  case NC_SHORT:
675  case NC_INT:
676  case NC_INT64: // this is a big stretch; we should introduce LONG tag
677  rval = mbImpl->tag_get_handle( tag_name.str().c_str(), 0, MB_TYPE_INTEGER, tagh,
678  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating tag " << tag_name.str() );
679  break;
680  case NC_FLOAT:
681  case NC_DOUBLE:
682  rval = mbImpl->tag_get_handle( tag_name.str().c_str(), 0, MB_TYPE_DOUBLE, tagh,
683  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating tag " << tag_name.str() );
684  break;
685  default:
686  MB_SET_ERR( MB_FAILURE, "Unexpected data type for variable " << var_data.varName );
687  }
688 
689  dbgOut.tprintf( 2, "Tag %s created\n", tag_name.str().c_str() );
690 
691  return rval;
692 }
693 
694 ErrorCode NCHelper::get_tag_to_nonset( ReadNC::VarData& var_data, int tstep_num, Tag& tagh, int num_lev )
695 {
696  Interface*& mbImpl = _readNC->mbImpl;
697  DebugOutput& dbgOut = _readNC->dbgOut;
698  int& tStepBase = _readNC->tStepBase;
699 
700  if( tStepBase > 0 ) tstep_num += tStepBase;
701 
702  std::ostringstream tag_name;
703  tag_name << var_data.varName << tstep_num;
704 
705  ErrorCode rval = MB_SUCCESS;
706  tagh = 0;
707  switch( var_data.varDataType )
708  {
709  case NC_BYTE:
710  case NC_CHAR:
711  rval = mbImpl->tag_get_handle( tag_name.str().c_str(), num_lev, MB_TYPE_OPAQUE, tagh,
712  MB_TAG_DENSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating tag " << tag_name.str() );
713  break;
714  case NC_SHORT:
715  case NC_INT:
716  case NC_INT64:
717  rval = mbImpl->tag_get_handle( tag_name.str().c_str(), num_lev, MB_TYPE_INTEGER, tagh,
718  MB_TAG_DENSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating tag " << tag_name.str() );
719  break;
720  case NC_FLOAT:
721  case NC_DOUBLE:
722  rval = mbImpl->tag_get_handle( tag_name.str().c_str(), num_lev, MB_TYPE_DOUBLE, tagh,
723  MB_TAG_DENSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating tag " << tag_name.str() );
724  break;
725  default:
726  MB_SET_ERR( MB_FAILURE, "Unexpected data type for variable " << var_data.varName );
727  }
728 
729  dbgOut.tprintf( 2, "Tag %s created\n", tag_name.str().c_str() );
730 
731  return rval;
732 }
733 
734 ErrorCode NCHelper::create_attrib_string( const std::map< std::string, ReadNC::AttData >& attMap,
735  std::string& attVal,
736  std::vector< int >& attLen )
737 {
738  int success;
739  std::stringstream ssAtt;
740  unsigned int sz = 0;
741  std::map< std::string, ReadNC::AttData >::const_iterator attIt = attMap.begin();
742  for( ; attIt != attMap.end(); ++attIt )
743  {
744  ssAtt << attIt->second.attName;
745  ssAtt << '\0';
746  void* attData = NULL;
747  switch( attIt->second.attDataType )
748  {
749  case NC_BYTE:
750  case NC_CHAR:
751  sz = attIt->second.attLen;
752  attData = (char*)malloc( sz );
753  success = NCFUNC( get_att_text )( _fileId, attIt->second.attVarId, attIt->second.attName.c_str(),
754  (char*)attData );
755  if( success )
756  MB_SET_ERR( MB_FAILURE, "Failed to read byte/char data for attribute " << attIt->second.attName );
757  ssAtt << "char;";
758  break;
759  case NC_SHORT:
760  sz = attIt->second.attLen * sizeof( short );
761  attData = (short*)malloc( sz );
762  success = NCFUNC( get_att_short )( _fileId, attIt->second.attVarId, attIt->second.attName.c_str(),
763  (short*)attData );
764  if( success )
765  MB_SET_ERR( MB_FAILURE, "Failed to read short data for attribute " << attIt->second.attName );
766  ssAtt << "short;";
767  break;
768  case NC_INT:
769  sz = attIt->second.attLen * sizeof( int );
770  attData = (int*)malloc( sz );
771  success = NCFUNC( get_att_int )( _fileId, attIt->second.attVarId, attIt->second.attName.c_str(),
772  (int*)attData );
773  if( success )
774  MB_SET_ERR( MB_FAILURE, "Failed to read int data for attribute " << attIt->second.attName );
775  ssAtt << "int;";
776  break;
777  case NC_INT64: // be careful here
778  sz = attIt->second.attLen * sizeof( long );
779  attData = (long*)malloc( sz );
780  success = NCFUNC( get_att_long )( _fileId, attIt->second.attVarId, attIt->second.attName.c_str(),
781  (long*)attData );
782  if( success )
783  MB_SET_ERR( MB_FAILURE, "Failed to read int data for attribute " << attIt->second.attName );
784  ssAtt << "long;";
785  break;
786  case NC_FLOAT:
787  sz = attIt->second.attLen * sizeof( float );
788  attData = (float*)malloc( sz );
789  success = NCFUNC( get_att_float )( _fileId, attIt->second.attVarId, attIt->second.attName.c_str(),
790  (float*)attData );
791  if( success )
792  MB_SET_ERR( MB_FAILURE, "Failed to read float data for attribute " << attIt->second.attName );
793  ssAtt << "float;";
794  break;
795  case NC_DOUBLE:
796  sz = attIt->second.attLen * sizeof( double );
797  attData = (double*)malloc( sz );
798  success = NCFUNC( get_att_double )( _fileId, attIt->second.attVarId, attIt->second.attName.c_str(),
799  (double*)attData );
800  if( success )
801  MB_SET_ERR( MB_FAILURE, "Failed to read double data for attribute " << attIt->second.attName );
802  ssAtt << "double;";
803  break;
804  default:
805  MB_SET_ERR( MB_FAILURE, "Unexpected data type for attribute " << attIt->second.attName );
806  }
807  char* tmpc = (char*)attData;
808  for( unsigned int counter = 0; counter != sz; ++counter )
809  ssAtt << tmpc[counter];
810  free( attData );
811  ssAtt << ';';
812  attLen.push_back( ssAtt.str().size() - 1 );
813  }
814  attVal = ssAtt.str();
815 
816  return MB_SUCCESS;
817 }
818 
820 {
821  Interface*& mbImpl = _readNC->mbImpl;
822  std::vector< std::string >& dimNames = _readNC->dimNames;
823  std::vector< int >& dimLens = _readNC->dimLens;
824  std::map< std::string, ReadNC::VarData >& varInfo = _readNC->varInfo;
825  DebugOutput& dbgOut = _readNC->dbgOut;
826 
827  // Hack: look at all dimensions, and see if we have one that does not appear in the list of
828  // varInfo names Right now, candidates are from unstructured meshes, such as ncol (HOMME) and
829  // nCells (MPAS) For each of them, create a dummy coordinate variable with a sparse tag to store
830  // the dimension length
831  for( unsigned int i = 0; i < dimNames.size(); i++ )
832  {
833  // If there is a variable with this dimension name, skip
834  if( varInfo.find( dimNames[i] ) != varInfo.end() ) continue;
835 
836  // Create a dummy coordinate variable
837  int sizeTotalVar = varInfo.size();
838  std::string var_name( dimNames[i] );
839  ReadNC::VarData& data = varInfo[var_name];
840  data.varName = var_name;
841  data.varId = sizeTotalVar;
842  data.varTags.resize( 1, 0 );
843  data.varDataType = NC_INT;
844  data.varDims.resize( 1 );
845  data.varDims[0] = (int)i;
846  data.numAtts = 0;
847  data.entLoc = ReadNC::ENTLOCSET;
848  dummyVarNames.insert( var_name );
849  dbgOut.tprintf( 2, "Dummy coordinate variable created for dimension %s\n", var_name.c_str() );
850 
851  // Create a corresponding sparse tag
852  Tag tagh;
853  ErrorCode rval = mbImpl->tag_get_handle( var_name.c_str(), 0, MB_TYPE_INTEGER, tagh,
854  MB_TAG_CREAT | MB_TAG_SPARSE | MB_TAG_VARLEN );MB_CHK_SET_ERR( rval, "Trouble creating tag for dummy coordinate variable " << var_name );
855 
856  // Tag value is the dimension length
857  const void* ptr = &dimLens[i];
858  // Tag size is 1
859  int size = 1;
860  rval = mbImpl->tag_set_by_ptr( tagh, &_fileSet, 1, &ptr, &size );MB_CHK_SET_ERR( rval, "Trouble setting tag data for dummy coordinate variable " << var_name );
861 
862  dbgOut.tprintf( 2, "Sparse tag created for dimension %s\n", var_name.c_str() );
863  }
864 
865  return MB_SUCCESS;
866 }
867 
868 ErrorCode NCHelper::read_variables_to_set_allocate( std::vector< ReadNC::VarData >& vdatas,
869  std::vector< int >& tstep_nums )
870 {
871  std::vector< int >& dimLens = _readNC->dimLens;
872  DebugOutput& dbgOut = _readNC->dbgOut;
873 
874  ErrorCode rval = MB_SUCCESS;
875 
876  for( unsigned int i = 0; i < vdatas.size(); i++ )
877  {
878  // Set up readStarts and readCounts
879  if( vdatas[i].has_tsteps )
880  {
881  // First: time
882  vdatas[i].readStarts.push_back( 0 ); // This value is timestep dependent, will be set later
883  vdatas[i].readCounts.push_back( 1 );
884 
885  // Next: other dimensions
886  for( unsigned int idx = 1; idx != vdatas[i].varDims.size(); idx++ )
887  {
888  vdatas[i].readStarts.push_back( 0 );
889  vdatas[i].readCounts.push_back( dimLens[vdatas[i].varDims[idx]] );
890  }
891  }
892  else
893  {
894  if( vdatas[i].varDims.empty() )
895  {
896  // Scalar variable
897  vdatas[i].readStarts.push_back( 0 );
898  vdatas[i].readCounts.push_back( 1 );
899  }
900  else
901  {
902  for( unsigned int idx = 0; idx != vdatas[i].varDims.size(); idx++ )
903  {
904  vdatas[i].readStarts.push_back( 0 );
905  vdatas[i].readCounts.push_back( dimLens[vdatas[i].varDims[idx]] );
906  }
907  }
908  }
909 
910  // Get variable size
911  vdatas[i].sz = 1;
912  for( std::size_t idx = 0; idx != vdatas[i].readCounts.size(); idx++ )
913  vdatas[i].sz *= vdatas[i].readCounts[idx];
914 
915  // Note, for set variables without timesteps, loop one time and then break
916  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
917  {
918  dbgOut.tprintf( 2, "Reading variable %s, time step %d\n", vdatas[i].varName.c_str(), tstep_nums[t] );
919 
920  if( tstep_nums[t] >= dimLens[tDim] )
921  {
922  MB_SET_ERR( MB_INDEX_OUT_OF_RANGE, "Wrong value for timestep number " << tstep_nums[t] );
923  }
924 
925  // Get the tag to read into
926  if( !vdatas[i].varTags[t] )
927  {
928  rval = get_tag_to_set( vdatas[i], tstep_nums[t], vdatas[i].varTags[t] );MB_CHK_SET_ERR( rval, "Trouble getting tag to set variable " << vdatas[i].varName );
929  }
930 
931  switch( vdatas[i].varDataType )
932  {
933  case NC_BYTE:
934  case NC_CHAR:
935  vdatas[i].varDatas[t] = new char[vdatas[i].sz];
936  break;
937  case NC_SHORT:
938  case NC_INT:
939  vdatas[i].varDatas[t] = new int[vdatas[i].sz];
940  break;
941  case NC_INT64:
942  vdatas[i].varDatas[t] = new long[vdatas[i].sz];
943  break;
944  case NC_FLOAT:
945  case NC_DOUBLE:
946  vdatas[i].varDatas[t] = new double[vdatas[i].sz];
947  break;
948  default:
949  MB_SET_ERR( MB_FAILURE, "Unexpected data type for variable " << vdatas[i].varName );
950  }
951 
952  // Loop continues only for set variables with timesteps, e.g. xtime(Time) or xtime(Time,
953  // StrLen)
954  if( !vdatas[i].has_tsteps ) break;
955  }
956  }
957 
958  return rval;
959 }
960 
962 {
963  Interface*& mbImpl = _readNC->mbImpl;
964 
965  // Get the number of vertices
966  int num_verts;
967  ErrorCode rval = mbImpl->get_number_entities_by_dimension( _fileSet, 0, num_verts );MB_CHK_SET_ERR( rval, "Trouble getting number of vertices" );
968 
969  /*
970  // Check against parameters
971  // When ghosting is used, this check might fail (to be updated later)
972  if (num_verts > 0) {
973  int expected_verts = (lDims[3] - lDims[0] + 1) * (lDims[4] - lDims[1] + 1) * (-1 == lDims[2] ?
974  1 : lDims[5] - lDims[2] + 1); if (num_verts != expected_verts) { MB_SET_ERR(MB_FAILURE, "Number
975  of vertices doesn't match");
976  }
977  }
978  */
979 
980  // Check the number of elements too
981  int num_elems;
982  rval = mbImpl->get_number_entities_by_dimension( _fileSet, ( -1 == lCDims[2] ? 2 : 3 ), num_elems );MB_CHK_SET_ERR( rval, "Trouble getting number of elements" );
983 
984  /*
985  // Check against parameters
986  // When ghosting is used, this check might fail (to be updated later)
987  if (num_elems > 0) {
988  int expected_elems = (lCDims[3] - lCDims[0] + 1) * (lCDims[4] - lCDims[1] + 1) * (-1 ==
989  lCDims[2] ? 1 : (lCDims[5] - lCDims[2] + 1)); if (num_elems != expected_elems) {
990  MB_SET_ERR(MB_FAILURE, "Number of elements doesn't match");
991  }
992  }
993  */
994 
995  return MB_SUCCESS;
996 }
997 
999 {
1000  Interface*& mbImpl = _readNC->mbImpl;
1001  Tag& mGlobalIdTag = _readNC->mGlobalIdTag;
1002  const Tag*& mpFileIdTag = _readNC->mpFileIdTag;
1003  DebugOutput& dbgOut = _readNC->dbgOut;
1004  ScdInterface* scdi = _readNC->scdi;
1005  ScdParData& parData = _readNC->parData;
1006 
1007  Range tmp_range;
1008  ScdBox* scd_box;
1009 
1010  ErrorCode rval =
1011  scdi->construct_box( HomCoord( lDims[0], lDims[1], lDims[2], 1 ), HomCoord( lDims[3], lDims[4], lDims[5], 1 ),
1012  NULL, 0, scd_box, locallyPeriodic, &parData, true );MB_CHK_SET_ERR( rval, "Trouble creating scd vertex sequence" );
1013 
1014  // Add verts to tmp_range first, so we can duplicate global ids in vertex ids
1015  tmp_range.insert( scd_box->start_vertex(), scd_box->start_vertex() + scd_box->num_vertices() - 1 );
1016 
1017  if( mpFileIdTag )
1018  {
1019  int count;
1020  void* data;
1021  rval = mbImpl->tag_iterate( *mpFileIdTag, tmp_range.begin(), tmp_range.end(), count, data );MB_CHK_SET_ERR( rval, "Failed to iterate file ID tag on local vertices" );
1022  assert( count == scd_box->num_vertices() );
1023  int* fid_data = (int*)data;
1024  rval = mbImpl->tag_iterate( mGlobalIdTag, tmp_range.begin(), tmp_range.end(), count, data );MB_CHK_SET_ERR( rval, "Failed to iterate global ID tag on local vertices" );
1025  assert( count == scd_box->num_vertices() );
1026  int* gid_data = (int*)data;
1027  for( int i = 0; i < count; i++ )
1028  fid_data[i] = gid_data[i];
1029  }
1030 
1031  // Then add box set and elements to the range, then to the file set
1032  tmp_range.insert( scd_box->start_element(), scd_box->start_element() + scd_box->num_elements() - 1 );
1033  tmp_range.insert( scd_box->box_set() );
1034  rval = mbImpl->add_entities( _fileSet, tmp_range );MB_CHK_SET_ERR( rval, "Couldn't add new vertices to current file set" );
1035 
1036  dbgOut.tprintf( 1, "scdbox %d quads, %d vertices\n", scd_box->num_elements(), scd_box->num_vertices() );
1037 
1038  // Set the vertex coordinates
1039  double *xc, *yc, *zc;
1040  rval = scd_box->get_coordinate_arrays( xc, yc, zc );MB_CHK_SET_ERR( rval, "Couldn't get vertex coordinate arrays" );
1041 
1042  int i, j, k, il, jl, kl;
1043  int dil = lDims[3] - lDims[0] + 1;
1044  int djl = lDims[4] - lDims[1] + 1;
1045  assert( dil == (int)ilVals.size() && djl == (int)jlVals.size() &&
1046  ( -1 == lDims[2] || lDims[5] - lDims[2] + 1 == (int)levVals.size() ) );
1047 
1048  for( kl = lDims[2]; kl <= lDims[5]; kl++ )
1049  {
1050  k = kl - lDims[2];
1051  for( jl = lDims[1]; jl <= lDims[4]; jl++ )
1052  {
1053  j = jl - lDims[1];
1054  for( il = lDims[0]; il <= lDims[3]; il++ )
1055  {
1056  i = il - lDims[0];
1057  unsigned int pos = i + j * dil + k * dil * djl;
1058  xc[pos] = ilVals[i];
1059  yc[pos] = jlVals[j];
1060  zc[pos] = ( -1 == lDims[2] ? 0.0 : levVals[k] );
1061  }
1062  }
1063  }
1064 
1065 #ifndef NDEBUG
1066  int num_verts =
1067  ( lDims[3] - lDims[0] + 1 ) * ( lDims[4] - lDims[1] + 1 ) * ( -1 == lDims[2] ? 1 : lDims[5] - lDims[2] + 1 );
1068  std::vector< int > gids( num_verts );
1069  Range verts( scd_box->start_vertex(), scd_box->start_vertex() + scd_box->num_vertices() - 1 );
1070  rval = mbImpl->tag_get_data( mGlobalIdTag, verts, &gids[0] );MB_CHK_SET_ERR( rval, "Trouble getting local gid values of vertices" );
1071  int vmin = *( std::min_element( gids.begin(), gids.end() ) ),
1072  vmax = *( std::max_element( gids.begin(), gids.end() ) );
1073  dbgOut.tprintf( 1, "Vertex gids %d-%d\n", vmin, vmax );
1074 #endif
1075 
1076  // Add elements to the range passed in
1077  faces.insert( scd_box->start_element(), scd_box->start_element() + scd_box->num_elements() - 1 );
1078 
1079  if( 2 <= dbgOut.get_verbosity() )
1080  {
1081  assert( scd_box->boundary_complete() );
1082  EntityHandle dum_ent = scd_box->start_element();
1083  rval = mbImpl->list_entities( &dum_ent, 1 );MB_CHK_SET_ERR( rval, "Trouble listing first hex" );
1084 
1085  std::vector< EntityHandle > connect;
1086  rval = mbImpl->get_connectivity( &dum_ent, 1, connect );MB_CHK_SET_ERR( rval, "Trouble getting connectivity" );
1087 
1088  rval = mbImpl->list_entities( &connect[0], connect.size() );MB_CHK_SET_ERR( rval, "Trouble listing element connectivity" );
1089  }
1090 
1091  Range edges;
1092  mbImpl->get_adjacencies( faces, 1, true, edges, Interface::UNION );
1093 
1094  // Create COORDS tag for quads
1095  rval = create_quad_coordinate_tag();MB_CHK_SET_ERR( rval, "Trouble creating COORDS tag for quads" );
1096 
1097  return MB_SUCCESS;
1098 }
1099 
1100 ErrorCode ScdNCHelper::read_variables( std::vector< std::string >& var_names, std::vector< int >& tstep_nums )
1101 {
1102  std::vector< ReadNC::VarData > vdatas;
1103  std::vector< ReadNC::VarData > vsetdatas;
1104 
1105  ErrorCode rval = read_variables_setup( var_names, tstep_nums, vdatas, vsetdatas );MB_CHK_SET_ERR( rval, "Trouble setting up to read variables" );
1106 
1107  if( !vsetdatas.empty() )
1108  {
1109  rval = read_variables_to_set( vsetdatas, tstep_nums );MB_CHK_SET_ERR( rval, "Trouble reading variables to set" );
1110  }
1111 
1112  if( !vdatas.empty() )
1113  {
1114  rval = read_scd_variables_to_nonset( vdatas, tstep_nums );MB_CHK_SET_ERR( rval, "Trouble reading variables to verts/edges/faces" );
1115  }
1116 
1117  return MB_SUCCESS;
1118 }
1119 
1120 ErrorCode ScdNCHelper::read_scd_variables_to_nonset_allocate( std::vector< ReadNC::VarData >& vdatas,
1121  std::vector< int >& tstep_nums )
1122 {
1123  Interface*& mbImpl = _readNC->mbImpl;
1124  std::vector< int >& dimLens = _readNC->dimLens;
1125  DebugOutput& dbgOut = _readNC->dbgOut;
1126 
1127  Range* range = NULL;
1128 
1129  // Get vertices
1130  Range verts;
1131  ErrorCode rval = mbImpl->get_entities_by_dimension( _fileSet, 0, verts );MB_CHK_SET_ERR( rval, "Trouble getting vertices in current file set" );
1132  assert( "Should only have a single vertex subrange, since they were read in one shot" && verts.psize() == 1 );
1133 
1134  Range edges;
1135  rval = mbImpl->get_entities_by_dimension( _fileSet, 1, edges );MB_CHK_SET_ERR( rval, "Trouble getting edges in current file set" );
1136 
1137  // Get faces
1138  Range faces;
1139  rval = mbImpl->get_entities_by_dimension( _fileSet, 2, faces );MB_CHK_SET_ERR( rval, "Trouble getting faces in current file set" );
1140  assert( "Should only have a single face subrange, since they were read in one shot" && faces.psize() == 1 );
1141 
1142 #ifdef MOAB_HAVE_MPI
1143  moab::Range faces_owned;
1144  bool& isParallel = _readNC->isParallel;
1145  if( isParallel )
1146  {
1147  ParallelComm*& myPcomm = _readNC->myPcomm;
1148  rval = myPcomm->filter_pstatus( faces, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1, &faces_owned );MB_CHK_SET_ERR( rval, "Trouble getting owned faces in current file set" );
1149  }
1150  else
1151  faces_owned = faces; // Not running in parallel, but still with MPI
1152 #endif
1153 
1154  for( unsigned int i = 0; i < vdatas.size(); i++ )
1155  {
1156  // Support non-set variables with 4 dimensions like (time, lev, lat, lon)
1157  assert( 4 == vdatas[i].varDims.size() );
1158 
1159  // For a non-set variable, time should be the first dimension
1160  assert( tDim == vdatas[i].varDims[0] );
1161 
1162  // Set up readStarts and readCounts
1163  vdatas[i].readStarts.resize( 4 );
1164  vdatas[i].readCounts.resize( 4 );
1165 
1166  // First: time
1167  vdatas[i].readStarts[0] = 0; // This value is timestep dependent, will be set later
1168  vdatas[i].readCounts[0] = 1;
1169 
1170  // Next: lev
1171  vdatas[i].readStarts[1] = 0;
1172  vdatas[i].readCounts[1] = vdatas[i].numLev;
1173 
1174  // Finally: lat (or slat) and lon (or slon)
1175  switch( vdatas[i].entLoc )
1176  {
1177  case ReadNC::ENTLOCVERT:
1178  // Vertices
1179  vdatas[i].readStarts[2] = lDims[1];
1180  vdatas[i].readCounts[2] = lDims[4] - lDims[1] + 1;
1181  vdatas[i].readStarts[3] = lDims[0];
1182  vdatas[i].readCounts[3] = lDims[3] - lDims[0] + 1;
1183  range = &verts;
1184  break;
1185  case ReadNC::ENTLOCNSEDGE:
1186  case ReadNC::ENTLOCEWEDGE:
1187  case ReadNC::ENTLOCEDGE:
1188  // Not implemented yet, set a global error
1189  MB_SET_GLB_ERR( MB_NOT_IMPLEMENTED, "Reading edge data is not implemented yet" );
1190  case ReadNC::ENTLOCFACE:
1191  // Faces
1192  vdatas[i].readStarts[2] = lCDims[1];
1193  vdatas[i].readCounts[2] = lCDims[4] - lCDims[1] + 1;
1194  vdatas[i].readStarts[3] = lCDims[0];
1195  vdatas[i].readCounts[3] = lCDims[3] - lCDims[0] + 1;
1196 #ifdef MOAB_HAVE_MPI
1197  range = &faces_owned;
1198 #else
1199  range = &faces;
1200 #endif
1201  break;
1202  default:
1203  MB_SET_ERR( MB_FAILURE, "Unexpected entity location type for variable " << vdatas[i].varName );
1204  }
1205 
1206  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
1207  {
1208  dbgOut.tprintf( 2, "Reading variable %s, time step %d\n", vdatas[i].varName.c_str(), tstep_nums[t] );
1209 
1210  if( tstep_nums[t] >= dimLens[tDim] )
1211  {
1212  MB_SET_ERR( MB_INDEX_OUT_OF_RANGE, "Wrong value for timestep number " << tstep_nums[t] );
1213  }
1214 
1215  // Get the tag to read into
1216  if( !vdatas[i].varTags[t] )
1217  {
1218  rval = get_tag_to_nonset( vdatas[i], tstep_nums[t], vdatas[i].varTags[t], vdatas[i].numLev );MB_CHK_SET_ERR( rval, "Trouble getting tag to non-set variable " << vdatas[i].varName );
1219  }
1220 
1221  // Get ptr to tag space
1222  void* data;
1223  int count;
1224  rval = mbImpl->tag_iterate( vdatas[i].varTags[t], range->begin(), range->end(), count, data );MB_CHK_SET_ERR( rval, "Failed to iterate tag for non-set variable " << vdatas[i].varName );
1225  assert( (unsigned)count == range->size() );
1226  vdatas[i].varDatas[t] = data;
1227  }
1228 
1229  // Get variable size
1230  vdatas[i].sz = 1;
1231  for( std::size_t idx = 0; idx != vdatas[i].readCounts.size(); idx++ )
1232  vdatas[i].sz *= vdatas[i].readCounts[idx];
1233  }
1234 
1235  return rval;
1236 }
1237 
1238 ErrorCode ScdNCHelper::read_scd_variables_to_nonset( std::vector< ReadNC::VarData >& vdatas,
1239  std::vector< int >& tstep_nums )
1240 {
1241  DebugOutput& dbgOut = _readNC->dbgOut;
1242 
1243  ErrorCode rval = read_scd_variables_to_nonset_allocate( vdatas, tstep_nums );MB_CHK_SET_ERR( rval, "Trouble allocating space to read non-set variables" );
1244 
1245  // Finally, read into that space
1246  int success;
1247  for( unsigned int i = 0; i < vdatas.size(); i++ )
1248  {
1249  std::size_t sz = vdatas[i].sz;
1250 
1251  // A typical supported variable: float T(time, lev, lat, lon)
1252  // For tag values, need transpose (lev, lat, lon) to (lat, lon, lev)
1253  size_t ni = vdatas[i].readCounts[3]; // lon or slon
1254  size_t nj = vdatas[i].readCounts[2]; // lat or slat
1255  size_t nk = vdatas[i].readCounts[1]; // lev
1256 
1257  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
1258  {
1259  // Tag data for this timestep
1260  void* data = vdatas[i].varDatas[t];
1261 
1262  // Set readStart for each timestep along time dimension
1263  vdatas[i].readStarts[0] = tstep_nums[t];
1264 
1265  switch( vdatas[i].varDataType )
1266  {
1267  case NC_BYTE:
1268  case NC_CHAR: {
1269  std::vector< char > tmpchardata( sz );
1270  success = NCFUNCAG( _vara_text )( _fileId, vdatas[i].varId, &vdatas[i].readStarts[0],
1271  &vdatas[i].readCounts[0], &tmpchardata[0] );
1272  if( success )
1273  MB_SET_ERR( MB_FAILURE, "Failed to read byte/char data for variable " << vdatas[i].varName );
1274  if( vdatas[i].numLev > 1 )
1275  // Transpose (lev, lat, lon) to (lat, lon, lev)
1276  kji_to_jik( ni, nj, nk, data, &tmpchardata[0] );
1277  else
1278  {
1279  for( std::size_t idx = 0; idx != tmpchardata.size(); idx++ )
1280  ( (char*)data )[idx] = tmpchardata[idx];
1281  }
1282  break;
1283  }
1284  case NC_SHORT:
1285  case NC_INT: {
1286  std::vector< int > tmpintdata( sz );
1287  success = NCFUNCAG( _vara_int )( _fileId, vdatas[i].varId, &vdatas[i].readStarts[0],
1288  &vdatas[i].readCounts[0], &tmpintdata[0] );
1289  if( success )
1290  MB_SET_ERR( MB_FAILURE, "Failed to read short/int data for variable " << vdatas[i].varName );
1291  if( vdatas[i].numLev > 1 )
1292  // Transpose (lev, lat, lon) to (lat, lon, lev)
1293  kji_to_jik( ni, nj, nk, data, &tmpintdata[0] );
1294  else
1295  {
1296  for( std::size_t idx = 0; idx != tmpintdata.size(); idx++ )
1297  ( (int*)data )[idx] = tmpintdata[idx];
1298  }
1299  break;
1300  }
1301  case NC_INT64: {
1302  std::vector< long > tmpintdata( sz );
1303  success = NCFUNCAG( _vara_long )( _fileId, vdatas[i].varId, &vdatas[i].readStarts[0],
1304  &vdatas[i].readCounts[0], &tmpintdata[0] );
1305  if( success )
1306  MB_SET_ERR( MB_FAILURE, "Failed to read long data for variable " << vdatas[i].varName );
1307  if( vdatas[i].numLev > 1 )
1308  // Transpose (lev, lat, lon) to (lat, lon, lev)
1309  kji_to_jik( ni, nj, nk, data, &tmpintdata[0] );
1310  else
1311  {
1312  for( std::size_t idx = 0; idx != tmpintdata.size(); idx++ )
1313  ( (long*)data )[idx] = tmpintdata[idx];
1314  }
1315  break;
1316  }
1317  case NC_FLOAT:
1318  case NC_DOUBLE: {
1319  std::vector< double > tmpdoubledata( sz );
1320  success = NCFUNCAG( _vara_double )( _fileId, vdatas[i].varId, &vdatas[i].readStarts[0],
1321  &vdatas[i].readCounts[0], &tmpdoubledata[0] );
1322  if( success )
1323  MB_SET_ERR( MB_FAILURE, "Failed to read float/double data for variable " << vdatas[i].varName );
1324  if( vdatas[i].numLev > 1 )
1325  // Transpose (lev, lat, lon) to (lat, lon, lev)
1326  kji_to_jik( ni, nj, nk, data, &tmpdoubledata[0] );
1327  else
1328  {
1329  for( std::size_t idx = 0; idx != tmpdoubledata.size(); idx++ )
1330  ( (double*)data )[idx] = tmpdoubledata[idx];
1331  }
1332  break;
1333  }
1334  default:
1335  MB_SET_ERR( MB_FAILURE, "Unexpected data type for variable " << vdatas[i].varName );
1336  }
1337  }
1338  }
1339 
1340  // Debug output, if requested
1341  if( 1 == dbgOut.get_verbosity() )
1342  {
1343  dbgOut.printf( 1, "Read variables: %s", vdatas.begin()->varName.c_str() );
1344  for( unsigned int i = 1; i < vdatas.size(); i++ )
1345  dbgOut.printf( 1, ", %s ", vdatas[i].varName.c_str() );
1346  dbgOut.tprintf( 1, "\n" );
1347  }
1348 
1349  return rval;
1350 }
1351 
1353 {
1354  Interface*& mbImpl = _readNC->mbImpl;
1355 
1356  Range ents;
1357  ErrorCode rval = mbImpl->get_entities_by_type( _fileSet, moab::MBQUAD, ents );MB_CHK_SET_ERR( rval, "Trouble getting quads" );
1358 
1359  std::size_t numOwnedEnts = 0;
1360 #ifdef MOAB_HAVE_MPI
1361  Range ents_owned;
1362  bool& isParallel = _readNC->isParallel;
1363  if( isParallel )
1364  {
1365  ParallelComm*& myPcomm = _readNC->myPcomm;
1366  rval = myPcomm->filter_pstatus( ents, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1, &ents_owned );MB_CHK_SET_ERR( rval, "Trouble getting owned quads" );
1367  numOwnedEnts = ents_owned.size();
1368  }
1369  else
1370  {
1371  numOwnedEnts = ents.size();
1372  ents_owned = ents;
1373  }
1374 #else
1375  numOwnedEnts = ents.size();
1376 #endif
1377 
1378  if( numOwnedEnts == 0 ) return MB_SUCCESS;
1379 
1380  assert( numOwnedEnts == ilCVals.size() * jlCVals.size() );
1381  std::vector< double > coords( numOwnedEnts * 3 );
1382  std::size_t pos = 0;
1383  for( std::size_t j = 0; j != jlCVals.size(); ++j )
1384  {
1385  for( std::size_t i = 0; i != ilCVals.size(); ++i )
1386  {
1387  pos = j * ilCVals.size() * 3 + i * 3;
1388  coords[pos] = ilCVals[i];
1389  coords[pos + 1] = jlCVals[j];
1390  coords[pos + 2] = 0.0;
1391  }
1392  }
1393  std::string tag_name = "COORDS";
1394  Tag tagh = 0;
1395  rval = mbImpl->tag_get_handle( tag_name.c_str(), 3, MB_TYPE_DOUBLE, tagh, MB_TAG_DENSE | MB_TAG_CREAT );MB_CHK_SET_ERR( rval, "Trouble creating COORDS tag" );
1396 
1397  void* data;
1398  int count;
1399 #ifdef MOAB_HAVE_MPI
1400  rval = mbImpl->tag_iterate( tagh, ents_owned.begin(), ents_owned.end(), count, data );MB_CHK_SET_ERR( rval, "Failed to iterate COORDS tag on quads" );
1401 #else
1402  rval = mbImpl->tag_iterate( tagh, ents.begin(), ents.end(), count, data );MB_CHK_SET_ERR( rval, "Failed to iterate COORDS tag on quads" );
1403 #endif
1404  assert( count == (int)numOwnedEnts );
1405  double* quad_data = (double*)data;
1406  std::copy( coords.begin(), coords.end(), quad_data );
1407 
1408  return MB_SUCCESS;
1409 }
1410 
1411 ErrorCode UcdNCHelper::read_variables( std::vector< std::string >& var_names, std::vector< int >& tstep_nums )
1412 {
1413  std::vector< ReadNC::VarData > vdatas;
1414  std::vector< ReadNC::VarData > vsetdatas;
1415 
1416  ErrorCode rval = read_variables_setup( var_names, tstep_nums, vdatas, vsetdatas );MB_CHK_SET_ERR( rval, "Trouble setting up to read variables" );
1417 
1418  if( !vsetdatas.empty() )
1419  {
1420  rval = read_variables_to_set( vsetdatas, tstep_nums );MB_CHK_SET_ERR( rval, "Trouble reading variables to set" );
1421  }
1422 
1423  if( !vdatas.empty() )
1424  {
1425 #ifdef MOAB_HAVE_PNETCDF
1426  // With pnetcdf support, we will use async read
1427  rval = read_ucd_variables_to_nonset_async( vdatas, tstep_nums );MB_CHK_SET_ERR( rval, "Trouble reading variables to verts/edges/faces" );
1428 #else
1429  // Without pnetcdf support, we will use old read
1430  rval = read_ucd_variables_to_nonset( vdatas, tstep_nums );MB_CHK_SET_ERR( rval, "Trouble reading variables to verts/edges/faces" );
1431 #endif
1432  }
1433 
1434  return MB_SUCCESS;
1435 }
1436 
1437 static double tolerance = 1.e-12;
1438 // Used to put points in an STL tree-based container
1440 {
1441  if( coords[0] <= other.coords[0] - tolerance )
1442  return true;
1443  else if( coords[0] >= other.coords[0] + tolerance )
1444  return false;
1445  if( coords[1] <= other.coords[1] - tolerance )
1446  return true;
1447  else if( coords[1] >= other.coords[1] + tolerance )
1448  return false;
1449  if( coords[2] <= other.coords[2] - tolerance )
1450  return true;
1451  else if( coords[0] >= other.coords[0] + tolerance )
1452  return false;
1453 
1454  return false;
1455 }
1456 /*bool NCHelper::Node3D::operator == ( const NCHelper::Node3D& other ) const
1457  return ( !(*this < other) && ! ( other < *this) ) ;*/
1458 } // namespace moab