Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
MBNcDispatch.cpp
Go to the documentation of this file.
1 //-------------------------------------------------------------------------
2 // Filename : MBNcDispatch.cpp
3 //
4 // Purpose : Runtime dispatch implementations. See MBNcDispatch.hpp
5 // for design notes.
6 //
7 // Creator : Vijay Mahadevan, 2026-06-11
8 //-------------------------------------------------------------------------
9 
10 #include "MBNcDispatch.hpp"
11 
12 #include <cstdio> // std::fopen / std::fread / std::fclose for format probe
13 #include <cstring> // std::memset
14 #include <vector> // std::vector buffers used by NCB_BUFFERED scatter helpers
15 
16 #ifdef MOAB_HAVE_MPI
17 #include <map>
18 #endif
19 
20 // Phase-1 limitation: this dispatch layer always needs the standard NetCDF
21 // C API for the serial / parallel-NetCDF / buffered backends. Pure-PNetCDF
22 // builds (HAVE_PNETCDF without HAVE_NETCDF) are rare and currently fall
23 // outside this refactor's scope — they continue to work via the legacy
24 // NCFUNC macro path until that case is wired up here.
25 #ifndef MOAB_HAVE_NETCDF
26 #error "MBNcDispatch currently requires MOAB_HAVE_NETCDF. PNetCDF-only builds are not yet supported by this layer."
27 #endif
28 
29 namespace moab
30 {
31 
32 // ============================================================================
33 // Internal helpers
34 // ============================================================================
35 
36 namespace
37 {
38 
39 /// Maximum NetCDF variable rank we expect to encounter. Plenty for any
40 /// climate / mesh file (typical max is 4–5). Used for stack-allocated
41 /// size_t ↔ MPI_Offset conversion buffers in PNetCDF dispatches.
42 constexpr int kMaxDims = 16;
43 
44 #ifdef MOAB_HAVE_PNETCDF
45 /// Inquire variable rank and convert size_t start/count arrays to MPI_Offset
46 /// in caller-supplied stack buffers. Returns NC_NOERR on success or the
47 /// underlying ncmpi error code.
48 inline int to_mpi_offset_pair( int libId, int varid, const size_t* start, const size_t* count, MPI_Offset* outStart,
49  MPI_Offset* outCount, int* outNdims )
50 {
51  int ndims;
52  int rc = ncmpi_inq_varndims( libId, varid, &ndims );
53  if( rc != NC_NOERR ) return rc;
54  if( ndims > kMaxDims ) return NC_EMAXDIMS;
55  for( int i = 0; i < ndims; ++i )
56  {
57  outStart[i] = static_cast< MPI_Offset >( start[i] );
58  outCount[i] = static_cast< MPI_Offset >( count[i] );
59  }
60  *outNdims = ndims;
61  return NC_NOERR;
62 }
63 #endif
64 
65 #ifdef MOAB_HAVE_MPI
66 /// Buffered-context registry. Only NCB_BUFFERED files appear here; the
67 /// other backends carry all needed state internally. Map is keyed by the
68 /// tagged file id (so PNetCDF / NetCDF id collisions are impossible).
69 struct BufferedCtx
70 {
71  MPI_Comm comm;
72  int rank;
73  int size;
74 };
75 
76 std::map< int, BufferedCtx >& buffered_registry()
77 {
78  static std::map< int, BufferedCtx > reg;
79  return reg;
80 }
81 
82 // ----------------------------------------------------------------------------
83 // NCB_BUFFERED helpers — rank-0-reads-and-distributes pattern
84 //
85 // In buffered mode only rank 0 has the file open (via plain serial nc_open).
86 // All NC calls are collective by convention: each helper broadcasts rank
87 // 0's result for cheap inquiries / attributes, or pulls per-rank slabs to
88 // rank 0 + ships back the answer for variable reads.
89 // ----------------------------------------------------------------------------
90 
91 /// Rank 0 already called the underlying nc_* function and produced
92 /// (rc_root, value_root). Broadcast both. Returns rc_root on failure;
93 /// otherwise writes value_root into *out and returns NC_NOERR.
94 inline int bsuf_bcast_int( int taggedId, int rc_root, int value_root, int* out )
95 {
96  MPI_Comm comm = mbnc_buffered_comm( taggedId );
97  MPI_Bcast( &rc_root, 1, MPI_INT, 0, comm );
98  if( rc_root != NC_NOERR ) return rc_root;
99  MPI_Bcast( &value_root, 1, MPI_INT, 0, comm );
100  if( out ) *out = value_root;
101  return NC_NOERR;
102 }
103 
104 /// As bsuf_bcast_int but for size_t outputs.
105 inline int bsuf_bcast_size( int taggedId, int rc_root, size_t value_root, size_t* out )
106 {
107  MPI_Comm comm = mbnc_buffered_comm( taggedId );
108  MPI_Bcast( &rc_root, 1, MPI_INT, 0, comm );
109  if( rc_root != NC_NOERR ) return rc_root;
110  MPI_Bcast( &value_root, static_cast< int >( sizeof( size_t ) ), MPI_BYTE, 0, comm );
111  if( out ) *out = value_root;
112  return NC_NOERR;
113 }
114 
115 /// Broadcast a fixed-length byte buffer (name strings, attribute text).
116 /// Caller pre-sized buffer; rank 0 filled it.
117 inline int bsuf_bcast_bytes( int taggedId, int rc_root, char* buffer, int nbytes )
118 {
119  MPI_Comm comm = mbnc_buffered_comm( taggedId );
120  MPI_Bcast( &rc_root, 1, MPI_INT, 0, comm );
121  if( rc_root != NC_NOERR ) return rc_root;
122  if( buffer && nbytes > 0 ) MPI_Bcast( buffer, nbytes, MPI_BYTE, 0, comm );
123  return NC_NOERR;
124 }
125 
126 /// Broadcast an int-array result (e.g. inq_vardimid output).
127 inline int bsuf_bcast_int_array( int taggedId, int rc_root, int* buf, int n )
128 {
129  MPI_Comm comm = mbnc_buffered_comm( taggedId );
130  MPI_Bcast( &rc_root, 1, MPI_INT, 0, comm );
131  if( rc_root != NC_NOERR ) return rc_root;
132  if( buf && n > 0 ) MPI_Bcast( buf, n, MPI_INT, 0, comm );
133  return NC_NOERR;
134 }
135 
136 /// Generic buffered get_vara_T: rank 0 owns the file; each non-root rank
137 /// sends its (start, count) to rank 0, rank 0 issues the underlying
138 /// nc_get_vara_T per requester and ships the data back.
139 ///
140 /// nc_get_fn is the serial libnetcdf entry point with signature
141 /// int(*)(int ncid, int varid, const size_t* start, const size_t* count, T* data)
142 template < typename T, typename Fn >
143 int bsuf_get_vara( int taggedFileId, int varid, const size_t* start, const size_t* count, T* data,
144  MPI_Datatype mpi_type, Fn nc_get_fn )
145 {
146  MPI_Comm comm = mbnc_buffered_comm( taggedFileId );
147  const int rank = mbnc_buffered_rank( taggedFileId );
148  const int size = mbnc_buffered_size( taggedFileId );
149  const int libId = mbnc_lib_id( taggedFileId );
150 
151  // Step 1: rank 0 inquires ndims (only rank 0 has the file), broadcast.
152  int ndims = 0;
153  int rc = NC_NOERR;
154  if( rank == 0 ) rc = nc_inq_varndims( libId, varid, &ndims );
155  MPI_Bcast( &rc, 1, MPI_INT, 0, comm );
156  if( rc != NC_NOERR ) return rc;
157  MPI_Bcast( &ndims, 1, MPI_INT, 0, comm );
158  if( ndims > kMaxDims ) return NC_EMAXDIMS;
159 
160  // Step 2: compute local total element count from caller's count[]
161  size_t my_n = 1;
162  for( int i = 0; i < ndims; ++i )
163  my_n *= count[i];
164 
165  if( rank == 0 )
166  {
167  // Step 3a: rank 0 services its own request directly
168  int my_rc = nc_get_fn( libId, varid, start, count, data );
169 
170  // Step 3b: serve other ranks in rank order. Each receives its
171  // own slab, computed from its own start/count.
172  for( int src = 1; src < size; ++src )
173  {
174  size_t srcStart[kMaxDims], srcCount[kMaxDims];
175  MPI_Recv( srcStart, ndims * static_cast< int >( sizeof( size_t ) ), MPI_BYTE, src, 0, comm,
176  MPI_STATUS_IGNORE );
177  MPI_Recv( srcCount, ndims * static_cast< int >( sizeof( size_t ) ), MPI_BYTE, src, 1, comm,
178  MPI_STATUS_IGNORE );
179  size_t n = 1;
180  for( int i = 0; i < ndims; ++i )
181  n *= srcCount[i];
182  std::vector< T > buf( n );
183  int s_rc = nc_get_fn( libId, varid, srcStart, srcCount, buf.data() );
184  MPI_Send( &s_rc, 1, MPI_INT, src, 2, comm );
185  if( s_rc == NC_NOERR && n > 0 ) MPI_Send( buf.data(), static_cast< int >( n ), mpi_type, src, 3, comm );
186  }
187  return my_rc;
188  }
189  else
190  {
191  // Non-root: send my (start, count) to rank 0, get back data.
192  MPI_Send( start, ndims * static_cast< int >( sizeof( size_t ) ), MPI_BYTE, 0, 0, comm );
193  MPI_Send( count, ndims * static_cast< int >( sizeof( size_t ) ), MPI_BYTE, 0, 1, comm );
194  int recv_rc;
195  MPI_Recv( &recv_rc, 1, MPI_INT, 0, 2, comm, MPI_STATUS_IGNORE );
196  if( recv_rc == NC_NOERR && my_n > 0 )
197  MPI_Recv( data, static_cast< int >( my_n ), mpi_type, 0, 3, comm, MPI_STATUS_IGNORE );
198  return recv_rc;
199  }
200 }
201 #endif
202 
203 } // namespace
204 
205 // ============================================================================
206 // Format probe
207 // ============================================================================
208 
209 int mbnc_detect_format( const char* path )
210 {
211  std::FILE* fp = std::fopen( path, "rb" );
212  if( !fp ) return NCFMT_UNKNOWN;
213 
214  unsigned char magic[8];
215  std::memset( magic, 0, sizeof( magic ) );
216  const size_t nread = std::fread( magic, 1, sizeof( magic ), fp );
217  std::fclose( fp );
218 
219  if( nread < 4 ) return NCFMT_UNKNOWN;
220 
221  // Classic NetCDF families: "CDF" + version byte (0x01, 0x02, or 0x05)
222  if( magic[0] == 'C' && magic[1] == 'D' && magic[2] == 'F' )
223  {
224  if( magic[3] == 0x01 || magic[3] == 0x02 || magic[3] == 0x05 ) return NCFMT_CLASSIC;
225  }
226 
227  // HDF5 superblock signature (used by NetCDF-4)
228  if( nread >= 8 && magic[0] == 0x89 && magic[1] == 'H' && magic[2] == 'D' && magic[3] == 'F' && magic[4] == 0x0D &&
229  magic[5] == 0x0A && magic[6] == 0x1A && magic[7] == 0x0A )
230  {
231  return NCFMT_NETCDF4;
232  }
233 
234  return NCFMT_UNKNOWN;
235 }
236 
237 // ============================================================================
238 // Backend chooser
239 // ============================================================================
240 
241 NcBackend mbnc_choose_backend_for_read( int format, int mpi_size )
242 {
243  if( format != NCFMT_CLASSIC && format != NCFMT_NETCDF4 ) return NCB_NONE;
244 
245  // Serial: plain nc_* always handles both formats (libnetcdf v4+).
246  if( mpi_size <= 1 ) return NCB_NETCDF_SERIAL;
247 
248  if( format == NCFMT_CLASSIC )
249  {
250 #ifdef MOAB_HAVE_PNETCDF
251  return NCB_PNETCDF; // best fit for classic in parallel
252 #elif defined( MOAB_HAVE_NETCDFPAR )
253  return NCB_NETCDF_PAR; // works if libnetcdf was built with PNetCDF backend
254 #else
255  // No parallel backend at all — degraded buffered fallback (rank 0
256  // reads via plain nc_*, scatters per-rank slabs).
257  return NCB_BUFFERED;
258 #endif
259  }
260 
261  // format == NCFMT_NETCDF4
262 #ifdef MOAB_HAVE_NETCDFPAR
263  return NCB_NETCDF_PAR;
264 #else
265  // PNetCDF alone cannot read NetCDF-4 in parallel. Buffered fallback:
266  // rank 0 opens with serial nc_open (libnetcdf handles HDF5 in serial),
267  // scatters per-rank slabs to the rest.
268  return NCB_BUFFERED;
269 #endif
270 }
271 
272 NcBackend mbnc_choose_backend_for_write( int requested_format, int mpi_size )
273 {
274  // For writes, format is the user's intent — same matrix.
275  return mbnc_choose_backend_for_read( requested_format, mpi_size );
276 }
277 
278 // ============================================================================
279 // Buffered-context registry
280 // ============================================================================
281 
282 #ifdef MOAB_HAVE_MPI
283 void mbnc_register_buffered( int taggedFileId, MPI_Comm comm )
284 {
285  BufferedCtx ctx;
286  ctx.comm = comm;
287  MPI_Comm_rank( comm, &ctx.rank );
288  MPI_Comm_size( comm, &ctx.size );
289  buffered_registry()[taggedFileId] = ctx;
290 }
291 
292 void mbnc_unregister_buffered( int taggedFileId )
293 {
294  buffered_registry().erase( taggedFileId );
295 }
296 
297 MPI_Comm mbnc_buffered_comm( int taggedFileId )
298 {
299  auto it = buffered_registry().find( taggedFileId );
300  return ( it == buffered_registry().end() ) ? MPI_COMM_NULL : it->second.comm;
301 }
302 
303 int mbnc_buffered_rank( int taggedFileId )
304 {
305  auto it = buffered_registry().find( taggedFileId );
306  return ( it == buffered_registry().end() ) ? -1 : it->second.rank;
307 }
308 
309 int mbnc_buffered_size( int taggedFileId )
310 {
311  auto it = buffered_registry().find( taggedFileId );
312  return ( it == buffered_registry().end() ) ? 0 : it->second.size;
313 }
314 #endif
315 
316 // ============================================================================
317 // File open / close / create
318 // ============================================================================
319 
320 #ifdef MOAB_HAVE_MPI
321 int mbnc_open_par( NcBackend backend, MPI_Comm comm, MPI_Info info, const char* path, int omode, int* taggedFileId )
322 {
323  int libId = -1;
324  int rc = NC_EBADID;
325 
326  switch( backend )
327  {
328 #ifdef MOAB_HAVE_PNETCDF
329  case NCB_PNETCDF:
330  rc = ncmpi_open( comm, path, omode, info, &libId );
331  break;
332 #endif
333 #ifdef MOAB_HAVE_NETCDFPAR
334  case NCB_NETCDF_PAR:
335  rc = nc_open_par( path, omode | NC_MPIIO, comm, info, &libId );
336  break;
337 #endif
338  case NCB_BUFFERED: {
339  // Rank 0 opens; other ranks defer to rank 0 for every subsequent call.
340  int rank = 0;
341  MPI_Comm_rank( comm, &rank );
342  if( rank == 0 ) rc = nc_open( path, omode, &libId );
343  MPI_Bcast( &rc, 1, MPI_INT, 0, comm );
344  // libId is meaningful only on rank 0, but every rank still gets a tagged
345  // handle — the tag carries the backend, and the registry carries the comm.
346  if( rc == NC_NOERR )
347  {
348  *taggedFileId = mbnc_make_tagged( libId, NCB_BUFFERED );
349  mbnc_register_buffered( *taggedFileId, comm );
350  }
351  return rc;
352  }
353  case NCB_NETCDF_SERIAL:
354  // Caller asked for serial open via the parallel entry point — honor it.
355  rc = nc_open( path, omode, &libId );
356  break;
357  default:
358  return NC_EBADID;
359  }
360 
361  if( rc == NC_NOERR ) *taggedFileId = mbnc_make_tagged( libId, backend );
362  return rc;
363 }
364 
365 int mbnc_create_par( NcBackend backend, MPI_Comm comm, MPI_Info info, const char* path, int cmode, int* taggedFileId )
366 {
367  int libId = -1;
368  int rc = NC_EBADID;
369 
370  switch( backend )
371  {
372 #ifdef MOAB_HAVE_PNETCDF
373  case NCB_PNETCDF:
374  rc = ncmpi_create( comm, path, cmode, info, &libId );
375  break;
376 #endif
377 #ifdef MOAB_HAVE_NETCDFPAR
378  case NCB_NETCDF_PAR:
379  rc = nc_create_par( path, cmode | NC_MPIIO, comm, info, &libId );
380  break;
381 #endif
382  case NCB_BUFFERED: {
383  int rank = 0;
384  MPI_Comm_rank( comm, &rank );
385  if( rank == 0 ) rc = nc_create( path, cmode, &libId );
386  MPI_Bcast( &rc, 1, MPI_INT, 0, comm );
387  if( rc == NC_NOERR )
388  {
389  *taggedFileId = mbnc_make_tagged( libId, NCB_BUFFERED );
390  mbnc_register_buffered( *taggedFileId, comm );
391  }
392  return rc;
393  }
394  case NCB_NETCDF_SERIAL:
395  rc = nc_create( path, cmode, &libId );
396  break;
397  default:
398  return NC_EBADID;
399  }
400 
401  if( rc == NC_NOERR ) *taggedFileId = mbnc_make_tagged( libId, backend );
402  return rc;
403 }
404 #endif // MOAB_HAVE_MPI
405 
406 int mbnc_open( const char* path, int omode, int* taggedFileId )
407 {
408  int libId = -1;
409  int rc = nc_open( path, omode, &libId );
410  if( rc == NC_NOERR ) *taggedFileId = mbnc_make_tagged( libId, NCB_NETCDF_SERIAL );
411  return rc;
412 }
413 
414 int mbnc_create( const char* path, int cmode, int* taggedFileId )
415 {
416  int libId = -1;
417  int rc = nc_create( path, cmode, &libId );
418  if( rc == NC_NOERR ) *taggedFileId = mbnc_make_tagged( libId, NCB_NETCDF_SERIAL );
419  return rc;
420 }
421 
422 int mbnc_close( int taggedFileId )
423 {
424  const int libId = mbnc_lib_id( taggedFileId );
425  const NcBackend backend = mbnc_backend_of( taggedFileId );
426  int rc = NC_EBADID;
427 
428  switch( backend )
429  {
430 #ifdef MOAB_HAVE_PNETCDF
431  case NCB_PNETCDF:
432  rc = ncmpi_close( libId );
433  break;
434 #endif
435 #ifdef MOAB_HAVE_NETCDFPAR
436  case NCB_NETCDF_PAR:
437  rc = nc_close( libId ); // parallel handle uses nc_close
438  break;
439 #endif
440  case NCB_NETCDF_SERIAL:
441  rc = nc_close( libId );
442  break;
443 #ifdef MOAB_HAVE_MPI
444  case NCB_BUFFERED: {
445  // Only rank 0 actually has an open file.
446  const int rank = mbnc_buffered_rank( taggedFileId );
447  if( rank == 0 ) rc = nc_close( libId );
448  MPI_Bcast( &rc, 1, MPI_INT, 0, mbnc_buffered_comm( taggedFileId ) );
449  mbnc_unregister_buffered( taggedFileId );
450  return rc;
451  }
452 #endif
453  default:
454  break;
455  }
456 
457  return rc;
458 }
459 
460 // ============================================================================
461 // Define mode
462 // ============================================================================
463 
464 int mbnc_redef( int taggedFileId )
465 {
466  const int libId = mbnc_lib_id( taggedFileId );
467  const NcBackend backend = mbnc_backend_of( taggedFileId );
468 #ifdef MOAB_HAVE_PNETCDF
469  if( backend == NCB_PNETCDF ) return ncmpi_redef( libId );
470 #endif
471  (void)backend;
472  return nc_redef( libId );
473 }
474 
475 int mbnc_enddef( int taggedFileId )
476 {
477  const int libId = mbnc_lib_id( taggedFileId );
478  const NcBackend backend = mbnc_backend_of( taggedFileId );
479 #ifdef MOAB_HAVE_PNETCDF
480  if( backend == NCB_PNETCDF ) return ncmpi_enddef( libId );
481 #endif
482  (void)backend;
483  return nc_enddef( libId );
484 }
485 
486 int mbnc_def_dim( int taggedFileId, const char* name, size_t len, int* dimid )
487 {
488  const int libId = mbnc_lib_id( taggedFileId );
489  const NcBackend backend = mbnc_backend_of( taggedFileId );
490 #ifdef MOAB_HAVE_PNETCDF
491  if( backend == NCB_PNETCDF ) return ncmpi_def_dim( libId, name, static_cast< MPI_Offset >( len ), dimid );
492 #endif
493  (void)backend;
494  return nc_def_dim( libId, name, len, dimid );
495 }
496 
497 int mbnc_def_var( int taggedFileId, const char* name, nc_type xtype, int ndims, const int* dimids, int* varid )
498 {
499  const int libId = mbnc_lib_id( taggedFileId );
500  const NcBackend backend = mbnc_backend_of( taggedFileId );
501 #ifdef MOAB_HAVE_PNETCDF
502  if( backend == NCB_PNETCDF ) return ncmpi_def_var( libId, name, xtype, ndims, dimids, varid );
503 #endif
504  (void)backend;
505  return nc_def_var( libId, name, xtype, ndims, dimids, varid );
506 }
507 
508 // ============================================================================
509 // PNetCDF independent / collective mode toggles
510 //
511 // On non-PNetCDF backends these are no-ops returning NC_NOERR — standard
512 // NetCDF does not separate independent and collective I/O modes the way
513 // PNetCDF does.
514 // ============================================================================
515 
516 int mbnc_begin_indep_data( int taggedFileId )
517 {
518  const NcBackend backend = mbnc_backend_of( taggedFileId );
519 #ifdef MOAB_HAVE_PNETCDF
520  if( backend == NCB_PNETCDF ) return ncmpi_begin_indep_data( mbnc_lib_id( taggedFileId ) );
521 #endif
522  (void)backend;
523  return NC_NOERR;
524 }
525 
526 int mbnc_end_indep_data( int taggedFileId )
527 {
528  const NcBackend backend = mbnc_backend_of( taggedFileId );
529 #ifdef MOAB_HAVE_PNETCDF
530  if( backend == NCB_PNETCDF ) return ncmpi_end_indep_data( mbnc_lib_id( taggedFileId ) );
531 #endif
532  (void)backend;
533  return NC_NOERR;
534 }
535 
536 // ============================================================================
537 // Inquiry wrappers
538 //
539 // All take size_t pointers for length-style outputs. The PNetCDF branch
540 // receives into a local MPI_Offset and converts on return.
541 // ============================================================================
542 
543 int mbnc_inq_natts( int taggedFileId, int* nattsp )
544 {
545  const int libId = mbnc_lib_id( taggedFileId );
546  const NcBackend backend = mbnc_backend_of( taggedFileId );
547 #ifdef MOAB_HAVE_MPI
548  if( backend == NCB_BUFFERED )
549  {
550  int rc = NC_NOERR, v = 0;
551  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_natts( libId, &v );
552  return bsuf_bcast_int( taggedFileId, rc, v, nattsp );
553  }
554 #endif
555 #ifdef MOAB_HAVE_PNETCDF
556  if( backend == NCB_PNETCDF ) return ncmpi_inq_natts( libId, nattsp );
557 #endif
558  (void)backend;
559  return nc_inq_natts( libId, nattsp );
560 }
561 
562 int mbnc_inq_ndims( int taggedFileId, int* ndimsp )
563 {
564  const int libId = mbnc_lib_id( taggedFileId );
565  const NcBackend backend = mbnc_backend_of( taggedFileId );
566 #ifdef MOAB_HAVE_MPI
567  if( backend == NCB_BUFFERED )
568  {
569  int rc = NC_NOERR, v = 0;
570  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_ndims( libId, &v );
571  return bsuf_bcast_int( taggedFileId, rc, v, ndimsp );
572  }
573 #endif
574 #ifdef MOAB_HAVE_PNETCDF
575  if( backend == NCB_PNETCDF ) return ncmpi_inq_ndims( libId, ndimsp );
576 #endif
577  (void)backend;
578  return nc_inq_ndims( libId, ndimsp );
579 }
580 
581 int mbnc_inq_nvars( int taggedFileId, int* nvarsp )
582 {
583  const int libId = mbnc_lib_id( taggedFileId );
584  const NcBackend backend = mbnc_backend_of( taggedFileId );
585 #ifdef MOAB_HAVE_MPI
586  if( backend == NCB_BUFFERED )
587  {
588  int rc = NC_NOERR, v = 0;
589  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_nvars( libId, &v );
590  return bsuf_bcast_int( taggedFileId, rc, v, nvarsp );
591  }
592 #endif
593 #ifdef MOAB_HAVE_PNETCDF
594  if( backend == NCB_PNETCDF ) return ncmpi_inq_nvars( libId, nvarsp );
595 #endif
596  (void)backend;
597  return nc_inq_nvars( libId, nvarsp );
598 }
599 
600 int mbnc_inq_dimid( int taggedFileId, const char* name, int* dimidp )
601 {
602  const int libId = mbnc_lib_id( taggedFileId );
603  const NcBackend backend = mbnc_backend_of( taggedFileId );
604 #ifdef MOAB_HAVE_MPI
605  if( backend == NCB_BUFFERED )
606  {
607  int rc = NC_NOERR, v = 0;
608  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_dimid( libId, name, &v );
609  return bsuf_bcast_int( taggedFileId, rc, v, dimidp );
610  }
611 #endif
612 #ifdef MOAB_HAVE_PNETCDF
613  if( backend == NCB_PNETCDF ) return ncmpi_inq_dimid( libId, name, dimidp );
614 #endif
615  (void)backend;
616  return nc_inq_dimid( libId, name, dimidp );
617 }
618 
619 int mbnc_inq_dim( int taggedFileId, int dimid, char* name, size_t* lenp )
620 {
621  const int libId = mbnc_lib_id( taggedFileId );
622  const NcBackend backend = mbnc_backend_of( taggedFileId );
623 #ifdef MOAB_HAVE_MPI
624  if( backend == NCB_BUFFERED )
625  {
626  // Use a fixed-size scratch buffer for the dimension name; libnetcdf
627  // caps names at NC_MAX_NAME (currently 256).
628  char nameBuf[NC_MAX_NAME + 1] = { 0 };
629  size_t len = 0;
630  int rc = NC_NOERR;
631  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_dim( libId, dimid, nameBuf, &len );
632  // Broadcast status, name, and length in three steps.
633  MPI_Bcast( &rc, 1, MPI_INT, 0, mbnc_buffered_comm( taggedFileId ) );
634  if( rc != NC_NOERR ) return rc;
635  if( name ) MPI_Bcast( nameBuf, NC_MAX_NAME + 1, MPI_BYTE, 0, mbnc_buffered_comm( taggedFileId ) );
636  MPI_Bcast( &len, static_cast< int >( sizeof( size_t ) ), MPI_BYTE, 0, mbnc_buffered_comm( taggedFileId ) );
637  if( name ) std::memcpy( name, nameBuf, NC_MAX_NAME + 1 );
638  if( lenp ) *lenp = len;
639  return NC_NOERR;
640  }
641 #endif
642 #ifdef MOAB_HAVE_PNETCDF
643  if( backend == NCB_PNETCDF )
644  {
645  MPI_Offset tmp = 0;
646  int rc = ncmpi_inq_dim( libId, dimid, name, &tmp );
647  if( lenp ) *lenp = static_cast< size_t >( tmp );
648  return rc;
649  }
650 #endif
651  (void)backend;
652  return nc_inq_dim( libId, dimid, name, lenp );
653 }
654 
655 int mbnc_inq_dimlen( int taggedFileId, int dimid, size_t* lenp )
656 {
657  const int libId = mbnc_lib_id( taggedFileId );
658  const NcBackend backend = mbnc_backend_of( taggedFileId );
659 #ifdef MOAB_HAVE_MPI
660  if( backend == NCB_BUFFERED )
661  {
662  int rc = NC_NOERR;
663  size_t v = 0;
664  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_dimlen( libId, dimid, &v );
665  return bsuf_bcast_size( taggedFileId, rc, v, lenp );
666  }
667 #endif
668 #ifdef MOAB_HAVE_PNETCDF
669  if( backend == NCB_PNETCDF )
670  {
671  MPI_Offset tmp = 0;
672  int rc = ncmpi_inq_dimlen( libId, dimid, &tmp );
673  if( lenp ) *lenp = static_cast< size_t >( tmp );
674  return rc;
675  }
676 #endif
677  (void)backend;
678  return nc_inq_dimlen( libId, dimid, lenp );
679 }
680 
681 int mbnc_inq_varid( int taggedFileId, const char* name, int* varidp )
682 {
683  const int libId = mbnc_lib_id( taggedFileId );
684  const NcBackend backend = mbnc_backend_of( taggedFileId );
685 #ifdef MOAB_HAVE_MPI
686  if( backend == NCB_BUFFERED )
687  {
688  int rc = NC_NOERR, v = 0;
689  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_varid( libId, name, &v );
690  return bsuf_bcast_int( taggedFileId, rc, v, varidp );
691  }
692 #endif
693 #ifdef MOAB_HAVE_PNETCDF
694  if( backend == NCB_PNETCDF ) return ncmpi_inq_varid( libId, name, varidp );
695 #endif
696  (void)backend;
697  return nc_inq_varid( libId, name, varidp );
698 }
699 
700 int mbnc_inq_varname( int taggedFileId, int varid, char* name )
701 {
702  const int libId = mbnc_lib_id( taggedFileId );
703  const NcBackend backend = mbnc_backend_of( taggedFileId );
704 #ifdef MOAB_HAVE_MPI
705  if( backend == NCB_BUFFERED )
706  {
707  char nameBuf[NC_MAX_NAME + 1] = { 0 };
708  int rc = NC_NOERR;
709  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_varname( libId, varid, nameBuf );
710  int rc2 = bsuf_bcast_bytes( taggedFileId, rc, nameBuf, NC_MAX_NAME + 1 );
711  if( rc2 == NC_NOERR && name ) std::memcpy( name, nameBuf, NC_MAX_NAME + 1 );
712  return rc2;
713  }
714 #endif
715 #ifdef MOAB_HAVE_PNETCDF
716  if( backend == NCB_PNETCDF ) return ncmpi_inq_varname( libId, varid, name );
717 #endif
718  (void)backend;
719  return nc_inq_varname( libId, varid, name );
720 }
721 
722 int mbnc_inq_vartype( int taggedFileId, int varid, nc_type* xtypep )
723 {
724  const int libId = mbnc_lib_id( taggedFileId );
725  const NcBackend backend = mbnc_backend_of( taggedFileId );
726 #ifdef MOAB_HAVE_MPI
727  if( backend == NCB_BUFFERED )
728  {
729  // nc_type is just an int alias
730  int rc = NC_NOERR, v = 0;
731  if( mbnc_buffered_rank( taggedFileId ) == 0 )
732  {
733  nc_type t = 0;
734  rc = nc_inq_vartype( libId, varid, &t );
735  v = static_cast< int >( t );
736  }
737  int out = 0;
738  int rc2 = bsuf_bcast_int( taggedFileId, rc, v, &out );
739  if( rc2 == NC_NOERR && xtypep ) *xtypep = static_cast< nc_type >( out );
740  return rc2;
741  }
742 #endif
743 #ifdef MOAB_HAVE_PNETCDF
744  if( backend == NCB_PNETCDF ) return ncmpi_inq_vartype( libId, varid, xtypep );
745 #endif
746  (void)backend;
747  return nc_inq_vartype( libId, varid, xtypep );
748 }
749 
750 int mbnc_inq_varndims( int taggedFileId, int varid, int* ndimsp )
751 {
752  const int libId = mbnc_lib_id( taggedFileId );
753  const NcBackend backend = mbnc_backend_of( taggedFileId );
754 #ifdef MOAB_HAVE_MPI
755  if( backend == NCB_BUFFERED )
756  {
757  int rc = NC_NOERR, v = 0;
758  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_varndims( libId, varid, &v );
759  return bsuf_bcast_int( taggedFileId, rc, v, ndimsp );
760  }
761 #endif
762 #ifdef MOAB_HAVE_PNETCDF
763  if( backend == NCB_PNETCDF ) return ncmpi_inq_varndims( libId, varid, ndimsp );
764 #endif
765  (void)backend;
766  return nc_inq_varndims( libId, varid, ndimsp );
767 }
768 
769 int mbnc_inq_vardimid( int taggedFileId, int varid, int* dimids )
770 {
771  const int libId = mbnc_lib_id( taggedFileId );
772  const NcBackend backend = mbnc_backend_of( taggedFileId );
773 #ifdef MOAB_HAVE_MPI
774  if( backend == NCB_BUFFERED )
775  {
776  // Two-step: inquire ndims, then inquire dimids of that length.
777  int ndims = 0;
778  int rc = NC_NOERR;
779  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_varndims( libId, varid, &ndims );
780  MPI_Bcast( &rc, 1, MPI_INT, 0, mbnc_buffered_comm( taggedFileId ) );
781  if( rc != NC_NOERR ) return rc;
782  MPI_Bcast( &ndims, 1, MPI_INT, 0, mbnc_buffered_comm( taggedFileId ) );
783  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_vardimid( libId, varid, dimids );
784  return bsuf_bcast_int_array( taggedFileId, rc, dimids, ndims );
785  }
786 #endif
787 #ifdef MOAB_HAVE_PNETCDF
788  if( backend == NCB_PNETCDF ) return ncmpi_inq_vardimid( libId, varid, dimids );
789 #endif
790  (void)backend;
791  return nc_inq_vardimid( libId, varid, dimids );
792 }
793 
794 int mbnc_inq_varnatts( int taggedFileId, int varid, int* nattsp )
795 {
796  const int libId = mbnc_lib_id( taggedFileId );
797  const NcBackend backend = mbnc_backend_of( taggedFileId );
798 #ifdef MOAB_HAVE_MPI
799  if( backend == NCB_BUFFERED )
800  {
801  int rc = NC_NOERR, v = 0;
802  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_varnatts( libId, varid, &v );
803  return bsuf_bcast_int( taggedFileId, rc, v, nattsp );
804  }
805 #endif
806 #ifdef MOAB_HAVE_PNETCDF
807  if( backend == NCB_PNETCDF ) return ncmpi_inq_varnatts( libId, varid, nattsp );
808 #endif
809  (void)backend;
810  return nc_inq_varnatts( libId, varid, nattsp );
811 }
812 
813 int mbnc_inq_attname( int taggedFileId, int varid, int attnum, char* name )
814 {
815  const int libId = mbnc_lib_id( taggedFileId );
816  const NcBackend backend = mbnc_backend_of( taggedFileId );
817 #ifdef MOAB_HAVE_MPI
818  if( backend == NCB_BUFFERED )
819  {
820  char nameBuf[NC_MAX_NAME + 1] = { 0 };
821  int rc = NC_NOERR;
822  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_attname( libId, varid, attnum, nameBuf );
823  int rc2 = bsuf_bcast_bytes( taggedFileId, rc, nameBuf, NC_MAX_NAME + 1 );
824  if( rc2 == NC_NOERR && name ) std::memcpy( name, nameBuf, NC_MAX_NAME + 1 );
825  return rc2;
826  }
827 #endif
828 #ifdef MOAB_HAVE_PNETCDF
829  if( backend == NCB_PNETCDF ) return ncmpi_inq_attname( libId, varid, attnum, name );
830 #endif
831  (void)backend;
832  return nc_inq_attname( libId, varid, attnum, name );
833 }
834 
835 int mbnc_inq_att( int taggedFileId, int varid, const char* name, nc_type* xtypep, size_t* lenp )
836 {
837  const int libId = mbnc_lib_id( taggedFileId );
838  const NcBackend backend = mbnc_backend_of( taggedFileId );
839 #ifdef MOAB_HAVE_MPI
840  if( backend == NCB_BUFFERED )
841  {
842  int rc = NC_NOERR;
843  nc_type t = 0;
844  size_t len = 0;
845  if( mbnc_buffered_rank( taggedFileId ) == 0 ) rc = nc_inq_att( libId, varid, name, &t, &len );
846  MPI_Bcast( &rc, 1, MPI_INT, 0, mbnc_buffered_comm( taggedFileId ) );
847  if( rc != NC_NOERR ) return rc;
848  int tInt = static_cast< int >( t );
849  MPI_Bcast( &tInt, 1, MPI_INT, 0, mbnc_buffered_comm( taggedFileId ) );
850  MPI_Bcast( &len, static_cast< int >( sizeof( size_t ) ), MPI_BYTE, 0, mbnc_buffered_comm( taggedFileId ) );
851  if( xtypep ) *xtypep = static_cast< nc_type >( tInt );
852  if( lenp ) *lenp = len;
853  return NC_NOERR;
854  }
855 #endif
856 #ifdef MOAB_HAVE_PNETCDF
857  if( backend == NCB_PNETCDF )
858  {
859  MPI_Offset tmp = 0;
860  int rc = ncmpi_inq_att( libId, varid, name, xtypep, &tmp );
861  if( lenp ) *lenp = static_cast< size_t >( tmp );
862  return rc;
863  }
864 #endif
865  (void)backend;
866  return nc_inq_att( libId, varid, name, xtypep, lenp );
867 }
868 
869 // ============================================================================
870 // Attribute get
871 // ============================================================================
872 
873 // Attribute readers under NCB_BUFFERED: rank 0 inquires the attribute
874 // length (number of elements) via nc_inq_attlen, reads the attribute
875 // itself, then both length and payload are broadcast to all ranks. A
876 // shared helper covers the bookkeeping; per-type wrappers differ only
877 // in the underlying nc_get_att_* call and MPI datatype.
878 #ifdef MOAB_HAVE_MPI
879 namespace
880 {
881 template < typename T, typename Fn >
882 int bsuf_get_att( int taggedFileId, int varid, const char* name, T* value, MPI_Datatype mpi_type, Fn nc_get_att_fn )
883 {
884  MPI_Comm comm = mbnc_buffered_comm( taggedFileId );
885  const int rank = mbnc_buffered_rank( taggedFileId );
886  const int libId = mbnc_lib_id( taggedFileId );
887  int rc = NC_NOERR;
888  size_t len = 0;
889  if( rank == 0 )
890  {
891  rc = nc_inq_attlen( libId, varid, name, &len );
892  if( rc == NC_NOERR ) rc = nc_get_att_fn( libId, varid, name, value );
893  }
894  MPI_Bcast( &rc, 1, MPI_INT, 0, comm );
895  if( rc != NC_NOERR ) return rc;
896  MPI_Bcast( &len, static_cast< int >( sizeof( size_t ) ), MPI_BYTE, 0, comm );
897  if( value && len > 0 ) MPI_Bcast( value, static_cast< int >( len ), mpi_type, 0, comm );
898  return NC_NOERR;
899 }
900 } // namespace
901 #endif
902 
903 int mbnc_get_att_text( int taggedFileId, int varid, const char* name, char* value )
904 {
905  const int libId = mbnc_lib_id( taggedFileId );
906  const NcBackend backend = mbnc_backend_of( taggedFileId );
907 #ifdef MOAB_HAVE_MPI
908  if( backend == NCB_BUFFERED ) return bsuf_get_att< char >( taggedFileId, varid, name, value, MPI_CHAR, nc_get_att_text );
909 #endif
910 #ifdef MOAB_HAVE_PNETCDF
911  if( backend == NCB_PNETCDF ) return ncmpi_get_att_text( libId, varid, name, value );
912 #endif
913  (void)backend;
914  return nc_get_att_text( libId, varid, name, value );
915 }
916 
917 int mbnc_get_att_int( int taggedFileId, int varid, const char* name, int* value )
918 {
919  const int libId = mbnc_lib_id( taggedFileId );
920  const NcBackend backend = mbnc_backend_of( taggedFileId );
921 #ifdef MOAB_HAVE_MPI
922  if( backend == NCB_BUFFERED ) return bsuf_get_att< int >( taggedFileId, varid, name, value, MPI_INT, nc_get_att_int );
923 #endif
924 #ifdef MOAB_HAVE_PNETCDF
925  if( backend == NCB_PNETCDF ) return ncmpi_get_att_int( libId, varid, name, value );
926 #endif
927  (void)backend;
928  return nc_get_att_int( libId, varid, name, value );
929 }
930 
931 int mbnc_get_att_short( int taggedFileId, int varid, const char* name, short* value )
932 {
933  const int libId = mbnc_lib_id( taggedFileId );
934  const NcBackend backend = mbnc_backend_of( taggedFileId );
935 #ifdef MOAB_HAVE_MPI
936  if( backend == NCB_BUFFERED )
937  return bsuf_get_att< short >( taggedFileId, varid, name, value, MPI_SHORT, nc_get_att_short );
938 #endif
939 #ifdef MOAB_HAVE_PNETCDF
940  if( backend == NCB_PNETCDF ) return ncmpi_get_att_short( libId, varid, name, value );
941 #endif
942  (void)backend;
943  return nc_get_att_short( libId, varid, name, value );
944 }
945 
946 int mbnc_get_att_long( int taggedFileId, int varid, const char* name, long* value )
947 {
948  const int libId = mbnc_lib_id( taggedFileId );
949  const NcBackend backend = mbnc_backend_of( taggedFileId );
950 #ifdef MOAB_HAVE_MPI
951  if( backend == NCB_BUFFERED )
952  return bsuf_get_att< long >( taggedFileId, varid, name, value, MPI_LONG, nc_get_att_long );
953 #endif
954 #ifdef MOAB_HAVE_PNETCDF
955  if( backend == NCB_PNETCDF ) return ncmpi_get_att_long( libId, varid, name, value );
956 #endif
957  (void)backend;
958  return nc_get_att_long( libId, varid, name, value );
959 }
960 
961 int mbnc_get_att_float( int taggedFileId, int varid, const char* name, float* value )
962 {
963  const int libId = mbnc_lib_id( taggedFileId );
964  const NcBackend backend = mbnc_backend_of( taggedFileId );
965 #ifdef MOAB_HAVE_MPI
966  if( backend == NCB_BUFFERED )
967  return bsuf_get_att< float >( taggedFileId, varid, name, value, MPI_FLOAT, nc_get_att_float );
968 #endif
969 #ifdef MOAB_HAVE_PNETCDF
970  if( backend == NCB_PNETCDF ) return ncmpi_get_att_float( libId, varid, name, value );
971 #endif
972  (void)backend;
973  return nc_get_att_float( libId, varid, name, value );
974 }
975 
976 int mbnc_get_att_double( int taggedFileId, int varid, const char* name, double* value )
977 {
978  const int libId = mbnc_lib_id( taggedFileId );
979  const NcBackend backend = mbnc_backend_of( taggedFileId );
980 #ifdef MOAB_HAVE_MPI
981  if( backend == NCB_BUFFERED )
982  return bsuf_get_att< double >( taggedFileId, varid, name, value, MPI_DOUBLE, nc_get_att_double );
983 #endif
984 #ifdef MOAB_HAVE_PNETCDF
985  if( backend == NCB_PNETCDF ) return ncmpi_get_att_double( libId, varid, name, value );
986 #endif
987  (void)backend;
988  return nc_get_att_double( libId, varid, name, value );
989 }
990 
991 // ============================================================================
992 // Attribute put
993 // ============================================================================
994 
995 int mbnc_put_att_text( int taggedFileId, int varid, const char* name, size_t len, const char* value )
996 {
997  const int libId = mbnc_lib_id( taggedFileId );
998  const NcBackend backend = mbnc_backend_of( taggedFileId );
999 #ifdef MOAB_HAVE_PNETCDF
1000  if( backend == NCB_PNETCDF )
1001  return ncmpi_put_att_text( libId, varid, name, static_cast< MPI_Offset >( len ), value );
1002 #endif
1003  (void)backend;
1004  return nc_put_att_text( libId, varid, name, len, value );
1005 }
1006 
1007 int mbnc_put_att_int( int taggedFileId, int varid, const char* name, nc_type xtype, size_t len, const int* value )
1008 {
1009  const int libId = mbnc_lib_id( taggedFileId );
1010  const NcBackend backend = mbnc_backend_of( taggedFileId );
1011 #ifdef MOAB_HAVE_PNETCDF
1012  if( backend == NCB_PNETCDF )
1013  return ncmpi_put_att_int( libId, varid, name, xtype, static_cast< MPI_Offset >( len ), value );
1014 #endif
1015  (void)backend;
1016  return nc_put_att_int( libId, varid, name, xtype, len, value );
1017 }
1018 
1019 int mbnc_put_att_short( int taggedFileId, int varid, const char* name, nc_type xtype, size_t len, const short* value )
1020 {
1021  const int libId = mbnc_lib_id( taggedFileId );
1022  const NcBackend backend = mbnc_backend_of( taggedFileId );
1023 #ifdef MOAB_HAVE_PNETCDF
1024  if( backend == NCB_PNETCDF )
1025  return ncmpi_put_att_short( libId, varid, name, xtype, static_cast< MPI_Offset >( len ), value );
1026 #endif
1027  (void)backend;
1028  return nc_put_att_short( libId, varid, name, xtype, len, value );
1029 }
1030 
1031 int mbnc_put_att_float( int taggedFileId, int varid, const char* name, nc_type xtype, size_t len, const float* value )
1032 {
1033  const int libId = mbnc_lib_id( taggedFileId );
1034  const NcBackend backend = mbnc_backend_of( taggedFileId );
1035 #ifdef MOAB_HAVE_PNETCDF
1036  if( backend == NCB_PNETCDF )
1037  return ncmpi_put_att_float( libId, varid, name, xtype, static_cast< MPI_Offset >( len ), value );
1038 #endif
1039  (void)backend;
1040  return nc_put_att_float( libId, varid, name, xtype, len, value );
1041 }
1042 
1043 int mbnc_put_att_double( int taggedFileId, int varid, const char* name, nc_type xtype, size_t len, const double* value )
1044 {
1045  const int libId = mbnc_lib_id( taggedFileId );
1046  const NcBackend backend = mbnc_backend_of( taggedFileId );
1047 #ifdef MOAB_HAVE_PNETCDF
1048  if( backend == NCB_PNETCDF )
1049  return ncmpi_put_att_double( libId, varid, name, xtype, static_cast< MPI_Offset >( len ), value );
1050 #endif
1051  (void)backend;
1052  return nc_put_att_double( libId, varid, name, xtype, len, value );
1053 }
1054 
1055 // ============================================================================
1056 // Variable get_vara — collective by default on parallel PNetCDF
1057 // ============================================================================
1058 
1059 int mbnc_get_vara_double( int taggedFileId, int varid, const size_t* start, const size_t* count, double* data )
1060 {
1061  const int libId = mbnc_lib_id( taggedFileId );
1062  const NcBackend backend = mbnc_backend_of( taggedFileId );
1063 #ifdef MOAB_HAVE_MPI
1064  if( backend == NCB_BUFFERED )
1065  return bsuf_get_vara< double >( taggedFileId, varid, start, count, data, MPI_DOUBLE, nc_get_vara_double );
1066 #endif
1067 #ifdef MOAB_HAVE_PNETCDF
1068  if( backend == NCB_PNETCDF )
1069  {
1070  MPI_Offset s[kMaxDims], c[kMaxDims];
1071  int ndims = 0;
1072  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1073  if( rc != NC_NOERR ) return rc;
1074  return ncmpi_get_vara_double_all( libId, varid, s, c, data );
1075  }
1076 #endif
1077  (void)backend;
1078  return nc_get_vara_double( libId, varid, start, count, data );
1079 }
1080 
1081 int mbnc_get_vara_int( int taggedFileId, int varid, const size_t* start, const size_t* count, int* data )
1082 {
1083  const int libId = mbnc_lib_id( taggedFileId );
1084  const NcBackend backend = mbnc_backend_of( taggedFileId );
1085 #ifdef MOAB_HAVE_MPI
1086  if( backend == NCB_BUFFERED )
1087  return bsuf_get_vara< int >( taggedFileId, varid, start, count, data, MPI_INT, nc_get_vara_int );
1088 #endif
1089 #ifdef MOAB_HAVE_PNETCDF
1090  if( backend == NCB_PNETCDF )
1091  {
1092  MPI_Offset s[kMaxDims], c[kMaxDims];
1093  int ndims = 0;
1094  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1095  if( rc != NC_NOERR ) return rc;
1096  return ncmpi_get_vara_int_all( libId, varid, s, c, data );
1097  }
1098 #endif
1099  (void)backend;
1100  return nc_get_vara_int( libId, varid, start, count, data );
1101 }
1102 
1103 int mbnc_get_vara_long( int taggedFileId, int varid, const size_t* start, const size_t* count, long* data )
1104 {
1105  const int libId = mbnc_lib_id( taggedFileId );
1106  const NcBackend backend = mbnc_backend_of( taggedFileId );
1107 #ifdef MOAB_HAVE_MPI
1108  if( backend == NCB_BUFFERED )
1109  return bsuf_get_vara< long >( taggedFileId, varid, start, count, data, MPI_LONG, nc_get_vara_long );
1110 #endif
1111 #ifdef MOAB_HAVE_PNETCDF
1112  if( backend == NCB_PNETCDF )
1113  {
1114  MPI_Offset s[kMaxDims], c[kMaxDims];
1115  int ndims = 0;
1116  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1117  if( rc != NC_NOERR ) return rc;
1118  return ncmpi_get_vara_long_all( libId, varid, s, c, data );
1119  }
1120 #endif
1121  (void)backend;
1122  return nc_get_vara_long( libId, varid, start, count, data );
1123 }
1124 
1125 int mbnc_get_vara_text( int taggedFileId, int varid, const size_t* start, const size_t* count, char* data )
1126 {
1127  const int libId = mbnc_lib_id( taggedFileId );
1128  const NcBackend backend = mbnc_backend_of( taggedFileId );
1129 #ifdef MOAB_HAVE_MPI
1130  if( backend == NCB_BUFFERED )
1131  return bsuf_get_vara< char >( taggedFileId, varid, start, count, data, MPI_CHAR, nc_get_vara_text );
1132 #endif
1133 #ifdef MOAB_HAVE_PNETCDF
1134  if( backend == NCB_PNETCDF )
1135  {
1136  MPI_Offset s[kMaxDims], c[kMaxDims];
1137  int ndims = 0;
1138  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1139  if( rc != NC_NOERR ) return rc;
1140  return ncmpi_get_vara_text_all( libId, varid, s, c, data );
1141  }
1142 #endif
1143  (void)backend;
1144  return nc_get_vara_text( libId, varid, start, count, data );
1145 }
1146 
1147 int mbnc_get_vars_double( int taggedFileId, int varid, const size_t* start, const size_t* count,
1148  const ptrdiff_t* stride, double* data )
1149 {
1150  const int libId = mbnc_lib_id( taggedFileId );
1151  const NcBackend backend = mbnc_backend_of( taggedFileId );
1152 #ifdef MOAB_HAVE_MPI
1153  if( backend == NCB_BUFFERED )
1154  {
1155  // Buffered: same per-rank scatter pattern as plain get_vara,
1156  // but each rank ships its stride array too. We inline rather than
1157  // generalize bsuf_get_vara since stride adds a third per-rank array.
1158  MPI_Comm comm = mbnc_buffered_comm( taggedFileId );
1159  const int rank = mbnc_buffered_rank( taggedFileId );
1160  const int size = mbnc_buffered_size( taggedFileId );
1161  int ndims = 0;
1162  int rc = NC_NOERR;
1163  if( rank == 0 ) rc = nc_inq_varndims( libId, varid, &ndims );
1164  MPI_Bcast( &rc, 1, MPI_INT, 0, comm );
1165  if( rc != NC_NOERR ) return rc;
1166  MPI_Bcast( &ndims, 1, MPI_INT, 0, comm );
1167  if( ndims > kMaxDims ) return NC_EMAXDIMS;
1168  size_t my_n = 1;
1169  for( int i = 0; i < ndims; ++i )
1170  my_n *= count[i];
1171  if( rank == 0 )
1172  {
1173  int my_rc = nc_get_vars_double( libId, varid, start, count, stride, data );
1174  for( int src = 1; src < size; ++src )
1175  {
1176  size_t s2[kMaxDims], c2[kMaxDims];
1177  ptrdiff_t st2[kMaxDims];
1178  MPI_Recv( s2, ndims * static_cast< int >( sizeof( size_t ) ), MPI_BYTE, src, 0, comm,
1179  MPI_STATUS_IGNORE );
1180  MPI_Recv( c2, ndims * static_cast< int >( sizeof( size_t ) ), MPI_BYTE, src, 1, comm,
1181  MPI_STATUS_IGNORE );
1182  MPI_Recv( st2, ndims * static_cast< int >( sizeof( ptrdiff_t ) ), MPI_BYTE, src, 4, comm,
1183  MPI_STATUS_IGNORE );
1184  size_t n = 1;
1185  for( int i = 0; i < ndims; ++i )
1186  n *= c2[i];
1187  std::vector< double > buf( n );
1188  int s_rc = nc_get_vars_double( libId, varid, s2, c2, st2, buf.data() );
1189  MPI_Send( &s_rc, 1, MPI_INT, src, 2, comm );
1190  if( s_rc == NC_NOERR && n > 0 )
1191  MPI_Send( buf.data(), static_cast< int >( n ), MPI_DOUBLE, src, 3, comm );
1192  }
1193  return my_rc;
1194  }
1195  else
1196  {
1197  MPI_Send( start, ndims * static_cast< int >( sizeof( size_t ) ), MPI_BYTE, 0, 0, comm );
1198  MPI_Send( count, ndims * static_cast< int >( sizeof( size_t ) ), MPI_BYTE, 0, 1, comm );
1199  MPI_Send( stride, ndims * static_cast< int >( sizeof( ptrdiff_t ) ), MPI_BYTE, 0, 4, comm );
1200  int recv_rc;
1201  MPI_Recv( &recv_rc, 1, MPI_INT, 0, 2, comm, MPI_STATUS_IGNORE );
1202  if( recv_rc == NC_NOERR && my_n > 0 )
1203  MPI_Recv( data, static_cast< int >( my_n ), MPI_DOUBLE, 0, 3, comm, MPI_STATUS_IGNORE );
1204  return recv_rc;
1205  }
1206  }
1207 #endif
1208 #ifdef MOAB_HAVE_PNETCDF
1209  if( backend == NCB_PNETCDF )
1210  {
1211  MPI_Offset s[kMaxDims], c[kMaxDims], st[kMaxDims];
1212  int ndims = 0;
1213  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1214  if( rc != NC_NOERR ) return rc;
1215  for( int i = 0; i < ndims; ++i )
1216  st[i] = static_cast< MPI_Offset >( stride[i] );
1217  return ncmpi_get_vars_double_all( libId, varid, s, c, st, data );
1218  }
1219 #endif
1220  (void)backend;
1221  return nc_get_vars_double( libId, varid, start, count, stride, data );
1222 }
1223 
1224 // ============================================================================
1225 // Variable put_vara
1226 // ============================================================================
1227 
1228 int mbnc_put_vara_double( int taggedFileId, int varid, const size_t* start, const size_t* count, const double* data )
1229 {
1230  const int libId = mbnc_lib_id( taggedFileId );
1231  const NcBackend backend = mbnc_backend_of( taggedFileId );
1232 #ifdef MOAB_HAVE_PNETCDF
1233  if( backend == NCB_PNETCDF )
1234  {
1235  MPI_Offset s[kMaxDims], c[kMaxDims];
1236  int ndims = 0;
1237  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1238  if( rc != NC_NOERR ) return rc;
1239  return ncmpi_put_vara_double_all( libId, varid, s, c, data );
1240  }
1241 #endif
1242  (void)backend;
1243  return nc_put_vara_double( libId, varid, start, count, data );
1244 }
1245 
1246 int mbnc_put_vara_int( int taggedFileId, int varid, const size_t* start, const size_t* count, const int* data )
1247 {
1248  const int libId = mbnc_lib_id( taggedFileId );
1249  const NcBackend backend = mbnc_backend_of( taggedFileId );
1250 #ifdef MOAB_HAVE_PNETCDF
1251  if( backend == NCB_PNETCDF )
1252  {
1253  MPI_Offset s[kMaxDims], c[kMaxDims];
1254  int ndims = 0;
1255  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1256  if( rc != NC_NOERR ) return rc;
1257  return ncmpi_put_vara_int_all( libId, varid, s, c, data );
1258  }
1259 #endif
1260  (void)backend;
1261  return nc_put_vara_int( libId, varid, start, count, data );
1262 }
1263 
1264 int mbnc_put_vara_text( int taggedFileId, int varid, const size_t* start, const size_t* count, const char* data )
1265 {
1266  const int libId = mbnc_lib_id( taggedFileId );
1267  const NcBackend backend = mbnc_backend_of( taggedFileId );
1268 #ifdef MOAB_HAVE_PNETCDF
1269  if( backend == NCB_PNETCDF )
1270  {
1271  MPI_Offset s[kMaxDims], c[kMaxDims];
1272  int ndims = 0;
1273  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1274  if( rc != NC_NOERR ) return rc;
1275  return ncmpi_put_vara_text_all( libId, varid, s, c, data );
1276  }
1277 #endif
1278  (void)backend;
1279  return nc_put_vara_text( libId, varid, start, count, data );
1280 }
1281 
1282 // ============================================================================
1283 // Nonblocking get + wait
1284 //
1285 // PNetCDF: real ncmpi_iget_* request aggregation.
1286 // Other backends: immediate blocking collective; *req = MBNC_REQ_NULL.
1287 // mbnc_wait_all is a no-op for non-PNetCDF (returns NC_NOERR, writes
1288 // NC_NOERR into statuses[] for any entries whose request is MBNC_REQ_NULL).
1289 // ============================================================================
1290 
1291 int mbnc_iget_vara_double( int taggedFileId, int varid, const size_t* start, const size_t* count, double* data,
1292  int* req )
1293 {
1294  const NcBackend backend = mbnc_backend_of( taggedFileId );
1295 #ifdef MOAB_HAVE_PNETCDF
1296  if( backend == NCB_PNETCDF )
1297  {
1298  const int libId = mbnc_lib_id( taggedFileId );
1299  MPI_Offset s[kMaxDims], c[kMaxDims];
1300  int ndims = 0;
1301  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1302  if( rc != NC_NOERR ) return rc;
1303  return ncmpi_iget_vara_double( libId, varid, s, c, data, req );
1304  }
1305 #endif
1306  (void)backend;
1307  if( req ) *req = MBNC_REQ_NULL;
1308  return mbnc_get_vara_double( taggedFileId, varid, start, count, data );
1309 }
1310 
1311 int mbnc_iget_vara_int( int taggedFileId, int varid, const size_t* start, const size_t* count, int* data, int* req )
1312 {
1313  const NcBackend backend = mbnc_backend_of( taggedFileId );
1314 #ifdef MOAB_HAVE_PNETCDF
1315  if( backend == NCB_PNETCDF )
1316  {
1317  const int libId = mbnc_lib_id( taggedFileId );
1318  MPI_Offset s[kMaxDims], c[kMaxDims];
1319  int ndims = 0;
1320  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1321  if( rc != NC_NOERR ) return rc;
1322  return ncmpi_iget_vara_int( libId, varid, s, c, data, req );
1323  }
1324 #endif
1325  (void)backend;
1326  if( req ) *req = MBNC_REQ_NULL;
1327  return mbnc_get_vara_int( taggedFileId, varid, start, count, data );
1328 }
1329 
1330 // ============================================================================
1331 // Independent-mode get/put — used inside begin_indep_data brackets on PNetCDF.
1332 // On non-PNetCDF backends these route to the same plain nc_*_vara_* calls
1333 // (per-var access mode is the caller's responsibility outside PNetCDF).
1334 // ============================================================================
1335 
1336 int mbnc_get_vara_double_indep( int taggedFileId, int varid, const size_t* start, const size_t* count, double* data )
1337 {
1338  const int libId = mbnc_lib_id( taggedFileId );
1339  const NcBackend backend = mbnc_backend_of( taggedFileId );
1340 #ifdef MOAB_HAVE_MPI
1341  if( backend == NCB_BUFFERED )
1342  return bsuf_get_vara< double >( taggedFileId, varid, start, count, data, MPI_DOUBLE, nc_get_vara_double );
1343 #endif
1344 #ifdef MOAB_HAVE_PNETCDF
1345  if( backend == NCB_PNETCDF )
1346  {
1347  MPI_Offset s[kMaxDims], c[kMaxDims];
1348  int ndims = 0;
1349  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1350  if( rc != NC_NOERR ) return rc;
1351  return ncmpi_get_vara_double( libId, varid, s, c, data );
1352  }
1353 #endif
1354  (void)backend;
1355  return nc_get_vara_double( libId, varid, start, count, data );
1356 }
1357 
1358 int mbnc_get_vara_int_indep( int taggedFileId, int varid, const size_t* start, const size_t* count, int* data )
1359 {
1360  const int libId = mbnc_lib_id( taggedFileId );
1361  const NcBackend backend = mbnc_backend_of( taggedFileId );
1362 #ifdef MOAB_HAVE_MPI
1363  if( backend == NCB_BUFFERED )
1364  return bsuf_get_vara< int >( taggedFileId, varid, start, count, data, MPI_INT, nc_get_vara_int );
1365 #endif
1366 #ifdef MOAB_HAVE_PNETCDF
1367  if( backend == NCB_PNETCDF )
1368  {
1369  MPI_Offset s[kMaxDims], c[kMaxDims];
1370  int ndims = 0;
1371  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1372  if( rc != NC_NOERR ) return rc;
1373  return ncmpi_get_vara_int( libId, varid, s, c, data );
1374  }
1375 #endif
1376  (void)backend;
1377  return nc_get_vara_int( libId, varid, start, count, data );
1378 }
1379 
1380 int mbnc_get_vara_long_indep( int taggedFileId, int varid, const size_t* start, const size_t* count, long* data )
1381 {
1382  const int libId = mbnc_lib_id( taggedFileId );
1383  const NcBackend backend = mbnc_backend_of( taggedFileId );
1384 #ifdef MOAB_HAVE_MPI
1385  if( backend == NCB_BUFFERED )
1386  return bsuf_get_vara< long >( taggedFileId, varid, start, count, data, MPI_LONG, nc_get_vara_long );
1387 #endif
1388 #ifdef MOAB_HAVE_PNETCDF
1389  if( backend == NCB_PNETCDF )
1390  {
1391  MPI_Offset s[kMaxDims], c[kMaxDims];
1392  int ndims = 0;
1393  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1394  if( rc != NC_NOERR ) return rc;
1395  return ncmpi_get_vara_long( libId, varid, s, c, data );
1396  }
1397 #endif
1398  (void)backend;
1399  return nc_get_vara_long( libId, varid, start, count, data );
1400 }
1401 
1402 int mbnc_get_vara_text_indep( int taggedFileId, int varid, const size_t* start, const size_t* count, char* data )
1403 {
1404  const int libId = mbnc_lib_id( taggedFileId );
1405  const NcBackend backend = mbnc_backend_of( taggedFileId );
1406 #ifdef MOAB_HAVE_MPI
1407  if( backend == NCB_BUFFERED )
1408  return bsuf_get_vara< char >( taggedFileId, varid, start, count, data, MPI_CHAR, nc_get_vara_text );
1409 #endif
1410 #ifdef MOAB_HAVE_PNETCDF
1411  if( backend == NCB_PNETCDF )
1412  {
1413  MPI_Offset s[kMaxDims], c[kMaxDims];
1414  int ndims = 0;
1415  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1416  if( rc != NC_NOERR ) return rc;
1417  return ncmpi_get_vara_text( libId, varid, s, c, data );
1418  }
1419 #endif
1420  (void)backend;
1421  return nc_get_vara_text( libId, varid, start, count, data );
1422 }
1423 
1424 int mbnc_get_vars_double_indep( int taggedFileId, int varid, const size_t* start, const size_t* count,
1425  const ptrdiff_t* stride, double* data )
1426 {
1427  const int libId = mbnc_lib_id( taggedFileId );
1428  const NcBackend backend = mbnc_backend_of( taggedFileId );
1429 #ifdef MOAB_HAVE_MPI
1430  if( backend == NCB_BUFFERED )
1431  {
1432  // Buffered backend doesn't distinguish indep / collective — re-use
1433  // the collective stride wrapper which already implements the
1434  // per-rank scatter pattern with stride.
1435  return mbnc_get_vars_double( taggedFileId, varid, start, count, stride, data );
1436  }
1437 #endif
1438 #ifdef MOAB_HAVE_PNETCDF
1439  if( backend == NCB_PNETCDF )
1440  {
1441  MPI_Offset s[kMaxDims], c[kMaxDims], st[kMaxDims];
1442  int ndims = 0;
1443  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1444  if( rc != NC_NOERR ) return rc;
1445  for( int i = 0; i < ndims; ++i )
1446  st[i] = static_cast< MPI_Offset >( stride[i] );
1447  return ncmpi_get_vars_double( libId, varid, s, c, st, data );
1448  }
1449 #endif
1450  (void)backend;
1451  return nc_get_vars_double( libId, varid, start, count, stride, data );
1452 }
1453 
1454 int mbnc_put_vara_double_indep( int taggedFileId, int varid, const size_t* start, const size_t* count,
1455  const double* data )
1456 {
1457  const int libId = mbnc_lib_id( taggedFileId );
1458  const NcBackend backend = mbnc_backend_of( taggedFileId );
1459 #ifdef MOAB_HAVE_PNETCDF
1460  if( backend == NCB_PNETCDF )
1461  {
1462  MPI_Offset s[kMaxDims], c[kMaxDims];
1463  int ndims = 0;
1464  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1465  if( rc != NC_NOERR ) return rc;
1466  return ncmpi_put_vara_double( libId, varid, s, c, data );
1467  }
1468 #endif
1469  (void)backend;
1470  return nc_put_vara_double( libId, varid, start, count, data );
1471 }
1472 
1473 int mbnc_put_vara_int_indep( int taggedFileId, int varid, const size_t* start, const size_t* count, const int* data )
1474 {
1475  const int libId = mbnc_lib_id( taggedFileId );
1476  const NcBackend backend = mbnc_backend_of( taggedFileId );
1477 #ifdef MOAB_HAVE_PNETCDF
1478  if( backend == NCB_PNETCDF )
1479  {
1480  MPI_Offset s[kMaxDims], c[kMaxDims];
1481  int ndims = 0;
1482  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1483  if( rc != NC_NOERR ) return rc;
1484  return ncmpi_put_vara_int( libId, varid, s, c, data );
1485  }
1486 #endif
1487  (void)backend;
1488  return nc_put_vara_int( libId, varid, start, count, data );
1489 }
1490 
1491 int mbnc_put_vara_text_indep( int taggedFileId, int varid, const size_t* start, const size_t* count, const char* data )
1492 {
1493  const int libId = mbnc_lib_id( taggedFileId );
1494  const NcBackend backend = mbnc_backend_of( taggedFileId );
1495 #ifdef MOAB_HAVE_PNETCDF
1496  if( backend == NCB_PNETCDF )
1497  {
1498  MPI_Offset s[kMaxDims], c[kMaxDims];
1499  int ndims = 0;
1500  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1501  if( rc != NC_NOERR ) return rc;
1502  return ncmpi_put_vara_text( libId, varid, s, c, data );
1503  }
1504 #endif
1505  (void)backend;
1506  return nc_put_vara_text( libId, varid, start, count, data );
1507 }
1508 
1509 int mbnc_iput_vara_double( int taggedFileId, int varid, const size_t* start, const size_t* count, const double* data,
1510  int* req )
1511 {
1512  const NcBackend backend = mbnc_backend_of( taggedFileId );
1513 #ifdef MOAB_HAVE_PNETCDF
1514  if( backend == NCB_PNETCDF )
1515  {
1516  const int libId = mbnc_lib_id( taggedFileId );
1517  MPI_Offset s[kMaxDims], c[kMaxDims];
1518  int ndims = 0;
1519  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1520  if( rc != NC_NOERR ) return rc;
1521  return ncmpi_iput_vara_double( libId, varid, s, c, data, req );
1522  }
1523 #endif
1524  (void)backend;
1525  if( req ) *req = MBNC_REQ_NULL;
1526  return mbnc_put_vara_double( taggedFileId, varid, start, count, data );
1527 }
1528 
1529 int mbnc_iput_vara_int( int taggedFileId, int varid, const size_t* start, const size_t* count, const int* data,
1530  int* req )
1531 {
1532  const NcBackend backend = mbnc_backend_of( taggedFileId );
1533 #ifdef MOAB_HAVE_PNETCDF
1534  if( backend == NCB_PNETCDF )
1535  {
1536  const int libId = mbnc_lib_id( taggedFileId );
1537  MPI_Offset s[kMaxDims], c[kMaxDims];
1538  int ndims = 0;
1539  int rc = to_mpi_offset_pair( libId, varid, start, count, s, c, &ndims );
1540  if( rc != NC_NOERR ) return rc;
1541  return ncmpi_iput_vara_int( libId, varid, s, c, data, req );
1542  }
1543 #endif
1544  (void)backend;
1545  if( req ) *req = MBNC_REQ_NULL;
1546  return mbnc_put_vara_int( taggedFileId, varid, start, count, data );
1547 }
1548 
1549 int mbnc_wait_all( int taggedFileId, int nreq, int* requests, int* statuses )
1550 {
1551  const NcBackend backend = mbnc_backend_of( taggedFileId );
1552 #ifdef MOAB_HAVE_PNETCDF
1553  if( backend == NCB_PNETCDF ) return ncmpi_wait_all( mbnc_lib_id( taggedFileId ), nreq, requests, statuses );
1554 #endif
1555  (void)backend;
1556  // No-op on non-PNetCDF backends: the corresponding iget_* already
1557  // performed the blocking call. Mark statuses as OK for sentinel reqs.
1558  if( statuses )
1559  {
1560  for( int i = 0; i < nreq; ++i )
1561  {
1562  if( !requests || requests[i] == MBNC_REQ_NULL ) statuses[i] = NC_NOERR;
1563  }
1564  }
1565  return NC_NOERR;
1566 }
1567 
1568 } // namespace moab