23#include "pism/util/Grid.hh"
24#include "pism/util/io/LocalInterpCtx.hh"
25#include "pism/util/io/IO_Flags.hh"
26#include "pism/util/error_handling.hh"
27#include "pism/util/VariableMetadata.hh"
28#include "pism/util/Profiling.hh"
29#include "pism/util/Context.hh"
30#include "pism/util/pism_utilities.hh"
31#include "pism/util/Logger.hh"
41 std::string internal_units = variable[
"units"];
42 if (input_units.empty() and not internal_units.empty()) {
44 "PISM WARNING: Variable '%s' ('%s') does not have the units attribute.\n"
45 " Assuming that it is in '%s'.\n",
47 internal_units.c_str());
48 return internal_units;
71 double *output_array) {
75 unsigned int nlevels = lic.
z->n_output();
78 auto input = [input_array, x_count, z_count](
int X,
int Y,
int Z) {
80 int index = (Y * x_count + X) * z_count + Z;
81 return input_array[index];
85 const int i_global = p.i(), j_global = p.j();
87 const int i = i_global - grid.xs, j = j_global - grid.ys;
90 const int X_m = lic.
x->left(i), X_p = lic.
x->right(i);
91 const int Y_m = lic.
y->left(j), Y_p = lic.
y->right(j);
93 for (
unsigned int k = 0;
k < nlevels;
k++) {
95 double a_mm = 0.0, a_mp = 0.0, a_pm = 0.0, a_pp = 0.0;
98 const int Z_m = lic.
z->left(
k), Z_p = lic.
z->right(
k);
100 const double alpha_z = lic.
z->alpha(
k);
106 mmm = input(X_m, Y_m, Z_m),
107 mmp = input(X_m, Y_m, Z_p),
108 pmm = input(X_p, Y_m, Z_m),
109 pmp = input(X_p, Y_m, Z_p),
110 mpm = input(X_m, Y_p, Z_m),
111 mpp = input(X_m, Y_p, Z_p),
112 ppm = input(X_p, Y_p, Z_m),
113 ppp = input(X_p, Y_p, Z_p);
116 a_mm = mmm * (1.0 - alpha_z) + mmp * alpha_z;
117 a_mp = pmm * (1.0 - alpha_z) + pmp * alpha_z;
118 a_pm = mpm * (1.0 - alpha_z) + mpp * alpha_z;
119 a_pp = ppm * (1.0 - alpha_z) + ppp * alpha_z;
122 a_mm = input(X_m, Y_m, 0);
123 a_mp = input(X_p, Y_m, 0);
124 a_pm = input(X_m, Y_p, 0);
125 a_pp = input(X_p, Y_p, 0);
129 const double x_alpha = lic.
x->alpha(i);
131 const double y_alpha = lic.
y->alpha(j);
134 const double a_m = a_mm * (1.0 - x_alpha) + a_mp * x_alpha;
135 const double a_p = a_pm * (1.0 - x_alpha) + a_pp * x_alpha;
137 auto index = (j * grid.xm + i) * nlevels +
k;
140 output_array[index] = a_m * (1.0 - y_alpha) + a_p * y_alpha;
156 std::vector<AxisType> storage, memory = {
Y_AXIS,
X_AXIS };
161 if (first && dimtype ==
T_AXIS) {
169 storage.push_back(dimtype);
170 }
else if (dimtype ==
Z_AXIS) {
171 memory.push_back(dimtype);
173 storage.push_back(dimtype);
181 assert(memory.size() <= 3);
183 return storage != memory;
187 std::shared_ptr<units::System> unit_system) {
188 std::vector<AxisType> result;
189 for (
const auto &dimension : file.
dimensions(var_name)) {
208static void transpose(
const double *input,
const std::vector<AxisType> &input_axes,
209 const std::array<int, 4> &
count,
double *output) {
212 auto axes = input_axes;
221 int N = (
int)axes.size();
222 for (
int k = 0;
k < N; ++
k) {
223 if (axes[
k] < 0 or axes[
k] > 3) {
231 std::vector<unsigned> delta = {1, 1, 1, 1};
235 std::vector<unsigned> tmp(N, 1);
236 for (
int k = 0;
k < N; ++
k) {
237 for (
int n =
k + 1;
n < N; ++
n) {
242 for (
int k = 0;
k < N; ++
k) {
243 delta[axes[
k]] = tmp[
k];
256 auto OUT = x * delta_x + y * delta_y + z * 1;
259 output[OUT] = input[IN];
270 double tolerance,
const double *buffer,
size_t buffer_length) {
272 if (attribute.size() != 1) {
280 for (
size_t k = 0;
k < buffer_length; ++
k) {
281 if (not std::isfinite(buffer[
k])) {
286 int failed_global = 0;
287 MPI_Allreduce(&failed, &failed_global, 1, MPI_INT, MPI_MAX, file.
com());
288 if (failed_global > 0) {
291 "Variable '%s' in '%s' contains values that are not finite (NaN or infinity)",
292 variable_name.c_str(), file.
name().c_str());
299 double fill_value = attribute[0];
300 bool fill_value_is_finite = std::isfinite(fill_value);
301 for (
size_t k = 0;
k < buffer_length; ++
k) {
302 if (fill_value_is_finite and fabs(buffer[
k] - fill_value) < tolerance) {
307 int failed_global = 0;
308 MPI_Allreduce(&failed, &failed_global, 1, MPI_INT, MPI_MAX, file.
com());
309 if (failed_global > 0) {
312 "Variable '%s' in '%s' contains values matching the _FillValue attribute",
313 variable_name.c_str(), file.
name().c_str());
331 const std::array<int, 4> &start,
332 const std::array<int, 4> &
count) {
333 auto ndims = dim_types.size();
337 result.
start.resize(ndims);
338 result.
count.resize(ndims);
341 for (
unsigned int j = 0; j < ndims; j++) {
342 switch (dim_types[j]) {
377 std::shared_ptr<units::System> unit_system,
378 const std::array<int,4> &start,
379 const std::array<int,4> &
count,
389 std::vector<double> tmp(size);
390 file.
read_variable(variable_name, sc.start, sc.count, tmp.data());
393 file.
read_variable(variable_name, sc.start, sc.count, output);
400 e.
add_context(
"reading variable '%s' from '%s'", variable_name.c_str(), file.
name().c_str());
416 const Grid &target_grid,
423 const auto &profiling = target_grid.
ctx()->profiling();
425 profiling.begin(
"io.regridding.read");
426 std::vector<double> buffer(interp_context.
buffer_size());
428 interp_context.
count, buffer.data());
429 profiling.end(
"io.regridding.read");
432 profiling.begin(
"io.regridding.interpolate");
433 interpolate(target_grid.
info(), interp_context, buffer.data(), output);
434 profiling.end(
"io.regridding.interpolate");
439 std::string internal_units = variable[
"units"];
441 input_units =
check_units(variable, input_units, log);
443 const size_t data_size = target_grid.
xm() * target_grid.
ym() * interp_context.
z->n_output();
452 const std::string &units,
453 std::shared_ptr<units::System> unit_system) {
462 if (dims.size() != 1) {
464 "variable '%s' in '%s' should to have 1 dimension (got %d)",
465 variable_name.c_str(), file.
name().c_str(), (
int)dims.size());
468 const auto &dimension_name = dims[0];
475 units::Unit internal_units(unit_system, units), input_units(unit_system,
"1");
479 if (not input_units_string.empty()) {
480 input_units =
units::Unit(unit_system, input_units_string);
484 "variable '%s' does not have the units attribute", variable_name.c_str());
487 std::vector<double> result(length);
489 file.
read_variable(variable_name, { 0 }, { length }, result.data());
495 e.
add_context(
"reading 1D variable '%s' from '%s'", variable_name.c_str(),
496 file.
name().c_str());
502 const std::string &units,
503 std::shared_ptr<units::System> unit_system,
504 size_t start,
size_t count) {
508 if (input_units_string.empty()) {
510 "variable '%s' does not have the units attribute",
511 variable_name.c_str());
514 std::vector<double> result(
count);
517 std::vector<unsigned int> start_{}, count_{};
519 for (
const auto &dim : file.
dimensions(variable_name)) {
521 start_.push_back(start);
522 count_.push_back(
count);
528 file.
read_variable(variable_name, start_, count_, result.data());
533 auto input_units =
units::Unit(unit_system, input_units_string);
543std::vector<double>
read_bounds(
const File &file,
const std::string &bounds_variable_name,
544 const std::string &internal_units,
545 std::shared_ptr<units::System> unit_system) {
555 auto dims = file.
dimensions(bounds_variable_name);
557 if (dims.size() != 2) {
561 const auto &dimension_name = dims[0];
562 const auto &bounds_dimension_name = dims[1];
568 "time-bounds variable " + bounds_variable_name +
" has to have exactly 2 bounds per time record");
582 "coordinate variable " + dimension_name +
" is missing");
588 if (input_units_string.empty()) {
590 "variable '%s' does not have the units attribute",
591 dimension_name.c_str());
594 input_units =
units::Unit(unit_system, input_units_string);
597 std::vector<double> result(length * 2);
599 file.
read_variable(bounds_variable_name, {0, 0}, {(unsigned)length, 2}, result.data());
605 e.
add_context(
"reading bounds variable '%s' from '%s'", bounds_variable_name.c_str(),
606 file.
name().c_str());
618 const std::string &time_name) {
620 auto unit_system = time_units.
system();
628 "cannot read time info from a file: calendar in '%s' (%s) does not match PISM's calendar (%s)",
629 file.
name().c_str(), input_calendar.c_str(),
calendar.c_str());
633 std::string input_reference_date;
634 double time_offset = 0;
638 size_t position = input_time_units.find(
"since");
640 if (position != std::string::npos) {
641 input_reference_date =
string_strip(input_time_units.substr(position + strlen(
"since")));
649 auto units =
"seconds since " + input_reference_date;
659 size_t N = result.
times.size();
660 for (
size_t k = 0;
k < N; ++
k) {
661 result.
times[
k] += time_offset;
667 auto N = result.
times.size();
670 auto units =
"seconds since " + input_reference_date;
672 if (time_bounds_name.empty()) {
679 if (2 * N != result.
bounds.size()) {
681 "each time record has to have 2 bounds");
685 for (
size_t k = 0;
k < N; ++
k) {
686 result.
bounds[2 *
k + 0] += time_offset;
687 result.
bounds[2 *
k + 1] += time_offset;
691 for (
size_t k = 0;
k < N; ++
k) {
695 "each time has to be contained in its time interval");
707 const File &file,
unsigned int time,
double *output) {
709 const auto &log = *grid.ctx()->log();
714 if (not var.exists) {
717 variable.
get_string(
"standard_name").c_str(), file.
name().c_str());
726 for (
const auto &dim : variable.
dimensions()) {
733 int input_spatial_dim_count = 0;
734 size_t matching_dim_count = 0;
737 for (
const auto &d : input_dims) {
741 ++input_spatial_dim_count;
744 if (axes.find(dim_type) != axes.end()) {
745 ++matching_dim_count;
749 if (axes.size() != matching_dim_count) {
754 "found the %dD variable %s (%s) in '%s' while trying to read\n"
755 "'%s' ('%s'), which is %d-dimensional.",
756 input_spatial_dim_count, var.name.c_str(),
join(input_dims,
",").c_str(),
758 variable.
get_string(
"long_name").c_str(),
static_cast<int>(axes.size()));
763 size_t nlevels = std::max(variable.
levels().size(), (
size_t)1);
766 {(int)time, grid.xs(), grid.ys(), 0},
767 {1, grid.xm(), grid.ym(), (int)nlevels},
771 const std::string &internal_units = variable[
"units"];
773 input_units =
check_units(variable, input_units, log);
776 size_t size = grid.xm() * grid.ym() * nlevels;
789 const std::vector<double> &z_internal) {
797 const double x_min = internal.
x0 - internal.
Lx, x_max = internal.
x0 + internal.
Lx,
798 y_min = internal.
y0 - internal.
Ly, y_max = internal.
y0 + internal.
Ly,
799 input_x_min = input.
x0 - input.
Lx, input_x_max = input.
x0 + input.
Lx,
800 input_y_min = input.
y0 - input.
Ly, input_y_max = input.
y0 + input.
Ly;
806 if (not(x_min >= input_x_min - eps and x_max <= input_x_max + eps and
807 y_min >= input_y_min - eps and y_max <= input_y_max + eps)) {
810 "PISM's computational domain is not a subset of the domain in '%s'\n"
811 "PISM grid: x: [%3.3f, %3.3f] y: [%3.3f, %3.3f] meters\n"
812 "input file grid: x: [%3.3f, %3.3f] y: [%3.3f, %3.3f] meters",
813 input.
filename.c_str(), x_min, x_max, y_min, y_max, input_x_min, input_x_max, input_y_min,
817 if (z_internal.empty()) {
819 "Internal vertical grid has 0 levels. This should never happen.");
822 if (z_internal.size() == 1 and input.
z.size() > 1) {
826 "trying to read in a 2D field but the input file %s contains\n"
827 "a 3D field with %d levels",
828 input.
filename.c_str(),
static_cast<int>(input.
z.size()));
831 if (z_internal.size() > 1 and input.
z.size() <= 1) {
835 "trying to read in a 3D field but the input file %s contains\n"
840 if (z_internal.size() > 1 and (not input.
z.empty())) {
844 z_min = z_internal.front(), z_max = z_internal.back();
846 if (not(z_min >= input_z_min - eps and z_max <= input_z_max + eps)) {
849 "PISM's computational domain is not a subset of the domain in '%s'\n"
850 "PISM grid: z: [%3.3f, %3.3f] meters\n"
851 "input file grid: z: [%3.3f, %3.3f] meters",
852 input.
filename.c_str(), z_min, z_max, input_z_min, input_z_max);
860 const std::vector<double> &internal_z_levels,
bool allow_extrapolation) {
863 "input x coordinate has to be strictly increasing");
868 "input y coordinate has to be strictly increasing");
873 "input vertical grid has to be strictly increasing");
876 if (not allow_extrapolation) {
888 const std::string &variable_name) {
892 for (
const auto &d : dims) {
902 const std::string &variable_name,
903 std::shared_ptr<units::System> unit_system) {
913 unsigned int nattrs = file.
nattributes(variable_name);
915 for (
unsigned int j = 0; j < nattrs; ++j) {
916 std::string attribute_name = file.
attribute_name(variable_name, j);
928 e.
add_context(
"reading attributes of variable '%s' from '%s'",
929 variable_name.c_str(), file.
name().c_str());
void read_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
std::string attribute_name(const std::string &var_name, unsigned int n) const
AxisType dimension_type(const std::string &name, units::System::Ptr unit_system) const
Get the "type" of a dimension.
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.
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
io::Type attribute_type(const std::string &var_name, const std::string &att_name) const
std::vector< double > read_double_attribute(const std::string &var_name, const std::string &att_name) const
Get a double attribute.
unsigned int nattributes(const std::string &var_name) const
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
std::vector< std::string > dimensions(const std::string &variable_name) const
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
High-level PISM I/O class.
const grid::DistributedGridInfo & info() const
std::shared_ptr< const Context > ctx() const
Return execution context this grid corresponds to.
int xm() const
Width of this processor's sub-domain.
int ym() const
Width of this processor's sub-domain.
Describes the PISM grid and the distribution of data across processors.
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.
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
double Lx
domain half-width in the X direction
std::vector< double > y
y coordinates
double y0
y-coordinate of the domain center
double x0
x-coordinate of the domain center
std::vector< double > x
x coordinates
double Ly
domain half-width in the Y direction
void convert_doubles(double *data, size_t length) const
std::shared_ptr< System > Ptr
double time(const DateTime &d) const
System::Ptr system() const
#define PISM_ERROR_LOCATION
std::vector< double > read_timeseries_variable(const File &file, const std::string &variable_name, const std::string &units, std::shared_ptr< units::System > unit_system, size_t start, size_t count)
void read_spatial_variable(const VariableMetadata &variable, const Grid &grid, const File &file, unsigned int time, double *output)
Read a variable from a file into an array output.
static StartCountInfo compute_start_and_count(std::vector< AxisType > &dim_types, const std::array< int, 4 > &start, const std::array< int, 4 > &count)
VariableMetadata read_attributes(const File &file, const std::string &variable_name, std::shared_ptr< units::System > unit_system)
void check_input_grid(const grid::InputGridInfo &input_grid, const grid::DistributedGridInfo &internal_grid, const std::vector< double > &internal_z_levels, bool allow_extrapolation)
Check that x, y, and z coordinates of the input grid are strictly increasing.
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)
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)
static void check_grid_overlap(const grid::InputGridInfo &input, const grid::DistributedGridInfo &internal, const std::vector< double > &z_internal)
static void interpolate(const grid::DistributedGridInfo &grid, const LocalInterpCtx &lic, double const *input_array, double *output_array)
Bi-(or tri-)linear interpolation.
static void check_for_missing_values(const File &file, const std::string &variable_name, double tolerance, const double *buffer, size_t buffer_length)
void regrid_spatial_variable(const VariableMetadata &variable, const Grid &target_grid, const LocalInterpCtx &interp_context, const File &file, const Logger &log, double *output)
Regrid from a NetCDF file into a distributed array output.
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)
std::string time_dimension(units::System::Ptr unit_system, const File &file, const std::string &variable_name)
TimeInfo read_time_info(const units::Unit &time_units, const std::string &calendar, const File &file, const std::string &time_name)
static bool transpose(std::vector< AxisType > dimension_types)
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.
double vector_min(const std::vector< double > &input)
std::string string_strip(const std::string &input)
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
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)
double vector_max(const std::vector< double > &input)
std::vector< unsigned int > start
std::vector< unsigned int > count
std::vector< double > times
std::vector< double > bounds