PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
interpolation.cc
Go to the documentation of this file.
1 /* Copyright (C) 2015, 2016, 2017, 2018, 2019, 2021, 2023 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 <gsl/gsl_interp.h>
21 #include <cassert>
22 
23 #include "pism/util/interpolation.hh"
24 #include "pism/util/error_handling.hh"
25 
26 namespace pism {
27 
29  const std::vector<double> &input_x,
30  const std::vector<double> &output_x)
31  : Interpolation(type, input_x.data(), input_x.size(),
32  output_x.data(), output_x.size()) {
33  // empty
34 }
35 
37  const double *input_x, unsigned int input_x_size,
38  const double *output_x, unsigned int output_x_size) {
39 
40  // the trivial case (the code below requires input_x_size >= 2)
41  if (input_x_size < 2) {
42  m_left.resize(output_x_size);
43  m_right.resize(output_x_size);
44  m_alpha.resize(output_x_size);
45 
46  for (unsigned int k = 0; k < output_x_size; ++k) {
47  m_left[k] = 0.0;
48  m_right[k] = 0.0;
49  m_alpha[k] = 0.0;
50  }
51 
52  return;
53  }
54 
55  // input grid points have to be stored in the increasing order
56  for (unsigned int i = 0; i < input_x_size - 1; ++i) {
57  if (input_x[i] >= input_x[i + 1]) {
59  "an input grid for interpolation has to be "
60  "strictly increasing");
61  }
62  }
63 
64  switch (type) {
65  case LINEAR:
66  init_linear(input_x, input_x_size, output_x, output_x_size);
67  break;
68  case NEAREST:
69  init_nearest(input_x, input_x_size, output_x, output_x_size);
70  break;
71  case PIECEWISE_CONSTANT:
72  init_piecewise_constant(input_x, input_x_size, output_x, output_x_size);
73  break;
74  // LCOV_EXCL_START
75  default:
76  throw RuntimeError(PISM_ERROR_LOCATION, "invalid interpolation type");
77  // LCOV_EXCL_STOP
78  }
79 }
80 
81 /**
82  * Compute linear interpolation indexes and weights.
83  *
84  * @param[in] input_x coordinates of the input grid
85  * @param[in] input_x_size number of points in the input grid
86  * @param[in] output_x coordinates of the output grid
87  * @param[in] output_x_size number of points in the output grid
88  */
89 void Interpolation::init_linear(const double *input_x, unsigned int input_x_size,
90  const double *output_x, unsigned int output_x_size) {
91  assert(input_x_size >= 2);
92 
93  m_left.resize(output_x_size);
94  m_right.resize(output_x_size);
95  m_alpha.resize(output_x_size);
96 
97  // compute indexes and weights
98  for (unsigned int i = 0; i < output_x_size; ++i) {
99  double x = output_x[i];
100 
101  // note: use "input_x_size" instead of "input_x_size - 1" to support extrapolation on
102  // the right
103  unsigned int
104  L = gsl_interp_bsearch(input_x, x, 0, input_x_size),
105  R = L + 1;
106 
107  double alpha = 0.0;
108  if (x >= input_x[L] and R < input_x_size) {
109  // regular case
110  alpha = (x - input_x[L]) / (input_x[R] - input_x[L]);
111  } else {
112  // extrapolation
113  alpha = 0.0;
114  R = L;
115  }
116 
117  assert(L < input_x_size);
118  assert(R < input_x_size);
119  assert(alpha >= 0.0 and alpha <= 1.0);
120 
121  m_left[i] = L;
122  m_right[i] = R;
123  m_alpha[i] = alpha;
124  }
125 }
126 
127 const std::vector<int>& Interpolation::left() const {
128  return m_left;
129 }
130 
131 const std::vector<int>& Interpolation::right() const {
132  return m_right;
133 }
134 
135 const std::vector<double>& Interpolation::alpha() const {
136  return m_alpha;
137 }
138 
139 int Interpolation::left(size_t j) const {
140  return m_left[j];
141 }
142 
143 int Interpolation::right(size_t j) const {
144  return m_right[j];
145 }
146 
147 double Interpolation::alpha(size_t j) const {
148  return m_alpha[j];
149 }
150 
152  return (int)m_alpha.size();
153 }
154 
155 std::vector<double> Interpolation::interpolate(const std::vector<double> &input_values) const {
156  std::vector<double> result(m_alpha.size());
157 
158  interpolate(input_values.data(), result.data());
159 
160  return result;
161 }
162 
163 void Interpolation::interpolate(const double *input, double *output) const {
164  size_t n = m_alpha.size();
165  for (size_t k = 0; k < n; ++k) {
166  const int
167  L = m_left[k],
168  R = m_right[k];
169  output[k] = input[L] + m_alpha[k] * (input[R] - input[L]);
170  }
171 }
172 
173 void Interpolation::init_nearest(const double *input_x, unsigned int input_x_size,
174  const double *output_x, unsigned int output_x_size) {
175 
176  init_linear(input_x, input_x_size, output_x, output_x_size);
177 
178  for (unsigned int j = 0; j < m_alpha.size(); ++j) {
179  m_alpha[j] = m_alpha[j] > 0.5 ? 1.0 : 0.0;
180  }
181 }
182 
183 /*!
184  * Input grid `input_x` corresponds to *left* end-points of intervals.
185  */
186 void Interpolation::init_piecewise_constant(const double *input_x, unsigned int input_x_size,
187  const double *output_x, unsigned int output_x_size) {
188  assert(input_x_size >= 2);
189 
190  m_left.resize(output_x_size);
191  m_right.resize(output_x_size);
192  m_alpha.resize(output_x_size);
193 
194  // compute indexes and weights
195  for (unsigned int i = 0; i < output_x_size; ++i) {
196 
197  // note: use "input_x_size" instead of "input_x_size - 1" to support extrapolation on
198  // the right
199  size_t L = gsl_interp_bsearch(input_x, output_x[i], 0, input_x_size);
200 
201  m_left[i] = L;
202  m_right[i] = L;
203  m_alpha[i] = 0.0;
204 
205  assert(m_left[i] >= 0 and m_left[i] < (int)input_x_size);
206  assert(m_right[i] >= 0 and m_right[i] < (int)input_x_size);
207  assert(m_alpha[i] >= 0.0 and m_alpha[i] <= 1.0);
208  }
209 }
210 
211 static std::map<size_t, double> weights_piecewise_constant(const double *x,
212  size_t x_size,
213  double a,
214  double b) {
215 
216  size_t
217  al = gsl_interp_bsearch(x, a, 0, x_size),
218  ar = (a >= x[al] and al + 1 < x_size) ? al + 1 : al,
219  bl = gsl_interp_bsearch(x, b, 0, x_size),
220  br = (b >= x[bl] and bl + 1 < x_size) ? bl + 1 : bl;
221 
222  std::map<size_t, double> result;
223 
224  if (al == bl and ar == br) {
225  // both end points are in the same interval
226  result[al] += (b - a);
227  } else {
228  // first interval
229  result[al] += (x[ar] - a);
230 
231  // intermediate intervals
232  for (size_t k = ar; k < bl; ++k) {
233  result[k] += x[k + 1] - x[k];
234  }
235 
236  // last interval
237  result[bl] += b - x[bl];
238  }
239 
240  return result;
241 }
242 
243 static std::map<size_t, double> weights_piecewise_linear(const double *x,
244  size_t x_size,
245  double a,
246  double b) {
247 
248  size_t
249  al = gsl_interp_bsearch(x, a, 0, x_size),
250  ar = (a >= x[al] and al + 1 < x_size) ? al + 1 : al,
251  bl = gsl_interp_bsearch(x, b, 0, x_size),
252  br = (b >= x[bl] and bl + 1 < x_size) ? bl + 1 : bl;
253 
254  double
255  alpha_a = (al == ar) ? 0.0 : (a - x[al]) / (x[ar] - x[al]),
256  alpha_b = (bl == br) ? 0.0 : (b - x[bl]) / (x[br] - x[bl]);
257 
258  std::map<size_t, double> result;
259 
260  if (al == bl and ar == br) {
261  // both end points are in the same interval
262 
263  result[al] += 0.5 * (2.0 - alpha_a - alpha_b) * (b - a);
264  result[ar] += 0.5 * (alpha_a + alpha_b) * (b - a);
265  } else {
266  // first interval
267  result[al] += 0.5 * (1.0 - alpha_a) * (x[ar] - a);
268  result[ar] += 0.5 * (1.0 + alpha_a) * (x[ar] - a);
269 
270  // intermediate intervals
271  for (size_t k = ar; k < bl; ++k) {
272  int
273  L = k,
274  R = k + 1;
275  result[L] += 0.5 * (x[R] - x[L]);
276  result[R] += 0.5 * (x[R] - x[L]);
277  }
278 
279  // last interval
280  result[bl] += 0.5 * (2.0 - alpha_b) * (b - x[bl]);
281  result[br] += 0.5 * alpha_b * (b - x[bl]);
282  }
283 
284  return result;
285 }
286 
287 /*!
288  * Compute weights for integrating a piece-wise linear or piece-wise constant function
289  * defined on the grid `x` from `a` to `b`.
290  *
291  * Uses constant extrapolation, both on the left and on the right.
292  *
293  * In the piece-wise constant case points in `x` are interpreted as left end points of
294  * intervals, i.e. the value `data[k]` corresponds to the interval `x[k], x[k + 1]`.
295  *
296  * To evaluate in the integral compute the dot product of data on the grid `x` with
297  * weights computed by this function:
298  *
299  * ```
300  * double result = 0.0;
301  * for (const auto &weight : weights) {
302  * size_t k = weight.first;
303  * double w = weight.second;
304  * result += w * data[k];
305  * }
306  * ```
307  */
308 std::map<size_t, double> integration_weights(const double *x,
309  size_t x_size,
310  InterpolationType type,
311  double a,
312  double b) {
313 
314  if (a >= b) {
316  "invalid integration interval (a >= b)");
317  }
318 
319  if (type != LINEAR and type != PIECEWISE_CONSTANT) {
321  "unsupported interpolation type");
322  }
323 
324  auto weights = (type == LINEAR) ?
327 
328  size_t N = x_size - 1;
329  double t0 = x[0];
330  double t1 = x[N];
331 
332  // both points are to the left of [t0, t1]
333  if (b <= t0) {
334  return {{0, b - a}};
335  }
336 
337  // both points are to the right of [t0, t1]
338  if (a >= t1) {
339  return {{N, b - a}};
340  }
341 
342  // a is to the left of [t0, t1]
343  if (a < t0) {
344  auto W = weights(x, x_size, t0, b);
345  W[0] += t0 - a;
346  return W;
347  }
348 
349  // b is to the right of [t0, t1]
350  if (b > t1) {
351  auto W = weights(x, x_size, a, t1);
352  W[N] += b - t1;
353  return W;
354  }
355 
356  // both a and b are inside [t0, t1]
357  return weights(x, x_size, a, b);
358 }
359 
360 std::map<size_t, double> integration_weights(const std::vector<double> &x,
361  InterpolationType type,
362  double a,
363  double b) {
364  return integration_weights(x.data(), x.size(), type, a, b);
365 }
366 
367 } // end of namespace pism
void init_linear(const double *input_x, unsigned int input_x_size, const double *output_x, unsigned int output_x_size)
std::vector< double > m_alpha
Interpolation weights.
Interpolation(InterpolationType type, const std::vector< double > &input_x, const std::vector< double > &output_x)
void init_piecewise_constant(const double *input_x, unsigned int input_x_size, const double *output_x, unsigned int output_x_size)
std::vector< int > m_left
Interpolation indexes.
void init_nearest(const double *input_x, unsigned int input_x_size, const double *output_x, unsigned int output_x_size)
const std::vector< double > & alpha() const
const std::vector< int > & right() const
const std::vector< int > & left() const
std::vector< double > interpolate(const std::vector< double > &input_values) const
Return interpolated values (on the output grid) given input_values on the input grid.
std::vector< int > m_right
#define PISM_ERROR_LOCATION
static const double L
Definition: exactTestL.cc:40
#define n
Definition: exactTestM.c:37
static std::map< size_t, double > weights_piecewise_linear(const double *x, size_t x_size, double a, double b)
InterpolationType
@ PIECEWISE_CONSTANT
std::map< size_t, double > integration_weights(const double *x, size_t x_size, InterpolationType type, double a, double b)
static const double k
Definition: exactTestP.cc:42
static std::map< size_t, double > weights_piecewise_constant(const double *x, size_t x_size, double a, double b)