https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
19LinearInterpolation::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
27void
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
43template <typename T>
44T
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
83template Real LinearInterpolation::sample<Real>(const Real &) const;
84template ADReal LinearInterpolation::sample<ADReal>(const ADReal &) const;
85template ChainedReal LinearInterpolation::sample<ChainedReal>(const ChainedReal &) const;
86
87template <typename T>
88T
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
118template Real LinearInterpolation::sampleDerivative<Real>(const Real &) const;
119template ADReal LinearInterpolation::sampleDerivative<ADReal>(const ADReal &) const;
120template ChainedReal LinearInterpolation::sampleDerivative<ChainedReal>(const ChainedReal &) const;
121
122Real
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
132template <typename T>
133T
134LinearInterpolation::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
267template Real LinearInterpolation::integratePartial(const Real &, const Real &) const;
268template ADReal LinearInterpolation::integratePartial(const ADReal &, const ADReal &) const;
269
270Real
272{
273 return _x[i];
274}
275
276Real
278{
279 return _y[i];
280}
281
282unsigned int
284{
285 return _x.size();
286}
DualNumber< Real, DNDerivativeType, true > ADReal
DualNumber< Real, Real > ChainedReal
Definition ChainedReal.h:30
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
T sampleDerivative(const T &x) const
This function will take an independent variable input and will return the derivative of the dependent...
T sample(const T &x) const
This function will take an independent variable input and will return the dependent variable based on...
Real integrate()
This function returns the integral of the function over the whole domain.
Real range(int i) const
Real domain(int i) const
std::vector< Real > _x
std::vector< Real > _y
T integratePartial(const T &x1, const T &x2) const
Returns the integral of the function over a specified domain.
unsigned int getSampleSize() const
This function returns the size of the array holding the points, i.e.
auto raw_value(const Eigen::Map< T > &in)