MARLEY (Model of Argon Reaction Low Energy Yields)  v1.2.0
A Monte Carlo event generator for tens-of-MeV neutrino interactions
InterpolationGrid.hh
1 //
5 // This file is part of MARLEY (Model of Argon Reaction Low Energy Yields)
6 //
7 // MARLEY is free software: you can redistribute it and/or modify it under the
8 // terms of version 3 of the GNU General Public License as published by the
9 // Free Software Foundation.
10 //
11 // For the full text of the license please see COPYING or
12 // visit http://opensource.org/licenses/GPL-3.0
13 //
14 // Please respect the MCnet academic usage guidelines. See GUIDELINES
15 // or visit https://www.montecarlonet.org/GUIDELINES for details.
16 
17 #pragma once
18 #include <algorithm>
19 #include <cmath>
20 #include <functional>
21 #include <utility>
22 #include <vector>
23 
24 #include "marley/Error.hh"
25 
26 namespace marley {
27 
35  template <typename FirstNumericType, typename SecondNumericType
36  = FirstNumericType> class InterpolationGrid
37  {
38  public:
39 
40  using OrderedPair = std::pair<FirstNumericType, SecondNumericType>;
41  using Grid = std::vector<OrderedPair>;
42  using GridConstIterator
43  = typename std::vector<OrderedPair>::const_iterator;
44 
63  enum class InterpolationMethod { Constant = 1,
64  LinearLinear = 2, LinearLog = 3, LogLinear = 4,
65  LogLog = 5 };
66 
96  enum class ExtrapolationMethod { Zero, Endpoint,
97  Continue, Throw };
98 
101  = InterpolationMethod::LinearLinear, ExtrapolationMethod extrap_method
102  = ExtrapolationMethod::Zero)
103  : interpolation_method_(interp_method),
104  extrapolation_method_(extrap_method)
105  {
106  }
107 
109  inline InterpolationGrid(const Grid& grid,
110  InterpolationMethod interp_method = InterpolationMethod::LinearLinear,
111  ExtrapolationMethod extrap_method = ExtrapolationMethod::Zero)
112  : interpolation_method_(interp_method),
113  extrapolation_method_(extrap_method), ordered_pairs_(grid)
114  {
116  }
117 
119  inline InterpolationGrid(const std::vector<FirstNumericType>& xs,
120  const std::vector<SecondNumericType>& ys,
121  InterpolationMethod interp_method = InterpolationMethod::LinearLinear,
122  ExtrapolationMethod extrap_method = ExtrapolationMethod::Zero)
123  : interpolation_method_(interp_method),
124  extrapolation_method_(extrap_method)
125  {
126  if (xs.size() != ys.size()) throw marley::Error(
127  std::string("Vectors of x and y values passed to the constructor")
128  + " of marley::InterpolationGrid have unequal sizes.");
129 
130  double old_x = marley_utils::minus_infinity;
131  for (size_t j = 0; j < xs.size(); ++j) {
132  double new_x = xs.at(j);
133  if (new_x <= old_x) throw marley::Error(std::string("The grid")
134  + " point x-values defined for a marley::InterpolationGrid object"
135  + " are not strictly increasing");
136  ordered_pairs_.push_back(OrderedPair(xs.at(j), ys.at(j)));
137  }
138  };
139 
141  SecondNumericType interpolate(FirstNumericType x) const;
142 
144  void insert(FirstNumericType x, SecondNumericType y);
145 
147  inline size_t size() const { return ordered_pairs_.size(); }
148 
150  inline void clear() { ordered_pairs_.clear(); }
151 
153  inline OrderedPair& at(size_t j) { return ordered_pairs_.at(j); }
154 
157  inline std::function<SecondNumericType(FirstNumericType)>
159  {
160  return [this](FirstNumericType x)
161  -> SecondNumericType { return this->interpolate(x); };
162  //return std::bind( &InterpolationGrid<FirstNumericType,
163  //SecondNumericType>::interpolate, this, std::placeholders::_1);
164  }
165 
168  inline GridConstIterator lower_bound(const
169  GridConstIterator& begin,
170  const GridConstIterator& end, FirstNumericType x) const
171  {
172  return std::lower_bound(begin, end, x,
173  [](const OrderedPair& pair, const FirstNumericType& f)
174  -> bool { return pair.first < f; });
175  }
176 
179  inline GridConstIterator upper_bound(const
180  GridConstIterator& begin,
181  const GridConstIterator& end, FirstNumericType x) const
182  {
183  return std::upper_bound(begin, end, x,
184  [](const FirstNumericType& f, const OrderedPair& pair)
185  -> bool { return f < pair.first; });
186  }
187 
189  inline const OrderedPair& front() const { return ordered_pairs_.front(); }
190 
192  inline const OrderedPair& back() const { return ordered_pairs_.back(); }
193 
196  { return interpolation_method_; }
197 
200  { interpolation_method_ = method; }
201 
204  { return extrapolation_method_; }
205 
208  { extrapolation_method_ = method; }
209 
210  private:
211 
213  InterpolationMethod interpolation_method_;
214 
217  ExtrapolationMethod extrapolation_method_;
218 
220  Grid ordered_pairs_;
221 
226  inline void check_grid() const {
228  if (ordered_pairs_.size() < 2) throw marley::Error(std::string("A")
229  + " class method was called for an InterpolationGrid object"
230  + " that contains less than two grid points.");
231  }
232 
236  inline bool find_bin_limits(FirstNumericType x,
237  GridConstIterator& lower_point, GridConstIterator& upper_point) const
238  {
239  // Check to make sure that the grid contains at least two ordered pairs
240  check_grid();
241 
242  // Find the first point on the grid that is not less than x
243  bool extrapolate = false;
244  GridConstIterator begin = ordered_pairs_.begin();
245  GridConstIterator end = ordered_pairs_.end();
246  GridConstIterator not_less_point = lower_bound(begin, end, x);
247 
248  // Check whether the requested grid point is within the grid limits
249  if (not_less_point == begin) {
250  lower_point = begin;
251  upper_point = begin + 1;
252  // First element of xs > x
253  if (begin->first != x) extrapolate = true;
254  }
255  else if (not_less_point == end) {
256  // last element of xs < x (extrapolate on the right)
257  extrapolate = true;
258  lower_point = end - 2;
259  upper_point = end - 1;
260  }
261  else {
262  // x is within the grid limits
263  lower_point = not_less_point - 1;
264  upper_point = not_less_point;
265  }
266 
267  return extrapolate;
268  }
269  };
270 
271  template <typename FirstNumericType, typename SecondNumericType>
272  SecondNumericType InterpolationGrid<FirstNumericType,
273  SecondNumericType>::interpolate(FirstNumericType x) const
274  {
275  // Find grid points just below [(x1, y1)] and just above [(x2, y2)]
276  InterpolationGrid::GridConstIterator lower_point, upper_point;
277  // Includes a call to check_grid()
278  bool extrapolate = find_bin_limits(x, lower_point, upper_point);
279 
280  // If the requested x value is outside of the grid, use the correct method
281  // for dealing with this situation based on the value of
282  if (extrapolate) {
283  if (extrapolation_method_ == ExtrapolationMethod::Zero)
284  return static_cast<SecondNumericType>(0.);
285  else if (extrapolation_method_ == ExtrapolationMethod::Endpoint) {
286  if (lower_point->first > x) return lower_point->second;
287  else return upper_point->second;
288  }
289  else if (extrapolation_method_ == ExtrapolationMethod::Throw) {
290  throw marley::Error(std::string("x = ") + std::to_string(x)
291  + " lies outside of the current interpolation grid object"
292  + " (which extends from x_min = "
293  + std::to_string(ordered_pairs_.front().first)
294  + " and x_max = " + std::to_string(ordered_pairs_.back().first)
295  + ") and extrapolation is disabled.");
296  }
297  }
298 
299  // Either the requested point falls within the grid or
300  // extrapolation_method == ExtrapolationMethod::Continue. If the "continue"
301  // method for extrapolation is selected, then we continue to use the same
302  // interpolation technique formula outside of the grid (for
303  // InterpolationMethod::LinearLinear, this is linear-linear extrapolation).
304 
305  // If the constant interpolation method is selected, then use the *lower
306  // bound* of each bin as the interpolated value. If we're extrapolating,
307  // on the right, use the upper bound.
308  if (interpolation_method_ == InterpolationMethod::Constant) {
309  if (!extrapolate) return lower_point->second;
310  else if (lower_point->first > x) return lower_point->second;
311  else return upper_point->second;
312  }
313 
314  // We'll use an interpolation formula for all other methods, so
315  // get the grid point x and y values for later use.
316  FirstNumericType x1 = lower_point->first;
317  FirstNumericType x2 = upper_point->first;
318  SecondNumericType y1 = lower_point->second;
319  SecondNumericType y2 = upper_point->second;
320 
321  bool log_x = false, log_y = false;
322  FirstNumericType x_to_use = x;
323  if (interpolation_method_ == InterpolationMethod::LinearLog)
324  log_x = true;
325  else if (interpolation_method_ == InterpolationMethod::LogLinear)
326  log_y = true;
327  else if (interpolation_method_ == InterpolationMethod::LogLog) {
328  log_x = true;
329  log_y = true;
330  }
331  if (log_x) {
332  x1 = std::log(x1);
333  x2 = std::log(x2);
334  x_to_use = std::log(x);
335  }
336  if (log_y) {
337  y1 = std::log(y1);
338  y2 = std::log(y2);
339  }
340 
341  FirstNumericType y_interp = y1 + ((y2 - y1)/(x2 - x1))*(x_to_use - x1);
342  if (log_y) y_interp = std::exp(y_interp);
343  return y_interp;
344  }
345 
346  template <typename FirstNumericType, typename SecondNumericType>
348  FirstNumericType x, SecondNumericType y)
349  {
350  // Figure out where this grid point should go in the grid. Use
351  // std::upper_bound so that entries with the same x value (discontinuities)
352  // are inserted in the order that they are passed to
353  // InterpolationGrid::insert.
354  GridConstIterator insert_point = upper_bound(ordered_pairs_.begin(),
355  ordered_pairs_.end(), x);
356 
357  // Insert the new grid point
358  ordered_pairs_.insert(insert_point, OrderedPair(x, y));
359  }
360 
361 }
Base class for all exceptions thrown by MARLEY functions.
Definition: Error.hh:32
One-dimensional function y(x) defined using a grid of ordered pairs (x,y) and an interpolation rule.
Definition: InterpolationGrid.hh:37
ExtrapolationMethod
Method to use for computing y(x) when the x value lies beyond the grid boundaries.
Definition: InterpolationGrid.hh:96
void insert(FirstNumericType x, SecondNumericType y)
Add a new ordered pair (x, y) to the grid.
Definition: InterpolationGrid.hh:347
GridConstIterator lower_bound(const GridConstIterator &begin, const GridConstIterator &end, FirstNumericType x) const
Returns a const_iterator to the first element of the grid for which the x value is not less than (i....
Definition: InterpolationGrid.hh:168
InterpolationGrid(const Grid &grid, InterpolationMethod interp_method=InterpolationMethod::LinearLinear, ExtrapolationMethod extrap_method=ExtrapolationMethod::Zero)
Create an InterpolationGrid from a vector of ordered pairs.
Definition: InterpolationGrid.hh:109
void clear()
Delete all ordered pairs from the grid.
Definition: InterpolationGrid.hh:150
InterpolationGrid(const std::vector< FirstNumericType > &xs, const std::vector< SecondNumericType > &ys, InterpolationMethod interp_method=InterpolationMethod::LinearLinear, ExtrapolationMethod extrap_method=ExtrapolationMethod::Zero)
Create an InterpolationGrid from vectors of x and y values.
Definition: InterpolationGrid.hh:119
OrderedPair & at(size_t j)
Get a reference to the jth ordered pair from the grid.
Definition: InterpolationGrid.hh:153
const OrderedPair & back() const
Returns a reference to the last ordered pair.
Definition: InterpolationGrid.hh:192
size_t size() const
Get the number of ordered pairs on the grid.
Definition: InterpolationGrid.hh:147
void set_interpolationMethod(InterpolationMethod method)
Set the InterpolationMethod to use.
Definition: InterpolationGrid.hh:199
SecondNumericType interpolate(FirstNumericType x) const
Compute y(x) using the current InterpolationMethod.
Definition: InterpolationGrid.hh:273
ExtrapolationMethod extrapolation_method() const
Get the ExtrapolationMethod used by this InterpolationGrid.
Definition: InterpolationGrid.hh:203
InterpolationGrid(InterpolationMethod interp_method=InterpolationMethod::LinearLinear, ExtrapolationMethod extrap_method=ExtrapolationMethod::Zero)
Create an InterpolationGrid without any grid points.
Definition: InterpolationGrid.hh:100
std::function< SecondNumericType(FirstNumericType)> get_interpolating_function()
Get a std::function object that represents y(x) for this InterpolationGrid.
Definition: InterpolationGrid.hh:158
void set_interpolationMethod(ExtrapolationMethod method)
Set the ExtrapolationMethod to use.
Definition: InterpolationGrid.hh:207
InterpolationMethod interpolation_method() const
Get the InterpolationMethod used by this InterpolationGrid.
Definition: InterpolationGrid.hh:195
InterpolationMethod
Method to use for interpolating between (x,y) grid points.
Definition: InterpolationGrid.hh:63
const OrderedPair & front() const
Returns a reference to the first ordered pair.
Definition: InterpolationGrid.hh:189
GridConstIterator upper_bound(const GridConstIterator &begin, const GridConstIterator &end, FirstNumericType x) const
Returns a const_iterator to the first element of the grid for which the x value is greater than x.
Definition: InterpolationGrid.hh:179