PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
NC_Serial.cc
Go to the documentation of this file.
1// Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2023, 2024, 2025, 2026 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;
376 int stat = NC_NOERR, com_size = 0, ndims = static_cast<int>(start.size());
377 int local_chunk_size = 1, processor_0_chunk_size = 0;
378
379 // get the size of the communicator
380 MPI_Comm_size(m_com, &com_size);
381
382 // compute the size of a local chunk
383 for (int k = 0; k < ndims; ++k) {
384 local_chunk_size *= (int)count[k];
385 }
386
387 // compute the maximum and send it to processor 0; this is the size of the
388 // buffer processor 0 will need
389 MPI_Reduce(&local_chunk_size, &processor_0_chunk_size, 1, MPI_INT, MPI_MAX, 0, m_com);
390
391 // now we need to send start and count data to processor 0 and receive data
392 if (m_rank == 0) {
393 std::vector<double> processor_0_buffer;
394 processor_0_buffer.resize(processor_0_chunk_size);
395
396 int varid;
397
398 stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid);
400
401 for (int r = 0; r < com_size; ++r) {
402
403 if (r != 0) {
404 // Note: start, count, and local_chunk_size on processor zero are used *before*
405 // they get overwritten by these calls
406 MPI_Recv(start.data(), ndims, MPI_UNSIGNED, r, start_tag, m_com, MPI_STATUS_IGNORE);
407 MPI_Recv(count.data(), ndims, MPI_UNSIGNED, r, count_tag, m_com, MPI_STATUS_IGNORE);
408
409 // get the size of the array sent by rank `r`:
410 MPI_Status mpi_stat;
411 MPI_Probe(r, data_tag, m_com, &mpi_stat);
412 MPI_Get_count(&mpi_stat, MPI_DOUBLE, &local_chunk_size);
413
414 MPI_Recv(processor_0_buffer.data(), local_chunk_size, MPI_DOUBLE, r, data_tag, m_com,
415 MPI_STATUS_IGNORE);
416 } else {
417 for (int k = 0; k < local_chunk_size; ++k) {
418 processor_0_buffer[k] = op[k];
419 }
420 }
421
422 // MPI_Send/Recv calls require C datatypes, so we make local copies of start and
423 // count to use in the nc_get_vara_double() call so that we don't have to worry
424 // about sizes of size_t and ptrdiff_t.
425 //
426 // This for loop uses start and count passed in as arguments when r == 0. For r > 0
427 // they are overwritten by MPI_Recv calls above.
428 std::vector<size_t> nc_start(ndims), nc_count(ndims);
429 for (int k = 0; k < ndims; ++k) {
430 nc_start[k] = start[k];
431 nc_count[k] = count[k];
432 }
433
434 stat = nc_put_vara_double(m_file_id, varid, nc_start.data(), nc_count.data(),
435 processor_0_buffer.data());
437 } // end of the for loop
438 } else {
439 MPI_Send(start.data(), ndims, MPI_UNSIGNED, 0, start_tag, m_com);
440 MPI_Send(count.data(), ndims, MPI_UNSIGNED, 0, count_tag, m_com);
441
442 MPI_Send(const_cast<double *>(op), local_chunk_size, MPI_DOUBLE, 0, data_tag, m_com);
443 }
444}
445
446
447void NC_Serial::put_vara_text_impl(const std::string &variable_name,
448 const std::vector<unsigned int> &start,
449 const std::vector<unsigned int> &count, const char *data) const {
450 int stat = NC_NOERR;
451
452 if (m_rank == 0) {
453 std::vector<size_t> nc_start{};
454 nc_start.reserve(start.size());
455 for (const auto &s : start) {
456 nc_start.push_back(s);
457 }
458
459 std::vector<size_t> nc_count{};
460 nc_count.reserve(count.size());
461 for (const auto &c : count) {
462 nc_count.push_back(c);
463 }
464
465 stat = nc_put_vara_text(m_file_id, get_varid(variable_name), nc_start.data(), nc_count.data(),
466 data);
467 }
468 MPI_Barrier(m_com);
469
470 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
472}
473
474
475//! \brief Get the number of variables.
476void NC_Serial::inq_nvars_impl(int &result) const {
477 int stat = NC_NOERR;
478
479 if (m_rank == 0) {
480 stat = nc_inq_nvars(m_file_id, &result);
481 }
482 MPI_Barrier(m_com);
483
484 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
486
487 MPI_Bcast(&result, 1, MPI_INT, 0, m_com);
488}
489
490//! \brief Get dimensions a variable depends on.
491void NC_Serial::inq_vardimid_impl(const std::string &variable_name,
492 std::vector<std::string> &result) const {
493 int stat, ndims, varid = -1;
494 std::vector<int> dimids;
495
496 if (m_rank == 0) {
497 stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid);
498
499 if (stat == NC_NOERR) {
500 stat = nc_inq_varndims(m_file_id, varid, &ndims);
501 }
502 }
503
504 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
506
507 MPI_Bcast(&ndims, 1, MPI_INT, 0, m_com);
508
509 if (ndims == 0) {
510 result.clear();
511 return;
512 }
513
514 result.resize(ndims);
515 dimids.resize(ndims);
516
517 if (m_rank == 0) {
518 stat = nc_inq_vardimid(m_file_id, varid, dimids.data());
519 }
520
521 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
523
524 MPI_Barrier(m_com);
525
526 for (int k = 0; k < ndims; ++k) {
527 std::vector<char> name(NC_MAX_NAME + 1, 0);
528
529 if (m_rank == 0) {
530 stat = nc_inq_dimname(m_file_id, dimids[k], name.data());
531 }
532
533 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
535
536 MPI_Barrier(m_com);
537 MPI_Bcast(name.data(), name.size(), MPI_CHAR, 0, m_com);
538
539 result[k] = name.data();
540 }
541}
542
543//! \brief Get the number of attributes of a variable.
544/*!
545 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
546 */
547void NC_Serial::inq_varnatts_impl(const std::string &variable_name, int &result) const {
548 int stat = NC_NOERR;
549
550 if (m_rank == 0) {
551 int varid = get_varid(variable_name);
552
553 if (varid >= NC_GLOBAL) {
554 stat = nc_inq_varnatts(m_file_id, varid, &result);
555 } else {
556 stat = varid; // LCOV_EXCL_LINE
557 }
558 }
559 MPI_Barrier(m_com);
560
561 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
563
564 MPI_Bcast(&result, 1, MPI_INT, 0, m_com);
565}
566
567//! \brief Finds a variable and sets the "exists" flag.
568void NC_Serial::inq_varid_impl(const std::string &variable_name, bool &exists) const {
569 int stat, flag = -1;
570
571 if (m_rank == 0) {
572 stat = nc_inq_varid(m_file_id, variable_name.c_str(), &flag);
573
574 flag = (stat == NC_NOERR) ? 1 : 0;
575 }
576 MPI_Barrier(m_com);
577 MPI_Bcast(&flag, 1, MPI_INT, 0, m_com);
578
579 exists = (flag == 1);
580}
581
582void NC_Serial::inq_varname_impl(unsigned int j, std::string &result) const {
583 int stat = NC_NOERR;
584 std::vector<char> varname(NC_MAX_NAME + 1, 0);
585
586 if (m_rank == 0) {
587 stat = nc_inq_varname(m_file_id, j, varname.data());
588 }
589
590 MPI_Barrier(m_com);
591
592 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
593 MPI_Bcast(varname.data(), NC_MAX_NAME, MPI_CHAR, 0, m_com);
594
596
597 result = varname.data();
598}
599
600//! \brief Gets a double attribute.
601/*!
602 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
603 */
604void NC_Serial::get_att_double_impl(const std::string &variable_name, const std::string &att_name,
605 std::vector<double> &result) const {
606 int stat = NC_NOERR, len = 0;
607
608 int varid = get_varid(variable_name);
609
610 // Read and broadcast the attribute length:
611 if (m_rank == 0) {
612 size_t attlen = 0;
613
614 if (varid >= NC_GLOBAL) {
615 stat = nc_inq_attlen(m_file_id, varid, att_name.c_str(), &attlen);
616 } else {
617 stat = varid; // LCOV_EXCL_LINE
618 }
619
620 if (stat == NC_NOERR) {
621 len = static_cast<int>(attlen);
622 } else {
623 len = 0;
624 }
625 }
626 MPI_Bcast(&len, 1, MPI_INT, 0, m_com);
627
628 if (len == 0) {
629 result.clear();
630 return;
631 }
632
633 result.resize(len);
634
635 // Now read data and broadcast stat to see if we succeeded:
636 if (m_rank == 0) {
637 stat = nc_get_att_double(m_file_id, varid, att_name.c_str(), result.data());
638 }
639 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
640
642
643 // Broadcast data
644 MPI_Bcast(result.data(), len, MPI_DOUBLE, 0, m_com);
645}
646
647// Get a text (character array) attribute on rank 0.
648static int get_att_text(int ncid, int varid, const std::string &att_name, std::string &result) {
649 int stat = NC_NOERR;
650
651 size_t attlen = 0;
652 stat = nc_inq_attlen(ncid, varid, att_name.c_str(), &attlen);
653 if (stat != NC_NOERR) {
654 result = "";
655 return 0;
656 }
657
658 std::vector<char> buffer(attlen + 1, 0);
659 stat = nc_get_att_text(ncid, varid, att_name.c_str(), buffer.data());
660 if (stat == NC_NOERR) {
661 result = buffer.data();
662 } else {
663 result = "";
664 }
665
666 return 0;
667}
668
669// Get a string attribute on rank 0. In "string array" attributes array elements are concatenated
670// using "," as the separator.
671static int get_att_string(int ncid, int varid, const std::string &att_name, std::string &result) {
672 int stat = NC_NOERR;
673
674 size_t attlen = 0;
675 stat = nc_inq_attlen(ncid, varid, att_name.c_str(), &attlen);
676 if (stat != NC_NOERR) {
677 result = "";
678 return 0;
679 }
680
681 std::vector<char *> buffer(attlen + 1, 0);
682 stat = nc_get_att_string(ncid, varid, att_name.c_str(), buffer.data());
683 if (stat == NC_NOERR) {
684 std::vector<std::string> strings(attlen);
685 for (size_t k = 0; k < attlen; ++k) {
686 strings[k] = buffer[k];
687 }
688 result = join(strings, ",");
689 } else {
690 result = "";
691 }
692 stat = nc_free_string(attlen, buffer.data());
693
694 return stat;
695}
696
697
698//! \brief Gets a text attribute.
699/*!
700 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
701 */
702void NC_Serial::get_att_text_impl(const std::string &variable_name, const std::string &att_name,
703 std::string &result) const {
704 int stat = NC_NOERR;
705
706 // Read and broadcast the attribute length:
707 if (m_rank == 0) {
708
709 int varid = get_varid(variable_name);
710
711 if (varid >= NC_GLOBAL) {
712 nc_type nctype = NC_NAT;
713 stat = nc_inq_atttype(m_file_id, varid, att_name.c_str(), &nctype);
714
715 if (stat == NC_NOERR) {
716 switch (nctype) {
717 case NC_CHAR:
718 stat = pism::io::get_att_text(m_file_id, varid, att_name, result);
719 break;
720 case NC_STRING:
721 stat = pism::io::get_att_string(m_file_id, varid, att_name, result);
722 break;
723 default:
724 result = "";
725 stat = NC_NOERR;
726 }
727 } else if (stat == NC_ENOTATT) {
728 result = "";
729 stat = NC_NOERR;
730 }
731 } else {
732 stat = varid; // LCOV_EXCL_LINE
733 }
734 }
735 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
737
738 unsigned int len = result.size();
739 MPI_Bcast(&len, 1, MPI_UNSIGNED, 0, m_com);
740
741 result.resize(len);
742 MPI_Bcast(&result[0], (int)len, MPI_CHAR, 0, m_com);
743}
744
745
746//! \brief Writes a double attribute.
747/*!
748 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
749 */
750void NC_Serial::put_att_double_impl(const std::string &variable_name, const std::string &att_name,
751 io::Type nctype, const std::vector<double> &data) const {
752 int stat = NC_NOERR;
753
754 if (m_rank == 0) {
755 int varid = get_varid(variable_name);
756
757 if (varid >= NC_GLOBAL) {
758 stat = nc_put_att_double(m_file_id, varid, att_name.c_str(), pism_type_to_nc_type(nctype),
759 data.size(), data.data());
760 } else {
761 stat = varid; // LCOV_EXCL_LINE
762 }
763 }
764
765 MPI_Barrier(m_com);
766 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
767
769}
770
771
772//! \brief Writes a text attribute.
773/*!
774 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
775 */
776void NC_Serial::put_att_text_impl(const std::string &variable_name, const std::string &att_name,
777 const std::string &value) const {
778 int stat = NC_NOERR;
779
780 if (m_rank == 0) {
781 int varid = get_varid(variable_name);
782
783 if (varid >= NC_GLOBAL) {
784 stat = nc_put_att_text(m_file_id, varid, att_name.c_str(), value.size(), value.c_str());
785 } else {
786 stat = varid; // LCOV_EXCL_LINE
787 }
788 }
789
790 MPI_Barrier(m_com);
791 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
792
794}
795
796//! \brief Gets the name of a numbered attribute.
797/*!
798 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
799 */
800void NC_Serial::inq_attname_impl(const std::string &variable_name, unsigned int n,
801 std::string &result) const {
802 int stat = NC_NOERR;
803 std::vector<char> name(NC_MAX_NAME + 1, 0);
804
805 if (m_rank == 0) {
806 int varid = get_varid(variable_name);
807
808 if (varid >= NC_GLOBAL) {
809 stat = nc_inq_attname(m_file_id, varid, n, name.data());
811 } else {
812 stat = varid; // LCOV_EXCL_LINE
813 }
814 }
815 MPI_Barrier(m_com);
816 MPI_Bcast(name.data(), NC_MAX_NAME, MPI_CHAR, 0, m_com);
817 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
818
820
821 result = name.data();
822}
823
824//! \brief Gets the type of an attribute.
825/*!
826 * Use "PISM_GLOBAL" as the "variable_name" to get the number of global attributes.
827 */
828void NC_Serial::inq_atttype_impl(const std::string &variable_name, const std::string &att_name,
829 io::Type &result) const {
830 int stat, tmp;
831
832 if (m_rank == 0) {
833 int varid = get_varid(variable_name);
834
835 if (varid >= NC_GLOBAL) {
836 // In NetCDF 3.6.x nc_type is an enum; in 4.x it is 'typedef int'.
837 nc_type nctype = NC_NAT;
838 stat = nc_inq_atttype(m_file_id, varid, att_name.c_str(), &nctype);
839 if (stat == NC_ENOTATT) {
840 tmp = NC_NAT;
841 stat = NC_NOERR;
842 } else {
843 tmp = static_cast<int>(nctype);
844 }
845 } else {
846 stat = varid; // LCOV_EXCL_LINE
847 }
848 }
849 MPI_Barrier(m_com);
850 MPI_Bcast(&tmp, 1, MPI_INT, 0, m_com);
851
852 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
854
855 result = nc_type_to_pism_type(tmp);
856}
857
858
859//! \brief Sets the fill mode.
860void NC_Serial::set_fill_impl(int fillmode, int &old_modep) const {
861 int stat = NC_NOERR;
862
863 if (m_rank == 0) {
864 stat = nc_set_fill(m_file_id, fillmode, &old_modep);
865 }
866
867 MPI_Barrier(m_com);
868 MPI_Bcast(&old_modep, 1, MPI_INT, 0, m_com);
869 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
870
872}
873
874std::string NC_Serial::get_format() const {
875 int format;
876
877 if (m_rank == 0) {
878 int stat = nc_inq_format(m_file_id, &format);
880 }
881 MPI_Barrier(m_com);
882 MPI_Bcast(&format, 1, MPI_INT, 0, m_com);
883
884 switch (format) {
885 case NC_FORMAT_CLASSIC:
886 case NC_FORMAT_64BIT_OFFSET:
887 return "netcdf3";
888 case NC_FORMAT_64BIT_DATA:
889 return "cdf5";
890 case NC_FORMAT_NETCDF4:
891 case NC_FORMAT_NETCDF4_CLASSIC:
892 default:
893 return "netcdf4";
894 }
895}
896
897void NC_Serial::del_att_impl(const std::string &variable_name, const std::string &att_name) const {
898 int stat = NC_NOERR;
899
900 if (m_rank == 0) {
901 int varid = get_varid(variable_name);
902
903 if (varid >= NC_GLOBAL) {
904 stat = nc_del_att(m_file_id, varid, att_name.c_str());
905 }
906 }
907
908 MPI_Barrier(m_com);
909 MPI_Bcast(&stat, 1, MPI_INT, 0, m_com);
910
912}
913
914/*!
915 * return the varid corresponding to a variable.
916 *
917 * If the value returned is NC_GLOBAL or greater, it is a varid, otherwise it is an error
918 * code.
919 */
920int NC_Serial::get_varid(const std::string &variable_name) const {
921 if (variable_name == "PISM_GLOBAL") {
922 return NC_GLOBAL;
923 }
924
925 if (m_rank == 0) {
926 int varid = -2;
927 int stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid);
928
929 if (stat == NC_NOERR) {
930 return varid;
931 }
932
933 return stat;
934 }
935
936 return -2; // this value will not be used
937}
938
939} // end of namespace io
940} // end of namespace pism
std::string m_filename
Definition NCFile.hh:233
MPI_Comm m_com
Definition NCFile.hh:231
The PISM wrapper for a subset of the NetCDF C API.
Definition NCFile.hh:61
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:702
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:582
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:860
std::string get_format() const
Definition NC_Serial.cc:874
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:491
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:776
void put_vara_text_impl(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const char *data) const
Definition NC_Serial.cc:447
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:604
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:750
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:547
void inq_varid_impl(const std::string &variable_name, bool &exists) const
Finds a variable and sets the "exists" flag.
Definition NC_Serial.cc:568
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:800
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:897
void inq_nvars_impl(int &result) const
Get the number of variables.
Definition NC_Serial.cc:476
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:920
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:828
#define PISM_ERROR_LOCATION
#define n
Definition exactTestM.c:37
@ PISM_READONLY
open an existing file for reading only
Definition IO_Flags.hh:69
static void check(const ErrorLocation &where, int return_code)
Prints an error message; for debugging.
Definition NC4_Par.cc:42
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:314
static void get_att_text(int ncid, int varid, const std::string &att_name, std::string &result)
Definition NC4File.cc:297
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