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
io_helpers.cc
Go to the documentation of this file.
1/* Copyright (C) 2015--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
20#include <cassert>
21#include <cmath> // isfinite
22#include <cstddef>
23#include <memory>
24#include <array>
25#include <string>
26#include <vector>
27
28#include "pism/util/ConfigInterface.hh"
29#include "pism/util/Context.hh"
30#include "pism/util/Grid.hh"
31#include "pism/util/Logger.hh"
32#include "pism/util/Profiling.hh"
33#include "pism/util/Time.hh"
34#include "pism/util/VariableMetadata.hh"
35#include "pism/util/error_handling.hh"
36#include "pism/util/Interpolation1D.hh"
37#include "pism/util/io/File.hh"
38#include "pism/util/io/IO_Flags.hh"
39#include "pism/util/io/LocalInterpCtx.hh"
40#include "pism/util/io/io_helpers.hh"
41#include "pism/util/pism_utilities.hh"
42#include "pism/util/projection.hh"
43
44namespace pism {
45namespace io {
46
47//! \brief Bi-(or tri-)linear interpolation.
48/*!
49 * This is the interpolation code itself.
50 *
51 * Note that its inputs are (essentially)
52 * - the definition of the input grid
53 * - the definition of the output grid
54 * - input array (lic->buffer)
55 * - output array (double *output_array)
56 *
57 * The `output_array` is expected to be big enough to contain
58 * `grid.xm()*`grid.ym()*length(zlevels_out)` numbers.
59 *
60 * We should be able to switch to using an external interpolation library
61 * fairly easily...
62 */
63static void interpolate(const Grid &grid, const LocalInterpCtx &lic, double const *input_array,
64 double *output_array) {
65 // We'll work with the raw storage here so that the array we are filling is
66 // indexed the same way as the buffer we are pulling from (input_array)
67
68 unsigned int nlevels = lic.z->n_output();
69
70 int x_count = lic.count[X_AXIS], z_count = lic.count[Z_AXIS];
71 auto input = [input_array, x_count, z_count](int X, int Y, int Z) {
72 // the map from logical to linear indices for the input array
73 int index = (Y * x_count + X) * z_count + Z;
74 return input_array[index];
75 };
76
77 for (auto p = grid.points(); p; p.next()) {
78 const int i_global = p.i(), j_global = p.j();
79
80 const int i = i_global - grid.xs(), j = j_global - grid.ys();
81
82 // Indices of neighboring points.
83 const int X_m = lic.x->left(i), X_p = lic.x->right(i);
84 const int Y_m = lic.y->left(j), Y_p = lic.y->right(j);
85
86 for (unsigned int k = 0; k < nlevels; k++) {
87
88 double a_mm = 0.0, a_mp = 0.0, a_pm = 0.0, a_pp = 0.0;
89
90 if (nlevels > 1) {
91 const int Z_m = lic.z->left(k), Z_p = lic.z->right(k);
92
93 const double alpha_z = lic.z->alpha(k);
94
95 // We pretend that there are always 8 neighbors (4 in the map plane,
96 // 2 vertical levels). And compute the indices into the input_array for
97 // those neighbors.
98 const double
99 mmm = input(X_m, Y_m, Z_m),
100 mmp = input(X_m, Y_m, Z_p),
101 pmm = input(X_p, Y_m, Z_m),
102 pmp = input(X_p, Y_m, Z_p),
103 mpm = input(X_m, Y_p, Z_m),
104 mpp = input(X_m, Y_p, Z_p),
105 ppm = input(X_p, Y_p, Z_m),
106 ppp = input(X_p, Y_p, Z_p);
107
108 // linear interpolation in the z-direction
109 a_mm = mmm * (1.0 - alpha_z) + mmp * alpha_z;
110 a_mp = pmm * (1.0 - alpha_z) + pmp * alpha_z;
111 a_pm = mpm * (1.0 - alpha_z) + mpp * alpha_z;
112 a_pp = ppm * (1.0 - alpha_z) + ppp * alpha_z;
113 } else {
114 // no interpolation in Z in the 2-D case
115 a_mm = input(X_m, Y_m, 0);
116 a_mp = input(X_p, Y_m, 0);
117 a_pm = input(X_m, Y_p, 0);
118 a_pp = input(X_p, Y_p, 0);
119 }
120
121 // interpolation coefficient in the x direction
122 const double x_alpha = lic.x->alpha(i);
123 // interpolation coefficient in the y direction
124 const double y_alpha = lic.y->alpha(j);
125
126 // interpolate in x direction
127 const double a_m = a_mm * (1.0 - x_alpha) + a_mp * x_alpha;
128 const double a_p = a_pm * (1.0 - x_alpha) + a_pp * x_alpha;
129
130 int index = (j * grid.xm() + i) * nlevels + k;
131
132 // index into the new array and interpolate in y direction
133 output_array[index] = a_m * (1.0 - y_alpha) + a_p * y_alpha;
134 // done with the point at (x,y,z)
135 } // end of the loop over vertical levels
136 }
137}
138
140 std::vector<unsigned int> start;
141 std::vector<unsigned int> count;
142};
143
144
145/*!
146 * Assemble start and count arrays for use with I/O function calls.
147 *
148 * This function re-arranges provided `start` and `count` (listed in the order T,X,Y,Z) to
149 * get start and count in the order matching the one in `dim_types`.
150 */
151static StartCountInfo compute_start_and_count(std::vector<AxisType> &dim_types,
152 const std::array<int, 4> &start,
153 const std::array<int, 4> &count) {
154 auto ndims = dim_types.size();
155
156 // Resize output vectors:
157 StartCountInfo result;
158 result.start.resize(ndims);
159 result.count.resize(ndims);
160
161 // Assemble start and count:
162 for (unsigned int j = 0; j < ndims; j++) {
163 switch (dim_types[j]) {
164 case T_AXIS:
165 result.start[j] = start[T_AXIS];
166 result.count[j] = count[T_AXIS];
167 break;
168 case Y_AXIS:
169 result.start[j] = start[Y_AXIS];
170 result.count[j] = count[Y_AXIS];
171 break;
172 case X_AXIS:
173 result.start[j] = start[X_AXIS];
174 result.count[j] = count[X_AXIS];
175 break;
176 default:
177 // Note: the "default" case is used to handle "3D" variables where the third axis is
178 // not a "Z" axis, i.e. dim_types[j] == UNKNOWN_AXIS. We use this to write
179 // deposition times in the isochrone tracking model, latitude and longitude bounds,
180 // etc. In all these cases the data for the "unknown" axis is input in the slot for
181 // the "Z" axis. (At least this matches the in-memory storage order.)
182 case Z_AXIS:
183 result.start[j] = start[Z_AXIS];
184 result.count[j] = count[Z_AXIS];
185 break;
186 }
187 }
188
189 return result;
190}
191
192//! \brief Define a dimension \b and the associated coordinate variable. Set attributes.
193void define_dimension(const File &file, unsigned long int length,
194 const VariableMetadata &metadata) {
195 std::string name = metadata.get_name();
196 try {
197 file.define_dimension(name, length);
198
199 file.define_variable(name, PISM_DOUBLE, { name });
200
201 write_attributes(file, metadata, PISM_DOUBLE);
202
203 } catch (RuntimeError &e) {
204 e.add_context("defining dimension '%s' in '%s'", name.c_str(), file.name().c_str());
205 throw;
206 }
207}
208
209
210//! Prepare a file for output.
211void define_time(const File &file, const Context &ctx) {
212 const Time &time = *ctx.time();
213 const Config &config = *ctx.config();
214
215 define_time(file, config.get_string("time.dimension_name"), time.calendar(), time.units_string(),
216 ctx.unit_system());
217}
218
219/*!
220 * Define a time dimension and the corresponding coordinate variable. Does nothing if the time
221 * variable is already present.
222 */
223void define_time(const File &file, const std::string &name, const std::string &calendar,
224 const std::string &units, units::System::Ptr unit_system) {
225 try {
226 if (file.variable_exists(name)) {
227 return;
228 }
229
230 // time
231 VariableMetadata time(name, unit_system);
232 time["long_name"] = "time";
233 time["calendar"] = calendar;
234 time["units"] = units;
235 time["axis"] = "T";
236
237 define_dimension(file, PISM_UNLIMITED, time);
238 } catch (RuntimeError &e) {
239 e.add_context("defining the time dimension in \"" + file.name() + "\"");
240 throw;
241 }
242}
243
244//! Prepare a file for output.
245void append_time(const File &file, const Config &config, double time_seconds) {
246 append_time(file, config.get_string("time.dimension_name"), time_seconds);
247}
248
249//! \brief Append to the time dimension.
250void append_time(const File &file, const std::string &name, double value) {
251 try {
252 unsigned int start = file.dimension_length(name);
253
254 file.write_variable(name, { start }, { 1 }, &value);
255 } catch (RuntimeError &e) {
256 e.add_context("appending to the time dimension in \"" + file.name() + "\"");
257 throw;
258 }
259}
260
261//! \brief Define dimensions a variable depends on.
262static void define_dimensions(const SpatialVariableMetadata &var, const Grid &grid,
263 const File &file) {
264
265 // x
266 std::string x_name = var.x().get_name();
267 if (not file.dimension_exists(x_name)) {
268 define_dimension(file, grid.Mx(), var.x());
269 file.write_attribute(x_name, "spacing_meters", PISM_DOUBLE, { grid.x(1) - grid.x(0) });
270 }
271
272 // y
273 std::string y_name = var.y().get_name();
274 if (not file.dimension_exists(y_name)) {
275 define_dimension(file, grid.My(), var.y());
276 file.write_attribute(y_name, "spacing_meters", PISM_DOUBLE, { grid.y(1) - grid.y(0) });
277 }
278
279 // z
280 std::string z_name = var.z().get_name();
281 if (not z_name.empty()) {
282 if (not file.dimension_exists(z_name)) {
283 const std::vector<double> &levels = var.levels();
284 // make sure we have at least one level
285 unsigned int nlevels = std::max(levels.size(), (size_t)1);
286 define_dimension(file, nlevels, var.z());
287
288 bool spatial_dim = not var.z().get_string("axis").empty();
289
290 if (nlevels > 1 and spatial_dim) {
291 double dz_max = levels[1] - levels[0];
292 double dz_min = levels.back() - levels.front();
293
294 for (unsigned int k = 0; k < nlevels - 1; ++k) {
295 double dz = levels[k + 1] - levels[k];
296 dz_max = std::max(dz_max, dz);
297 dz_min = std::min(dz_min, dz);
298 }
299
300 file.write_attribute(z_name, "spacing_min_meters", PISM_DOUBLE, { dz_min });
301 file.write_attribute(z_name, "spacing_max_meters", PISM_DOUBLE, { dz_max });
302 }
303 }
304 }
305}
306
307static void write_dimension_data(const File &file, const std::string &name,
308 const std::vector<double> &data) {
309 bool written = file.get_variable_was_written(name);
310 if (not written) {
311 file.write_variable(name, { 0 }, { (unsigned int)data.size() }, data.data());
312 file.set_variable_was_written(name);
313 }
314}
315
316void write_dimensions(const SpatialVariableMetadata &var, const Grid &grid, const File &file) {
317 // x
318 std::string x_name = var.x().get_name();
319 if (file.dimension_exists(x_name)) {
320 write_dimension_data(file, x_name, grid.x());
321 }
322
323 // y
324 std::string y_name = var.y().get_name();
325 if (file.dimension_exists(y_name)) {
326 write_dimension_data(file, y_name, grid.y());
327 }
328
329 // z
330 std::string z_name = var.z().get_name();
331 if (file.dimension_exists(z_name)) {
332 write_dimension_data(file, z_name, var.levels());
333 }
334}
335
336/**
337 * Check if the storage order of a variable in the current file
338 * matches the memory storage order used by PISM.
339 *
340 * @param[in] file input file
341 * @param var_name name of the variable to check
342 * @returns false if storage orders match, true otherwise
343 */
344static bool transpose(std::vector<AxisType> dimension_types) {
345
346 std::vector<AxisType> storage, memory = { Y_AXIS, X_AXIS };
347
348 bool first = true;
349 for (auto dimtype : dimension_types) {
350
351 if (first && dimtype == T_AXIS) {
352 // ignore the time dimension, but only if it is the first
353 // dimension in the list
354 first = false;
355 continue;
356 }
357
358 if (dimtype == X_AXIS || dimtype == Y_AXIS) {
359 storage.push_back(dimtype);
360 } else if (dimtype == Z_AXIS) {
361 memory.push_back(dimtype); // now memory = {Y_AXIS, X_AXIS, Z_AXIS}
362 // assume that this variable has only one Z_AXIS in the file
363 storage.push_back(dimtype);
364 } else {
365 // an UNKNOWN_AXIS or T_AXIS at index != 0 was found, use transposed I/O
366 return true;
367 }
368 }
369
370 // we support 2D and 3D in-memory arrays, but not 4D
371 assert(memory.size() <= 3);
372
373 return storage != memory;
374}
375
376static std::vector<AxisType> dimension_types(const File &file, const std::string &var_name,
377 std::shared_ptr<units::System> unit_system) {
378 std::vector<AxisType> result;
379 for (const auto &dimension : file.dimensions(var_name)) {
380 result.push_back(file.dimension_type(dimension, unit_system));
381 }
382 return result;
383}
384
385/*!
386 * Transpose data in `input`, putting results in `output`.
387 *
388 * We assume that both `input` and `output` hold prod(`count`) elements.
389 *
390 * The `output` array uses the Y,X,Z order (columns in Z are contiguous).
391 *
392 * The `input` array uses the ordering corresponding to `input_axes` (ordering present in
393 * an input file).
394 *
395 * The array `count` provides the size of a block in `input`, listing axes in the order of
396 * values of AxisType (T,X,Y,Z).
397 */
398static void transpose(const double *input, const std::vector<AxisType> &input_axes,
399 const std::array<int, 4> &count, double *output) {
400 // delta[X_AXIS] is the change in the linear index corresponding to incrementing x in
401 // the `input` ordering. delta[Y_AXIS], delta[Z_AXIS] and delta[T_AXIS] correspond to
402 // changes in y, z, t.
403 std::vector<unsigned> delta = {1, 1, 1, 1}; // 4 to store steps for T,Y,X,Z axes
404 {
405 int N = (int)input_axes.size();
406 // compute changes in the linear index corresponding to incrementing one of the
407 // "spatial" indexes, in the order used in `input`:
408 std::vector<unsigned> tmp(N, 1);
409 for (int k = 0; k < N; ++k) {
410 for (int n = k + 1; n < N; ++n) {
411 tmp[k] *= count[input_axes[n]];
412 }
413 }
414 // re-arrange so that they are stored in the `T,X,Y,Z` order:
415 for (int k = 0; k < N; ++k) {
416 delta[input_axes[k]] = tmp[k];
417 }
418 }
419
420 // change in the linear index corresponding to incrementing x (`output` ordering):
421 unsigned delta_x = count[Z_AXIS];
422 // change in the linear index corresponding to incrementing y (`output` ordering):
423 unsigned delta_y = count[X_AXIS] * count[Z_AXIS];
424
425 // traverse in the memory storage order:
426 for (int y = 0; y < count[Y_AXIS]; ++y) {
427 for (int x = 0; x < count[X_AXIS]; ++x) {
428 for (int z = 0; z < count[Z_AXIS]; ++z) {
429 auto OUT = x * delta_x + y * delta_y + z * 1;
430 auto IN = x * delta[X_AXIS] + y * delta[Y_AXIS] + z * delta[Z_AXIS];
431
432 output[OUT] = input[IN];
433 }
434 }
435 }
436}
437
438/*!
439 * Check if some values in `buffer` match the _FillValue attribute and stop with an error
440 * message if such values are found.
441 */
442static void check_for_missing_values(const File &file, const std::string &variable_name,
443 double tolerance, const double *buffer, size_t buffer_length) {
444 auto attribute = file.read_double_attribute(variable_name, "_FillValue");
445 if (attribute.size() == 1) {
446 double fill_value = attribute[0];
447
448 for (size_t k = 0; k < buffer_length; ++k) {
449 if (fabs(buffer[k] - fill_value) < tolerance) {
452 "Variable '%s' in '%s' contains values matching the _FillValue attribute",
453 variable_name.c_str(), file.name().c_str());
454 }
455 if (not std::isfinite(buffer[k])) {
458 "Variable '%s' in '%s' contains values that are not finite (NaN or infinity)",
459 variable_name.c_str(), file.name().c_str());
460 }
461 }
462 }
463}
464
465//! \brief Read an array distributed according to the grid.
466static void read_distributed_array(const File &file, const std::string &variable_name,
467 std::shared_ptr<units::System> unit_system,
468 const std::array<int,4> &start,
469 const std::array<int,4> &count,
470 double *output) {
471 try {
472 auto dim_types = dimension_types(file, variable_name, unit_system);
473
474 auto sc = compute_start_and_count(dim_types, start, count);
475
476 auto size = count[X_AXIS] * count[Y_AXIS] * count[Z_AXIS];
477
478 if (transpose(dim_types)) {
479 std::vector<double> tmp(size);
480 file.read_variable(variable_name, sc.start, sc.count, tmp.data());
481 transpose(tmp.data(), dim_types, count, output);
482 } else {
483 file.read_variable(variable_name, sc.start, sc.count, output);
484 }
485
486 // Stop with an error message if some values match the _FillValue attribute:
487 check_for_missing_values(file, variable_name, 1e-12, output, size);
488
489 } catch (RuntimeError &e) {
490 e.add_context("reading variable '%s' from '%s'", variable_name.c_str(), file.name().c_str());
491 throw;
492 }
493}
494
495//! Define a NetCDF variable corresponding to a VariableMetadata object.
496void define_spatial_variable(const SpatialVariableMetadata &metadata, const Grid &grid,
497 const File &file, io::Type default_type) {
498 auto config = grid.ctx()->config();
499
500 // make a copy of `metadata` so we can override `output_units` if "output.use_MKS" is
501 // set.
502 SpatialVariableMetadata var = metadata;
503 if (config->get_flag("output.use_MKS")) {
504 var.output_units(var["units"]);
505 }
506
507 std::vector<std::string> dims;
508 std::string name = var.get_name();
509
510 if (file.variable_exists(name)) {
511 return;
512 }
513
514 define_dimensions(var, grid, file);
515
516 std::string x = var.x().get_name(), y = var.y().get_name(), z = var.z().get_name();
517
518 if (not var.get_time_independent()) {
519 dims.push_back(config->get_string("time.dimension_name"));
520 }
521
522 dims.push_back(y);
523 dims.push_back(x);
524
525 if (not z.empty()) {
526 dims.push_back(z);
527 }
528
529 assert(dims.size() > 1);
530
531 io::Type type = var.get_output_type();
532 if (type == PISM_NAT) {
533 type = default_type;
534 }
535 file.define_variable(name, type, dims);
536
537 write_attributes(file, var, type);
538
539 // add the "grid_mapping" attribute if the grid has an associated mapping. Variables lat, lon,
540 // lat_bnds, and lon_bnds should not have the grid_mapping attribute to support CDO (see issue
541 // #384).
542 const VariableMetadata &mapping = grid.get_mapping_info().cf_mapping;
543 if (mapping.has_attributes() and not member(name, { "lat_bnds", "lon_bnds", "lat", "lon" })) {
544 file.write_attribute(var.get_name(), "grid_mapping", mapping.get_name());
545 }
546}
547
548/*!
549 * Check if units are set in an input file and warn if they are not.
550 */
551static std::string check_units(const VariableMetadata &variable, const std::string &input_units,
552 const Logger &log) {
553 std::string internal_units = variable["units"];
554 if (input_units.empty() and not internal_units.empty()) {
555 log.message(2,
556 "PISM WARNING: Variable '%s' ('%s') does not have the units attribute.\n"
557 " Assuming that it is in '%s'.\n",
558 variable.get_name().c_str(), variable.get_string("long_name").c_str(),
559 internal_units.c_str());
560 return internal_units;
561 }
562
563 return input_units;
564}
565
566//! Read a variable from a file into an array `output`.
567/*! This also converts data from input units to internal units if needed.
568 */
569void read_spatial_variable(const SpatialVariableMetadata &variable, const Grid &grid,
570 const File &file, unsigned int time, double *output) {
571
572 const Logger &log = *grid.ctx()->log();
573
574 // Find the variable:
575 auto var = file.find_variable(variable.get_name(), variable["standard_name"]);
576
577 if (not var.exists) {
579 PISM_ERROR_LOCATION, "Can't find '%s' (%s) in '%s'.", variable.get_name().c_str(),
580 variable.get_string("standard_name").c_str(), file.name().c_str());
581 }
582
583 // Sanity check: the variable in an input file should have the expected
584 // number of spatial dimensions.
585 {
586 // Set of spatial dimensions for this field:
587 std::set<int> axes{ X_AXIS, Y_AXIS };
588 if (axis_type_from_string(variable.z()["axis"]) == Z_AXIS) {
589 axes.insert(Z_AXIS);
590 }
591
592 int input_spatial_dim_count = 0; // number of spatial dimensions (input file)
593 size_t matching_dim_count = 0; // number of matching dimensions
594
595 auto input_dims = file.dimensions(var.name);
596 for (const auto &d : input_dims) {
597 auto dim_type = file.dimension_type(d, variable.unit_system());
598
599 if (dim_type != T_AXIS) {
600 ++input_spatial_dim_count;
601 }
602
603 if (axes.find(dim_type) != axes.end()) {
604 ++matching_dim_count;
605 }
606 }
607
608 if (axes.size() != matching_dim_count) {
609
610 // Print the error message and stop:
613 "found the %dD variable %s (%s) in '%s' while trying to read\n"
614 "'%s' ('%s'), which is %d-dimensional.",
615 input_spatial_dim_count, var.name.c_str(), join(input_dims, ",").c_str(),
616 file.name().c_str(), variable.get_name().c_str(),
617 variable.get_string("long_name").c_str(), static_cast<int>(axes.size()));
618 }
619 }
620
621 // make sure we have at least one level
622 size_t nlevels = std::max(variable.levels().size(), (size_t)1);
623
624 read_distributed_array(file, var.name, variable.unit_system(),
625 {(int)time, grid.xs(), grid.ys(), 0},
626 {1, grid.xm(), grid.ym(), (int)nlevels},
627 output);
628
629 auto input_units = file.read_text_attribute(var.name, "units");
630 const std::string &internal_units = variable["units"];
631
632 input_units = check_units(variable, input_units, log);
633
634 // Convert data:
635 size_t size = grid.xm() * grid.ym() * nlevels;
636
637 units::Converter(variable.unit_system(), input_units, internal_units)
638 .convert_doubles(output, size);
639}
640
641//! \brief Write a double array to a file.
642/*!
643 Converts units if internal and "output" units are different.
644 */
645void write_spatial_variable(const SpatialVariableMetadata &metadata, const Grid &grid,
646 const File &file, const double *input) {
647 auto config = grid.ctx()->config();
648
649 // make a copy of `metadata` so we can override `output_units` if "output.use_MKS" is
650 // set.
651 SpatialVariableMetadata var = metadata;
652 if (config->get_flag("output.use_MKS")) {
653 var.output_units(var["units"]);
654 }
655
656 auto name = var.get_name();
657
658 if (not file.variable_exists(name)) {
659 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "Can't find '%s' in '%s'.", name.c_str(),
660 file.name().c_str());
661 }
662
663 write_dimensions(var, grid, file);
664
665 bool time_independent = var.get_time_independent();
666 bool written = file.get_variable_was_written(var.get_name());
667
668 // avoid writing time-independent variables more than once (saves time when writing to
669 // extra_files)
670 if (written and time_independent) {
671 return;
672 }
673
674 // make sure we have at least one level
675 unsigned int nlevels = std::max(var.levels().size(), (size_t)1);
676
677 std::string units = var["units"], output_units = var["output_units"];
678
679 if (units != output_units) {
680 size_t data_size = grid.xm() * grid.ym() * nlevels;
681
682 // create a temporary array, convert to output units, and
683 // save
684 std::vector<double> tmp(data_size);
685 for (size_t k = 0; k < data_size; ++k) {
686 tmp[k] = input[k];
687 }
688
689 units::Converter(var.unit_system(), units, output_units)
690 .convert_doubles(tmp.data(), tmp.size());
691
692 file.write_distributed_array(name, grid, nlevels, not time_independent, tmp.data());
693 } else {
694 file.write_distributed_array(name, grid, nlevels, not time_independent, input);
695 }
697}
698
699/*!
700 * Check the overlap of the input grid and the internal grid.
701 *
702 * Set `allow_extrapolation` to `true` to "extend" the vertical grid during "bootstrapping".
703 */
704static void check_grid_overlap(const grid::InputGridInfo &input, const Grid &internal,
705 const std::vector<double> &z_internal) {
706
707 // Grid spacing (assume that the grid is equally-spaced) and the
708 // extent of the domain. To compute the extent of the domain, assume
709 // that the grid is cell-centered, so edge of the domain is half of
710 // the grid spacing away from grid points at the edge.
711 //
712 // Note that x_min is not the same as internal.x(0).
713 const double x_min = internal.x0() - internal.Lx(), x_max = internal.x0() + internal.Lx(),
714 y_min = internal.y0() - internal.Ly(), y_max = internal.y0() + internal.Ly(),
715 input_x_min = input.x0 - input.Lx, input_x_max = input.x0 + input.Lx,
716 input_y_min = input.y0 - input.Ly, input_y_max = input.y0 + input.Ly;
717
718 // tolerance (one micron)
719 double eps = 1e-6;
720
721 // horizontal grid extent
722 if (not(x_min >= input_x_min - eps and x_max <= input_x_max + eps and
723 y_min >= input_y_min - eps and y_max <= input_y_max + eps)) {
726 "PISM's computational domain is not a subset of the domain in '%s'\n"
727 "PISM grid: x: [%3.3f, %3.3f] y: [%3.3f, %3.3f] meters\n"
728 "input file grid: x: [%3.3f, %3.3f] y: [%3.3f, %3.3f] meters",
729 input.filename.c_str(), x_min, x_max, y_min, y_max, input_x_min, input_x_max, input_y_min,
730 input_y_max);
731 }
732
733 if (z_internal.empty()) {
735 "Internal vertical grid has 0 levels. This should never happen.");
736 }
737
738 if (z_internal.size() == 1 and input.z.size() > 1) {
739 // internal field is 2D or 3D with one level, input variable is 3D with more than one
740 // vertical level
742 "trying to read in a 2D field but the input file %s contains\n"
743 "a 3D field with %d levels",
744 input.filename.c_str(), static_cast<int>(input.z.size()));
745 }
746
747 if (z_internal.size() > 1 and input.z.size() <= 1) {
748 // internal field is 3D with more than one vertical level, input variable is 2D or 3D
749 // with 1 level
751 "trying to read in a 3D field but the input file %s contains\n"
752 "a 2D field",
753 input.filename.c_str());
754 }
755
756 if (z_internal.size() > 1 and (not input.z.empty())) {
757 // both internal field and input variable are 3D: check vertical grid extent
758 // Note: in PISM 2D fields have one vertical level (z = 0).
759 const double input_z_min = input.z.front(), input_z_max = input.z.back(),
760 z_min = z_internal.front(), z_max = z_internal.back();
761
762 if (not(z_min >= input.z_min - eps and z_max <= input.z_max + eps)) {
765 "PISM's computational domain is not a subset of the domain in '%s'\n"
766 "PISM grid: z: [%3.3f, %3.3f] meters\n"
767 "input file grid: z: [%3.3f, %3.3f] meters",
768 input.filename.c_str(), z_min, z_max, input_z_min, input_z_max);
769 }
770 }
771}
772
773/*! @brief Check that x, y, and z coordinates of the input grid are strictly increasing. */
775 const Grid& internal_grid,
776 const std::vector<double> &internal_z_levels) {
777 if (not is_increasing(input_grid.x)) {
779 "input x coordinate has to be strictly increasing");
780 }
781
782 if (not is_increasing(input_grid.y)) {
784 "input y coordinate has to be strictly increasing");
785 }
786
787 if (not is_increasing(input_grid.z)) {
789 "input vertical grid has to be strictly increasing");
790 }
791
792 bool allow_extrapolation = internal_grid.ctx()->config()->get_flag("grid.allow_extrapolation");
793
794 if (not allow_extrapolation) {
795 check_grid_overlap(input_grid, internal_grid, internal_z_levels);
796 }
797}
798
799//! \brief Regrid from a NetCDF file into a distributed array `output`.
800/*!
801 - if `flag` is `CRITICAL` or `CRITICAL_FILL_MISSING`, stops if the
802 variable was not found in the input file
803 - if `flag` is one of `CRITICAL_FILL_MISSING` and
804 `OPTIONAL_FILL_MISSING`, replace _FillValue with `default_value`.
805 - sets `v` to `default_value` if `flag` is `OPTIONAL` and the
806 variable was not found in the input file
807 - uses the last record in the file
808*/
809
811 const Grid &target_grid,
812 const LocalInterpCtx &interp_context, const File &file,
813 double *output) {
814
815 auto var = file.find_variable(variable.get_name(), variable["standard_name"]);
816
817 const Profiling &profiling = target_grid.ctx()->profiling();
818
819 profiling.begin("io.regridding.read");
820 std::vector<double> buffer(interp_context.buffer_size());
821 read_distributed_array(file, var.name, variable.unit_system(), interp_context.start,
822 interp_context.count, buffer.data());
823 profiling.end("io.regridding.read");
824
825 // interpolate
826 profiling.begin("io.regridding.interpolate");
827 interpolate(target_grid, interp_context, buffer.data(), output);
828 profiling.end("io.regridding.interpolate");
829
830 // Get the units string from the file and convert the units:
831 {
832 std::string input_units = file.read_text_attribute(var.name, "units");
833 std::string internal_units = variable["units"];
834
835 input_units = check_units(variable, input_units, *target_grid.ctx()->log());
836
837 const size_t data_size = target_grid.xm() * target_grid.ym() * interp_context.z->n_output();
838
839 // Convert data:
840 units::Converter(variable.unit_system(), input_units, internal_units)
841 .convert_doubles(output, data_size);
842 }
843}
844
845
846//! Define a NetCDF variable corresponding to a time-series.
847void define_timeseries(const VariableMetadata &var, const std::string &dimension_name,
848 const File &file, io::Type nctype) {
849
850 std::string name = var.get_name();
851
852 if (file.variable_exists(name)) {
853 return;
854 }
855
856 if (not file.dimension_exists(dimension_name)) {
857 define_dimension(file, PISM_UNLIMITED, VariableMetadata(dimension_name, var.unit_system()));
858 }
859
860 if (not file.variable_exists(name)) {
861 file.define_variable(name, nctype, { dimension_name });
862 }
863
864 write_attributes(file, var, nctype);
865}
866
867std::vector<double> read_1d_variable(const File &file, const std::string &variable_name,
868 const std::string &units,
869 std::shared_ptr<units::System> unit_system) {
870
871
872 try {
873 if (not file.variable_exists(variable_name)) {
874 throw RuntimeError(PISM_ERROR_LOCATION, "variable " + variable_name + " is missing");
875 }
876
877 auto dims = file.dimensions(variable_name);
878 if (dims.size() != 1) {
880 "variable '%s' in '%s' should to have 1 dimension (got %d)",
881 variable_name.c_str(), file.name().c_str(), (int)dims.size());
882 }
883
884 const auto &dimension_name = dims[0];
885
886 unsigned int length = file.dimension_length(dimension_name);
887 if (length == 0) {
888 throw RuntimeError(PISM_ERROR_LOCATION, "dimension " + dimension_name + " has length zero");
889 }
890
891 units::Unit internal_units(unit_system, units), input_units(unit_system, "1");
892
893 auto input_units_string = file.read_text_attribute(variable_name, "units");
894
895 if (not input_units_string.empty()) {
896 input_units = units::Unit(unit_system, input_units_string);
897 } else {
900 "variable '%s' does not have the units attribute", variable_name.c_str());
901 }
902
903 std::vector<double> result(length); // memory allocation happens here
904
905 file.read_variable(variable_name, { 0 }, { length }, result.data());
906
907 units::Converter(input_units, internal_units).convert_doubles(result.data(), result.size());
908
909 return result;
910 } catch (RuntimeError &e) {
911 e.add_context("reading 1D variable '%s' from '%s'", variable_name.c_str(),
912 file.name().c_str());
913 throw;
914 }
915}
916
917/** @brief Write a time-series `data` to a file.
918 *
919 * Always use output units when saving time-series.
920 */
921void write_timeseries(const File &file, const VariableMetadata &metadata, size_t t_start,
922 const std::vector<double> &data) {
923
924 std::string name = metadata.get_name();
925 try {
926 if (not file.variable_exists(name)) {
927 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "variable '%s' not found", name.c_str());
928 }
929
930 // create a copy of "data":
931 std::vector<double> tmp = data;
932
933 // convert to output units:
934 units::Converter(metadata.unit_system(), metadata["units"], metadata["output_units"])
935 .convert_doubles(tmp.data(), tmp.size());
936
937 file.write_variable(name, {(unsigned int)t_start}, {(unsigned int)tmp.size()}, tmp.data());
938
939 } catch (RuntimeError &e) {
940 e.add_context("writing time-series variable '%s' to '%s'", name.c_str(),
941 file.name().c_str());
942 throw;
943 }
944}
945
947 const std::string &dimension_name,
948 const std::string &bounds_name,
949 const File &file, io::Type nctype) {
950 std::string name = var.get_name();
951
952 if (file.variable_exists(name)) {
953 return;
954 }
955
956 if (not file.dimension_exists(dimension_name)) {
957 file.define_dimension(dimension_name, PISM_UNLIMITED);
958 }
959
960 if (not file.dimension_exists(bounds_name)) {
961 file.define_dimension(bounds_name, 2);
962 }
963
964 file.define_variable(name, nctype, {dimension_name, bounds_name});
965
966 write_attributes(file, var, nctype);
967}
968
969std::vector<double> read_bounds(const File &file, const std::string &bounds_variable_name,
970 const std::string &internal_units,
971 std::shared_ptr<units::System> unit_system) {
972
973 try {
974 units::Unit internal(unit_system, internal_units);
975
976 // Find the variable:
977 if (not file.variable_exists(bounds_variable_name)) {
978 throw RuntimeError(PISM_ERROR_LOCATION, "variable " + bounds_variable_name + " is missing");
979 }
980
981 auto dims = file.dimensions(bounds_variable_name);
982
983 if (dims.size() != 2) {
984 throw RuntimeError(PISM_ERROR_LOCATION, "variable " + bounds_variable_name + " has to have two dimensions");
985 }
986
987 const auto &dimension_name = dims[0];
988 const auto &bounds_dimension_name = dims[1];
989
990 // Check that we have 2 vertices (interval end-points) per record.
991 size_t length = file.dimension_length(bounds_dimension_name);
992 if (length != 2) {
994 "time-bounds variable " + bounds_variable_name + " has to have exactly 2 bounds per time record");
995 }
996
997 // Get the number of time records.
998 length = file.dimension_length(dimension_name);
999 if (length <= 0) {
1000 throw RuntimeError(PISM_ERROR_LOCATION, "dimension " + dimension_name + " has length zero");
1001 }
1002
1003 // Find the corresponding coordinate variable. (We get units from the 'time'
1004 // variable, because according to CF-1.5 section 7.1 a "boundary variable"
1005 // may not have metadata set.)
1006 if (not file.variable_exists(dimension_name)) {
1008 "coordinate variable " + dimension_name + " is missing");
1009 }
1010
1011 units::Unit input_units(unit_system, "1");
1012
1013 std::string input_units_string = file.read_text_attribute(dimension_name, "units");
1014 if (input_units_string.empty()) {
1016 "variable '%s' does not have the units attribute",
1017 dimension_name.c_str());
1018 }
1019
1020 input_units = units::Unit(unit_system, input_units_string);
1021
1022
1023 std::vector<double> result(length * 2); // memory allocation happens here
1024
1025 file.read_variable(bounds_variable_name, {0, 0}, {(unsigned)length, 2}, result.data());
1026
1027 units::Converter(input_units, internal).convert_doubles(result.data(), result.size());
1028
1029 return result;
1030 } catch (RuntimeError &e) {
1031 e.add_context("reading bounds variable '%s' from '%s'", bounds_variable_name.c_str(),
1032 file.name().c_str());
1033 throw;
1034 }
1035}
1036
1037void write_time_bounds(const File &file, const VariableMetadata &metadata,
1038 size_t t_start, const std::vector<double> &data) {
1039
1040 VariableMetadata var = metadata;
1041
1042 std::string name = var.get_name();
1043 try {
1044 bool variable_exists = file.variable_exists(name);
1045 if (not variable_exists) {
1046 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "variable '%s' not found",
1047 name.c_str());
1048 }
1049
1050 // make a copy of "data"
1051 std::vector<double> tmp = data;
1052
1053 // convert to output units:
1054 units::Converter(var.unit_system(), var["units"], var["output_units"])
1055 .convert_doubles(tmp.data(), tmp.size());
1056
1057 file.write_variable(name,
1058 {(unsigned int)t_start, 0},
1059 {(unsigned int)tmp.size() / 2, 2},
1060 tmp.data());
1061
1062 } catch (RuntimeError &e) {
1063 e.add_context("writing time-bounds variable '%s' to '%s'", name.c_str(),
1064 file.name().c_str());
1065 throw;
1066 }
1067}
1068
1069/*!
1070 * Reads and validates times and time bounds.
1071 */
1072void read_time_info(std::shared_ptr<units::System> unit_system, const File &file,
1073 const std::string &time_name, const std::string &time_units,
1074 std::vector<double> &times, std::vector<double> &bounds) {
1075
1076 size_t N = 0;
1077 {
1078 times = io::read_1d_variable(file, time_name, time_units, unit_system);
1079
1080 if (not is_increasing(times)) {
1081 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "times have to be strictly increasing");
1082 }
1083 N = times.size();
1084 }
1085
1086 // Read time bounds
1087 {
1088 std::string time_bounds_name = file.read_text_attribute(time_name, "bounds");
1089
1090 if (time_bounds_name.empty()) {
1091 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "please provide time bounds for '%s'",
1092 time_name.c_str());
1093 }
1094
1095 bounds = io::read_bounds(file, time_bounds_name, time_units, unit_system);
1096
1097 if (2 * N != bounds.size()) {
1099 "each time record has to have 2 bounds");
1100 }
1101
1102 for (size_t k = 0; k < N; ++k) {
1103 if (not (times[k] >= bounds[2 * k + 0] and
1104 times[k] <= bounds[2 * k + 1])) {
1106 "each time has to be contained in its time interval");
1107 }
1108 }
1109 } // end of the block reading time bounds
1110}
1111
1112bool file_exists(MPI_Comm com, const std::string &filename) {
1113 int file_exists_flag = 0, rank = 0;
1114 MPI_Comm_rank(com, &rank);
1115
1116 if (rank == 0) {
1117 // Check if the file exists:
1118 if (FILE *f = fopen(filename.c_str(), "r")) {
1119 file_exists_flag = 1;
1120 fclose(f);
1121 } else {
1122 file_exists_flag = 0;
1123 }
1124 }
1125 MPI_Bcast(&file_exists_flag, 1, MPI_INT, 0, com);
1126
1127 return file_exists_flag == 1;
1128}
1129
1131 const std::string &variable_name,
1132 std::shared_ptr<units::System> unit_system) {
1133
1134 VariableMetadata variable(variable_name, unit_system);
1135
1136 try {
1137
1138 if (not file.variable_exists(variable_name)) {
1139 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "variable '%s' is missing", variable_name.c_str());
1140 }
1141
1142 unsigned int nattrs = file.nattributes(variable_name);
1143
1144 for (unsigned int j = 0; j < nattrs; ++j) {
1145 std::string attribute_name = file.attribute_name(variable_name, j);
1146 io::Type nctype = file.attribute_type(variable_name, attribute_name);
1147
1148 if (nctype == PISM_CHAR) {
1149 variable[attribute_name] = file.read_text_attribute(variable_name,
1150 attribute_name);
1151 } else {
1152 variable[attribute_name] = file.read_double_attribute(variable_name,
1153 attribute_name);
1154 }
1155 } // end of for (int j = 0; j < nattrs; ++j)
1156 } catch (RuntimeError &e) {
1157 e.add_context("reading attributes of variable '%s' from '%s'",
1158 variable_name.c_str(), file.name().c_str());
1159 throw;
1160 }
1161 return variable;
1162}
1163
1164//! Write variable attributes to a NetCDF file.
1165/*!
1166 - If both valid_min and valid_max are set, then valid_range is written
1167 instead of the valid_min, valid_max pair.
1168
1169 - Skips empty text attributes.
1170*/
1171void write_attributes(const File &file, const VariableMetadata &variable, io::Type nctype) {
1172 std::string var_name = variable.get_name();
1173
1174 try {
1175 std::string
1176 units = variable["units"],
1177 output_units = variable["output_units"];
1178
1179 bool use_output_units = units != output_units;
1180
1181 // units, valid_min, valid_max and valid_range need special treatment:
1182 if (variable.has_attribute("units")) {
1183 file.write_attribute(var_name, "units", use_output_units ? output_units : units);
1184 }
1185
1186 std::vector<double> bounds(2);
1187 if (variable.has_attribute("valid_range")) {
1188 bounds = variable.get_numbers("valid_range");
1189 } else {
1190 if (variable.has_attribute("valid_min")) {
1191 bounds[0] = variable.get_number("valid_min");
1192 }
1193 if (variable.has_attribute("valid_max")) {
1194 bounds[1] = variable.get_number("valid_max");
1195 }
1196 }
1197
1198 double fill_value = 0.0;
1199 if (variable.has_attribute("_FillValue")) {
1200 fill_value = variable.get_number("_FillValue");
1201 }
1202
1203 // We need to save valid_min, valid_max and valid_range in the units
1204 // matching the ones in the output.
1205 if (use_output_units) {
1206
1207 units::Converter c(variable.unit_system(), units, output_units);
1208
1209 bounds[0] = c(bounds[0]);
1210 bounds[1] = c(bounds[1]);
1211 fill_value = c(fill_value);
1212 }
1213
1214 if (variable.has_attribute("_FillValue")) {
1215 file.write_attribute(var_name, "_FillValue", nctype, {fill_value});
1216 }
1217
1218 if (variable.has_attribute("valid_range")) {
1219 file.write_attribute(var_name, "valid_range", nctype, bounds);
1220 } else if (variable.has_attribute("valid_min") and
1221 variable.has_attribute("valid_max")) {
1222 file.write_attribute(var_name, "valid_range", nctype, bounds);
1223 } else if (variable.has_attribute("valid_min")) {
1224 file.write_attribute(var_name, "valid_min", nctype, {bounds[0]});
1225 } else if (variable.has_attribute("valid_max")) {
1226 file.write_attribute(var_name, "valid_max", nctype, {bounds[1]});
1227 }
1228
1229 // Write text attributes:
1230 for (const auto& s : variable.all_strings()) {
1231 std::string
1232 name = s.first,
1233 value = s.second;
1234
1235 if (name == "units" or
1236 name == "output_units" or
1237 value.empty()) {
1238 continue;
1239 }
1240
1241 file.write_attribute(var_name, name, value);
1242 }
1243
1244 // Write double attributes:
1245 for (const auto& d : variable.all_doubles()) {
1246 std::string name = d.first;
1247 std::vector<double> values = d.second;
1248
1249 if (member(name, {"valid_min", "valid_max", "valid_range", "_FillValue"}) or
1250 values.empty()) {
1251 continue;
1252 }
1253
1254 file.write_attribute(var_name, name, nctype, values);
1255 }
1256
1257 } catch (RuntimeError &e) {
1258 e.add_context("writing attributes of variable '%s' to '%s'",
1259 var_name.c_str(), file.name().c_str());
1260 throw;
1261 }
1262}
1263
1264/*!
1265 * Return the name of the time dimension corresponding to a NetCDF variable.
1266 *
1267 * Returns an empty string if this variable is time-independent.
1268 */
1269std::string time_dimension(units::System::Ptr unit_system,
1270 const File &file,
1271 const std::string &variable_name) {
1272
1273 auto dims = file.dimensions(variable_name);
1274
1275 for (const auto &d : dims) {
1276 if (file.dimension_type(d, unit_system) == T_AXIS) {
1277 return d;
1278 }
1279 }
1280
1281 return "";
1282}
1283
1284//! \brief Moves the file aside (file.nc -> file.nc~).
1285/*!
1286 * Note: only one processor does the renaming.
1287 */
1288void move_if_exists(MPI_Comm com, const std::string &file_to_move, int rank_to_use) {
1289 int stat = 0, rank = 0;
1290 MPI_Comm_rank(com, &rank);
1291 std::string backup_filename = file_to_move + "~";
1292
1293 if (rank == rank_to_use) {
1294 bool exists = false;
1295
1296 // Check if the file exists:
1297 if (FILE *f = fopen(file_to_move.c_str(), "r")) {
1298 fclose(f);
1299 exists = true;
1300 } else {
1301 exists = false;
1302 }
1303
1304 if (exists) {
1305 stat = rename(file_to_move.c_str(), backup_filename.c_str());
1306 }
1307 } // end of "if (rank == rank_to_use)"
1308
1309 int global_stat = 0;
1310 MPI_Allreduce(&stat, &global_stat, 1, MPI_INT, MPI_SUM, com);
1311
1312 if (global_stat != 0) {
1314 "PISM ERROR: can't move '%s' to '%s'",
1315 file_to_move.c_str(), backup_filename.c_str());
1316 }
1317}
1318
1319//! \brief Check if a file is present are remove it.
1320/*!
1321 * Note: only processor 0 does the job.
1322 */
1323void remove_if_exists(MPI_Comm com, const std::string &file_to_remove, int rank_to_use) {
1324 int stat = 0, rank = 0;
1325 MPI_Comm_rank(com, &rank);
1326
1327 if (rank == rank_to_use) {
1328 bool exists = false;
1329
1330 // Check if the file exists:
1331 if (FILE *f = fopen(file_to_remove.c_str(), "r")) {
1332 fclose(f);
1333 exists = true;
1334 } else {
1335 exists = false;
1336 }
1337
1338 if (exists) {
1339 stat = remove(file_to_remove.c_str());
1340 }
1341 } // end of "if (rank == rank_to_use)"
1342
1343 int global_stat = 0;
1344 MPI_Allreduce(&stat, &global_stat, 1, MPI_INT, MPI_SUM, com);
1345
1346 if (global_stat != 0) {
1348 "PISM ERROR: can't remove '%s'", file_to_remove.c_str());
1349 }
1350}
1351
1352} // end of namespace io
1353} // end of namespace pism
std::string get_string(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
A class for storing and accessing PISM configuration flags and parameters.
std::shared_ptr< const Config > config() const
Definition Context.cc:89
std::shared_ptr< units::System > unit_system() const
Definition Context.cc:81
std::shared_ptr< const Time > time() const
Definition Context.cc:101
bool dimension_exists(const std::string &name) const
Checks if a dimension exists.
Definition File.cc:404
void write_distributed_array(const std::string &variable_name, const Grid &grid, unsigned int z_count, bool time_dependent, const double *input) const
Definition File.cc:725
void read_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
Definition File.cc:699
void define_dimension(const std::string &name, size_t length) const
Definition File.cc:532
std::string attribute_name(const std::string &var_name, unsigned int n) const
Definition File.cc:675
AxisType dimension_type(const std::string &name, units::System::Ptr unit_system) const
Get the "type" of a dimension.
Definition File.cc:460
void set_variable_was_written(const std::string &name) const
Definition File.cc:767
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
Definition File.cc:328
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
Definition File.cc:378
void write_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const double *op) const
Definition File.cc:712
void define_variable(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Define a variable.
Definition File.cc:543
io::Type attribute_type(const std::string &var_name, const std::string &att_name) const
Definition File.cc:687
std::string name() const
Definition File.cc:274
void write_attribute(const std::string &var_name, const std::string &att_name, io::Type nctype, const std::vector< double > &values) const
Write a multiple-valued double attribute.
Definition File.cc:590
std::vector< double > read_double_attribute(const std::string &var_name, const std::string &att_name) const
Get a double attribute.
Definition File.cc:617
bool get_variable_was_written(const std::string &name) const
Definition File.cc:771
unsigned int nattributes(const std::string &var_name) const
Definition File.cc:663
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition File.cc:420
std::vector< std::string > dimensions(const std::string &variable_name) const
Definition File.cc:390
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
Definition File.cc:645
High-level PISM I/O class.
Definition File.hh:55
double y0() const
Y-coordinate of the center of the domain.
Definition Grid.cc:707
double Ly() const
Half-width of the computational domain.
Definition Grid.cc:692
double Lx() const
Half-width of the computational domain.
Definition Grid.cc:687
double x0() const
X-coordinate of the center of the domain.
Definition Grid.cc:702
std::shared_ptr< const Context > ctx() const
Return execution context this grid corresponds to.
Definition Grid.cc:540
int xm() const
Width of this processor's sub-domain.
Definition Grid.cc:598
int ym() const
Width of this processor's sub-domain.
Definition Grid.cc:603
Describes the PISM grid and the distribution of data across processors.
Definition Grid.hh:290
std::array< int, 4 > count
std::shared_ptr< Interpolation1D > z
std::array< int, 4 > start
std::shared_ptr< Interpolation1D > x
std::shared_ptr< Interpolation1D > y
void message(int threshold, const char format[],...) const __attribute__((format(printf
Print a message to the log.
Definition Logger.cc:49
A basic logging class.
Definition Logger.hh:40
void begin(const char *name) const
Definition Profiling.cc:75
void end(const char *name) const
Definition Profiling.cc:91
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
const std::vector< double > & levels() const
Spatial NetCDF variable (corresponding to a 2D or 3D scalar field).
std::string units_string() const
Internal time units as a string.
Definition Time.cc:473
std::string calendar() const
Returns the calendar string.
Definition Time.cc:482
Time management class.
Definition Time.hh:55
double get_number(const std::string &name) const
Get a single-valued scalar attribute.
std::string get_string(const std::string &name) const
Get a string attribute.
const std::map< std::string, std::string > & all_strings() const
VariableMetadata & output_units(const std::string &input)
units::System::Ptr unit_system() const
io::Type get_output_type() const
bool get_time_independent() const
std::vector< double > get_numbers(const std::string &name) const
Get an array-of-doubles attribute.
bool has_attribute(const std::string &name) const
std::string get_name() const
const std::map< std::string, std::vector< double > > & all_doubles() const
std::vector< double > y
y coordinates
Definition Grid.hh:93
double Lx
domain half-width
Definition Grid.hh:82
double z_min
minimal value of the z dimension
Definition Grid.hh:86
std::string filename
Definition Grid.hh:97
double Ly
domain half-height
Definition Grid.hh:84
double y0
y-coordinate of the domain center
Definition Grid.hh:80
double x0
x-coordinate of the domain center
Definition Grid.hh:78
std::vector< double > z
z coordinates
Definition Grid.hh:95
double z_max
maximal value of the z dimension
Definition Grid.hh:88
std::vector< double > x
x coordinates
Definition Grid.hh:91
Contains parameters of an input file grid.
Definition Grid.hh:68
void convert_doubles(double *data, size_t length) const
Definition Units.cc:250
std::shared_ptr< System > Ptr
Definition Units.hh:47
#define PISM_ERROR_LOCATION
#define n
Definition exactTestM.c:37
static StartCountInfo compute_start_and_count(std::vector< AxisType > &dim_types, const std::array< int, 4 > &start, const std::array< int, 4 > &count)
void append_time(const File &file, const Config &config, double time_seconds)
Prepare a file for output.
void read_spatial_variable(const SpatialVariableMetadata &variable, const Grid &grid, const File &file, unsigned int time, double *output)
Read a variable from a file into an array output.
VariableMetadata read_attributes(const File &file, const std::string &variable_name, std::shared_ptr< units::System > unit_system)
static void define_dimensions(const SpatialVariableMetadata &var, const Grid &grid, const File &file)
Define dimensions a variable depends on.
void define_dimension(const File &file, unsigned long int length, const VariableMetadata &metadata)
Define a dimension and the associated coordinate variable. Set attributes.
void read_time_info(std::shared_ptr< units::System > unit_system, const File &file, const std::string &time_name, const std::string &time_units, std::vector< double > &times, std::vector< double > &bounds)
@ PISM_UNLIMITED
Definition IO_Flags.hh:79
std::vector< double > read_1d_variable(const File &file, const std::string &variable_name, const std::string &units, std::shared_ptr< units::System > unit_system)
void write_spatial_variable(const SpatialVariableMetadata &metadata, const Grid &grid, const File &file, const double *input)
Write a double array to a file.
void write_attributes(const File &file, const VariableMetadata &variable, io::Type nctype)
Write variable attributes to a NetCDF file.
@ PISM_DOUBLE
Definition IO_Flags.hh:52
@ PISM_CHAR
Definition IO_Flags.hh:48
void define_time_bounds(const VariableMetadata &var, const std::string &dimension_name, const std::string &bounds_name, const File &file, io::Type nctype)
void write_time_bounds(const File &file, const VariableMetadata &metadata, size_t t_start, const std::vector< double > &data)
static void read_distributed_array(const File &file, const std::string &variable_name, std::shared_ptr< units::System > unit_system, const std::array< int, 4 > &start, const std::array< int, 4 > &count, double *output)
Read an array distributed according to the grid.
static std::string check_units(const VariableMetadata &variable, const std::string &input_units, const Logger &log)
void define_spatial_variable(const SpatialVariableMetadata &metadata, const Grid &grid, const File &file, io::Type default_type)
Define a NetCDF variable corresponding to a VariableMetadata object.
static void check_for_missing_values(const File &file, const std::string &variable_name, double tolerance, const double *buffer, size_t buffer_length)
static void check_grid_overlap(const grid::InputGridInfo &input, const Grid &internal, const std::vector< double > &z_internal)
static void interpolate(const Grid &grid, const LocalInterpCtx &lic, double const *input_array, double *output_array)
Bi-(or tri-)linear interpolation.
Definition io_helpers.cc:63
void check_input_grid(const grid::InputGridInfo &input_grid, const Grid &internal_grid, const std::vector< double > &internal_z_levels)
Check that x, y, and z coordinates of the input grid are strictly increasing.
void write_dimensions(const SpatialVariableMetadata &var, const Grid &grid, const File &file)
void move_if_exists(MPI_Comm com, const std::string &file_to_move, int rank_to_use)
Moves the file aside (file.nc -> file.nc~).
void write_timeseries(const File &file, const VariableMetadata &metadata, size_t t_start, const std::vector< double > &data)
Write a time-series data to a file.
bool file_exists(MPI_Comm com, const std::string &filename)
std::vector< double > read_bounds(const File &file, const std::string &bounds_variable_name, const std::string &internal_units, std::shared_ptr< units::System > unit_system)
void remove_if_exists(MPI_Comm com, const std::string &file_to_remove, int rank_to_use)
Check if a file is present are remove it.
void regrid_spatial_variable(const SpatialVariableMetadata &variable, const Grid &target_grid, const LocalInterpCtx &interp_context, const File &file, double *output)
Regrid from a NetCDF file into a distributed array output.
std::string time_dimension(units::System::Ptr unit_system, const File &file, const std::string &variable_name)
static bool transpose(std::vector< AxisType > dimension_types)
static void write_dimension_data(const File &file, const std::string &name, const std::vector< double > &data)
void define_time(const File &file, const Context &ctx)
Prepare a file for output.
void define_timeseries(const VariableMetadata &var, const std::string &dimension_name, const File &file, io::Type nctype)
Define a NetCDF variable corresponding to a time-series.
static std::vector< AxisType > dimension_types(const File &file, const std::string &var_name, std::shared_ptr< units::System > unit_system)
bool is_increasing(const std::vector< double > &a)
Checks if a vector of doubles is strictly increasing.
@ T_AXIS
Definition IO_Flags.hh:33
@ X_AXIS
Definition IO_Flags.hh:33
@ Z_AXIS
Definition IO_Flags.hh:33
@ Y_AXIS
Definition IO_Flags.hh:33
static const double k
Definition exactTestP.cc:42
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
Definition Time.cc:146
bool member(const std::string &string, const std::set< std::string > &set)
std::string join(const std::vector< std::string > &strings, const std::string &separator)
Concatenate strings, inserting separator between elements.
AxisType axis_type_from_string(const std::string &input)
Definition File.cc:436
std::vector< unsigned int > start
std::vector< unsigned int > count
int count
Definition test_cube.c:16