PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
InputInterpolationYAC.cc
Go to the documentation of this file.
1/* Copyright (C) 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
20#include <cstddef>
21#include <memory>
22#include <vector>
23#include <cmath>
24
25#include "InputInterpolation.hh"
26#include "Interpolation1D.hh"
27#include "pism/util/VariableMetadata.hh"
28#include "pism/util/projection.hh"
29#include "pism/util/Grid.hh"
30#include "pism/util/error_handling.hh"
31#include "pism/util/Context.hh"
32#include "pism/util/Logger.hh"
33#include "pism/util/petscwrappers/Vec.hh"
34#include "pism/util/array/Scalar.hh"
35#include "pism/util/InputInterpolationYAC.hh"
36#include "pism/util/pism_utilities.hh" // GlobalMin()
37
38#if (Pism_USE_PROJ == 0)
39#error "This code requires PROJ"
40#endif
41
42#include <proj.h>
43
44#include "pism/util/Proj.hh"
45#include "pism/util/LonLatGrid.hh"
46
47#if (Pism_USE_YAC == 0)
48#error "This code requires YAC"
49#endif
50
51extern "C" {
52#include "yac.h"
53}
54
55namespace pism {
56
57/*!
58 * Define the PISM grid. Each PE defines its own subdomain.
59 *
60 * Returns the point ID that can be used to define a "field".
61 */
62int InputInterpolationYAC::define_grid(const std::vector<double> &x_cell,
63 const std::vector<double> &y_cell,
64 const std::string &grid_name,
65 const std::string &projection) {
66
67 if (projection.empty()) {
69 PISM_ERROR_LOCATION, "grid '%s' has no projection information", grid_name.c_str());
70 }
71
72 // Shift x and y by half a grid spacing and add one more row and column to get
73 // coordinates of corners of cells in the local sub-domain:
74 std::vector<double> x_node(x_cell.size() + 1), y_node(y_cell.size() + 1);
75 {
76 // note: dx and dy may be negative here
77 double dx = x_cell[1] - x_cell[0];
78 double dy = y_cell[1] - y_cell[0];
79
80 for (size_t k = 0; k < x_cell.size(); ++k) {
81 x_node[k] = x_cell[k] - 0.5 * dx;
82 }
83 x_node.back() = x_cell.back() + 0.5 * dx;
84
85 for (size_t k = 0; k < y_cell.size(); ++k) {
86 y_node[k] = y_cell[k] - 0.5 * dy;
87 }
88 y_node.back() = y_cell.back() + 0.5 * dy;
89 }
90
91 // Compute lon,lat coordinates of cell centers:
92 LonLatGrid cells(x_cell, y_cell, projection);
93 // Compute lon,lat coordinates of cell corners:
94 LonLatGrid nodes(x_node, y_node, projection);
95
96 int point_id = 0;
97 {
98 int cyclic[] = { 0, 0 };
99
100 int grid_id = 0;
101
102 int n_nodes[2] = { (int)x_node.size(), (int)y_node.size() };
103 yac_cdef_grid_curve2d(grid_name.c_str(), n_nodes, cyclic, nodes.lon.data(), nodes.lat.data(),
104 &grid_id);
105
106 int n_cells[2] = { (int)x_cell.size(), (int)y_cell.size() };
107 yac_cdef_points_curve2d(grid_id, n_cells, YAC_LOCATION_CELL, cells.lon.data(), cells.lat.data(),
108 &point_id);
109 }
110 return point_id;
111}
112
113/*!
114 * Return the YAC field_id corresponding to a given PISM grid and its
115 * projection info.
116 *
117 * @param[in] component_id YAC component ID
118 * @param[in] pism_grid PISM's grid
119 * @param[in] name string describing this grid and field
120 */
121int InputInterpolationYAC::define_field(int component_id, const std::vector<double> &x,
122 const std::vector<double> &y,
123 const std::string &proj_string, const std::string &name) {
124
125 int point_id = define_grid(x, y, name, proj_string);
126
127 const char *time_step_length = "1";
128 const int point_set_size = 1;
129 const int collection_size = 1;
130
131 int field_id = 0;
132 yac_cdef_field(name.c_str(), component_id, &point_id, point_set_size, collection_size,
133 time_step_length, YAC_TIME_UNIT_SECOND, &field_id);
134 return field_id;
135}
136
137/*!
138 * Extract the "local" (corresponding to the current sub-domain) grid subset.
139 */
140static std::vector<double> grid_subset(int xs, int xm, const std::vector<double> &coords) {
141 std::vector<double> result(xm);
142 for (int k = 0; k < xm; ++k) {
143 result[k] = coords[xs + k];
144 }
145
146 return result;
147}
148
149static double dx_estimate(Proj &mapping, double x1, double x2, double y) {
150 PJ_COORD p1 = proj_coord(0, 0, 0, 0), p2 = proj_coord(0, 0, 0, 0);
151 p1.lp = {proj_torad(x1), proj_torad(y)};
152 p2.lp = {proj_torad(x2), proj_torad(y)};
153
154 return proj_lp_dist(mapping, p1, p2);
155}
156
157static double dx_min(const std::string &proj_string,
158 const std::vector<double> &x,
159 const std::vector<double> &y) {
160 size_t Nx = x.size();
161 size_t Ny = y.size();
162
163 Proj mapping(proj_string, "EPSG:4326");
164
165 double dx = dx_estimate(mapping, x[0], x[1], y[0]);
166 for (size_t j = 0; j < Ny; ++j) { // y
167 for (size_t i = 1; i < Nx; ++i) { // x; note: starts from 1
168 dx = std::min(dx, dx_estimate(mapping, x[i-1], x[i], y[j]));
169 }
170 }
171 return dx;
172}
173
174static double dy_estimate(Proj &mapping, double x, double y1, double y2) {
175 PJ_COORD p1 = proj_coord(0, 0, 0, 0), p2 = proj_coord(0, 0, 0, 0);
176 p1.lp = {proj_torad(x), proj_torad(y1)};
177 p2.lp = {proj_torad(x), proj_torad(y2)};
178
179 return proj_lp_dist(mapping, p1, p2);
180}
181
182static double dy_min(const std::string &proj_string,
183 const std::vector<double> &x,
184 const std::vector<double> &y) {
185 size_t Nx = x.size();
186 size_t Ny = y.size();
187
188 Proj mapping(proj_string, "EPSG:4326");
189
190 double dy = dy_estimate(mapping, x[0], x[1], y[0]);
191 for (size_t i = 0; i < Nx; ++i) { // x
192 for (size_t j = 1; j < Ny; ++j) { // y; note: starts from 1
193 dy = std::min(dy, dy_estimate(mapping, x[i], y[j-1], y[j]));
194 }
195 }
196 return dy;
197}
198
200 const pism::File &input_file,
201 const std::string &variable_name,
203 : m_instance_id(0), m_source_field_id(0), m_target_field_id(0) {
204 auto ctx = target_grid.ctx();
205
206 std::string target_proj_params = target_grid.get_mapping_info()["proj_params"];
207
208 if (target_proj_params.empty()) {
209 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "internal grid projection is not known");
210 }
211
212 try {
213 auto log = ctx->log();
214
215 auto source_grid_name = grid_name(input_file, variable_name, ctx->unit_system(),
216 type == PIECEWISE_CONSTANT);
217
218 log->message(
219 2, "* Initializing 2D interpolation on the sphere from '%s' to the internal grid...\n",
220 source_grid_name.c_str());
221
222 grid::InputGridInfo source_grid_info(input_file, variable_name, ctx->unit_system(),
224
225 auto source_grid_mapping = mapping_info_from_file(input_file, variable_name, ctx->unit_system());
226
227 std::string grid_mapping_name = source_grid_mapping["grid_mapping_name"];
228
229 log->message(2, "Input grid:\n");
230 if (not grid_mapping_name.empty()) {
231 log->message(2, " Grid mapping: %s\n", grid_mapping_name.c_str());
232 }
233
234 std::string source_proj_params = source_grid_mapping["proj_params"];
235 if (not source_proj_params.empty()) {
236 log->message(2, " PROJ string: '%s'\n", source_proj_params.c_str());
237 }
238 source_grid_info.report(*log, 2, ctx->unit_system());
239
240 grid::Parameters P(*ctx->config(), source_grid_info.x.size(), source_grid_info.y.size(),
241 source_grid_info.Lx, source_grid_info.Ly);
242
243 P.x0 = source_grid_info.x0;
244 P.y0 = source_grid_info.y0;
246 P.variable_name = variable_name;
247 P.vertical_grid_from_options(*ctx->config());
248 P.ownership_ranges_from_options(*ctx->config(), ctx->size());
249
250 auto source_grid = std::make_shared<Grid>(ctx, P);
251
252 source_grid->set_mapping_info(source_grid_mapping);
253
254 if (source_proj_params.empty()) {
256 "unsupported or missing projection info for the grid '%s'",
257 source_grid_name.c_str());
258 }
259
260 m_buffer = std::make_shared<pism::array::Scalar>(source_grid, variable_name);
261
262 std::string target_grid_name = "internal for " + source_grid_name;
263 {
264 // Initialize YAC:
265 {
266 yac_cinit_comm_instance(PETSC_COMM_WORLD, &m_instance_id);
267 yac_cdef_calendar(YAC_YEAR_OF_365_DAYS);
268 // Note: zero-padding of months and days *is* required.
269 yac_cdef_datetime_instance(m_instance_id, "-1-01-01", "+1-01-01");
270 }
271
272 // Define components: this has to be done using *one* call
273 // (cannot call yac_cdef_comp?_instance() more than once)
274 const int n_comps = 2;
275 const char *comp_names[n_comps] = { "source_component", "target_component" };
276 int comp_ids[n_comps] = { 0, 0 };
277 yac_cdef_comps_instance(m_instance_id, comp_names, n_comps, comp_ids);
278
279 double source_grid_spacing = 0.0;
280 log->message(2, "Defining the source grid (%s)...\n", source_grid_name.c_str());
281 {
282 auto x = grid_subset(source_grid->xs(), source_grid->xm(), source_grid_info.x);
283 auto y = grid_subset(source_grid->ys(), source_grid->ym(), source_grid_info.y);
284
286 comp_ids[0], x, y, source_proj_params, source_grid_name);
287
288 double dx = 0.0;
289 double dy = 0.0;
290 if (source_grid_info.longitude_latitude) {
291 dx = dx_min(source_proj_params, x, y);
292 dy = dy_min(source_proj_params, x, y);
293 } else {
294 dx = std::abs(x[1] - x[0]);
295 dy = std::abs(y[1] - y[0]);
296 }
297 source_grid_spacing = GlobalMin(ctx->com(), std::min(dx, dy));
298 log->message(2, " Source grid spacing: ~%3.3f m\n", source_grid_spacing);
299 }
300
301 double target_grid_spacing = 0.0;
302 log->message(2, "Defining the target grid (%s)...\n", target_grid_name.c_str());
303 {
304 auto x = grid_subset(target_grid.xs(), target_grid.xm(), target_grid.x());
305 auto y = grid_subset(target_grid.ys(), target_grid.ym(), target_grid.y());
306
308 comp_ids[1], x, y, target_proj_params, target_grid_name);
309
310 target_grid_spacing = GlobalMin(ctx->com(), std::min(target_grid.dx(), target_grid.dy()));
311 log->message(2, " Target grid spacing: %3.3f m\n", target_grid_spacing);
312 }
313
314 // Define the interpolation stack:
315 {
316 std::string method;
317 int interp_stack_id = 0;
318 yac_cget_interp_stack_config(&interp_stack_id);
319
320 if (type == PIECEWISE_CONSTANT) {
321 method = "nearest neighbor";
322
323 // use nearest neighbor interpolation to interpolate integer fields:
324 {
325 // nearest neighbor
326 int n_neighbors = 1; // only one neighbor
327 double scaling = 1.0;
328 double max_search_distance = 0.0; // unlimited
329 yac_cadd_interp_stack_config_nnn(interp_stack_id, YAC_NNN_DIST, n_neighbors,
330 max_search_distance, scaling);
331 }
332 } else {
333 int partial_coverage = 0;
334 if (source_grid_spacing < target_grid_spacing) {
335 method = "1st order conservative";
336
337 int order = 1;
338 int enforce_conservation = 1;
339
340 yac_cadd_interp_stack_config_conservative(interp_stack_id, order, enforce_conservation,
341 partial_coverage, YAC_CONSERV_DESTAREA);
342 } else {
343 method = "weighted average of source cell nodes";
344
345 // use average over source grid nodes containing a target point as a backup:
346 yac_cadd_interp_stack_config_average(interp_stack_id, YAC_AVG_BARY, partial_coverage);
347 }
348
349 {
350 // nearest neighbor as a fallback
351 int n_neighbors = 1;
352 double scaling = 1.0;
353 double max_search_distance = 0.0; // unlimited
354 yac_cadd_interp_stack_config_nnn(interp_stack_id, YAC_NNN_DIST, n_neighbors,
355 max_search_distance, scaling);
356 }
357 }
358
359 log->message(2, "Interpolation method: %s\n", method.c_str());
360
361 // Define the coupling between fields:
362 const int src_lag = 0;
363 const int tgt_lag = 0;
364 yac_cdef_couple_instance(m_instance_id,
365 "source_component", // source component name
366 source_grid_name.c_str(), // source grid name
367 source_grid_name.c_str(), // source field name
368 "target_component", // target component name
369 target_grid_name.c_str(), // target grid name
370 target_grid_name.c_str(), // target field name
371 "1", // time step length in units below
372 YAC_TIME_UNIT_SECOND, // time step length units
373 YAC_REDUCTION_TIME_NONE, // reduction in time (for
374 // asynchronous coupling)
375 interp_stack_id, src_lag, tgt_lag);
376
377 // free the interpolation stack config now that we defined the coupling
378 yac_cfree_interp_stack_config(interp_stack_id);
379 }
380
381 double start = MPI_Wtime();
382 yac_cenddef_instance(m_instance_id);
383 double end = MPI_Wtime();
384 log->message(2, "Initialized interpolation from %s in %f seconds.\n",
385 source_grid_name.c_str(), end - start);
386 }
387 } catch (pism::RuntimeError &e) {
388 e.add_context("initializing interpolation from %s to the internal grid",
389 input_file.name().c_str());
390 throw;
391 }
392}
393
397
399 pism::petsc::Vec &target) const {
400
401 pism::petsc::VecArray input_array(source.vec());
402 pism::petsc::VecArray output_array(target);
403
404 double *send_field_ = input_array.get();
405 double **send_field[1] = { &send_field_ };
406
407 double *recv_field[1] = { output_array.get() };
408
409 int ierror = 0;
410 int send_info = 0;
411 int recv_info = 0;
412 int collection_size = 1;
413 double start = MPI_Wtime();
414 yac_cexchange(m_source_field_id, m_target_field_id, collection_size, send_field, recv_field,
415 &send_info, &recv_info, &ierror);
416 double end = MPI_Wtime();
417
418 return end - start;
419}
420
421
423
424 double time_spent =
425 InputInterpolation::regrid(output.metadata(0), file, -1, *output.grid(), output.vec());
426
427 auto log = output.grid()->ctx()->log();
428
429 log->message(2, "Interpolation took %f seconds.\n", time_spent);
430}
431
432
434 const pism::File &file, int record_index,
435 const Grid & /* target_grid (unused) */,
436 petsc::Vec &output) const {
437
438 // set metadata to help the following call find the variable, convert units, etc
439 m_buffer->metadata(0) = metadata;
440 m_buffer->read(file, record_index);
441
442 double time_spent = interpolate(*m_buffer, output);
443
444 return time_spent;
445}
446
447} // namespace pism
std::string name() const
Definition File.cc:274
High-level PISM I/O class.
Definition File.hh:57
const std::vector< double > & x() const
X-coordinates.
Definition Grid.cc:594
const std::vector< double > & y() const
Y-coordinates.
Definition Grid.cc:604
int ys() const
Global starting index of this processor's subset.
Definition Grid.cc:564
double dx() const
Horizontal grid spacing.
Definition Grid.cc:624
const VariableMetadata & get_mapping_info() const
Definition Grid.cc:1567
std::shared_ptr< const Context > ctx() const
Return execution context this grid corresponds to.
Definition Grid.cc:507
double dy() const
Horizontal grid spacing.
Definition Grid.cc:629
int xs() const
Global starting index of this processor's subset.
Definition Grid.cc:559
int xm() const
Width of this processor's sub-domain.
Definition Grid.cc:569
int ym() const
Width of this processor's sub-domain.
Definition Grid.cc:574
Describes the PISM grid and the distribution of data across processors.
Definition Grid.hh:285
static int define_field(int component_id, const std::vector< double > &x, const std::vector< double > &y, const std::string &proj_string, const std::string &name)
static int define_grid(const std::vector< double > &x, const std::vector< double > &y, const std::string &grid_name, const std::string &projection)
double interpolate(const array::Scalar &source, petsc::Vec &target) const
double regrid_impl(const VariableMetadata &metadata, const pism::File &file, int record_index, const Grid &target_grid, petsc::Vec &output) const
InputInterpolationYAC(const Grid &target_grid, const File &input_file, const std::string &variable_name, InterpolationType type)
std::shared_ptr< array::Scalar > m_buffer
void regrid(const File &file, array::Scalar &output) const
double regrid(const VariableMetadata &metadata, const pism::File &file, int record_index, const Grid &grid, petsc::Vec &output) const
A wrapper for PJ that makes sure pj_destroy is called.
Definition Proj.hh:32
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
petsc::Vec & vec() const
Definition Array.cc:313
std::shared_ptr< const Grid > grid() const
Definition Array.cc:134
VariableMetadata & metadata(unsigned int N=0)
Returns a reference to the VariableMetadata object containing metadata for the compoment N.
Definition Array.cc:467
double Lx
domain half-width in the X direction
Definition GridInfo.hh:46
std::vector< double > y
y coordinates
Definition GridInfo.hh:53
double y0
y-coordinate of the domain center
Definition GridInfo.hh:44
double x0
x-coordinate of the domain center
Definition GridInfo.hh:42
std::vector< double > x
x coordinates
Definition GridInfo.hh:51
double Ly
domain half-width in the Y direction
Definition GridInfo.hh:48
void report(const Logger &log, int threshold, std::shared_ptr< units::System > s) const
Definition Grid.cc:855
std::string variable_name
Name of the variable used to initialize the instance (empty if not used)
Definition Grid.hh:149
void vertical_grid_from_options(const Config &config)
Process -Mz and -Lz; set z;.
Definition Grid.cc:1316
double y0
Domain center in the Y direction.
Definition Grid.hh:132
double x0
Domain center in the X direction.
Definition Grid.hh:130
void ownership_ranges_from_options(const Config &config, unsigned int size)
Re-compute ownership ranges. Uses current values of Mx and My.
Definition Grid.cc:1181
Registration registration
Grid registration.
Definition Grid.hh:138
Grid parameters; used to collect defaults before an Grid is allocated.
Definition Grid.hh:100
double * get()
Definition Vec.cc:55
Wrapper around VecGetArray and VecRestoreArray.
Definition Vec.hh:47
#define PISM_ERROR_LOCATION
@ PIECEWISE_CONSTANT
static double dy_estimate(Proj &mapping, double x, double y1, double y2)
static std::vector< double > grid_subset(int xs, int xm, const std::vector< double > &coords)
static const double k
Definition exactTestP.cc:42
static double dy_min(const std::string &proj_string, const std::vector< double > &x, const std::vector< double > &y)
VariableMetadata mapping_info_from_file(const File &input_file, const std::string &variable_name, units::System::Ptr unit_system)
std::string grid_name(const pism::File &file, const std::string &variable_name, pism::units::System::Ptr sys, bool piecewise_constant)
static double dx_estimate(Proj &mapping, double x1, double x2, double y)
void GlobalMin(MPI_Comm comm, double *local, double *result, int count)
static double dx_min(const std::string &proj_string, const std::vector< double > &x, const std::vector< double > &y)
std::vector< double > lat
Definition LonLatGrid.hh:35
std::vector< double > lon
Definition LonLatGrid.hh:34