Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
NC_Serial.cc
Go to the documentation of this file.
1// Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2023, 2024 PISM Authors
2//
3// This file is part of PISM.
4//
5// PISM is free software; you can redistribute it and/or modify it under the
6// terms of the GNU General Public License as published by the Free Software
7// Foundation; either version 3 of the License, or (at your option) any later
8// version.
9//
10// PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13// details.
14//
15// You should have received a copy of the GNU General Public License
16// along with PISM; if not, write to the Free Software
17// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19#include <mpi.h>
20
21#include "pism/util/io/NC_Serial.hh"
22
23// The following is a stupid kludge necessary to make NetCDF 4.x work in
24// serial mode in an MPI program:
25#ifndef MPI_INCLUDED
26#define MPI_INCLUDED 1
27#endif
28#include <netcdf.h>
29
30#include <cstdio> // stderr, fprintf
31
32#include "pism/util/pism_utilities.hh" // join
33#include "pism/util/error_handling.hh"
34
35#include "pism/util/io/pism_type_conversion.hh" // This has to be included *after* netcdf.h.
36
37namespace pism {
38namespace io {
39
40//! \brief Prints an error message; for debugging.
41static void check(const ErrorLocation &where, int return_code) {
42 if (return_code != NC_NOERR) {
43 throw RuntimeError(where, nc_strerror(return_code));
44 }
45}
46
47//! call MPI_Abort() if a NetCDF call failed
48static void check_and_abort(MPI_Comm com, const ErrorLocation &where, int return_code) {
49 if (return_code != NC_NOERR) {
50 fprintf(stderr, "%s:%d: %s\n", where.filename, where.line_number, nc_strerror(return_code));
51 MPI_Abort(com, -1);
52 }
53}
54
56 : NCFile(c), m_rank(0) {
57 MPI_Comm_rank(m_com, &m_rank);
58}
59
61 if (m_file_id >= 0) {
62 if (m_rank == 0) {
63 nc_close(m_file_id);
64 fprintf(stderr, "NC_Serial::~NC_Serial: NetCDF file %s is still open\n",
65 m_filename.c_str());
66 }
67 m_file_id = -1;
68 }
69}
70
72 (void) level;
73 // NetCDF-3 does not support compression.
74}
75
76// open/create/close
77void NC_Serial::open_impl(const std::string &fname, io::Mode mode) {
78 int stat = NC_NOERR;
79
80 int open_mode = mode == io::PISM_READONLY ? NC_NOWRITE : NC_WRITE;
81
82 if (m_rank == 0) {
83 stat = nc_open(fname.c_str(), open_mode, &m_file_id);
84 }
85
86 MPI_Barrier(m_com);
87 MPI_Bcast(&m_file_id, 1, MPI_INT, 0, m_com);
88 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
89
91}
92
93//! \brief Create a NetCDF file.
94void NC_Serial::create_impl(const std::string &fname) {
95 int stat = NC_NOERR;
96
97 if (m_rank == 0) {
98 stat = nc_create(fname.c_str(), NC_CLOBBER | NC_64BIT_OFFSET, &m_file_id);
99 }
100
101 MPI_Barrier(m_com);
102 MPI_Bcast(&m_file_id, 1, MPI_INT, 0, m_com);
103 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
104
106}
107
108//! \brief Close a NetCDF file.
110 int stat = NC_NOERR;
111
112 if (m_rank == 0) {
113 stat = nc_close(m_file_id);
114 }
115
116 m_file_id = -1;
117
118 MPI_Barrier(m_com);
119 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
120
122}
123
124
126 int stat = NC_NOERR;
127
128 if (m_rank == 0) {
129 stat = nc_sync(m_file_id);
130 }
131
132 MPI_Barrier(m_com);
133 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
134
136}
137
138
139//! \brief Exit define mode.
141 int stat = NC_NOERR;
142
143 int header_size = 200 * 1024;
144
145 if (m_rank == 0) {
146 stat = nc__enddef(m_file_id, header_size, 4, 0, 4);
147 }
148
149 MPI_Barrier(m_com);
150 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
151
153}
154
155//! \brief Enter define mode.
157 int stat = NC_NOERR;
158
159 if (m_rank == 0) {
160 stat = nc_redef(m_file_id);
161 }
162
163 MPI_Barrier(m_com);
164 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
165
167}
168
169
170//! \brief Define a dimension.
171void NC_Serial::def_dim_impl(const std::string &name, size_t length) const {
172 int stat = NC_NOERR;
173
174 if (m_rank == 0) {
175 int dimid;
176 stat = nc_def_dim(m_file_id, name.c_str(), length, &dimid);
177 }
178
179 MPI_Barrier(m_com);
180 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
181
183}
184
185void NC_Serial::inq_dimid_impl(const std::string &dimension_name, bool &exists) const {
186 int stat, flag = -1;
187
188 if (m_rank == 0) {
189 stat = nc_inq_dimid(m_file_id, dimension_name.c_str(), &flag);
190
191 flag = (stat == NC_NOERR) ? 1 : 0;
192 }
193 MPI_Barrier(m_com);
194 MPI_Bcast(&flag, 1, MPI_INT, 0, m_com);
195
196 exists = (flag == 1);
197}
198
199
200//! \brief Get a dimension length.
201void NC_Serial::inq_dimlen_impl(const std::string &dimension_name, unsigned int &result) const {
202 int stat = NC_NOERR;
203
204 if (m_rank == 0) {
205 int dimid;
206 size_t length;
207
208 stat = nc_inq_dimid(m_file_id, dimension_name.c_str(), &dimid);
209
210 if (stat == NC_NOERR) {
211 stat = nc_inq_dimlen(m_file_id, dimid, &length);
212 result = static_cast<unsigned int>(length);
213 }
214 }
215
216 MPI_Barrier(m_com);
217 MPI_Bcast(&result, 1, MPI_UNSIGNED, 0, m_com);
218 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
219
221}
222
223//! \brief Get an unlimited dimension.
224void NC_Serial::inq_unlimdim_impl(std::string &result) const {
225 int stat = NC_NOERR;
226 std::vector<char> dimname(NC_MAX_NAME + 1, 0);
227
228 if (m_rank == 0) {
229 int dimid;
230 stat = nc_inq_unlimdim(m_file_id, &dimid);
231
232 // nc_inq_unlimdim() sets dimid to -1 if there is no unlimited dimension
233 if (stat == NC_NOERR and dimid != -1) {
234 stat = nc_inq_dimname(m_file_id, dimid, dimname.data());
235 } else {
236 // leave dimname empty
237 stat = NC_NOERR;
238 }
239 }
240
241 MPI_Barrier(m_com);
242
243 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
244 MPI_Bcast(dimname.data(), NC_MAX_NAME, MPI_CHAR, 0, m_com);
245
247
248 result = dimname.data();
249}
250
251//! \brief Define a variable.
252void NC_Serial::def_var_impl(const std::string &name, io::Type nctype,
253 const std::vector<std::string> &dims) const {
254 int stat = NC_NOERR;
255
256 if (m_rank == 0) {
257 std::vector<int> dimids;
258 int varid;
259
260 for (auto d : dims) {
261 int dimid;
262 stat = nc_inq_dimid(m_file_id, d.c_str(), &dimid);
263 if (stat != NC_NOERR) {
264 break;
265 }
266 dimids.push_back(dimid);
267 }
268
269 if (stat == NC_NOERR) {
270 stat = nc_def_var(m_file_id, name.c_str(), pism_type_to_nc_type(nctype),
271 static_cast<int>(dims.size()), dimids.data(), &varid);
272 }
273 }
274
275 MPI_Barrier(m_com);
276 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
277
279}
280
281void NC_Serial::get_vara_double_impl(const std::string &variable_name,
282 const std::vector<unsigned int> &start,
283 const std::vector<unsigned int> &count, double *op) const {
284 return this->get_var_double(variable_name, start, count, op);
285}
286
287//! \brief Get variable data.
288void NC_Serial::get_var_double(const std::string &variable_name,
289 const std::vector<unsigned int> &start_input,
290 const std::vector<unsigned int> &count_input,
291 double *ip) const {
292 std::vector<unsigned int> start = start_input;
293 std::vector<unsigned int> count = count_input;
294
295 const int start_tag = 1, count_tag = 2, data_tag = 3, chunk_size_tag = 4;
296 int stat = NC_NOERR, com_size, ndims = static_cast<int>(start.size());
297 MPI_Status mpi_stat;
298 unsigned int local_chunk_size = 1, processor_0_chunk_size = 0;
299
300 // get the size of the communicator
301 MPI_Comm_size(m_com, &com_size);
302
303 // compute the size of a local chunk
304 for (int k = 0; k < ndims; ++k) {
305 local_chunk_size *= count[k];
306 }
307
308 // compute the maximum and send it to processor 0; this is the size of the
309 // buffer processor 0 will need
310 MPI_Reduce(&local_chunk_size, &processor_0_chunk_size, 1, MPI_UNSIGNED, MPI_MAX, 0, m_com);
311
312 // now we need to send start and count data to processor 0 and receive data
313 if (m_rank == 0) {
314 std::vector<double> processor_0_buffer;
315 // Note: this could be optimized: if processor_0_chunk_size <=
316 // max(local_chunk_size) we can avoid allocating this buffer. The inner for
317 // loop will have to be re-ordered, though.
318 processor_0_buffer.resize(processor_0_chunk_size);
319
320 // MPI calls below require C datatypes (so that we don't have to worry about sizes of
321 // size_t and ptrdiff_t), so we make local copies of start and count to use in the
322 // nc_get_vara_double() call.
323 std::vector<size_t> nc_start(ndims), nc_count(ndims);
324 int varid;
325
326 stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid);
328
329 for (int r = 0; r < com_size; ++r) {
330
331 if (r != 0) {
332 // Note: start, count, and local_chunk_size on processor zero are
333 // used *before* they get overwritten by these calls
334 MPI_Recv(start.data(), ndims, MPI_UNSIGNED, r, start_tag, m_com, &mpi_stat);
335 MPI_Recv(count.data(), ndims, MPI_UNSIGNED, r, count_tag, m_com, &mpi_stat);
336 MPI_Recv(&local_chunk_size, 1, MPI_UNSIGNED, r, chunk_size_tag, m_com, &mpi_stat);
337 }
338
339 // This for loop uses start and count passed in as arguments when r == 0. For r > 0
340 // they are overwritten by MPI_Recv calls above.
341 for (int k = 0; k < ndims; ++k) {
342 nc_start[k] = start[k];
343 nc_count[k] = count[k];
344 }
345
346 stat = nc_get_vara_double(m_file_id, varid, nc_start.data(), nc_count.data(),
347 processor_0_buffer.data());
349
350 if (r != 0) {
351 MPI_Send(processor_0_buffer.data(), (int)local_chunk_size, MPI_DOUBLE, r, data_tag, m_com);
352 } else {
353 for (unsigned int k = 0; k < local_chunk_size; ++k) {
354 ip[k] = processor_0_buffer[k];
355 }
356 }
357 } // end of the for loop
358
359 } else {
360 MPI_Send(start.data(), ndims, MPI_UNSIGNED, 0, start_tag, m_com);
361 MPI_Send(count.data(), ndims, MPI_UNSIGNED, 0, count_tag, m_com);
362 MPI_Send(&local_chunk_size, 1, MPI_UNSIGNED, 0, chunk_size_tag, m_com);
363
364 MPI_Recv(ip, (int)local_chunk_size, MPI_DOUBLE, 0, data_tag, m_com, &mpi_stat);
365 }
366}
367
368void NC_Serial::put_vara_double_impl(const std::string &variable_name,
369 const std::vector<unsigned int> &start_input,
370 const std::vector<unsigned int> &count_input,
371 const double *op) const {
372 // make copies of start and count so that we can use them in MPI_Recv() calls below
373 std::vector<unsigned int> start = start_input;
374 std::vector<unsigned int> count = count_input;
375 const int start_tag = 1, count_tag = 2, data_tag = 3, chunk_size_tag = 4;
376 int stat = NC_NOERR, com_size = 0, ndims = static_cast<int>(start.size());
377 MPI_Status mpi_stat;
378 unsigned int local_chunk_size = 1, processor_0_chunk_size = 0;
379
380 // get the size of the communicator
381 MPI_Comm_size(m_com, &com_size);
382
383 // compute the size of a local chunk
384 for (int k = 0; k < ndims; ++k) {
385 local_chunk_size *= count[k];
386 }
387
388 // compute the maximum and send it to processor 0; this is the size of the
389 // buffer processor 0 will need
390 MPI_Reduce(&local_chunk_size, &processor_0_chunk_size, 1, MPI_UNSIGNED, MPI_MAX, 0, m_com);
391
392 // now we need to send start and count data to processor 0 and receive data
393 if (m_rank == 0) {
394 std::vector<double> processor_0_buffer;
395 processor_0_buffer.resize(processor_0_chunk_size);
396
397 // MPI calls below require C datatypes (so that we don't have to worry about sizes of
398 // size_t and ptrdiff_t), so we make local copies of start and count to use in the
399 // nc_get_vara_double() call.
400 std::vector<size_t> nc_start(ndims), nc_count(ndims);
401 int varid;
402
403 stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid);
405
406 for (int r = 0; r < com_size; ++r) {
407
408 if (r != 0) {
409 // Note: start, count, and local_chunk_size on processor zero are used *before*
410 // they get overwritten by these calls
411 MPI_Recv(start.data(), ndims, MPI_UNSIGNED, r, start_tag, m_com, &mpi_stat);
412 MPI_Recv(count.data(), ndims, MPI_UNSIGNED, r, count_tag, m_com, &mpi_stat);
413 MPI_Recv(&local_chunk_size, 1, MPI_UNSIGNED, r, chunk_size_tag, m_com, &mpi_stat);
414
415 MPI_Recv(processor_0_buffer.data(), local_chunk_size, MPI_DOUBLE, r, data_tag, m_com,
416 &mpi_stat);
417 } else {
418 for (unsigned int k = 0; k < local_chunk_size; ++k) {
419 processor_0_buffer[k] = op[k];
420 }
421 }
422
423 // This for loop uses start and count passed in as arguments when r == 0. For r > 0
424 // they are overwritten by MPI_Recv calls above.
425 for (int k = 0; k < ndims; ++k) {
426 nc_start[k] = start[k];
427 nc_count[k] = count[k];
428 }
429
430 stat = nc_put_vara_double(m_file_id, varid, nc_start.data(), nc_count.data(),
431 processor_0_buffer.data());
433 } // end of the for loop
434 } else {
435 MPI_Send(start.data(), ndims, MPI_UNSIGNED, 0, start_tag, m_com);
436 MPI_Send(count.data(), ndims, MPI_UNSIGNED, 0, count_tag, m_com);
437 MPI_Send(&local_chunk_size, 1, MPI_UNSIGNED, 0, chunk_size_tag, m_com);
438
439 MPI_Send(const_cast<double *>(op), (int)local_chunk_size, MPI_DOUBLE, 0, data_tag, m_com);
440 }
441}
442
443//! \brief Get the number of variables.
444void NC_Serial::inq_nvars_impl(int &result) const {
445 int stat = NC_NOERR;
446
447 if (m_rank == 0) {
448 stat = nc_inq_nvars(m_file_id, &result);
449 }
450 MPI_Barrier(m_com);
451
452 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
454
455 MPI_Bcast(&result, 1, MPI_INT, 0, m_com);
456}
457
458//! \brief Get dimensions a variable depends on.
459void NC_Serial::inq_vardimid_impl(const std::string &variable_name,
460 std::vector<std::string> &result) const {
461 int stat, ndims, varid = -1;
462 std::vector<int> dimids;
463
464 if (m_rank == 0) {
465 stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid);
466
467 if (stat == NC_NOERR) {
468 stat = nc_inq_varndims(m_file_id, varid, &ndims);
469 }
470 }
471
472 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
474
475 MPI_Bcast(&ndims, 1, MPI_INT, 0, m_com);
476
477 if (ndims == 0) {
478 result.clear();
479 return;
480 }
481
482 result.resize(ndims);
483 dimids.resize(ndims);
484
485 if (m_rank == 0) {
486 stat = nc_inq_vardimid(m_file_id, varid, dimids.data());
487 }
488
489 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
491
492 MPI_Barrier(m_com);
493
494 for (int k = 0; k < ndims; ++k) {
495 std::vector<char> name(NC_MAX_NAME + 1, 0);
496
497 if (m_rank == 0) {
498 stat = nc_inq_dimname(m_file_id, dimids[k], name.data());
499 }
500
501 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
503
504 MPI_Barrier(m_com);
505 MPI_Bcast(name.data(), name.size(), MPI_CHAR, 0, m_com);
506
507 result[k] = name.data();
508 }
509}
510
511//! \brief Get the number of attributes of a variable.
512/*!
513 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
514 */
515void NC_Serial::inq_varnatts_impl(const std::string &variable_name, int &result) const {
516 int stat = NC_NOERR;
517
518 if (m_rank == 0) {
519 int varid = get_varid(variable_name);
520
521 if (varid >= NC_GLOBAL) {
522 stat = nc_inq_varnatts(m_file_id, varid, &result);
523 } else {
524 stat = varid; // LCOV_EXCL_LINE
525 }
526 }
527 MPI_Barrier(m_com);
528
529 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
531
532 MPI_Bcast(&result, 1, MPI_INT, 0, m_com);
533}
534
535//! \brief Finds a variable and sets the "exists" flag.
536void NC_Serial::inq_varid_impl(const std::string &variable_name, bool &exists) const {
537 int stat, flag = -1;
538
539 if (m_rank == 0) {
540 stat = nc_inq_varid(m_file_id, variable_name.c_str(), &flag);
541
542 flag = (stat == NC_NOERR) ? 1 : 0;
543 }
544 MPI_Barrier(m_com);
545 MPI_Bcast(&flag, 1, MPI_INT, 0, m_com);
546
547 exists = (flag == 1);
548}
549
550void NC_Serial::inq_varname_impl(unsigned int j, std::string &result) const {
551 int stat = NC_NOERR;
552 std::vector<char> varname(NC_MAX_NAME + 1, 0);
553
554 if (m_rank == 0) {
555 stat = nc_inq_varname(m_file_id, j, varname.data());
556 }
557
558 MPI_Barrier(m_com);
559
560 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
561 MPI_Bcast(varname.data(), NC_MAX_NAME, MPI_CHAR, 0, m_com);
562
564
565 result = varname.data();
566}
567
568//! \brief Gets a double attribute.
569/*!
570 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
571 */
572void NC_Serial::get_att_double_impl(const std::string &variable_name, const std::string &att_name,
573 std::vector<double> &result) const {
574 int stat = NC_NOERR, len = 0;
575
576 int varid = get_varid(variable_name);
577
578 // Read and broadcast the attribute length:
579 if (m_rank == 0) {
580 size_t attlen = 0;
581
582 if (varid >= NC_GLOBAL) {
583 stat = nc_inq_attlen(m_file_id, varid, att_name.c_str(), &attlen);
584 } else {
585 stat = varid; // LCOV_EXCL_LINE
586 }
587
588 if (stat == NC_NOERR) {
589 len = static_cast<int>(attlen);
590 } else {
591 len = 0;
592 }
593 }
594 MPI_Bcast(&len, 1, MPI_INT, 0, m_com);
595
596 if (len == 0) {
597 result.clear();
598 return;
599 }
600
601 result.resize(len);
602
603 // Now read data and broadcast stat to see if we succeeded:
604 if (m_rank == 0) {
605 stat = nc_get_att_double(m_file_id, varid, att_name.c_str(), result.data());
606 }
607 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
608
610
611 // Broadcast data
612 MPI_Bcast(result.data(), len, MPI_DOUBLE, 0, m_com);
613}
614
615// Get a text (character array) attribute on rank 0.
616static int get_att_text(int ncid, int varid, const std::string &att_name, std::string &result) {
617 int stat = NC_NOERR;
618
619 size_t attlen = 0;
620 stat = nc_inq_attlen(ncid, varid, att_name.c_str(), &attlen);
621 if (stat != NC_NOERR) {
622 result = "";
623 return 0;
624 }
625
626 std::vector<char> buffer(attlen + 1, 0);
627 stat = nc_get_att_text(ncid, varid, att_name.c_str(), buffer.data());
628 if (stat == NC_NOERR) {
629 result = buffer.data();
630 } else {
631 result = "";
632 }
633
634 return 0;
635}
636
637// Get a string attribute on rank 0. In "string array" attributes array elements are concatenated
638// using "," as the separator.
639static int get_att_string(int ncid, int varid, const std::string &att_name, std::string &result) {
640 int stat = NC_NOERR;
641
642 size_t attlen = 0;
643 stat = nc_inq_attlen(ncid, varid, att_name.c_str(), &attlen);
644 if (stat != NC_NOERR) {
645 result = "";
646 return 0;
647 }
648
649 std::vector<char *> buffer(attlen + 1, 0);
650 stat = nc_get_att_string(ncid, varid, att_name.c_str(), buffer.data());
651 if (stat == NC_NOERR) {
652 std::vector<std::string> strings(attlen);
653 for (size_t k = 0; k < attlen; ++k) {
654 strings[k] = buffer[k];
655 }
656 result = join(strings, ",");
657 } else {
658 result = "";
659 }
660 stat = nc_free_string(attlen, buffer.data());
661
662 return stat;
663}
664
665
666//! \brief Gets a text attribute.
667/*!
668 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
669 */
670void NC_Serial::get_att_text_impl(const std::string &variable_name, const std::string &att_name,
671 std::string &result) const {
672 int stat = NC_NOERR;
673
674 // Read and broadcast the attribute length:
675 if (m_rank == 0) {
676
677 int varid = get_varid(variable_name);
678
679 if (varid >= NC_GLOBAL) {
680 nc_type nctype = NC_NAT;
681 stat = nc_inq_atttype(m_file_id, varid, att_name.c_str(), &nctype);
682
683 if (stat == NC_NOERR) {
684 switch (nctype) {
685 case NC_CHAR:
686 stat = pism::io::get_att_text(m_file_id, varid, att_name, result);
687 break;
688 case NC_STRING:
689 stat = pism::io::get_att_string(m_file_id, varid, att_name, result);
690 break;
691 default:
692 result = "";
693 stat = NC_NOERR;
694 }
695 } else if (stat == NC_ENOTATT) {
696 result = "";
697 stat = NC_NOERR;
698 }
699 } else {
700 stat = varid; // LCOV_EXCL_LINE
701 }
702 }
703 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
705
706 unsigned int len = result.size();
707 MPI_Bcast(&len, 1, MPI_UNSIGNED, 0, m_com);
708
709 result.resize(len);
710 MPI_Bcast(&result[0], (int)len, MPI_CHAR, 0, m_com);
711}
712
713
714//! \brief Writes a double attribute.
715/*!
716 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
717 */
718void NC_Serial::put_att_double_impl(const std::string &variable_name, const std::string &att_name,
719 io::Type nctype, const std::vector<double> &data) const {
720 int stat = NC_NOERR;
721
722 if (m_rank == 0) {
723 int varid = get_varid(variable_name);
724
725 if (varid >= NC_GLOBAL) {
726 stat = nc_put_att_double(m_file_id, varid, att_name.c_str(), pism_type_to_nc_type(nctype),
727 data.size(), data.data());
728 } else {
729 stat = varid; // LCOV_EXCL_LINE
730 }
731 }
732
733 MPI_Barrier(m_com);
734 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
735
737}
738
739
740//! \brief Writes a text attribute.
741/*!
742 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
743 */
744void NC_Serial::put_att_text_impl(const std::string &variable_name, const std::string &att_name,
745 const std::string &value) const {
746 int stat = NC_NOERR;
747
748 if (m_rank == 0) {
749 int varid = get_varid(variable_name);
750
751 if (varid >= NC_GLOBAL) {
752 stat = nc_put_att_text(m_file_id, varid, att_name.c_str(), value.size(), value.c_str());
753 } else {
754 stat = varid; // LCOV_EXCL_LINE
755 }
756 }
757
758 MPI_Barrier(m_com);
759 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
760
762}
763
764//! \brief Gets the name of a numbered attribute.
765/*!
766 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
767 */
768void NC_Serial::inq_attname_impl(const std::string &variable_name, unsigned int n,
769 std::string &result) const {
770 int stat = NC_NOERR;
771 std::vector<char> name(NC_MAX_NAME + 1, 0);
772
773 if (m_rank == 0) {
774 int varid = get_varid(variable_name);
775
776 if (varid >= NC_GLOBAL) {
777 stat = nc_inq_attname(m_file_id, varid, n, name.data());
779 } else {
780 stat = varid; // LCOV_EXCL_LINE
781 }
782 }
783 MPI_Barrier(m_com);
784 MPI_Bcast(name.data(), NC_MAX_NAME, MPI_CHAR, 0, m_com);
785 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
786
788
789 result = name.data();
790}
791
792//! \brief Gets the type of an attribute.
793/*!
794 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
795 */
796void NC_Serial::inq_atttype_impl(const std::string &variable_name, const std::string &att_name,
797 io::Type &result) const {
798 int stat, tmp;
799
800 if (m_rank == 0) {
801 int varid = get_varid(variable_name);
802
803 if (varid >= NC_GLOBAL) {
804 // In NetCDF 3.6.x nc_type is an enum; in 4.x it is 'typedef int'.
805 nc_type nctype = NC_NAT;
806 stat = nc_inq_atttype(m_file_id, varid, att_name.c_str(), &nctype);
807 if (stat == NC_ENOTATT) {
808 tmp = NC_NAT;
809 stat = NC_NOERR;
810 } else {
811 tmp = static_cast<int>(nctype);
812 }
813 } else {
814 stat = varid; // LCOV_EXCL_LINE
815 }
816 }
817 MPI_Barrier(m_com);
818 MPI_Bcast(&tmp, 1, MPI_INT, 0, m_com);
819
820 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
822
823 result = nc_type_to_pism_type(tmp);
824}
825
826
827//! \brief Sets the fill mode.
828void NC_Serial::set_fill_impl(int fillmode, int &old_modep) const {
829 int stat = NC_NOERR;
830
831 if (m_rank == 0) {
832 stat = nc_set_fill(m_file_id, fillmode, &old_modep);
833 }
834
835 MPI_Barrier(m_com);
836 MPI_Bcast(&old_modep, 1, MPI_INT, 0, m_com);
837 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
838
840}
841
842std::string NC_Serial::get_format() const {
843 int format;
844
845 if (m_rank == 0) {
846 int stat = nc_inq_format(m_file_id, &format);
848 }
849 MPI_Barrier(m_com);
850 MPI_Bcast(&format, 1, MPI_INT, 0, m_com);
851
852 switch (format) {
853 case NC_FORMAT_CLASSIC:
854 case NC_FORMAT_64BIT_OFFSET:
855 return "netcdf3";
856 case NC_FORMAT_64BIT_DATA:
857 return "cdf5";
858 case NC_FORMAT_NETCDF4:
859 case NC_FORMAT_NETCDF4_CLASSIC:
860 default:
861 return "netcdf4";
862 }
863}
864
865void NC_Serial::del_att_impl(const std::string &variable_name, const std::string &att_name) const {
866 int stat = NC_NOERR;
867
868 if (m_rank == 0) {
869 int varid = get_varid(variable_name);
870
871 if (varid >= NC_GLOBAL) {
872 stat = nc_del_att(m_file_id, varid, att_name.c_str());
873 }
874 }
875
876 MPI_Barrier(m_com);
877 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
878
880}
881
882/*!
883 * return the varid corresponding to a variable.
884 *
885 * If the value returned is NC_GLOBAL or greater, it is a varid, otherwise it is an error
886 * code.
887 */
888int NC_Serial::get_varid(const std::string &variable_name) const {
889 if (variable_name == "PISM_GLOBAL") {
890 return NC_GLOBAL;
891 }
892
893 if (m_rank == 0) {
894 int varid = -2;
895 int stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid);
896
897 if (stat == NC_NOERR) {
898 return varid;
899 }
900
901 return stat;
902 }
903
904 return -2; // this value will not be used
905}
906
907} // end of namespace io
908} // end of namespace pism
std::string m_filename
Definition NCFile.hh:223
MPI_Comm m_com
Definition NCFile.hh:221
The PISM wrapper for a subset of the NetCDF C API.
Definition NCFile.hh:59
void get_att_text_impl(const std::string &variable_name, const std::string &att_name, std::string &result) const
Gets a text attribute.
Definition NC_Serial.cc:670
virtual void create_impl(const std::string &filename)
Create a NetCDF file.
Definition NC_Serial.cc:94
void def_dim_impl(const std::string &name, size_t length) const
Define a dimension.
Definition NC_Serial.cc:171
void inq_varname_impl(unsigned int j, std::string &result) const
Definition NC_Serial.cc:550
void put_vara_double_impl(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const double *op) const
Definition NC_Serial.cc:368
void set_fill_impl(int fillmode, int &old_modep) const
Sets the fill mode.
Definition NC_Serial.cc:828
std::string get_format() const
Definition NC_Serial.cc:842
void enddef_impl() const
Exit define mode.
Definition NC_Serial.cc:140
void get_vara_double_impl(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
Definition NC_Serial.cc:281
void inq_vardimid_impl(const std::string &variable_name, std::vector< std::string > &result) const
Get dimensions a variable depends on.
Definition NC_Serial.cc:459
void put_att_text_impl(const std::string &variable_name, const std::string &att_name, const std::string &value) const
Writes a text attribute.
Definition NC_Serial.cc:744
void get_att_double_impl(const std::string &variable_name, const std::string &att_name, std::vector< double > &result) const
Gets a double attribute.
Definition NC_Serial.cc:572
void put_att_double_impl(const std::string &variable_name, const std::string &att_name, io::Type xtype, const std::vector< double > &data) const
Writes a double attribute.
Definition NC_Serial.cc:718
void open_impl(const std::string &filename, io::Mode mode)
Definition NC_Serial.cc:77
void inq_varnatts_impl(const std::string &variable_name, int &result) const
Get the number of attributes of a variable.
Definition NC_Serial.cc:515
void inq_varid_impl(const std::string &variable_name, bool &exists) const
Finds a variable and sets the "exists" flag.
Definition NC_Serial.cc:536
void get_var_double(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
Get variable data.
Definition NC_Serial.cc:288
void inq_attname_impl(const std::string &variable_name, unsigned int n, std::string &result) const
Gets the name of a numbered attribute.
Definition NC_Serial.cc:768
virtual void def_var_impl(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Define a variable.
Definition NC_Serial.cc:252
virtual void set_compression_level_impl(int level) const
Definition NC_Serial.cc:71
void close_impl()
Close a NetCDF file.
Definition NC_Serial.cc:109
NC_Serial(MPI_Comm com)
Definition NC_Serial.cc:55
void del_att_impl(const std::string &variable_name, const std::string &att_name) const
Definition NC_Serial.cc:865
void inq_nvars_impl(int &result) const
Get the number of variables.
Definition NC_Serial.cc:444
void sync_impl() const
Definition NC_Serial.cc:125
void inq_dimlen_impl(const std::string &dimension_name, unsigned int &result) const
Get a dimension length.
Definition NC_Serial.cc:201
void redef_impl() const
Enter define mode.
Definition NC_Serial.cc:156
void inq_dimid_impl(const std::string &dimension_name, bool &exists) const
Definition NC_Serial.cc:185
virtual ~NC_Serial()
Definition NC_Serial.cc:60
int get_varid(const std::string &variable_name) const
Definition NC_Serial.cc:888
void inq_unlimdim_impl(std::string &result) const
Get an unlimited dimension.
Definition NC_Serial.cc:224
void inq_atttype_impl(const std::string &variable_name, const std::string &att_name, io::Type &result) const
Gets the type of an attribute.
Definition NC_Serial.cc:796
#define PISM_ERROR_LOCATION
#define n
Definition exactTestM.c:37
@ PISM_READONLY
open an existing file for reading only
Definition IO_Flags.hh:68
static void check(const ErrorLocation &where, int return_code)
Prints an error message; for debugging.
Definition NC4_Par.cc:36
static void check_and_abort(MPI_Comm com, const ErrorLocation &where, int return_code)
call MPI_Abort() if a NetCDF call failed
Definition NC_Serial.cc:48
static void get_att_string(int ncid, int varid, const std::string &att_name, std::string &result)
Definition NC4File.cc:293
static void get_att_text(int ncid, int varid, const std::string &att_name, std::string &result)
Definition NC4File.cc:276
static const double k
Definition exactTestP.cc:42
std::string join(const std::vector< std::string > &strings, const std::string &separator)
Concatenate strings, inserting separator between elements.
static pism::io::Type nc_type_to_pism_type(int input)
static nc_type pism_type_to_nc_type(pism::io::Type input)
int count
Definition test_cube.c:16