ECLPiecewiseLinearInterpolant.hpp
Go to the documentation of this file.
1/*
2 Copyright 2017 Statoil ASA.
3
4 This file is part of the Open Porous Media Project (OPM).
5
6 OPM is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 OPM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with OPM. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#ifndef OPM_ECLSIMPLE1DINTERPOLANT_HEADER_INCLUDED
21#define OPM_ECLSIMPLE1DINTERPOLANT_HEADER_INCLUDED
22
24
25#include <cassert>
26#include <cmath>
27#include <exception>
28#include <stdexcept>
29#include <type_traits>
30#include <utility>
31#include <vector>
32
33namespace Opm { namespace Interp1D { namespace PiecewisePolynomial {
34
50 template <class Extrapolation, bool IsAscendingRange = true>
51 class Linear
52 {
53 public:
64 explicit Linear(Extrapolation&& extrap)
65 : extrap_(std::forward<Extrapolation>(extrap))
66 , nCols_ (0)
67 {}
68
106 template <class ElmIterator, class ValueTransform>
107 Linear(Extrapolation&& extrap,
108 ElmIterator xBegin,
109 ElmIterator xEnd,
110 std::vector<ElmIterator>& colIt,
111 const ValueTransform& xTransform,
112 const std::vector<ValueTransform>& colTransform);
113
123 LocalInterpPoint classifyPoint(const double x) const
124 {
125 return LocalInterpPoint::identify(this->x_, x, Ascending_P{});
126 }
127
138 double evaluate(const std::size_t col,
139 const LocalInterpPoint& pt) const
140 {
141 if (pt.cat == PointCategory::InRange) {
142 // Common case. Input point is within range of x_. Placed
143 // first to enable early return.
144 return this->interpolate(pt, col);
145 }
146
147 auto yval = [this](const std::size_t i,
148 const std::size_t j) -> double
149 {
150 return this->y(i, j);
151 };
152
153 if (pt.cat == PointCategory::LeftOfRange) {
154 // Extrapolate function ot the left of input range.
155 const auto xmin = this->x_.front();
156
157 return this->extrap_.left(this->x_, xmin + pt.t, col, yval);
158 }
159
160 assert (pt.cat == PointCategory::RightOfRange);
161
162 // Extrapolate function ot the right of input range.
163 const auto xmax = this->x_.back();
164 return this->extrap_.right(this->x_, xmax + pt.t, col, yval);
165 }
166
168 const std::vector<double>& independentVariable() const
169 {
170 return this->x_;
171 }
172
181 std::vector<double> resultVariable(const std::size_t col) const
182 {
183 auto result = std::vector<double>{};
184
185 if (col >= this->nCols_) {
186 throw std::domain_error {
187 "Result Column Identifier Ouf of Bounds"
188 };
189 }
190
191 result.reserve(this->x_.size());
192 for (auto n = this->x_.size(), i = 0*n; i < n; ++i) {
193 result.push_back(this->y(i, col));
194 }
195
196 return result;
197 }
198
199 private:
202 using Ascending_P =
203 std::integral_constant<bool, IsAscendingRange>;
204
207 Extrapolation extrap_;
208
210 std::size_t nCols_;
211
214 std::vector<double> x_;
215
219 std::vector<double> y_;
220
234 double interpolate(const LocalInterpPoint& pt,
235 const std::size_t col) const
236 {
237 assert (pt.cat == PointCategory::InRange);
238 assert (pt.interval + 1 < this->x_.size());
239
240 const auto left = pt.interval + 0;
241 const auto xl = this->x_[left];
242 const auto yl = this->y (left, col);
243
244 const auto right = pt.interval + 1;
245 const auto xr = this->x_[right];
246 const auto yr = this->y (right, col);
247
248 const auto t = pt.t / (xr - xl);
249
250 return t*yr + (1.0 - t)*yl;
251 }
252
260 double y(const std::size_t row,
261 const std::size_t col) const
262 {
263 // Recall: this->y_ stored with column index cycling the most
264 // rapidly.
265
266 assert (col < this->nCols_);
267 assert (row*this->nCols_ + col < this->y_.size());
268
269 return this->y_[row*this->nCols_ + col];
270 }
271 };
272
273 template <class Extrapolation, bool IsAscendingRange>
274 template <class ElmIterator, class ValueTransform>
276 Linear(Extrapolation&& extrap,
277 ElmIterator xBegin,
278 ElmIterator xEnd,
279 std::vector<ElmIterator>& colIt,
280 const ValueTransform& xTransform,
281 const std::vector<ValueTransform>& colTransform)
282 : extrap_(std::forward<Extrapolation>(extrap))
283 , nCols_ (colIt.size())
284 {
285 // There must be at least one dependent variable/result variable.
286 assert (colIt.size() >= 1);
287
288 const auto nRows = std::distance(xBegin, xEnd);
289
290 this->x_.reserve(nRows);
291 this->y_.reserve(nRows * colIt.size());
292
293 auto keyValid = [](const double xi)
294 {
295 // Indep. variable values <= -1.0e20 or >= 1.0e20 signal
296 // "unused" table nodes (rows). These nodes are in the table to
297 // fill out the allocated size if one particular sub-table does
298 // not use all nodes. The magic value 1.0e20 is documented in
299 // the Fileformats Reference Manual.
300 return std::abs(xi) < 1.0e20;
301 };
302
303 while (xBegin != xEnd) {
304 // Extract relevant portion of the table. Preallocated rows
305 // that are not actually part of the result set (i.e., those
306 // that are set to a sentinel value) are discarded.
307 if (keyValid(*xBegin)) {
308 this->x_.push_back(xTransform(*xBegin));
309
310 auto colID = 0*colTransform.size();
311 for (auto ci : colIt) {
312 // Store 'y_' with column index cycling most rapidly.
313 this->y_.push_back(colTransform[colID++](*ci));
314 }
315 }
316
317 // -------------------------------------------------------------
318 // Advance iterators.
319
320 // 1) Independent variable.
321 ++xBegin;
322
323 // 2) Dependent/result/columns.
324 for (auto& ci : colIt) {
325 ++ci;
326 }
327 }
328
329 // Dispose of any excess capacity.
330 if (this->x_.size() < static_cast<decltype(this->x_.size())>(nRows)) {
331 this->x_.shrink_to_fit();
332 this->y_.shrink_to_fit();
333 }
334
335 if (this->x_.size() < 2) {
336 // Table has no interval that supports interpolation. Either
337 // just a single node or no nodes at all. We can't do anything
338 // useful here, so don't pretend that this is okay.
339
340 throw std::invalid_argument {
341 "No Interpolation Intervals of Non-Zero Size"
342 };
343 }
344 }
345
346}}} // Opm::Interp1D::PiecewisePolynomial
347
348#endif // OPM_ECLSIMPLE1DINTERPOLANT_HEADER_INCLUDED
Definition: ECLPiecewiseLinearInterpolant.hpp:52
std::vector< double > resultVariable(const std::size_t col) const
Definition: ECLPiecewiseLinearInterpolant.hpp:181
const std::vector< double > & independentVariable() const
Retrieve abscissas of interpolant's independent variate.
Definition: ECLPiecewiseLinearInterpolant.hpp:168
Linear(Extrapolation &&extrap, ElmIterator xBegin, ElmIterator xEnd, std::vector< ElmIterator > &colIt, const ValueTransform &xTransform, const std::vector< ValueTransform > &colTransform)
Definition: ECLPiecewiseLinearInterpolant.hpp:276
LocalInterpPoint classifyPoint(const double x) const
Definition: ECLPiecewiseLinearInterpolant.hpp:123
Linear(Extrapolation &&extrap)
Definition: ECLPiecewiseLinearInterpolant.hpp:64
double evaluate(const std::size_t col, const LocalInterpPoint &pt) const
Definition: ECLPiecewiseLinearInterpolant.hpp:138
Definition: A.hpp:4
x y t t *t x y t t t x y t t t x *y t *t t x *y t *t t x y t t t x y t t t t(t+t)") define_sfop3(16
x y t t *t x y t t t x y t t t x *y t *t t x *y t *t t x y t t t x y t t t x(y+z)