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