https://mooseframework.inl.gov
LinearInterpolation.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "LinearInterpolation.h"
11 #include "MooseUtils.h"
12 
13 #include "ChainedReal.h"
14 
15 #include <cassert>
16 #include <fstream>
17 #include <stdexcept>
18 
19 LinearInterpolation::LinearInterpolation(const std::vector<Real> & x,
20  const std::vector<Real> & y,
21  const bool extrap)
22  : _x(x), _y(y), _extrap(extrap)
23 {
24  errorCheck();
25 }
26 
27 void
29 {
30  if (_x.size() != _y.size())
31  throw std::domain_error("Vectors are not the same length");
32 
33  for (unsigned int i = 0; i + 1 < _x.size(); ++i)
34  if (_x[i] >= _x[i + 1])
35  {
36  std::ostringstream oss;
37  oss << "x-values are not strictly increasing: x[" << i << "]: " << _x[i] << " x[" << i + 1
38  << "]: " << _x[i + 1];
39  throw std::domain_error(oss.str());
40  }
41 }
42 
43 template <typename T>
44 T
45 LinearInterpolation::sample(const T & x) const
46 {
47  // this is a hard error
48  if (_x.empty())
49  mooseError("Trying to evaluate an empty LinearInterpolation");
50 
51  // special case for single function nodes
52  if (_x.size() == 1)
53  return _y[0];
54 
55  // endpoint cases
56  if (_extrap)
57  {
58  if (x < _x[0])
59  return _y[0] + (x - _x[0]) / (_x[1] - _x[0]) * (_y[1] - _y[0]);
60  if (x >= _x.back())
61  return _y.back() +
62  (x - _x.back()) / (_x[_x.size() - 2] - _x.back()) * (_y[_y.size() - 2] - _y.back());
63  }
64  else
65  {
66  if (x < _x[0])
67  return _y[0];
68  if (x >= _x.back())
69  return _y.back();
70  }
71 
72  auto upper = std::upper_bound(_x.begin(), _x.end(), x);
73  const auto i = cast_int<std::size_t>(std::distance(_x.begin(), upper) - 1);
74  if (i == cast_int<std::size_t>(_x.size() - 1))
75  // std::upper_bound returns the end() iterator if there are no elements that are
76  // an upper bound to the value. Since x >= _x.back() has already returned above,
77  // this means x is a NaN, so we return a NaN here.
78  return std::nan("");
79  else
80  return _y[i] + (_y[i + 1] - _y[i]) * (x - _x[i]) / (_x[i + 1] - _x[i]);
81 }
82 
83 template Real LinearInterpolation::sample<Real>(const Real &) const;
84 template ADReal LinearInterpolation::sample<ADReal>(const ADReal &) const;
85 template ChainedReal LinearInterpolation::sample<ChainedReal>(const ChainedReal &) const;
86 
87 template <typename T>
88 T
90 {
91  // endpoint cases
92  if (_extrap)
93  {
94  if (x <= _x[0])
95  return (_y[1] - _y[0]) / (_x[1] - _x[0]);
96  if (x >= _x.back())
97  return (_y[_y.size() - 2] - _y.back()) / (_x[_x.size() - 2] - _x.back());
98  }
99  else
100  {
101  if (x < _x[0])
102  return 0.0;
103  if (x >= _x.back())
104  return 0.0;
105  }
106 
107  auto upper = std::upper_bound(_x.begin(), _x.end(), x);
108  const auto i = cast_int<std::size_t>(std::distance(_x.begin(), upper) - 1);
109  if (i == cast_int<std::size_t>(_x.size() - 1))
110  // std::upper_bound returns the end() iterator if there are no elements that are
111  // an upper bound to the value. Since x >= _x.back() has already returned above,
112  // this means x is a NaN, so we return a NaN here.
113  return std::nan("");
114  else
115  return (_y[i + 1] - _y[i]) / (_x[i + 1] - _x[i]);
116 }
117 
118 template Real LinearInterpolation::sampleDerivative<Real>(const Real &) const;
119 template ADReal LinearInterpolation::sampleDerivative<ADReal>(const ADReal &) const;
120 template ChainedReal LinearInterpolation::sampleDerivative<ChainedReal>(const ChainedReal &) const;
121 
122 Real
124 {
125  Real answer = 0;
126  for (unsigned int i = 1; i < _x.size(); ++i)
127  answer += 0.5 * (_y[i] + _y[i - 1]) * (_x[i] - _x[i - 1]);
128 
129  return answer;
130 }
131 
132 template <typename T>
133 T
134 LinearInterpolation::integratePartial(const T & xA, const T & xB) const
135 {
136  // integral computation below will assume that x2 > x1; if this is not the
137  // case, compute as if it is and then use identity to convert
138  const T *x1_ptr, *x2_ptr;
139  bool switch_bounds;
140  const Real rawA = MetaPhysicL::raw_value(xA), rawB = MetaPhysicL::raw_value(xB);
141  Real raw1, raw2;
142  if (MooseUtils::absoluteFuzzyEqual(rawA, rawB))
143  return 0.0;
144  else if (rawB > rawA)
145  {
146  x1_ptr = &xA;
147  x2_ptr = &xB;
148  raw1 = rawA;
149  raw2 = rawB;
150  switch_bounds = false;
151  }
152  else
153  {
154  x1_ptr = &xB;
155  x2_ptr = &xA;
156  raw1 = rawB;
157  raw2 = rawA;
158  switch_bounds = true;
159  }
160  const auto & x1 = *x1_ptr;
161  const auto & x2 = *x2_ptr;
162 
163  // compute integral with knowledge that x2 > x1
164  T integral = 0.0;
165  // find minimum i : x[i] > x; if x > x[n-1], i = n
166  const auto n = _x.size();
167  const unsigned int i1 =
168  raw1 <= _x[n - 1] ? std::distance(_x.begin(), std::upper_bound(_x.begin(), _x.end(), raw1))
169  : n;
170  const unsigned int i2 =
171  raw2 <= _x[n - 1] ? std::distance(_x.begin(), std::upper_bound(_x.begin(), _x.end(), raw2))
172  : n;
173  unsigned int i = i1;
174  while (i <= i2)
175  {
176  if (i == 0)
177  {
178  // note i1 = i
179  T integral1, integral2;
180  if (_extrap)
181  {
182  const auto dydx = (_y[1] - _y[0]) / (_x[1] - _x[0]);
183  const auto y1 = _y[0] + dydx * (x1 - _x[0]);
184  integral1 = 0.5 * (y1 + _y[0]) * (_x[0] - x1);
185  if (i2 == i)
186  {
187  const auto y2 = _y[0] + dydx * (x2 - _x[0]);
188  integral2 = 0.5 * (y2 + _y[0]) * (_x[0] - x2);
189  }
190  else
191  integral2 = 0.0;
192  }
193  else
194  {
195  integral1 = _y[0] * (_x[0] - x1);
196  if (i2 == i)
197  integral2 = _y[0] * (_x[0] - x2);
198  else
199  integral2 = 0.0;
200  }
201 
202  integral += integral1 - integral2;
203  }
204  else if (i == n)
205  {
206  // note i2 = i
207  T integral1, integral2;
208  if (_extrap)
209  {
210  const auto dydx = (_y[n - 1] - _y[n - 2]) / (_x[n - 1] - _x[n - 2]);
211  const auto y2 = _y[n - 1] + dydx * (x2 - _x[n - 1]);
212  integral2 = 0.5 * (y2 + _y[n - 1]) * (x2 - _x[n - 1]);
213  if (i1 == n)
214  {
215  const auto y1 = _y[n - 1] + dydx * (x1 - _x[n - 1]);
216  integral1 = 0.5 * (y1 + _y[n - 1]) * (x1 - _x[n - 1]);
217  }
218  else
219  integral1 = 0.0;
220  }
221  else
222  {
223  integral2 = _y[n - 1] * (x2 - _x[n - 1]);
224  if (i1 == n)
225  integral1 = _y[n - 1] * (x1 - _x[n - 1]);
226  else
227  integral1 = 0.0;
228  }
229 
230  integral += integral2 - integral1;
231  }
232  else
233  {
234  T integral1;
235  if (i == i1)
236  {
237  const auto dydx = (_y[i] - _y[i - 1]) / (_x[i] - _x[i - 1]);
238  const auto y1 = _y[i - 1] + dydx * (x1 - _x[i - 1]);
239  integral1 = 0.5 * (y1 + _y[i - 1]) * (x1 - _x[i - 1]);
240  }
241  else
242  integral1 = 0.0;
243 
244  T integral2;
245  if (i == i2)
246  {
247  const auto dydx = (_y[i] - _y[i - 1]) / (_x[i] - _x[i - 1]);
248  const auto y2 = _y[i - 1] + dydx * (x2 - _x[i - 1]);
249  integral2 = 0.5 * (y2 + _y[i - 1]) * (x2 - _x[i - 1]);
250  }
251  else
252  integral2 = 0.5 * (_y[i] + _y[i - 1]) * (_x[i] - _x[i - 1]);
253 
254  integral += integral2 - integral1;
255  }
256 
257  i++;
258  }
259 
260  // apply identity if bounds were switched
261  if (switch_bounds)
262  return -1.0 * integral;
263  else
264  return integral;
265 }
266 
267 template Real LinearInterpolation::integratePartial(const Real &, const Real &) const;
268 template ADReal LinearInterpolation::integratePartial(const ADReal &, const ADReal &) const;
269 
270 Real
272 {
273  return _x[i];
274 }
275 
276 Real
278 {
279  return _y[i];
280 }
281 
282 unsigned int
284 {
285  return _x.size();
286 }
std::vector< Real > _x
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
DualNumber< Real, Real > ChainedReal
Definition: ChainedReal.h:30
Real integrate()
This function returns the integral of the function over the whole domain.
auto raw_value(const Eigen::Map< T > &in)
Definition: EigenADReal.h:100
T sample(const T &x) const
This function will take an independent variable input and will return the dependent variable based on...
std::vector< Real > _y
unsigned int getSampleSize() const
This function returns the size of the array holding the points, i.e.
Real range(int i) const
DualNumber< Real, DNDerivativeType, true > ADReal
Definition: ADRealForward.h:42
Real domain(int i) const
T integratePartial(const T &x1, const T &x2) const
Returns the integral of the function over a specified domain.
T sampleDerivative(const T &x) const
This function will take an independent variable input and will return the derivative of the dependent...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real