Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
NCHelperHOMME.cpp
Go to the documentation of this file.
1 #include "NCHelperHOMME.hpp"
2 #include "moab/ReadUtilIface.hpp"
3 #include "moab/FileOptions.hpp"
5 
6 #include <cmath>
7 
8 namespace moab
9 {
10 
11 NCHelperHOMME::NCHelperHOMME( ReadNC* readNC, int fileId, const FileOptions& opts, EntityHandle fileSet )
12  : UcdNCHelper( readNC, fileId, opts, fileSet ), _spectralOrder( -1 ), connectId( -1 ), isConnFile( false )
13 {
14  // Calculate spectral order
15  std::map< std::string, ReadNC::AttData >::iterator attIt = readNC->globalAtts.find( "np" );
16  if( attIt != readNC->globalAtts.end() )
17  {
18  int success = NCFUNC( get_att_int )( readNC->fileId, attIt->second.attVarId, attIt->second.attName.c_str(),
19  &_spectralOrder );
20  if( 0 == success ) _spectralOrder--; // Spectral order is one less than np
21  }
22  else
23  {
24  // As can_read_file() returns true and there is no global attribute "np", it should be a
25  // connectivity file
26  isConnFile = true;
27  _spectralOrder = 3; // Assume np is 4
28  }
29 }
30 
31 bool NCHelperHOMME::can_read_file( ReadNC* readNC, int fileId )
32 {
33  // If global attribute "np" exists then it should be the HOMME grid
34  if( readNC->globalAtts.find( "np" ) != readNC->globalAtts.end() )
35  {
36  // Make sure it is CAM grid
37  std::map< std::string, ReadNC::AttData >::iterator attIt = readNC->globalAtts.find( "source" );
38  if( attIt == readNC->globalAtts.end() ) return false;
39  unsigned int sz = attIt->second.attLen;
40  std::string att_data;
41  att_data.resize( sz + 1 );
42  att_data[sz] = '\000';
43  int success =
44  NCFUNC( get_att_text )( fileId, attIt->second.attVarId, attIt->second.attName.c_str(), &att_data[0] );
45  if( success ) return false;
46  if( att_data.find( "CAM" ) == std::string::npos ) return false;
47 
48  return true;
49  }
50  else
51  {
52  // If dimension names "ncol" AND "ncorners" AND "ncells" exist, then it should be the HOMME
53  // connectivity file In this case, the mesh can still be created
54  std::vector< std::string >& dimNames = readNC->dimNames;
55  if( ( std::find( dimNames.begin(), dimNames.end(), std::string( "ncol" ) ) != dimNames.end() ) &&
56  ( std::find( dimNames.begin(), dimNames.end(), std::string( "ncorners" ) ) != dimNames.end() ) &&
57  ( std::find( dimNames.begin(), dimNames.end(), std::string( "ncells" ) ) != dimNames.end() ) )
58  return true;
59  }
60 
61  return false;
62 }
63 
65 {
66  std::vector< std::string >& dimNames = _readNC->dimNames;
67  std::vector< int >& dimLens = _readNC->dimLens;
68  std::map< std::string, ReadNC::VarData >& varInfo = _readNC->varInfo;
69 
70  unsigned int idx;
71  std::vector< std::string >::iterator vit;
72 
73  // Look for time dimension
74  if( isConnFile )
75  {
76  // Connectivity file might not have time dimension
77  }
78  else
79  {
80  if( ( vit = std::find( dimNames.begin(), dimNames.end(), "time" ) ) != dimNames.end() )
81  idx = vit - dimNames.begin();
82  else if( ( vit = std::find( dimNames.begin(), dimNames.end(), "t" ) ) != dimNames.end() )
83  idx = vit - dimNames.begin();
84  else
85  {
86  MB_SET_ERR( MB_FAILURE, "Couldn't find 'time' or 't' dimension" );
87  }
88  tDim = idx;
89  nTimeSteps = dimLens[idx];
90  }
91 
92  // Get number of vertices (labeled as number of columns)
93  if( ( vit = std::find( dimNames.begin(), dimNames.end(), "ncol" ) ) != dimNames.end() )
94  idx = vit - dimNames.begin();
95  else
96  {
97  MB_SET_ERR( MB_FAILURE, "Couldn't find 'ncol' dimension" );
98  }
99  vDim = idx;
100  nVertices = dimLens[idx];
101 
102  // Set number of cells
103  nCells = nVertices - 2;
104 
105  // Get number of levels
106  if( isConnFile )
107  {
108  // Connectivity file might not have level dimension
109  }
110  else
111  {
112  if( ( vit = std::find( dimNames.begin(), dimNames.end(), "lev" ) ) != dimNames.end() )
113  idx = vit - dimNames.begin();
114  else if( ( vit = std::find( dimNames.begin(), dimNames.end(), "ilev" ) ) != dimNames.end() )
115  idx = vit - dimNames.begin();
116  else
117  {
118  MB_SET_ERR( MB_FAILURE, "Couldn't find 'lev' or 'ilev' dimension" );
119  }
120  levDim = idx;
121  nLevels = dimLens[idx];
122  }
123 
124  // Store lon values in xVertVals
125  std::map< std::string, ReadNC::VarData >::iterator vmit;
126  if( ( vmit = varInfo.find( "lon" ) ) != varInfo.end() && ( *vmit ).second.varDims.size() == 1 )
127  {
128  MB_CHK_SET_ERR( read_coordinate( "lon", 0, nVertices - 1, xVertVals ), "Trouble reading 'lon' variable" );
129  }
130  else
131  {
132  MB_SET_ERR( MB_FAILURE, "Couldn't find 'lon' variable" );
133  }
134 
135  // Store lat values in yVertVals
136  if( ( vmit = varInfo.find( "lat" ) ) != varInfo.end() && ( *vmit ).second.varDims.size() == 1 )
137  {
138  MB_CHK_SET_ERR( read_coordinate( "lat", 0, nVertices - 1, yVertVals ), "Trouble reading 'lat' variable" );
139  }
140  else
141  {
142  MB_SET_ERR( MB_FAILURE, "Couldn't find 'lat' variable" );
143  }
144 
145  // Store lev values in levVals
146  if( isConnFile )
147  {
148  // Connectivity file might not have level variable
149  }
150  else
151  {
152  if( ( vmit = varInfo.find( "lev" ) ) != varInfo.end() && ( *vmit ).second.varDims.size() == 1 )
153  {
154  MB_CHK_SET_ERR( read_coordinate( "lev", 0, nLevels - 1, levVals ), "Trouble reading 'lev' variable" );
155 
156  // Decide whether down is positive
157  char posval[10] = { 0 };
158  int success = NCFUNC( get_att_text )( _fileId, ( *vmit ).second.varId, "positive", posval );
159  if( 0 == success && !strcmp( posval, "down" ) )
160  {
161  for( std::vector< double >::iterator dvit = levVals.begin(); dvit != levVals.end(); ++dvit )
162  ( *dvit ) *= -1.0;
163  }
164  }
165  else
166  {
167  MB_SET_ERR( MB_FAILURE, "Couldn't find 'lev' variable" );
168  }
169  }
170 
171  // Store time coordinate values in tVals
172  if( isConnFile )
173  {
174  // Connectivity file might not have time variable
175  }
176  else
177  {
178  if( ( vmit = varInfo.find( "time" ) ) != varInfo.end() && ( *vmit ).second.varDims.size() == 1 )
179  {
180  MB_CHK_SET_ERR( read_coordinate( "time", 0, nTimeSteps - 1, tVals ), "Trouble reading 'time' variable" );
181  }
182  else if( ( vmit = varInfo.find( "t" ) ) != varInfo.end() && ( *vmit ).second.varDims.size() == 1 )
183  {
184  MB_CHK_SET_ERR( read_coordinate( "t", 0, nTimeSteps - 1, tVals ), "Trouble reading 't' variable" );
185  }
186  else
187  {
188  // If expected time variable does not exist, set dummy values to tVals
189  for( int t = 0; t < nTimeSteps; t++ )
190  tVals.push_back( (double)t );
191  }
192  }
193 
194  // For each variable, determine the entity location type and number of levels
195  std::map< std::string, ReadNC::VarData >::iterator mit;
196  for( mit = varInfo.begin(); mit != varInfo.end(); ++mit )
197  {
198  ReadNC::VarData& vd = ( *mit ).second;
199 
200  // Default entLoc is ENTLOCSET
201  if( std::find( vd.varDims.begin(), vd.varDims.end(), tDim ) != vd.varDims.end() )
202  {
203  if( std::find( vd.varDims.begin(), vd.varDims.end(), vDim ) != vd.varDims.end() )
205  }
206 
207  // Default numLev is 0
208  if( std::find( vd.varDims.begin(), vd.varDims.end(), levDim ) != vd.varDims.end() ) vd.numLev = nLevels;
209  }
210 
211  // Hack: create dummy variables for dimensions (like ncol) with no corresponding coordinate
212  // variables
213  MB_CHK_SET_ERR( create_dummy_variables(), "Failed to create dummy variables" );
214 
215  return MB_SUCCESS;
216 }
217 
218 // When noMesh option is used on this read, the old ReadNC class instance for last read can get out
219 // of scope (and deleted). The old instance initialized localGidVerts properly when the mesh was
220 // created, but it is now lost. The new instance (will not create the mesh with noMesh option) has
221 // to restore it based on the existing mesh from last read
223 {
224  Interface*& mbImpl = _readNC->mbImpl;
225  Tag& mGlobalIdTag = _readNC->mGlobalIdTag;
226  bool& noMesh = _readNC->noMesh;
227 
228  if( noMesh && localGidVerts.empty() )
229  {
230  // We need to populate localGidVerts range with the gids of vertices from current file set
231  // localGidVerts is important in reading the variable data into the nodes
232  // Also, for our purposes, localGidVerts is truly the GLOBAL_ID tag data, not other
233  // file_id tags that could get passed around in other scenarios for parallel reading
234 
235  // Get all vertices from current file set (it is the input set in no_mesh scenario)
236  Range local_verts;
237  MB_CHK_SET_ERR( mbImpl->get_entities_by_dimension( _fileSet, 0, local_verts ),
238  "Trouble getting local vertices in current file set" );
239 
240  if( !local_verts.empty() )
241  {
242  std::vector< int > gids( local_verts.size() );
243 
244  // !IMPORTANT : this has to be the GLOBAL_ID tag
245  MB_CHK_SET_ERR( mbImpl->tag_get_data( mGlobalIdTag, local_verts, &gids[0] ),
246  "Trouble getting local gid values of vertices" );
247 
248  // Restore localGidVerts
249  std::copy( gids.rbegin(), gids.rend(), range_inserter( localGidVerts ) );
251  }
252  }
253 
254  return MB_SUCCESS;
255 }
256 
258 {
259  Interface*& mbImpl = _readNC->mbImpl;
260  std::string& fileName = _readNC->fileName;
261  Tag& mGlobalIdTag = _readNC->mGlobalIdTag;
262  const Tag*& mpFileIdTag = _readNC->mpFileIdTag;
263  DebugOutput& dbgOut = _readNC->dbgOut;
264  bool& spectralMesh = _readNC->spectralMesh;
265  int& gatherSetRank = _readNC->gatherSetRank;
266  int& trivialPartitionShift = _readNC->trivialPartitionShift;
267 
268  int rank = 0;
269  int procs = 1;
270 #ifdef MOAB_HAVE_MPI
271  bool& isParallel = _readNC->isParallel;
272  if( isParallel )
273  {
274  ParallelComm*& myPcomm = _readNC->myPcomm;
275  rank = myPcomm->proc_config().proc_rank();
276  procs = myPcomm->proc_config().proc_size();
277  }
278 #endif
279 
280  int success = 0;
281 
282  // Need to get/read connectivity data before creating elements
283  std::string conn_fname;
284 
285  if( isConnFile )
286  {
287  // Connectivity file has already been read
289  }
290  else
291  {
292  // Try to open the connectivity file through CONN option, if used
293  if( MB_SUCCESS != _opts.get_str_option( "CONN", conn_fname ) )
294  {
295  // Default convention for reading HOMME is a file HommeMapping.nc in same dir as data
296  // file
297  conn_fname = std::string( fileName );
298  size_t idx = conn_fname.find_last_of( "/" );
299  if( idx != std::string::npos )
300  conn_fname = conn_fname.substr( 0, idx ).append( "/HommeMapping.nc" );
301  else
302  conn_fname = "HommeMapping.nc";
303  }
304  // Format-aware open via the runtime dispatch layer. Mirrors the
305  // open path in ReadNC::load_file — probe the on-disk format on
306  // rank 0, broadcast, pick the backend, then dispatch.
307  int connFormat = NCFMT_UNKNOWN;
308 #ifdef MOAB_HAVE_MPI
309  if( isParallel )
310  {
311  ParallelComm*& myPcomm = _readNC->myPcomm;
312  int rank = myPcomm->proc_config().proc_rank();
313  if( rank == 0 ) connFormat = mbnc_detect_format( conn_fname.c_str() );
314  MPI_Bcast( &connFormat, 1, MPI_INT, 0, myPcomm->proc_config().proc_comm() );
315  }
316  else
317 #endif
318  {
319  connFormat = mbnc_detect_format( conn_fname.c_str() );
320  }
321 
322 #ifdef MOAB_HAVE_MPI
323  const int conn_mpi_size = isParallel ? _readNC->myPcomm->proc_config().proc_size() : 1;
324 #else
325  const int conn_mpi_size = 1;
326 #endif
327 
328  const NcBackend connBackend = mbnc_choose_backend_for_read( connFormat, conn_mpi_size );
329  if( connBackend == NCB_NONE )
330  {
331  MB_SET_ERR( MB_FAILURE, "Cannot find a compatible reader for HOMME connectivity file '"
332  << conn_fname << "'" );
333  }
334 
335 #ifdef MOAB_HAVE_MPI
336  if( connBackend == NCB_NETCDF_SERIAL || conn_mpi_size == 1 )
337  {
338  success = mbnc_open( conn_fname.c_str(), 0, &connectId );
339  }
340  else
341  {
342  ParallelComm*& myPcomm = _readNC->myPcomm;
343  success = mbnc_open_par( connBackend, myPcomm->proc_config().proc_comm(), MPI_INFO_NULL,
344  conn_fname.c_str(), 0, &connectId );
345  }
346 #else
347  success = mbnc_open( conn_fname.c_str(), 0, &connectId );
348 #endif
349  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on open" );
350  }
351 
352  std::vector< std::string > conn_names;
353  std::vector< int > conn_vals;
354  MB_CHK_SET_ERR( _readNC->get_dimensions( connectId, conn_names, conn_vals ),
355  "Failed to get dimensions for connectivity" );
356 
357  // Read connectivity into temporary variable
358  int num_fine_quads = 0;
359  int num_coarse_quads = 0;
360  int start_idx = 0;
361  std::vector< std::string >::iterator vit;
362  int idx = 0;
363  if( ( vit = std::find( conn_names.begin(), conn_names.end(), "ncells" ) ) != conn_names.end() )
364  idx = vit - conn_names.begin();
365  else if( ( vit = std::find( conn_names.begin(), conn_names.end(), "ncenters" ) ) != conn_names.end() )
366  idx = vit - conn_names.begin();
367  else
368  {
369  MB_SET_ERR( MB_FAILURE, "Failed to get number of quads" );
370  }
371  int num_quads = conn_vals[idx];
372  if( !isConnFile && num_quads != nCells )
373  {
374  dbgOut.tprintf( 1,
375  "Warning: number of quads from %s and cells from %s are inconsistent; "
376  "num_quads = %d, nCells = %d.\n",
377  conn_fname.c_str(), fileName.c_str(), num_quads, nCells );
378  }
379 
380  // Get the connectivity into tmp_conn2 and permute into tmp_conn
381  int cornerVarId;
382  success = NCFUNC( inq_varid )( connectId, "element_corners", &cornerVarId );
383  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get variable id of 'element_corners'" );
384  NCDF_SIZE tmp_starts[2] = { 0, 0 };
385  NCDF_SIZE tmp_counts[2] = { 4, static_cast< NCDF_SIZE >( num_quads ) };
386  std::vector< int > tmp_conn( 4 * num_quads ), tmp_conn2( 4 * num_quads );
387  success = NCFUNCAG( _vara_int )( connectId, cornerVarId, tmp_starts, tmp_counts, &tmp_conn2[0] );
388  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get temporary connectivity" );
389  if( isConnFile )
390  {
391  // This data/connectivity file will be closed later in ReadNC::load_file()
392  }
393  else
394  {
395  success = NCFUNC( close )( connectId );
396  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on close" );
397  }
398  // Permute the connectivity
399  for( int i = 0; i < num_quads; i++ )
400  {
401  tmp_conn[4 * i] = tmp_conn2[i];
402  tmp_conn[4 * i + 1] = tmp_conn2[i + 1 * num_quads];
403  tmp_conn[4 * i + 2] = tmp_conn2[i + 2 * num_quads];
404  tmp_conn[4 * i + 3] = tmp_conn2[i + 3 * num_quads];
405  }
406 
407  // Need to know whether we'll be creating gather mesh later, to make sure
408  // we allocate enough space in one shot
409  bool create_gathers = false;
410  if( rank == gatherSetRank ) create_gathers = true;
411 
412  // Shift rank to obtain a rotated trivial partition
413  int shifted_rank = rank;
414  if( procs >= 2 && trivialPartitionShift > 0 ) shifted_rank = ( rank + trivialPartitionShift ) % procs;
415 
416  // Compute the number of local quads, accounting for coarse or fine representation
417  // spectral_unit is the # fine quads per coarse quad, or spectralOrder^2
418  int spectral_unit = ( spectralMesh ? _spectralOrder * _spectralOrder : 1 );
419  // num_coarse_quads is the number of quads instantiated in MOAB; if !spectralMesh,
420  // num_coarse_quads = num_fine_quads
421  num_coarse_quads = int( std::floor( 1.0 * num_quads / ( spectral_unit * procs ) ) );
422  // start_idx is the starting index in the HommeMapping connectivity list for this proc, before
423  // converting to coarse quad representation
424  start_idx = 4 * shifted_rank * num_coarse_quads * spectral_unit;
425  // iextra = # coarse quads extra after equal split over procs
426  int iextra = num_quads % ( procs * spectral_unit );
427  if( shifted_rank < iextra ) num_coarse_quads++;
428  start_idx += 4 * spectral_unit * std::min( shifted_rank, iextra );
429  // num_fine_quads is the number of quads in the connectivity list in HommeMapping file assigned
430  // to this proc
431  num_fine_quads = spectral_unit * num_coarse_quads;
432 
433  // Now create num_coarse_quads
434  EntityHandle* conn_arr;
435  EntityHandle start_vertex;
436  Range tmp_range;
437 
438  // Read connectivity into that space
439  EntityHandle* sv_ptr = NULL;
440  EntityHandle start_quad;
441  SpectralMeshTool smt( mbImpl, _spectralOrder );
442  if( !spectralMesh )
443  {
445  num_coarse_quads, 4, MBQUAD, 0, start_quad, conn_arr,
446  // Might have to create gather mesh later
447  ( create_gathers ? num_coarse_quads + num_quads : num_coarse_quads ) ),
448  "Failed to create local quads" );
449  tmp_range.insert( start_quad, start_quad + num_coarse_quads - 1 );
450  int* tmp_conn_end = ( &tmp_conn[start_idx + 4 * num_fine_quads - 1] ) + 1;
451  std::copy( &tmp_conn[start_idx], tmp_conn_end, conn_arr );
452  std::copy( conn_arr, conn_arr + 4 * num_fine_quads, range_inserter( localGidVerts ) );
453  }
454  else
455  {
456  MB_CHK_SET_ERR( smt.create_spectral_elems( &tmp_conn[0], num_fine_quads, 2, tmp_range, start_idx,
457  &localGidVerts ),
458  "Failed to create spectral elements" );
459  int count, v_per_e;
460  MB_CHK_SET_ERR( mbImpl->connect_iterate( tmp_range.begin(), tmp_range.end(), conn_arr, v_per_e, count ),
461  "Failed to get connectivity of spectral elements" );
462  MB_CHK_SET_ERR( mbImpl->tag_iterate( smt.spectral_vertices_tag( true ), tmp_range.begin(), tmp_range.end(),
463  count, (void*&)sv_ptr ),
464  "Failed to get fine connectivity of spectral elements" );
465  }
466 
467  // Create vertices
469  std::vector< double* > arrays;
471  _readNC->readMeshIface->get_node_coords( 3, nLocalVertices, 0, start_vertex, arrays,
472  // Might have to create gather mesh later
473  ( create_gathers ? nLocalVertices + nVertices : nLocalVertices ) ),
474  "Failed to create local vertices" );
475 
476  // Set vertex coordinates
477  Range::iterator rit;
478  double* xptr = arrays[0];
479  double* yptr = arrays[1];
480  double* zptr = arrays[2];
481  int i;
482  for( i = 0, rit = localGidVerts.begin(); i < nLocalVertices; i++, ++rit )
483  {
484  assert( *rit < xVertVals.size() + 1 );
485  xptr[i] = xVertVals[( *rit ) - 1]; // lon
486  yptr[i] = yVertVals[( *rit ) - 1]; // lat
487  }
488 
489  // Convert lon/lat/rad to x/y/z
490  const double pideg = acos( -1.0 ) / 180.0;
491  double rad = ( isConnFile ) ? 8000.0 : 8000.0 + levVals[0];
492  for( i = 0; i < nLocalVertices; i++ )
493  {
494  double cosphi = cos( pideg * yptr[i] );
495  double zmult = sin( pideg * yptr[i] );
496  double xmult = cosphi * cos( xptr[i] * pideg );
497  double ymult = cosphi * sin( xptr[i] * pideg );
498  xptr[i] = rad * xmult;
499  yptr[i] = rad * ymult;
500  zptr[i] = rad * zmult;
501  }
502 
503  // Get ptr to gid memory for vertices
504  Range vert_range( start_vertex, start_vertex + nLocalVertices - 1 );
505  void* data;
506  int count;
507  MB_CHK_SET_ERR( mbImpl->tag_iterate( mGlobalIdTag, vert_range.begin(), vert_range.end(), count, data ),
508  "Failed to iterate global id tag on local vertices" );
509  assert( count == nLocalVertices );
510  int* gid_data = (int*)data;
511  std::copy( localGidVerts.begin(), localGidVerts.end(), gid_data );
512 
513  // Duplicate global id data, which will be used to resolve sharing
514  if( mpFileIdTag )
515  {
516  MB_CHK_SET_ERR( mbImpl->tag_iterate( *mpFileIdTag, vert_range.begin(), vert_range.end(), count, data ),
517  "Failed to iterate file id tag on local vertices" );
518  assert( count == nLocalVertices );
519  int bytes_per_tag = 4;
520  MB_CHK_SET_ERR( mbImpl->tag_get_bytes( *mpFileIdTag, bytes_per_tag ),
521  "Can't get number of bytes for file id tag" );
522  if( 4 == bytes_per_tag )
523  {
524  gid_data = (int*)data;
525  std::copy( localGidVerts.begin(), localGidVerts.end(), gid_data );
526  }
527  else if( 8 == bytes_per_tag )
528  { // Should be a handle tag on 64 bit machine?
529  long* handle_tag_data = (long*)data;
530  std::copy( localGidVerts.begin(), localGidVerts.end(), handle_tag_data );
531  }
532  }
533 
534  // Create map from file ids to vertex handles, used later to set connectivity
535  std::map< EntityHandle, EntityHandle > vert_handles;
536  for( rit = localGidVerts.begin(), i = 0; rit != localGidVerts.end(); ++rit, i++ )
537  vert_handles[*rit] = start_vertex + i;
538 
539  // Compute proper handles in connectivity using offset
540  for( int q = 0; q < 4 * num_coarse_quads; q++ )
541  {
542  conn_arr[q] = vert_handles[conn_arr[q]];
543  assert( conn_arr[q] );
544  }
545  if( spectralMesh )
546  {
547  int verts_per_quad = ( _spectralOrder + 1 ) * ( _spectralOrder + 1 );
548  for( int q = 0; q < verts_per_quad * num_coarse_quads; q++ )
549  {
550  sv_ptr[q] = vert_handles[sv_ptr[q]];
551  assert( sv_ptr[q] );
552  }
553  }
554 
555  // Add new vertices and quads to current file set
556  faces.merge( tmp_range );
557  tmp_range.insert( start_vertex, start_vertex + nLocalVertices - 1 );
558  MB_CHK_SET_ERR( mbImpl->add_entities( _fileSet, tmp_range ),
559  "Failed to add new vertices and quads to current file set" );
560 
561  // Mark the set with the spectral order
562  Tag sporder;
563  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "SPECTRAL_ORDER", 1, MB_TYPE_INTEGER, sporder,
565  "Trouble creating SPECTRAL_ORDER tag" );
566  MB_CHK_SET_ERR( mbImpl->tag_set_data( sporder, &_fileSet, 1, &_spectralOrder ),
567  "Trouble setting data to SPECTRAL_ORDER tag" );
568 
569  if( create_gathers )
570  {
571  EntityHandle gather_set;
572  MB_CHK_SET_ERR( _readNC->readMeshIface->create_gather_set( gather_set ), "Failed to create gather set" );
573 
574  // Create vertices
575  arrays.clear();
576  // Don't need to specify allocation number here, because we know enough verts were created
577  // before
578  MB_CHK_SET_ERR( _readNC->readMeshIface->get_node_coords( 3, nVertices, 0, start_vertex, arrays ),
579  "Failed to create gather set vertices" );
580 
581  xptr = arrays[0];
582  yptr = arrays[1];
583  zptr = arrays[2];
584  for( i = 0; i < nVertices; i++ )
585  {
586  double cosphi = cos( pideg * yVertVals[i] );
587  double zmult = sin( pideg * yVertVals[i] );
588  double xmult = cosphi * cos( xVertVals[i] * pideg );
589  double ymult = cosphi * sin( xVertVals[i] * pideg );
590  xptr[i] = rad * xmult;
591  yptr[i] = rad * ymult;
592  zptr[i] = rad * zmult;
593  }
594 
595  // Get ptr to gid memory for vertices
596  Range gather_set_verts_range( start_vertex, start_vertex + nVertices - 1 );
597  MB_CHK_SET_ERR( mbImpl->tag_iterate( mGlobalIdTag, gather_set_verts_range.begin(), gather_set_verts_range.end(),
598  count, data ),
599  "Failed to iterate global id tag on gather set vertices" );
600  assert( count == nVertices );
601  gid_data = (int*)data;
602  for( int j = 1; j <= nVertices; j++ )
603  gid_data[j - 1] = j;
604  // Set the file id tag too, it should be bigger something not interfering with global id
605  if( mpFileIdTag )
606  {
607  MB_CHK_SET_ERR( mbImpl->tag_iterate( *mpFileIdTag, gather_set_verts_range.begin(),
608  gather_set_verts_range.end(), count, data ),
609  "Failed to iterate file id tag on gather set vertices" );
610  assert( count == nVertices );
611  int bytes_per_tag = 4;
612  MB_CHK_SET_ERR( mbImpl->tag_get_bytes( *mpFileIdTag, bytes_per_tag ),
613  "Can't get number of bytes for file id tag" );
614  if( 4 == bytes_per_tag )
615  {
616  gid_data = (int*)data;
617  for( int j = 1; j <= nVertices; j++ )
618  gid_data[j - 1] = nVertices + j; // Bigger than global id tag
619  }
620  else if( 8 == bytes_per_tag )
621  { // Should be a handle tag on 64 bit machine?
622  long* handle_tag_data = (long*)data;
623  for( int j = 1; j <= nVertices; j++ )
624  handle_tag_data[j - 1] = nVertices + j; // Bigger than global id tag
625  }
626  }
627 
628  MB_CHK_SET_ERR( mbImpl->add_entities( gather_set, gather_set_verts_range ),
629  "Failed to add vertices to the gather set" );
630 
631  // Create quads
632  Range gather_set_quads_range;
633  // Don't need to specify allocation number here, because we know enough quads were created
634  // before
635  MB_CHK_SET_ERR( _readNC->readMeshIface->get_element_connect( num_quads, 4, MBQUAD, 0, start_quad, conn_arr ),
636  "Failed to create gather set quads" );
637  gather_set_quads_range.insert( start_quad, start_quad + num_quads - 1 );
638  int* tmp_conn_end = ( &tmp_conn[4 * num_quads - 1] ) + 1;
639  std::copy( &tmp_conn[0], tmp_conn_end, conn_arr );
640  for( i = 0; i != 4 * num_quads; i++ )
641  conn_arr[i] += start_vertex - 1; // Connectivity array is shifted by where the gather verts start
642  MB_CHK_SET_ERR( mbImpl->add_entities( gather_set, gather_set_quads_range ),
643  "Failed to add quads to the gather set" );
644  }
645 
646  return MB_SUCCESS;
647 }
648 
649 ErrorCode NCHelperHOMME::read_ucd_variables_to_nonset_allocate( std::vector< ReadNC::VarData >& vdatas,
650  std::vector< int >& tstep_nums )
651 {
652  Interface*& mbImpl = _readNC->mbImpl;
653  std::vector< int >& dimLens = _readNC->dimLens;
654  DebugOutput& dbgOut = _readNC->dbgOut;
655 
656  Range* range = NULL;
657 
658  // Get vertices
659  Range verts;
660  MB_CHK_SET_ERR( mbImpl->get_entities_by_dimension( _fileSet, 0, verts ),
661  "Trouble getting vertices in current file set" );
662  assert( "Should only have a single vertex subrange, since they were read in one shot" && verts.psize() == 1 );
663 
664  for( unsigned int i = 0; i < vdatas.size(); i++ )
665  {
666  // Support non-set variables with 3 dimensions like (time, lev, ncol)
667  assert( 3 == vdatas[i].varDims.size() );
668 
669  // For a non-set variable, time should be the first dimension
670  assert( tDim == vdatas[i].varDims[0] );
671 
672  // Set up readStarts and readCounts
673  vdatas[i].readStarts.resize( 3 );
674  vdatas[i].readCounts.resize( 3 );
675 
676  // First: time
677  vdatas[i].readStarts[0] = 0; // This value is timestep dependent, will be set later
678  vdatas[i].readCounts[0] = 1;
679 
680  // Next: lev
681  vdatas[i].readStarts[1] = 0;
682  vdatas[i].readCounts[1] = vdatas[i].numLev;
683 
684  // Finally: ncol
685  switch( vdatas[i].entLoc )
686  {
687  case ReadNC::ENTLOCVERT:
688  // Vertices
689  // Start from the first localGidVerts
690  // Actually, this will be reset later on in a loop
691  vdatas[i].readStarts[2] = localGidVerts[0] - 1;
692  vdatas[i].readCounts[2] = nLocalVertices;
693  range = &verts;
694  break;
695  default:
696  MB_SET_ERR( MB_FAILURE, "Unexpected entity location type for variable " << vdatas[i].varName );
697  }
698 
699  // Get variable size
700  vdatas[i].sz = 1;
701  for( std::size_t idx = 0; idx != 3; idx++ )
702  vdatas[i].sz *= vdatas[i].readCounts[idx];
703 
704  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
705  {
706  dbgOut.tprintf( 2, "Reading variable %s, time step %d\n", vdatas[i].varName.c_str(), tstep_nums[t] );
707 
708  if( tstep_nums[t] >= dimLens[tDim] )
709  {
710  MB_SET_ERR( MB_INDEX_OUT_OF_RANGE, "Wrong value for timestep number " << tstep_nums[t] );
711  }
712 
713  // Get the tag to read into
714  if( !vdatas[i].varTags[t] )
715  {
716  MB_CHK_SET_ERR( get_tag_to_nonset( vdatas[i], tstep_nums[t], vdatas[i].varTags[t], vdatas[i].numLev ),
717  "Trouble getting tag for variable " << vdatas[i].varName );
718  }
719 
720  // Get ptr to tag space
721  void* data;
722  int count;
723  MB_CHK_SET_ERR( mbImpl->tag_iterate( vdatas[i].varTags[t], range->begin(), range->end(), count, data ),
724  "Failed to iterate tag for variable " << vdatas[i].varName );
725  assert( (unsigned)count == range->size() );
726  vdatas[i].varDatas[t] = data;
727  }
728  }
729 
730  return MB_SUCCESS;
731 }
732 
733 #ifdef MOAB_HAVE_PNETCDF
734 ErrorCode NCHelperHOMME::read_ucd_variables_to_nonset_async( std::vector< ReadNC::VarData >& vdatas,
735  std::vector< int >& tstep_nums )
736 {
737  DebugOutput& dbgOut = _readNC->dbgOut;
738 
740  "Trouble allocating space to read non-set variables" );
741 
742  // Finally, read into that space
743  int success;
744 
745  for( unsigned int i = 0; i < vdatas.size(); i++ )
746  {
747  std::size_t sz = vdatas[i].sz;
748 
749  // A typical supported variable: float T(time, lev, ncol)
750  // For tag values, need transpose (lev, ncol) to (ncol, lev)
751  size_t ni = vdatas[i].readCounts[2]; // ncol
752  size_t nj = 1; // Here we should just set nj to 1
753  size_t nk = vdatas[i].readCounts[1]; // lev
754 
755  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
756  {
757  // We will synchronize all these reads with the other processors,
758  // so the wait will be inside this double loop; is it too much?
759  size_t nb_reads = localGidVerts.psize();
760  std::vector< int > requests( nb_reads ), statuss( nb_reads );
761  size_t idxReq = 0;
762 
763  // Tag data for this timestep
764  void* data = vdatas[i].varDatas[t];
765 
766  // Set readStart for each timestep along time dimension
767  vdatas[i].readStarts[0] = tstep_nums[t];
768 
769  switch( vdatas[i].varDataType )
770  {
771  case NC_FLOAT:
772  case NC_DOUBLE: {
773  // Read float as double
774  std::vector< double > tmpdoubledata( sz );
775 
776  // In the case of ucd mesh, and on multiple proc,
777  // we need to read as many times as subranges we have in the
778  // localGidVerts range;
779  // basically, we have to give a different point
780  // for data to start, for every subrange :(
781  size_t indexInDoubleArray = 0;
782  size_t ic = 0;
783  for( Range::pair_iterator pair_iter = localGidVerts.pair_begin();
784  pair_iter != localGidVerts.pair_end(); ++pair_iter, ic++ )
785  {
786  EntityHandle starth = pair_iter->first;
787  EntityHandle endh = pair_iter->second; // Inclusive
788  vdatas[i].readStarts[2] = (NCDF_SIZE)( starth - 1 );
789  vdatas[i].readCounts[2] = (NCDF_SIZE)( endh - starth + 1 );
790 
791  // Do a partial read, in each subrange
792  // Wait outside this loop
793  success =
794  NCFUNCREQG( _vara_double )( _fileId, vdatas[i].varId, &( vdatas[i].readStarts[0] ),
795  &( vdatas[i].readCounts[0] ),
796  &( tmpdoubledata[indexInDoubleArray] ), &requests[idxReq++] );
797  if( success )
798  MB_SET_ERR( MB_FAILURE,
799  "Failed to read double data in a loop for variable " << vdatas[i].varName );
800  // We need to increment the index in double array for the
801  // next subrange
802  indexInDoubleArray += ( endh - starth + 1 ) * 1 * vdatas[i].numLev;
803  }
804  assert( ic == localGidVerts.psize() );
805 
806  success = mbnc_wait_all( _fileId, requests.size(), &requests[0], &statuss[0] );
807  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on wait_all" );
808 
809  if( vdatas[i].numLev > 1 )
810  // Transpose (lev, ncol) to (ncol, lev)
811  kji_to_jik_stride( ni, nj, nk, data, &tmpdoubledata[0], localGidVerts );
812  else
813  {
814  for( std::size_t idx = 0; idx != tmpdoubledata.size(); idx++ )
815  ( (double*)data )[idx] = tmpdoubledata[idx];
816  }
817 
818  break;
819  }
820  default:
821  MB_SET_ERR( MB_FAILURE, "Unexpected data type for variable " << vdatas[i].varName );
822  }
823  }
824  }
825 
826  // Debug output, if requested
827  if( 1 == dbgOut.get_verbosity() )
828  {
829  dbgOut.printf( 1, "Read variables: %s", vdatas.begin()->varName.c_str() );
830  for( unsigned int i = 1; i < vdatas.size(); i++ )
831  dbgOut.printf( 1, ", %s ", vdatas[i].varName.c_str() );
832  dbgOut.tprintf( 1, "\n" );
833  }
834 
835  return MB_SUCCESS;
836 }
837 #else
838 ErrorCode NCHelperHOMME::read_ucd_variables_to_nonset( std::vector< ReadNC::VarData >& vdatas,
839  std::vector< int >& tstep_nums )
840 {
841  DebugOutput& dbgOut = _readNC->dbgOut;
842 
844  "Trouble allocating space to read non-set variables" );
845 
846  // Finally, read into that space
847  int success;
848  for( unsigned int i = 0; i < vdatas.size(); i++ )
849  {
850  std::size_t sz = vdatas[i].sz;
851 
852  // A typical supported variable: float T(time, lev, ncol)
853  // For tag values, need transpose (lev, ncol) to (ncol, lev)
854  size_t ni = vdatas[i].readCounts[2]; // ncol
855  size_t nj = 1; // Here we should just set nj to 1
856  size_t nk = vdatas[i].readCounts[1]; // lev
857 
858  for( unsigned int t = 0; t < tstep_nums.size(); t++ )
859  {
860  // Tag data for this timestep
861  void* data = vdatas[i].varDatas[t];
862 
863  // Set readStart for each timestep along time dimension
864  vdatas[i].readStarts[0] = tstep_nums[t];
865 
866  switch( vdatas[i].varDataType )
867  {
868  case NC_FLOAT:
869  case NC_DOUBLE: {
870  // Read float as double
871  std::vector< double > tmpdoubledata( sz );
872 
873  // In the case of ucd mesh, and on multiple proc,
874  // we need to read as many times as subranges we have in the
875  // localGidVerts range;
876  // basically, we have to give a different point
877  // for data to start, for every subrange :(
878  size_t indexInDoubleArray = 0;
879  size_t ic = 0;
880  for( Range::pair_iterator pair_iter = localGidVerts.pair_begin();
881  pair_iter != localGidVerts.pair_end(); ++pair_iter, ic++ )
882  {
883  EntityHandle starth = pair_iter->first;
884  EntityHandle endh = pair_iter->second; // Inclusive
885  vdatas[i].readStarts[2] = (NCDF_SIZE)( starth - 1 );
886  vdatas[i].readCounts[2] = (NCDF_SIZE)( endh - starth + 1 );
887 
888  success = NCFUNCAG( _vara_double )( _fileId, vdatas[i].varId, &( vdatas[i].readStarts[0] ),
889  &( vdatas[i].readCounts[0] ),
890  &( tmpdoubledata[indexInDoubleArray] ) );
891  if( success )
892  MB_SET_ERR( MB_FAILURE,
893  "Failed to read double data in a loop for variable " << vdatas[i].varName );
894  // We need to increment the index in double array for the
895  // next subrange
896  indexInDoubleArray += ( endh - starth + 1 ) * 1 * vdatas[i].numLev;
897  }
898  assert( ic == localGidVerts.psize() );
899 
900  if( vdatas[i].numLev > 1 )
901  // Transpose (lev, ncol) to (ncol, lev)
902  kji_to_jik_stride( ni, nj, nk, data, &tmpdoubledata[0], localGidVerts );
903  else
904  {
905  for( std::size_t idx = 0; idx != tmpdoubledata.size(); idx++ )
906  ( (double*)data )[idx] = tmpdoubledata[idx];
907  }
908 
909  break;
910  }
911  default:
912  MB_SET_ERR( MB_FAILURE, "Unexpected data type for variable " << vdatas[i].varName );
913  }
914  }
915  }
916 
917  // Debug output, if requested
918  if( 1 == dbgOut.get_verbosity() )
919  {
920  dbgOut.printf( 1, "Read variables: %s", vdatas.begin()->varName.c_str() );
921  for( unsigned int i = 1; i < vdatas.size(); i++ )
922  dbgOut.printf( 1, ", %s ", vdatas[i].varName.c_str() );
923  dbgOut.tprintf( 1, "\n" );
924  }
925 
926  return MB_SUCCESS;
927 }
928 #endif
929 
930 } // namespace moab