UniformXTabulated2DFunction.hpp
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
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 2 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 Consult the COPYING file in the top-level source directory of this
20 module for the precise wording of the license and the list of
21 copyright holders.
22*/
28#ifndef OPM_UNIFORM_X_TABULATED_2D_FUNCTION_HPP
29#define OPM_UNIFORM_X_TABULATED_2D_FUNCTION_HPP
30
34
35#include <iostream>
36#include <vector>
37#include <limits>
38#include <tuple>
39#include <sstream>
40#include <cassert>
41#include <cmath>
42
43namespace Opm {
52template <class Scalar>
54{
55public:
56 typedef std::tuple</*x=*/Scalar, /*y=*/Scalar, /*value=*/Scalar> SamplePoint;
57
72 };
73
75 : interpolationGuide_(interpolationGuide)
76 { }
77
78 UniformXTabulated2DFunction(const std::vector<Scalar>& xPos,
79 const std::vector<Scalar>& yPos,
80 const std::vector<std::vector<SamplePoint>>& samples,
82 : samples_(samples)
83 , xPos_(xPos)
84 , yPos_(yPos)
85 , interpolationGuide_(interpolationGuide)
86 { }
87
91 Scalar xMin() const
92 { return xPos_.front(); }
93
97 Scalar xMax() const
98 { return xPos_.back(); }
99
103 Scalar xAt(size_t i) const
104 { return xPos_[i]; }
105
109 Scalar yAt(size_t i, size_t j) const
110 { return std::get<1>(samples_[i][j]); }
111
115 Scalar valueAt(size_t i, size_t j) const
116 { return std::get<2>(samples_[i][j]); }
117
121 size_t numX() const
122 { return xPos_.size(); }
123
127 Scalar yMin(unsigned i) const
128 { return std::get<1>(samples_.at(i).front()); }
129
133 Scalar yMax(unsigned i) const
134 { return std::get<1>(samples_.at(i).back()); }
135
139 size_t numY(unsigned i) const
140 { return samples_.at(i).size(); }
141
145 Scalar iToX(unsigned i) const
146 {
147 assert(i < numX());
148
149 return xPos_.at(i);
150 }
151
152 const std::vector<std::vector<SamplePoint>>& samples() const
153 {
154 return samples_;
155 }
156
157 const std::vector<Scalar>& xPos() const
158 {
159 return xPos_;
160 }
161
162 const std::vector<Scalar>& yPos() const
163 {
164 return yPos_;
165 }
166
168 {
169 return interpolationGuide_;
170 }
171
175 Scalar jToY(unsigned i, unsigned j) const
176 {
177 assert(i < numX());
178 assert(size_t(j) < samples_[i].size());
179
180 return std::get<1>(samples_.at(i).at(j));
181 }
182
186 template <class Evaluation>
187 unsigned xSegmentIndex(const Evaluation& x,
188 [[maybe_unused]] bool extrapolate = false) const
189 {
190 assert(extrapolate || (xMin() <= x && x <= xMax()));
191
192 // we need at least two sampling points!
193 assert(xPos_.size() >= 2);
194
195 if (x <= xPos_[1])
196 return 0;
197 else if (x >= xPos_[xPos_.size() - 2])
198 return xPos_.size() - 2;
199 else {
200 assert(xPos_.size() >= 3);
201
202 // bisection
203 unsigned lowerIdx = 1;
204 unsigned upperIdx = xPos_.size() - 2;
205 while (lowerIdx + 1 < upperIdx) {
206 unsigned pivotIdx = (lowerIdx + upperIdx) / 2;
207 if (x < xPos_[pivotIdx])
208 upperIdx = pivotIdx;
209 else
210 lowerIdx = pivotIdx;
211 }
212
213 return lowerIdx;
214 }
215 }
216
223 template <class Evaluation>
224 Evaluation xToAlpha(const Evaluation& x, unsigned segmentIdx) const
225 {
226 Scalar x1 = xPos_[segmentIdx];
227 Scalar x2 = xPos_[segmentIdx + 1];
228 return (x - x1)/(x2 - x1);
229 }
230
234 template <class Evaluation>
235 unsigned ySegmentIndex(const Evaluation& y, unsigned xSampleIdx,
236 [[maybe_unused]] bool extrapolate = false) const
237 {
238 assert(xSampleIdx < numX());
239 const auto& colSamplePoints = samples_.at(xSampleIdx);
240
241 assert(colSamplePoints.size() >= 2);
242 assert(extrapolate || (yMin(xSampleIdx) <= y && y <= yMax(xSampleIdx)));
243
244 if (y <= std::get<1>(colSamplePoints[1]))
245 return 0;
246 else if (y >= std::get<1>(colSamplePoints[colSamplePoints.size() - 2]))
247 return colSamplePoints.size() - 2;
248 else {
249 assert(colSamplePoints.size() >= 3);
250
251 // bisection
252 unsigned lowerIdx = 1;
253 unsigned upperIdx = colSamplePoints.size() - 2;
254 while (lowerIdx + 1 < upperIdx) {
255 unsigned pivotIdx = (lowerIdx + upperIdx) / 2;
256 if (y < std::get<1>(colSamplePoints[pivotIdx]))
257 upperIdx = pivotIdx;
258 else
259 lowerIdx = pivotIdx;
260 }
261
262 return lowerIdx;
263 }
264 }
265
272 template <class Evaluation>
273 Evaluation yToBeta(const Evaluation& y, unsigned xSampleIdx, unsigned ySegmentIdx) const
274 {
275 assert(xSampleIdx < numX());
276 assert(ySegmentIdx < numY(xSampleIdx) - 1);
277
278 const auto& colSamplePoints = samples_.at(xSampleIdx);
279
280 Scalar y1 = std::get<1>(colSamplePoints[ySegmentIdx]);
281 Scalar y2 = std::get<1>(colSamplePoints[ySegmentIdx + 1]);
282
283 return (y - y1)/(y2 - y1);
284 }
285
289 template <class Evaluation>
290 bool applies(const Evaluation& x, const Evaluation& y) const
291 {
292 if (x < xMin() || xMax() < x)
293 return false;
294
295 unsigned i = xSegmentIndex(x, /*extrapolate=*/false);
296 Scalar alpha = xToAlpha(decay<Scalar>(x), i);
297
298 const auto& col1SamplePoints = samples_.at(i);
299 const auto& col2SamplePoints = samples_.at(i + 1);
300
301 Scalar minY =
302 alpha*std::get<1>(col1SamplePoints.front()) +
303 (1 - alpha)*std::get<1>(col2SamplePoints.front());
304
305 Scalar maxY =
306 alpha*std::get<1>(col1SamplePoints.back()) +
307 (1 - alpha)*std::get<1>(col2SamplePoints.back());
308
309 return minY <= y && y <= maxY;
310 }
317 template <class Evaluation>
318 Evaluation eval(const Evaluation& x, const Evaluation& y, bool extrapolate=false) const
319 {
320#ifndef NDEBUG
321 if (!extrapolate && !applies(x, y)) {
322 std::ostringstream oss;
323 oss << "Attempt to get undefined table value (" << x << ", " << y << ")";
324 throw NumericalIssue(oss.str());
325 };
326#endif
327
328 // bi-linear interpolation: first, calculate the x and y indices in the lookup
329 // table ...
330 unsigned i = xSegmentIndex(x, extrapolate);
331 const Evaluation& alpha = xToAlpha(x, i);
332 // The 'shift' is used to shift the points used to interpolate within
333 // the (i) and (i+1) sets of sample points, so that when approaching
334 // the boundary of the domain given by the samples, one gets the same
335 // value as one would get by interpolating along the boundary curve
336 // itself.
337 Evaluation shift = 0.0;
338 if (interpolationGuide_ == InterpolationPolicy::Vertical) {
339 // Shift is zero, no need to reset it.
340 } else {
341 // find upper and lower y value
342 if (interpolationGuide_ == InterpolationPolicy::LeftExtreme) {
343 // The domain is above the boundary curve, up to y = infinity.
344 // The shift is therefore the same for all values of y.
345 shift = yPos_[i+1] - yPos_[i];
346 } else {
347 assert(interpolationGuide_ == InterpolationPolicy::RightExtreme);
348 // The domain is below the boundary curve, down to y = 0.
349 // The shift is therefore no longer the the same for all
350 // values of y, since at y = 0 the shift must be zero.
351 // The shift is computed by linear interpolation between
352 // the maximal value at the domain boundary curve, and zero.
353 shift = yPos_[i+1] - yPos_[i];
354 auto yEnd = yPos_[i]*(1.0 - alpha) + yPos_[i+1]*alpha;
355 if (yEnd > 0.) {
356 shift = shift * y / yEnd;
357 } else {
358 shift = 0.;
359 }
360 }
361 }
362 auto yLower = y - alpha*shift;
363 auto yUpper = y + (1-alpha)*shift;
364
365 unsigned j1 = ySegmentIndex(yLower, i, extrapolate);
366 unsigned j2 = ySegmentIndex(yUpper, i + 1, extrapolate);
367 const Evaluation& beta1 = yToBeta(yLower, i, j1);
368 const Evaluation& beta2 = yToBeta(yUpper, i + 1, j2);
369
370 // evaluate the two function values for the same y value ...
371 const Evaluation& s1 = valueAt(i, j1)*(1.0 - beta1) + valueAt(i, j1 + 1)*beta1;
372 const Evaluation& s2 = valueAt(i + 1, j2)*(1.0 - beta2) + valueAt(i + 1, j2 + 1)*beta2;
373
376
377 // ... and combine them using the x position
378 const Evaluation& result = s1*(1.0 - alpha) + s2*alpha;
380
381 return result;
382 }
383
389 size_t appendXPos(Scalar nextX)
390 {
391 if (xPos_.empty() || xPos_.back() < nextX) {
392 xPos_.push_back(nextX);
393 yPos_.push_back(-1e100);
394 samples_.push_back({});
395 return xPos_.size() - 1;
396 }
397 else if (xPos_.front() > nextX) {
398 // this is slow, but so what?
399 xPos_.insert(xPos_.begin(), nextX);
400 yPos_.insert(yPos_.begin(), -1e100);
401 samples_.insert(samples_.begin(), std::vector<SamplePoint>());
402 return 0;
403 }
404 throw std::invalid_argument("Sampling points should be specified either monotonically "
405 "ascending or descending.");
406 }
407
413 size_t appendSamplePoint(size_t i, Scalar y, Scalar value)
414 {
415 assert(i < numX());
416 Scalar x = iToX(i);
417 if (samples_[i].empty() || std::get<1>(samples_[i].back()) < y) {
418 samples_[i].push_back(SamplePoint(x, y, value));
419 if (interpolationGuide_ == InterpolationPolicy::RightExtreme) {
420 yPos_[i] = y;
421 }
422 return samples_[i].size() - 1;
423 }
424 else if (std::get<1>(samples_[i].front()) > y) {
425 // slow, but we still don't care...
426 samples_[i].insert(samples_[i].begin(), SamplePoint(x, y, value));
427 if (interpolationGuide_ == InterpolationPolicy::LeftExtreme) {
428 yPos_[i] = y;
429 }
430 return 0;
431 }
432
433 throw std::invalid_argument("Sampling points must be specified in either monotonically "
434 "ascending or descending order.");
435 }
436
443 void print(std::ostream& os = std::cout) const
444 {
445 Scalar x0 = xMin();
446 Scalar x1 = xMax();
447 int m = numX();
448
449 Scalar y0 = 1e30;
450 Scalar y1 = -1e30;
451 size_t n = 0;
452 for (int i = 0; i < m; ++ i) {
453 y0 = std::min(y0, yMin(i));
454 y1 = std::max(y1, yMax(i));
455 n = std::max(n, numY(i));
456 }
457
458 m *= 3;
459 n *= 3;
460 for (int i = 0; i <= m; ++i) {
461 Scalar x = x0 + (x1 - x0)*i/m;
462 for (size_t j = 0; j <= n; ++j) {
463 Scalar y = y0 + (y1 - y0)*j/n;
464 os << x << " " << y << " " << eval(x, y) << "\n";
465 }
466 os << "\n";
467 }
468 }
469
471 return this->xPos() == data.xPos() &&
472 this->yPos() == data.yPos() &&
473 this->samples() == data.samples() &&
474 this->interpolationGuide() == data.interpolationGuide();
475 }
476
477private:
478 // the vector which contains the values of the sample points
479 // f(x_i, y_j). don't use this directly, use getSamplePoint(i,j)
480 // instead!
481 std::vector<std::vector<SamplePoint> > samples_;
482
483 // the position of each vertical line on the x-axis
484 std::vector<Scalar> xPos_;
485 // the position on the y-axis of the guide point
486 std::vector<Scalar> yPos_;
487 InterpolationPolicy interpolationGuide_;
488};
489} // namespace Opm
490
491#endif
Provides the opm-material specific exception classes.
A traits class which provides basic mathematical functions for arbitrary scalar floating point values...
Some templates to wrap the valgrind client request macros.
Definition: Exceptions.hpp:46
Implements a scalar function that depends on two variables and which is sampled uniformly in the X di...
Definition: UniformXTabulated2DFunction.hpp:54
Scalar xMax() const
Returns the maximum of the X coordinate of the sampling points.
Definition: UniformXTabulated2DFunction.hpp:97
size_t appendSamplePoint(size_t i, Scalar y, Scalar value)
Append a sample point.
Definition: UniformXTabulated2DFunction.hpp:413
Evaluation xToAlpha(const Evaluation &x, unsigned segmentIdx) const
Return the relative position of an x value in an intervall.
Definition: UniformXTabulated2DFunction.hpp:224
std::tuple< Scalar, Scalar, Scalar > SamplePoint
Definition: UniformXTabulated2DFunction.hpp:56
Evaluation yToBeta(const Evaluation &y, unsigned xSampleIdx, unsigned ySegmentIdx) const
Return the relative position of an y value in an interval.
Definition: UniformXTabulated2DFunction.hpp:273
Scalar xAt(size_t i) const
Returns the value of the X coordinate of the sampling points.
Definition: UniformXTabulated2DFunction.hpp:103
void print(std::ostream &os=std::cout) const
Print the table for debugging purposes.
Definition: UniformXTabulated2DFunction.hpp:443
InterpolationPolicy interpolationGuide() const
Definition: UniformXTabulated2DFunction.hpp:167
Scalar jToY(unsigned i, unsigned j) const
Return the position on the y-axis of the j-th interval.
Definition: UniformXTabulated2DFunction.hpp:175
InterpolationPolicy
Indicates how interpolation will be performed.
Definition: UniformXTabulated2DFunction.hpp:68
@ LeftExtreme
Definition: UniformXTabulated2DFunction.hpp:69
@ Vertical
Definition: UniformXTabulated2DFunction.hpp:71
@ RightExtreme
Definition: UniformXTabulated2DFunction.hpp:70
Scalar valueAt(size_t i, size_t j) const
Returns the value of a sampling point.
Definition: UniformXTabulated2DFunction.hpp:115
UniformXTabulated2DFunction(const InterpolationPolicy interpolationGuide=Vertical)
Definition: UniformXTabulated2DFunction.hpp:74
unsigned ySegmentIndex(const Evaluation &y, unsigned xSampleIdx, bool extrapolate=false) const
Return the interval index of a given position on the y-axis.
Definition: UniformXTabulated2DFunction.hpp:235
bool operator==(const UniformXTabulated2DFunction< Scalar > &data) const
Definition: UniformXTabulated2DFunction.hpp:470
Scalar xMin() const
Returns the minimum of the X coordinate of the sampling points.
Definition: UniformXTabulated2DFunction.hpp:91
Scalar yMin(unsigned i) const
Returns the minimum of the Y coordinate of the sampling points for a given column.
Definition: UniformXTabulated2DFunction.hpp:127
unsigned xSegmentIndex(const Evaluation &x, bool extrapolate=false) const
Return the interval index of a given position on the x-axis.
Definition: UniformXTabulated2DFunction.hpp:187
size_t numY(unsigned i) const
Returns the number of sampling points in Y direction a given column.
Definition: UniformXTabulated2DFunction.hpp:139
size_t numX() const
Returns the number of sampling points in X direction.
Definition: UniformXTabulated2DFunction.hpp:121
const std::vector< std::vector< SamplePoint > > & samples() const
Definition: UniformXTabulated2DFunction.hpp:152
Scalar yMax(unsigned i) const
Returns the maximum of the Y coordinate of the sampling points for a given column.
Definition: UniformXTabulated2DFunction.hpp:133
Scalar iToX(unsigned i) const
Return the position on the x-axis of the i-th interval.
Definition: UniformXTabulated2DFunction.hpp:145
UniformXTabulated2DFunction(const std::vector< Scalar > &xPos, const std::vector< Scalar > &yPos, const std::vector< std::vector< SamplePoint > > &samples, InterpolationPolicy interpolationGuide)
Definition: UniformXTabulated2DFunction.hpp:78
Scalar yAt(size_t i, size_t j) const
Returns the value of the Y coordinate of a sampling point.
Definition: UniformXTabulated2DFunction.hpp:109
size_t appendXPos(Scalar nextX)
Set the x-position of a vertical line.
Definition: UniformXTabulated2DFunction.hpp:389
const std::vector< Scalar > & yPos() const
Definition: UniformXTabulated2DFunction.hpp:162
Evaluation eval(const Evaluation &x, const Evaluation &y, bool extrapolate=false) const
Evaluate the function at a given (x,y) position.
Definition: UniformXTabulated2DFunction.hpp:318
const std::vector< Scalar > & xPos() const
Definition: UniformXTabulated2DFunction.hpp:157
bool applies(const Evaluation &x, const Evaluation &y) const
Returns true iff a coordinate lies in the tabulated range.
Definition: UniformXTabulated2DFunction.hpp:290
bool CheckDefined(const T &value)
Make valgrind complain if any of the memory occupied by an object is undefined.
Definition: Valgrind.hpp:74
Definition: Air_Mesitylene.hpp:34
ReturnEval_< Evaluation1, Evaluation2 >::type min(const Evaluation1 &arg1, const Evaluation2 &arg2)
Definition: MathToolbox.hpp:346
ReturnEval_< Evaluation1, Evaluation2 >::type max(const Evaluation1 &arg1, const Evaluation2 &arg2)
Definition: MathToolbox.hpp:341