Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
NCHelperESMF.cpp
Go to the documentation of this file.
1 /*
2  * NCHelperESMF.cpp
3  *
4  * Created on: Sep. 12, 2023
5  */
6 
7 #include "NCHelperESMF.hpp"
8 #include "moab/ReadUtilIface.hpp"
9 #include "moab/FileOptions.hpp"
10 #include "MBTagConventions.hpp"
11 
12 #ifdef MOAB_HAVE_ZOLTAN
14 #endif
15 
16 #include <cmath>
17 
18 namespace moab
19 {
20 
22 const double pideg = acos( -1.0 ) / 180.0;
23 
24 NCHelperESMF::NCHelperESMF( ReadNC* readNC, int fileId, const FileOptions& opts, EntityHandle fileSet )
25  : UcdNCHelper( readNC, fileId, opts, fileSet ), maxEdgesPerCell( DEFAULT_MAX_EDGES_PER_CELL ), coordDim( 0 ),
26  centerCoordsId( -1 ), degrees( true )
27 {
28 }
29 
31 {
32  std::vector< std::string >& dimNames = readNC->dimNames;
33  if( ( std::find( dimNames.begin(), dimNames.end(), std::string( "nodeCount" ) ) != dimNames.end() ) &&
34  ( std::find( dimNames.begin(), dimNames.end(), std::string( "elementCount" ) ) != dimNames.end() ) &&
35  ( std::find( dimNames.begin(), dimNames.end(), std::string( "maxNodePElement" ) ) != dimNames.end() ) &&
36  ( std::find( dimNames.begin(), dimNames.end(), std::string( "coordDim" ) ) != dimNames.end() ) )
37  {
38  return true;
39  }
40 
41  return false;
42 }
43 
45 {
46  std::vector< std::string >& dimNames = _readNC->dimNames;
47  std::vector< int >& dimLens = _readNC->dimLens;
48  std::map< std::string, ReadNC::VarData >& varInfo = _readNC->varInfo;
49 
50  unsigned int idx;
51  std::vector< std::string >::iterator vit;
52 
53  // Get max edges per cell reported in the ESMF file header
54  if( ( vit = std::find( dimNames.begin(), dimNames.end(), "maxNodePElement" ) ) != dimNames.end() )
55  {
56  idx = vit - dimNames.begin();
57  maxEdgesPerCell = dimLens[idx];
59  {
61  "maxEdgesPerCell read from the ESMF file header has exceeded " << DEFAULT_MAX_EDGES_PER_CELL );
62  }
63  }
64 
65  // Get number of cells
66  if( ( vit = std::find( dimNames.begin(), dimNames.end(), "elementCount" ) ) != dimNames.end() )
67  idx = vit - dimNames.begin();
68  else
69  {
70  MB_SET_ERR( MB_FAILURE, "Couldn't find 'elementCount' dimension" );
71  }
72  cDim = idx;
73  nCells = dimLens[idx];
74 
75  // Get number of vertices per cell (max)
76  if( ( vit = std::find( dimNames.begin(), dimNames.end(), "maxNodePElement" ) ) != dimNames.end() )
77  idx = vit - dimNames.begin();
78  else
79  {
80  MB_SET_ERR( MB_FAILURE, "Couldn't find 'maxNodePElement' dimension" );
81  }
82 
83  maxEdgesPerCell = dimLens[idx];
84 
85  // Get number of vertices
86  vDim = -1;
87  if( ( vit = std::find( dimNames.begin(), dimNames.end(), "nodeCount" ) ) != dimNames.end() )
88  idx = vit - dimNames.begin();
89  else
90  {
91  MB_SET_ERR( MB_FAILURE, "Couldn't find 'nodeCount' dimension" );
92  }
93  vDim = idx;
94  nVertices = dimLens[idx];
95 
96  // Get number of coord dimensions
97  if( ( vit = std::find( dimNames.begin(), dimNames.end(), "coordDim" ) ) != dimNames.end() )
98  idx = vit - dimNames.begin();
99  else
100  {
101  MB_SET_ERR( MB_FAILURE, "Couldn't find 'coordDim' dimension" );
102  }
103 
104  coordDim = dimLens[idx];
105 
106  int success = NCFUNC( inq_varid )( _fileId, "centerCoords", &centerCoordsId );
107  if( success ) centerCoordsId = -1; // no center coords variable
108 
109  // decide now the units, by looking at nodeCoords; they should always exist
110  int nodeCoordsId;
111  success = NCFUNC( inq_varid )( _fileId, "nodeCoords", &nodeCoordsId );
112  if( success ) MB_CHK_SET_ERR( MB_FAILURE, "Trouble getting nodeCoords" );
113 
114  auto vmit = varInfo.find( "nodeCoords" );
115  if( varInfo.end() == vmit ) MB_SET_ERR( MB_FAILURE, "Couldn't find variable " << "nodeCoords" );
116  ReadNC::VarData& glData = vmit->second;
117  auto attIt = glData.varAtts.find( "units" );
118  if( attIt != glData.varAtts.end() )
119  {
120  unsigned int sz = attIt->second.attLen;
121  std::string att_data;
122  att_data.resize( sz + 1 );
123  att_data[sz] = '\000';
124  success =
125  NCFUNC( get_att_text )( _fileId, attIt->second.attVarId, attIt->second.attName.c_str(), &att_data[0] );
126  if( 0 == success && att_data.find( "radians" ) != std::string::npos ) degrees = false;
127  }
128 
129  // Hack: create dummy variables for dimensions (like nCells) with no corresponding coordinate
130  // variables
131  MB_CHK_SET_ERR( create_dummy_variables(), "Failed to create dummy variables" );
132 
133  return MB_SUCCESS;
134 }
135 
137 {
138  bool& noMixedElements = _readNC->noMixedElements;
139  DebugOutput& dbgOut = _readNC->dbgOut;
140 
141 #ifdef MOAB_HAVE_MPI
142  int rank = 0;
143  int procs = 1;
144  bool& isParallel = _readNC->isParallel;
145  ParallelComm* myPcomm = NULL;
146  if( isParallel )
147  {
148  myPcomm = _readNC->myPcomm;
149  rank = myPcomm->proc_config().proc_rank();
150  procs = myPcomm->proc_config().proc_size();
151  }
152 
153  if( procs >= 2 )
154  {
155  // Shift rank to obtain a rotated trivial partition
156  int shifted_rank = rank;
157  int& trivialPartitionShift = _readNC->trivialPartitionShift;
158  if( trivialPartitionShift > 0 ) shifted_rank = ( rank + trivialPartitionShift ) % procs;
159 
160  // Compute the number of local cells on this proc
161  nLocalCells = int( std::floor( 1.0 * nCells / procs ) );
162 
163  // The starting global cell index in the ESMF file for this proc
164  int start_cell_idx = shifted_rank * nLocalCells;
165 
166  // Number of extra cells after equal split over procs
167  int iextra = nCells % procs;
168 
169  // Allocate extra cells over procs
170  if( shifted_rank < iextra ) nLocalCells++;
171  start_cell_idx += std::min( shifted_rank, iextra );
172 
173  start_cell_idx++; // 0 based -> 1 based
174 
175  // Redistribute local cells after trivial partition (e.g. apply Zoltan partition)
176  MB_CHK_SET_ERR( redistribute_local_cells( start_cell_idx, myPcomm ),
177  "Failed to redistribute local cells after trivial partition" );
178  }
179  else
180  {
183  }
184 #else
187 #endif
188  dbgOut.tprintf( 1, " localGidCells.psize() = %d\n", (int)localGidCells.psize() );
189  dbgOut.tprintf( 1, " localGidCells.size() = %d\n", (int)localGidCells.size() );
190 
191  // Read number of edges on each local cell, to calculate actual maxEdgesPerCell
192  int nEdgesOnCellVarId;
193  int success = NCFUNC( inq_varid )( _fileId, "numElementConn", &nEdgesOnCellVarId );
194  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get variable id of numElementConn" );
195  std::vector< int > num_edges_on_local_cells( nLocalCells );
196 
197 #ifdef MOAB_HAVE_PNETCDF
198  size_t nb_reads = localGidCells.psize();
199  std::vector< int > requests( nb_reads );
200  std::vector< int > statuss( nb_reads );
201  size_t idxReq = 0;
202 #endif
203  size_t indexInArray = 0;
204  for( Range::pair_iterator pair_iter = localGidCells.pair_begin(); pair_iter != localGidCells.pair_end();
205  ++pair_iter )
206  {
207  EntityHandle starth = pair_iter->first;
208  EntityHandle endh = pair_iter->second;
209  NCDF_SIZE read_start = (NCDF_SIZE)( starth - 1 );
210  NCDF_SIZE read_count = (NCDF_SIZE)( endh - starth + 1 );
211 
212  // Do a partial read in each subrange
213 #ifdef MOAB_HAVE_PNETCDF
214  success = NCFUNCREQG( _vara_int )( _fileId, nEdgesOnCellVarId, &read_start, &read_count,
215  &( num_edges_on_local_cells[indexInArray] ), &requests[idxReq++] );
216 #else
217  success = NCFUNCAG( _vara_int )( _fileId, nEdgesOnCellVarId, &read_start, &read_count,
218  &( num_edges_on_local_cells[indexInArray] ) );
219 #endif
220  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to read nEdgesOnCell data in a loop" );
221 
222  // Increment the index for next subrange
223  indexInArray += ( endh - starth + 1 );
224  }
225 
226 #ifdef MOAB_HAVE_PNETCDF
227  // Wait outside the loop
228  success = NCFUNC( wait_all )( _fileId, requests.size(), &requests[0], &statuss[0] );
229  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on wait_all" );
230 #endif
231 
232  // Read vertices on each local cell, to get localGidVerts and cell connectivity later
233  int verticesOnCellVarId;
234  success = NCFUNC( inq_varid )( _fileId, "elementConn", &verticesOnCellVarId );
235  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get variable id of elementConn" );
236  std::vector< int > vertices_on_local_cells( nLocalCells * maxEdgesPerCell );
237 #ifdef MOAB_HAVE_PNETCDF
238  idxReq = 0;
239 #endif
240  indexInArray = 0;
241  for( Range::pair_iterator pair_iter = localGidCells.pair_begin(); pair_iter != localGidCells.pair_end();
242  ++pair_iter )
243  {
244  EntityHandle starth = pair_iter->first;
245  EntityHandle endh = pair_iter->second;
246  NCDF_SIZE read_starts[2] = { static_cast< NCDF_SIZE >( starth - 1 ), 0 };
247  NCDF_SIZE read_counts[2] = { static_cast< NCDF_SIZE >( endh - starth + 1 ),
248  static_cast< NCDF_SIZE >( maxEdgesPerCell ) };
249 
250  // Do a partial read in each subrange
251 #ifdef MOAB_HAVE_PNETCDF
252  success = NCFUNCREQG( _vara_int )( _fileId, verticesOnCellVarId, read_starts, read_counts,
253  &( vertices_on_local_cells[indexInArray] ), &requests[idxReq++] );
254 #else
255  success = NCFUNCAG( _vara_int )( _fileId, verticesOnCellVarId, read_starts, read_counts,
256  &( vertices_on_local_cells[indexInArray] ) );
257 #endif
258  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to read verticesOnCell data in a loop" );
259 
260  // Increment the index for next subrange
261  indexInArray += ( endh - starth + 1 ) * maxEdgesPerCell;
262  }
263 
264 #ifdef MOAB_HAVE_PNETCDF
265  // Wait outside the loop
266  success = NCFUNC( wait_all )( _fileId, requests.size(), &requests[0], &statuss[0] );
267  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on wait_all" );
268 #endif
269 
270  // Correct local cell vertices array, replace the padded vertices with the last vertices
271  // in the corresponding cells; sometimes the padded vertices are 0, sometimes a large
272  // vertex id. Make sure they are consistent to our padded option
273  for( int local_cell_idx = 0; local_cell_idx < nLocalCells; local_cell_idx++ )
274  {
275  int num_edges = num_edges_on_local_cells[local_cell_idx];
276  int idx_in_local_vert_arr = local_cell_idx * maxEdgesPerCell;
277  int last_vert_idx = vertices_on_local_cells[idx_in_local_vert_arr + num_edges - 1];
278  for( int i = num_edges; i < maxEdgesPerCell; i++ )
279  vertices_on_local_cells[idx_in_local_vert_arr + i] = last_vert_idx;
280  }
281 
282  // Create local vertices
283  EntityHandle start_vertex;
284  MB_CHK_SET_ERR( create_local_vertices( vertices_on_local_cells, start_vertex ),
285  "Failed to create local vertices for MPAS mesh" );
286 
287  // Create local cells, either unpadded or padded
288  if( noMixedElements )
289  {
290  MB_CHK_SET_ERR( create_padded_local_cells( vertices_on_local_cells, start_vertex, faces ),
291  "Failed to create padded local cells for MPAS mesh" );
292  }
293  else
294  {
295  MB_CHK_SET_ERR( create_local_cells( vertices_on_local_cells, num_edges_on_local_cells, start_vertex, faces ),
296  "Failed to create local cells for MPAS mesh" );
297  }
298 
299  return MB_SUCCESS;
300 }
301 
302 #ifdef MOAB_HAVE_MPI
303 ErrorCode NCHelperESMF::redistribute_local_cells( int start_cell_idx, ParallelComm* pco )
304 {
305  // If possible, apply Zoltan partition
306  // will start from trivial partition in cell space
307  // will read cell connectivities, coordinates of vertices in conn, and then compute centers of the cells
308  //
309 #ifdef MOAB_HAVE_ZOLTAN
311  {
312  // if we have centerCoords, use them; if not, compute them for Zoltan to work
313  std::vector< double > xverts, yverts, zverts;
314  xverts.resize( nLocalCells );
315  yverts.resize( nLocalCells );
316  zverts.resize( nLocalCells );
317 
318  if( centerCoordsId >= 0 )
319  {
320  // read from file
321  std::vector< double > coords( nLocalCells * coordDim );
322 
323  NCDF_SIZE read_starts[2] = { static_cast< NCDF_SIZE >( start_cell_idx - 1 ), 0 };
324  NCDF_SIZE read_counts[2] = { static_cast< NCDF_SIZE >( nLocalCells ),
325  static_cast< NCDF_SIZE >( coordDim ) };
326  int success = NCFUNCAG( _vara_double )( _fileId, centerCoordsId, read_starts, read_counts, &( coords[0] ) );
327  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on reading center coordinates" );
328  if( 2 == coordDim )
329  {
330  double factor = 1.;
331  if( degrees ) factor = pideg;
332  for( int i = 0; i < nLocalCells; i++ )
333  {
334  double lon = coords[i * 2] * factor;
335  double lat = coords[i * 2 + 1] * factor;
336  double cosphi = cos( lat );
337  zverts[i] = sin( lat );
338  xverts[i] = cosphi * cos( lon );
339  yverts[i] = cosphi * sin( lon );
340  }
341  }
342  }
343  else
344  {
345  // Read connectivities
346  // Read vertices on each local cell, to get localGidVerts and cell connectivity later
347  int verticesOnCellVarId;
348  int success = NCFUNC( inq_varid )( _fileId, "elementConn", &verticesOnCellVarId );
349  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get variable id of elementConn" );
350  std::vector< int > vertices_on_local_cells( nLocalCells * maxEdgesPerCell );
351 
352  NCDF_SIZE read_starts[2] = { static_cast< NCDF_SIZE >( start_cell_idx - 1 ), 0 };
353  NCDF_SIZE read_counts[2] = { static_cast< NCDF_SIZE >( nLocalCells ),
354  static_cast< NCDF_SIZE >( maxEdgesPerCell ) };
355 
356  success = NCFUNCAG( _vara_int )( _fileId, verticesOnCellVarId, read_starts, read_counts,
357  &( vertices_on_local_cells[0] ) );
358  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on reading elementConn variable" );
359  std::vector< int > num_edges_on_local_cells( nLocalCells );
360 
361  NCDF_SIZE read_start = start_cell_idx - 1;
362  NCDF_SIZE read_count = (NCDF_SIZE)( nLocalCells );
363 
364  int nEdgesOnCellVarId;
365  success = NCFUNC( inq_varid )( _fileId, "numElementConn", &nEdgesOnCellVarId );
366  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get variable id of numElementConn" );
367  success = NCFUNCAG( _vara_int )( _fileId, nEdgesOnCellVarId, &read_start, &read_count,
368  &( num_edges_on_local_cells[0] ) );
369  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get numElementConn" );
370  // Make a copy of vertices_on_local_cells for sorting (keep original one to set cell
371  // connectivity later)
372 
373  // Correct local cell vertices array, replace the padded vertices with the last vertices
374  // in the corresponding cells; sometimes the padded vertices are 0, sometimes a large
375  // vertex id. Make sure they are consistent to our padded option
376  for( int local_cell_idx = 0; local_cell_idx < nLocalCells; local_cell_idx++ )
377  {
378  int num_edges = num_edges_on_local_cells[local_cell_idx];
379  int idx_in_local_vert_arr = local_cell_idx * maxEdgesPerCell;
380  int last_vert_idx = vertices_on_local_cells[idx_in_local_vert_arr + num_edges - 1];
381  for( int i = num_edges; i < maxEdgesPerCell; i++ )
382  vertices_on_local_cells[idx_in_local_vert_arr + i] = last_vert_idx;
383  }
384 
385  std::vector< int > vertices_on_local_cells_sorted( vertices_on_local_cells );
386  std::sort( vertices_on_local_cells_sorted.begin(), vertices_on_local_cells_sorted.end() );
387  std::copy( vertices_on_local_cells_sorted.rbegin(), vertices_on_local_cells_sorted.rend(),
388  range_inserter( localGidVerts ) );
390 
391  // Read nodeCoords for local vertices
392  double* coords = new double[localGidVerts.size() * coordDim];
393  int nodeCoordVarId;
394  success = NCFUNC( inq_varid )( _fileId, "nodeCoords", &nodeCoordVarId );
395  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get variable id of nodeCoords" );
396 #ifdef MOAB_HAVE_PNETCDF
397  size_t nb_reads = localGidVerts.psize();
398  std::vector< int > requests( nb_reads );
399  std::vector< int > statuss( nb_reads );
400  size_t idxReq = 0;
401 #endif
402  size_t indexInArray = 0;
403  for( Range::pair_iterator pair_iter = localGidVerts.pair_begin(); pair_iter != localGidVerts.pair_end();
404  ++pair_iter )
405  {
406  EntityHandle starth = pair_iter->first;
407  EntityHandle endh = pair_iter->second;
408  NCDF_SIZE read_starts[2] = { static_cast< NCDF_SIZE >( starth - 1 ), 0 };
409  NCDF_SIZE read_counts[2] = { static_cast< NCDF_SIZE >( endh - starth + 1 ),
410  static_cast< NCDF_SIZE >( coordDim ) };
411 
412  // Do a partial read in each subrange
413 #ifdef MOAB_HAVE_PNETCDF
414  success = NCFUNCREQG( _vara_double )( _fileId, nodeCoordVarId, read_starts, read_counts,
415  &coords[indexInArray], &requests[idxReq++] );
416 #else
417  success = NCFUNCAG( _vara_double )( _fileId, nodeCoordVarId, read_starts, read_counts,
418  &coords[indexInArray] );
419 #endif
420  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to read nodeCoords data in a loop" );
421 
422  // Increment the index for next subrange
423  indexInArray += ( endh - starth + 1 ) * coordDim;
424  }
425 
426 #ifdef MOAB_HAVE_PNETCDF
427  // Wait outside the loop
428  success = NCFUNC( wait_all )( _fileId, requests.size(), &requests[0], &statuss[0] );
429  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on wait_all" );
430 #endif
431 
432  double* coords3d = new double[localGidVerts.size() * 3];
433  // now convert from lat/lon to 3d
434  if( 2 == coordDim )
435  {
436  // basically convert from degrees to xyz on a sphere
437  double factor = 1.;
438  if( degrees ) factor = pideg;
439  for( int i = 0; i < (int)localGidVerts.size(); i++ )
440  {
441  double lon = coords[i * 2] * factor;
442  double lat = coords[i * 2 + 1] * factor;
443  double cosphi = cos( lat );
444  coords3d[3 * i + 2] = sin( lat );
445  coords3d[3 * i] = cosphi * cos( lon );
446  coords3d[3 * i + 1] = cosphi * sin( lon );
447  }
448  }
449 
450  // find the center for each cell
451  for( int i = 0; i < nLocalCells; i++ )
452  {
453  int nv = num_edges_on_local_cells[i];
454  double x = 0., y = 0., z = 0.;
455  for( int j = 0; j < nv; j++ )
456  {
457  int vertexId = vertices_on_local_cells[i * maxEdgesPerCell + j];
458  // now find what index is in gid
459  int index = localGidVerts.index( vertexId ); // it should be a binary search!
460  x += coords3d[3 * index];
461  y += coords3d[3 * index + 1];
462  z += coords3d[3 * index + 2];
463  }
464  if( nv != 0 )
465  {
466  x /= nv;
467  y /= nv;
468  z /= nv;
469  }
470  xverts[i] = x;
471  yverts[i] = y;
472  zverts[i] = z;
473  }
474  delete[] coords3d;
475  }
476  // Zoltan partition using RCB; maybe more studies would be good, as to which partition
477  // is better
478  Interface*& mbImpl = _readNC->mbImpl;
479  DebugOutput& dbgOut = _readNC->dbgOut;
480  ZoltanPartitioner* mbZTool = new ZoltanPartitioner( mbImpl, pco, false, 0, NULL );
481  MB_CHK_SET_ERR( mbZTool->repartition( xverts, yverts, zverts, start_cell_idx, "RCB", localGidCells ),
482  "Error in Zoltan partitioning" );
483  delete mbZTool;
484 
485  dbgOut.tprintf( 1, "After Zoltan partitioning, localGidCells.psize() = %d\n", (int)localGidCells.psize() );
486  dbgOut.tprintf( 1, " localGidCells.size() = %d\n", (int)localGidCells.size() );
487 
488  // This is important: local cells are now redistributed, so nLocalCells might be different!
491  return MB_SUCCESS;
492  }
493 #else
494  UNUSED( pco );
495 #endif
496 
497  // By default, apply trivial partition
498  localGidCells.insert( start_cell_idx, start_cell_idx + nLocalCells - 1 );
499 
500  return MB_SUCCESS;
501 }
502 #endif
503 
504 ErrorCode NCHelperESMF::create_local_vertices( const std::vector< int >& vertices_on_local_cells,
505  EntityHandle& start_vertex )
506 {
507  Interface*& mbImpl = _readNC->mbImpl;
508  Tag& mGlobalIdTag = _readNC->mGlobalIdTag;
509  const Tag*& mpFileIdTag = _readNC->mpFileIdTag;
510  DebugOutput& dbgOut = _readNC->dbgOut;
511 
512  // Make a copy of vertices_on_local_cells for sorting (keep original one to set cell
513  // connectivity later)
514  std::vector< int > vertices_on_local_cells_sorted( vertices_on_local_cells );
515  std::sort( vertices_on_local_cells_sorted.begin(), vertices_on_local_cells_sorted.end() );
516  std::copy( vertices_on_local_cells_sorted.rbegin(), vertices_on_local_cells_sorted.rend(),
519 
520  dbgOut.tprintf( 1, " localGidVerts.psize() = %d\n", (int)localGidVerts.psize() );
521  dbgOut.tprintf( 1, " localGidVerts.size() = %d\n", (int)localGidVerts.size() );
522 
523  // Create local vertices
524  std::vector< double* > arrays;
525  MB_CHK_SET_ERR( _readNC->readMeshIface->get_node_coords( 3, nLocalVertices, 0, start_vertex, arrays,
526  nLocalVertices ),
527  "Failed to create local vertices" );
528 
529  // Add local vertices to current file set
530  Range local_verts_range( start_vertex, start_vertex + nLocalVertices - 1 );
531  MB_CHK_SET_ERR( _readNC->mbImpl->add_entities( _fileSet, local_verts_range ),
532  "Failed to add local vertices to current file set" );
533 
534  // Get ptr to GID memory for local vertices
535  int count = 0;
536  void* data = NULL;
537  MB_CHK_SET_ERR( mbImpl->tag_iterate( mGlobalIdTag, local_verts_range.begin(), local_verts_range.end(), count,
538  data ),
539  "Failed to iterate global id tag on local vertices" );
540  assert( count == nLocalVertices );
541  int* gid_data = (int*)data;
542  std::copy( localGidVerts.begin(), localGidVerts.end(), gid_data );
543 
544  // Duplicate GID data, which will be used to resolve sharing
545  if( mpFileIdTag )
546  {
547  MB_CHK_SET_ERR( mbImpl->tag_iterate( *mpFileIdTag, local_verts_range.begin(), local_verts_range.end(), count,
548  data ),
549  "Failed to iterate file id tag on local vertices" );
550  assert( count == nLocalVertices );
551  int bytes_per_tag = 4;
552  MB_CHK_SET_ERR( mbImpl->tag_get_bytes( *mpFileIdTag, bytes_per_tag ),
553  "Can't get number of bytes for file id tag" );
554  if( 4 == bytes_per_tag )
555  {
556  gid_data = (int*)data;
557  std::copy( localGidVerts.begin(), localGidVerts.end(), gid_data );
558  }
559  else if( 8 == bytes_per_tag )
560  { // Should be a handle tag on 64 bit machine?
561  long* handle_tag_data = (long*)data;
562  std::copy( localGidVerts.begin(), localGidVerts.end(), handle_tag_data );
563  }
564  }
565 
566 #ifdef MOAB_HAVE_PNETCDF
567  size_t nb_reads = localGidVerts.psize();
568  std::vector< int > requests( nb_reads );
569  std::vector< int > statuss( nb_reads );
570  size_t idxReq = 0;
571 #endif
572 
573  // Read nodeCoords for local vertices
574  double* coords = new double[localGidVerts.size() * coordDim];
575  int nodeCoordVarId;
576  int success = NCFUNC( inq_varid )( _fileId, "nodeCoords", &nodeCoordVarId );
577  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to get variable id of nodeCoords" );
578  size_t indexInArray = 0;
579  for( Range::pair_iterator pair_iter = localGidVerts.pair_begin(); pair_iter != localGidVerts.pair_end();
580  ++pair_iter )
581  {
582  EntityHandle starth = pair_iter->first;
583  EntityHandle endh = pair_iter->second;
584  NCDF_SIZE read_starts[2] = { static_cast< NCDF_SIZE >( starth - 1 ), 0 };
585  NCDF_SIZE read_counts[2] = { static_cast< NCDF_SIZE >( endh - starth + 1 ),
586  static_cast< NCDF_SIZE >( coordDim ) };
587 
588  // Do a partial read in each subrange
589 #ifdef MOAB_HAVE_PNETCDF
590  success = NCFUNCREQG( _vara_double )( _fileId, nodeCoordVarId, read_starts, read_counts, &coords[indexInArray],
591  &requests[idxReq++] );
592 #else
593  success = NCFUNCAG( _vara_double )( _fileId, nodeCoordVarId, read_starts, read_counts, &coords[indexInArray] );
594 #endif
595  if( success ) MB_SET_ERR( MB_FAILURE, "Failed to read nodeCoords data in a loop" );
596 
597  // Increment the index for next subrange
598  indexInArray += ( endh - starth + 1 ) * coordDim;
599  }
600 
601 #ifdef MOAB_HAVE_PNETCDF
602  // Wait outside the loop
603  success = NCFUNC( wait_all )( _fileId, requests.size(), &requests[0], &statuss[0] );
604  if( success ) MB_SET_ERR( MB_FAILURE, "Failed on wait_all" );
605 #endif
606 
607  // now convert from lat/lon to 3d
608  if( 2 == coordDim )
609  {
610  // basically convert from degrees to xyz on a sphere
611  double factor = 1;
612  if( degrees ) factor = pideg;
613  for( int i = 0; i < (int)localGidVerts.size(); i++ )
614  {
615  double lon = coords[i * 2] * factor;
616  double lat = coords[i * 2 + 1] * factor;
617  double cosphi = cos( lat );
618  arrays[2][i] = sin( lat );
619  arrays[0][i] = cosphi * cos( lon );
620  arrays[1][i] = cosphi * sin( lon );
621  }
622  }
623  delete[] coords;
624  return MB_SUCCESS;
625 }
626 
627 ErrorCode NCHelperESMF::create_local_cells( const std::vector< int >& vertices_on_local_cells,
628  const std::vector< int >& num_edges_on_local_cells,
629  EntityHandle start_vertex,
630  Range& faces )
631 {
632  Interface*& mbImpl = _readNC->mbImpl;
633  Tag& mGlobalIdTag = _readNC->mGlobalIdTag;
634 
635  // Divide local cells into groups based on the number of edges
636  Range local_cells_with_n_edges[DEFAULT_MAX_EDGES_PER_CELL + 1];
637  // Insert larger values before smaller ones to increase efficiency
638  for( int i = nLocalCells - 1; i >= 0; i-- )
639  {
640  int num_edges = num_edges_on_local_cells[i];
641  local_cells_with_n_edges[num_edges].insert( localGidCells[i] ); // Global cell index
642  }
643 
644  std::vector< int > num_edges_on_cell_groups;
645  for( int i = 3; i <= maxEdgesPerCell; i++ )
646  {
647  if( local_cells_with_n_edges[i].size() > 0 ) num_edges_on_cell_groups.push_back( i );
648  }
649  int numCellGroups = (int)num_edges_on_cell_groups.size();
650  EntityHandle* conn_arr_local_cells_with_n_edges[DEFAULT_MAX_EDGES_PER_CELL + 1];
651  for( int i = 0; i < numCellGroups; i++ )
652  {
653  int num_edges_per_cell = num_edges_on_cell_groups[i];
654  int num_group_cells = (int)local_cells_with_n_edges[num_edges_per_cell].size();
655 
656  EntityType typeEl = MBTRI;
657  if( num_edges_per_cell == 4 ) typeEl = MBQUAD;
658  if( num_edges_per_cell > 4 ) typeEl = MBPOLYGON;
659  // Create local cells for each non-empty cell group
660  EntityHandle start_element;
662  num_group_cells, num_edges_per_cell, typeEl, 0, start_element,
663  conn_arr_local_cells_with_n_edges[num_edges_per_cell], num_group_cells ),
664  "Failed to create local cells" );
665  faces.insert( start_element, start_element + num_group_cells - 1 );
666 
667  // Add local cells to current file set
668  Range local_cells_range( start_element, start_element + num_group_cells - 1 );
669  MB_CHK_SET_ERR( _readNC->mbImpl->add_entities( _fileSet, local_cells_range ),
670  "Failed to add local cells to current file set" );
671 
672  // Get ptr to gid memory for local cells
673  int count = 0;
674  void* data = NULL;
675  MB_CHK_SET_ERR( mbImpl->tag_iterate( mGlobalIdTag, local_cells_range.begin(), local_cells_range.end(), count,
676  data ),
677  "Failed to iterate global id tag on local cells" );
678  assert( count == num_group_cells );
679  int* gid_data = (int*)data;
680  std::copy( local_cells_with_n_edges[num_edges_per_cell].begin(),
681  local_cells_with_n_edges[num_edges_per_cell].end(), gid_data );
682 
683  // Set connectivity array with proper local vertices handles
684  for( int j = 0; j < num_group_cells; j++ )
685  {
686  EntityHandle global_cell_idx =
687  local_cells_with_n_edges[num_edges_per_cell][j]; // Global cell index, 1 based
688  int local_cell_idx = localGidCells.index( global_cell_idx ); // Local cell index, 0 based
689  assert( local_cell_idx != -1 );
690 
691  for( int k = 0; k < num_edges_per_cell; k++ )
692  {
693  EntityHandle global_vert_idx =
694  vertices_on_local_cells[local_cell_idx * maxEdgesPerCell + k]; // Global vertex index, 1 based
695  int local_vert_idx = localGidVerts.index( global_vert_idx ); // Local vertex index, 0 based
696  assert( local_vert_idx != -1 );
697  conn_arr_local_cells_with_n_edges[num_edges_per_cell][j * num_edges_per_cell + k] =
698  start_vertex + local_vert_idx;
699  }
700  }
701  }
702 
703  return MB_SUCCESS;
704 }
705 
706 ErrorCode NCHelperESMF::create_padded_local_cells( const std::vector< int >& vertices_on_local_cells,
707  EntityHandle start_vertex,
708  Range& faces )
709 {
710  Interface*& mbImpl = _readNC->mbImpl;
711  Tag& mGlobalIdTag = _readNC->mGlobalIdTag;
712 
713  // Create cells for this cell group
714  EntityHandle start_element;
715  EntityHandle* conn_arr_local_cells = NULL;
717  start_element, conn_arr_local_cells, nLocalCells ),
718  "Failed to create local cells" );
719  faces.insert( start_element, start_element + nLocalCells - 1 );
720 
721  // Add local cells to current file set
722  Range local_cells_range( start_element, start_element + nLocalCells - 1 );
723  MB_CHK_SET_ERR( _readNC->mbImpl->add_entities( _fileSet, local_cells_range ),
724  "Failed to add local cells to current file set" );
725 
726  // Get ptr to GID memory for local cells
727  int count = 0;
728  void* data = NULL;
729  MB_CHK_SET_ERR( mbImpl->tag_iterate( mGlobalIdTag, local_cells_range.begin(), local_cells_range.end(), count,
730  data ),
731  "Failed to iterate global id tag on local cells" );
732  assert( count == nLocalCells );
733  int* gid_data = (int*)data;
734  std::copy( localGidCells.begin(), localGidCells.end(), gid_data );
735 
736  // Set connectivity array with proper local vertices handles
737  // vertices_on_local_cells array was already corrected to have the last vertices padded
738  // no need for extra checks considering
739  for( int local_cell_idx = 0; local_cell_idx < nLocalCells; local_cell_idx++ )
740  {
741  for( int i = 0; i < maxEdgesPerCell; i++ )
742  {
743  EntityHandle global_vert_idx =
744  vertices_on_local_cells[local_cell_idx * maxEdgesPerCell + i]; // Global vertex index, 1 based
745  int local_vert_idx = localGidVerts.index( global_vert_idx ); // Local vertex index, 0 based
746  assert( local_vert_idx != -1 );
747  conn_arr_local_cells[local_cell_idx * maxEdgesPerCell + i] = start_vertex + local_vert_idx;
748  }
749  }
750 
751  return MB_SUCCESS;
752 }
753 
754 } // namespace moab