BubbleProfiler  0.3.0
by Peter Athron, Csaba Balazs, Michael Bardsley, Andrew Fowlie, Dylan Harries & Graham White
gsl_interpolator.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of BubbleProfiler.
3  *
4  * BubbleProfiler is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * BubbleProfiler is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with BubbleProfiler. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
23 #include "gsl_interpolator.hpp"
24 #include "error.hpp"
25 #include "raii_guard.hpp"
26 
27 #include <gsl/gsl_errno.h>
28 
29 #include <algorithm>
30 #include <string>
31 
32 namespace BubbleProfiler {
33 
34 namespace {
35 
36 gsl_interp_accel* clone_gsl_interp_accel(const gsl_interp_accel* original)
37 {
38  if (!original) {
39  return nullptr;
40  }
41 
42  gsl_interp_accel* cloned = gsl_interp_accel_alloc();
43 
44  if (cloned) {
45  cloned->cache = original->cache;
46  cloned->miss_count = original->miss_count;
47  cloned->hit_count = original->hit_count;
48  }
49 
50  return cloned;
51 }
52 
53 gsl_spline* clone_gsl_spline(const gsl_spline* original)
54 {
55  if (!original) {
56  return nullptr;
57  }
58 
59  const gsl_interp_type* t = original->interp->type;
60  const auto size = original->size;
61 
62  gsl_spline* cloned = gsl_spline_alloc(t, size);
63 
64  if (!cloned) {
65  return nullptr;
66  }
67 
68  const auto old_handler = gsl_set_error_handler_off();
69  const auto handler_guard = make_raii_guard([old_handler]() {
70  gsl_set_error_handler(old_handler);
71  });
72 
73  const int error = gsl_spline_init(cloned, original->x, original->y, size);
74 
75  if (error) {
76  gsl_spline_free(cloned);
77  return nullptr;
78  }
79 
80  return cloned;
81 }
82 
83 } // anonymous namespace
84 
93 Eigen::VectorXd interpolate_f_at(const Eigen::VectorXd& x_old,
94  const Eigen::VectorXd& y_old,
95  const Eigen::VectorXd& x_new)
96 {
97  const std::size_t nx = x_old.size();
98  const std::size_t ny = y_old.size();
99 
100  if (nx != ny) {
101  throw Setup_error("initial x and y vectors have different lengths");
102  }
103 
104  return interpolate_f_at(x_old, y_old, nx, x_new, x_new.size());
105 }
106 
114 Eigen::VectorXd interpolate_f_at(const Eigen::VectorXd& x_old,
115  const Eigen::VectorXd& y_old, int n_old,
116  const Eigen::VectorXd& x_new, int n_new)
117 {
118  if (n_old < 2) {
119  throw Setup_error("spline interpolation requires at least two points");
120  }
121 
122  if (n_old > x_old.size() || n_old > y_old.size()) {
123  throw Setup_error("requested number of points exceeds given number");
124  }
125 
126  if (n_new > x_new.size()) {
127  throw Setup_error(
128  "interpolate_f_at: "
129  "requested number of evaluation points exceeds given number");
130  }
131 
132  const auto handler = gsl_set_error_handler_off();
133  const auto handler_guard = make_raii_guard([handler]() {
134  gsl_set_error_handler(handler);
135  });
136 
137  gsl_interp_accel* acc = gsl_interp_accel_alloc();
138  if (!acc) {
139  throw Out_of_memory_error(
140  "interpolate_f_at: "
141  "unable to allocate memory for spline interpolation");
142  }
143 
144  const auto accel_guard = make_raii_guard([acc]() {
145  gsl_interp_accel_free(acc);
146  });
147 
148  gsl_spline* spline = gsl_spline_alloc(gsl_interp_cspline, n_old);
149  if (!spline) {
150  throw Out_of_memory_error(
151  "interpolate_f_at: "
152  "unable to allocate memory for spline interpolation");
153  }
154 
155  const auto spline_guard = make_raii_guard([spline]() {
156  gsl_spline_free(spline);
157  });
158 
159  int error = gsl_spline_init(spline, x_old.data(), y_old.data(), n_old);
160  if (error) {
161  throw Numerical_error("unable to initialize cubic spline");
162  }
163 
164  Eigen::VectorXd y_new(Eigen::VectorXd::Zero(n_new));
165  for (int i = 0; i < n_new; ++i) {
166  error = gsl_spline_eval_e(spline, x_new(i), acc, &y_new(i));
167  if (error) {
168  throw Numerical_error("interpolate_f_at: "
169  "unable to evaluate spline interpolant");
170  }
171  }
172 
173  return y_new;
174 }
175 
177  : accelerator(gsl_interp_accel_alloc())
178 {
179  if (!accelerator) {
180  throw Setup_error("GSL_interpolator::GSL_interpolator: "
181  "unable to allocate memory for interpolator");
182  }
183 }
184 
186 {
187  interp_type = other.interp_type;
188 
189  accelerator.reset(clone_gsl_interp_accel(other.accelerator.get()));
190  if (other.accelerator && !accelerator) {
191  throw Setup_error("GSL_interpolator::GSL_interpolator: "
192  "unable to allocate memory for interpolator");
193  }
194 
195  interpolant.reset(clone_gsl_spline(other.interpolant.get()));
196  if (other.interpolant && !interpolant) {
197  throw Setup_error("GSL_interpolator::GSL_interpolator: "
198  "unable to allocate memory for interpolator");
199  }
200 }
201 
203 {
204  if (&other == this) {
205  return *this;
206  }
207 
208  interp_type = other.interp_type;
209 
210  accelerator.reset(clone_gsl_interp_accel(other.accelerator.get()));
211  if (other.accelerator && !accelerator) {
212  throw Setup_error("GSL_interpolator::GSL_interpolator: "
213  "unable to allocate memory for interpolator");
214  }
215 
216  interpolant.reset(clone_gsl_spline(other.interpolant.get()));
217  if (other.interpolant && !interpolant) {
218  throw Setup_error("GSL_interpolator::GSL_interpolator: "
219  "unable to allocate memory for interpolator");
220  }
221 
222  return *this;
223 }
224 
233 void GSL_interpolator::construct_interpolant(const Eigen::VectorXd& x,
234  const Eigen::VectorXd& y)
235 {
236  const auto nx = x.size();
237  const auto ny = y.size();
238 
239  if (nx != ny) {
240  throw Setup_error("GSL_interpolator::construct_interpolant: "
241  "number of x values does not match number of y values");
242  }
243 
244  construct_interpolant(x, y, nx);
245 }
246 
255 void GSL_interpolator::construct_interpolant(const Eigen::VectorXd& x,
256  const Eigen::VectorXd& y,
257  int n)
258 {
259  const gsl_interp_type* t = to_gsl_interp_type(interp_type);
260  const int min_n = gsl_interp_type_min_size(t);
261 
262  if (n < min_n) {
263  const std::string name(t->name);
264  std::string msg("GSL_interpolator::construct_interpolant: "
265  "interpolation with interpolator '");
266  msg += name;
267  msg += "' requires at least " + std::to_string(min_n)
268  + " points (" + std::to_string(n) + " given";
269  throw Setup_error(msg);
270  }
271 
272  if (n > x.size() || n > y.size()) {
273  throw Setup_error("GSL_interpolator::construct_interpolant: "
274  "requested number of points exceeds given number");
275  }
276 
277  reset(interp_type, n);
278 
279  const int error = gsl_spline_init(interpolant.get(), x.data(), y.data(), n);
280  if (error) {
281  throw Setup_error("GSL_interpolator::construct_interpolant: "
282  "unable to initialize interpolant");
283  }
284 }
285 
290 double GSL_interpolator::evaluate_at(double x) const
291 {
294 
295  const auto handler = gsl_set_error_handler_off();
296  const auto handler_guard = make_raii_guard([handler]() {
297  gsl_set_error_handler(handler);
298  });
299 
300  double result = 0.;
301  const int error = gsl_spline_eval_e(interpolant.get(), x, accelerator.get(),
302  &result);
303 
304  if (error) {
305  throw Numerical_error("GSL_interpolator::evaluate_at: "
306  "unable to evaluate interpolant ("
307  + std::to_string(error) + ")");
308  }
309 
310  return result;
311 }
312 
318 {
321 
322  const auto handler = gsl_set_error_handler_off();
323  const auto handler_guard = make_raii_guard([handler]() {
324  gsl_set_error_handler(handler);
325  });
326 
327  double result = 0.;
328  const int error = gsl_spline_eval_deriv_e(interpolant.get(), x,
329  accelerator.get(), &result);
330 
331  if (error) {
332  throw Numerical_error("GSL_interpolator::evaluate_deriv_at: "
333  "unable to evaluate interpolant");
334  }
335 
336  return result;
337 }
338 
344 {
347 
348  const auto handler = gsl_set_error_handler_off();
349  const auto handler_guard = make_raii_guard([handler]() {
350  gsl_set_error_handler(handler);
351  });
352 
353  double result = 0.;
354  const int error = gsl_spline_eval_deriv2_e(interpolant.get(), x,
355  accelerator.get(), &result);
356 
357  if (error) {
358  throw Numerical_error("GSL_interpolator::evaluate_second_deriv_at: "
359  "unable to evaluate interpolant");
360  }
361 
362  return result;
363 }
364 
366  Interpolation_type t) const
367 {
368  switch (t) {
370  return gsl_interp_linear;
371  }
373  return gsl_interp_polynomial;
374  }
376  return gsl_interp_cspline;
377  }
379  return gsl_interp_cspline_periodic;
380  }
382  return gsl_interp_akima;
383  }
385  return gsl_interp_akima_periodic;
386  }
387 #ifdef GSL_MAJOR_VERSION
388 #if (GSL_MAJOR_VERSION >= 2)
389  case Interpolation_type::GSL_steffen: {
390  return gsl_interp_steffen;
391  }
392 #endif
393 #endif
394  default:
395  throw Setup_error("GSL_interpolator::to_gsl_interp_type: "
396  "unrecognized interpolator type");
397  }
398 
399  return nullptr;
400 }
401 
403  int n_points)
404 {
405  gsl_interp_accel_reset(accelerator.get());
406 
407  interpolant.reset(gsl_spline_alloc(to_gsl_interp_type(t), n_points));
408  if (!interpolant) {
409  throw Out_of_memory_error("GSL_interpolator::construct_interpolant: "
410  "unable to allocate memory for interpolator");
411  }
412 }
413 
415 {
416  if (!interpolant) {
417  throw Setup_error("GSL_interpolator::evaluate_second_deriv_at: "
418  "interpolant not computed");
419  }
420 }
421 
423 {
424  const double x_min = interpolant.get()->interp->xmin;
425  const double x_max = interpolant.get()->interp->xmax;
426 
427  if (x < x_min || x > x_max) {
428  throw Domain_error("GSL_interpolator::check_within_bounds: "
429  "cannot interpolate outside of domain");
430  }
431 }
432 
433 } // namespace BubbleProfiler
std::unique_ptr< gsl_interp_accel, GSL_accel_deleter > accelerator
std::unique_ptr< gsl_spline, GSL_spline_deleter > interpolant
Exception indicating function evaluation out of allowed domain.
Definition: error.hpp:110
void reset(Interpolation_type, int)
double evaluate_deriv_at(double) const
Evaluates derivative of computed interpolant at a point.
Exception indicating that memory allocation failed.
Definition: error.hpp:98
double evaluate_at(double) const
Evaluates computed interpolant at a point.
double evaluate_second_deriv_at(double) const
Evaluates second derivative of computed interpolant at a point.
void construct_interpolant(const Eigen::VectorXd &, const Eigen::VectorXd &)
Computes interpolant using given x and y values.
Eigen::VectorXd interpolate_f_at(const Eigen::VectorXd &x_old, const Eigen::VectorXd &y_old, const Eigen::VectorXd &x_new)
Computes interpolated function values at a new set of grid points.
constexpr RAII_guard< F > make_raii_guard(F f)
Definition: raii_guard.hpp:43
contains the definition of the GSL_interpolator class
const gsl_interp_type * to_gsl_interp_type(Interpolation_type) const
Exception indicating general setup error.
Definition: error.hpp:32
GSL_interpolator & operator=(const GSL_interpolator &)
Provides routines for carrying out 1D interpolation.
Exception indicating generic numerical error.
Definition: error.hpp:116