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  Copyright (C) 2013 by Andreas Lauser
5 
6  This file is part of the Open Porous Media project (OPM).
7 
8  OPM is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 2 of the License, or
11  (at your option) any later version.
12 
13  OPM is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with OPM. If not, see <http://www.gnu.org/licenses/>.
20 */
26 #ifndef OPM_UNIFORM_X_TABULATED_2D_FUNCTION_HPP
27 #define OPM_UNIFORM_X_TABULATED_2D_FUNCTION_HPP
28 
30 #include <opm/common/Exceptions.hpp>
31 #include <opm/common/ErrorMacros.hpp>
34 
35 #include <iostream>
36 #include <vector>
37 #include <limits>
38 #include <tuple>
39 
40 #include <assert.h>
41 
42 namespace Opm {
43 
52 template <class Scalar>
54 {
55  typedef std::tuple</*x=*/Scalar, /*y=*/Scalar, /*value=*/Scalar> SamplePoint;
56 
57 public:
59  { }
60 
64  Scalar xMin() const
65  { return xPos_.front(); }
66 
70  Scalar xMax() const
71  { return xPos_.back(); }
72 
76  Scalar xAt(size_t i) const
77  { return xPos_[i]; }
78 
82  Scalar yAt(size_t i, size_t j) const
83  { return std::get<1>(samples_[i][j]); }
84 
88  Scalar valueAt(size_t i, size_t j) const
89  { return std::get<2>(samples_[i][j]); }
90 
94  size_t numX() const
95  { return xPos_.size(); }
96 
100  Scalar yMin(size_t i) const
101  { return std::get<1>(samples_.at(i).front()); }
102 
106  Scalar yMax(size_t i) const
107  { return std::get<1>(samples_.at(i).back()); }
108 
112  size_t numY(size_t i) const
113  { return samples_.at(i).size(); }
114 
118  Scalar iToX(size_t i) const
119  {
120  assert(0 <= i && i < numX());
121 
122  return xPos_.at(i);
123  }
124 
128  Scalar jToY(size_t i, size_t j) const
129  {
130  assert(0 <= i && i < numX());
131  assert(0 <= j && size_t(j) < samples_[i].size());
132 
133  return std::get<1>(samples_.at(i).at(j));
134  }
135 
144  Scalar xToI(Scalar x, OPM_OPTIM_UNUSED bool extrapolate = false) const
145  {
146  assert(extrapolate || (xMin() <= x && x <= xMax()));
147 
148  // we need at least two sampling points!
149  assert(xPos_.size() >= 2);
150 
151  size_t segmentIdx;
152  if (x <= xPos_[1])
153  segmentIdx = 0;
154  else if (x >= xPos_[xPos_.size() - 2])
155  segmentIdx = xPos_.size() - 2;
156  else {
157  // bisection
158  segmentIdx = 1;
159  size_t upperIdx = xPos_.size() - 2;
160  while (segmentIdx + 1 < upperIdx) {
161  size_t pivotIdx = (segmentIdx + upperIdx) / 2;
162  if (x < xPos_[pivotIdx])
163  upperIdx = pivotIdx;
164  else
165  segmentIdx = pivotIdx;
166  }
167 
168  assert(xPos_[segmentIdx] <= x);
169  assert(x <= xPos_[segmentIdx + 1]);
170  }
171 
172  Scalar x1 = xPos_[segmentIdx];
173  Scalar x2 = xPos_[segmentIdx + 1];
174  return segmentIdx + (x - x1)/(x2 - x1);
175  }
176 
185  Scalar yToJ(size_t i, Scalar y, OPM_OPTIM_UNUSED bool extrapolate = false) const
186  {
187  assert(0 <= i && i < numX());
188  const auto &colSamplePoints = samples_.at(i);
189 
190  assert(extrapolate || (yMin(i) <= y && y <= yMax(i)));
191 
192  Scalar y1;
193  Scalar y2;
194 
195  // interval halving
196  size_t lowerIdx = 0;
197  size_t upperIdx = colSamplePoints.size() - 1;
198  size_t pivotIdx = (lowerIdx + upperIdx) / 2;
199  while (lowerIdx + 1 < upperIdx) {
200  if (y < std::get<1>(colSamplePoints[pivotIdx]))
201  upperIdx = pivotIdx;
202  else
203  lowerIdx = pivotIdx;
204  pivotIdx = (lowerIdx + upperIdx) / 2;
205  }
206 
207  y1 = std::get<1>(colSamplePoints[lowerIdx]);
208  y2 = std::get<1>(colSamplePoints[lowerIdx + 1]);
209 
210  assert(y1 <= y || (extrapolate && lowerIdx == 0));
211  assert(y <= y2 || (extrapolate && lowerIdx == colSamplePoints.size() - 2));
212 
213  return lowerIdx + (y - y1)/(y2 - y1);
214  }
215 
219  bool applies(Scalar x, Scalar y) const
220  {
221  if (x < xMin() || xMax() < x)
222  return false;
223 
224  Scalar i = xToI(x, /*extrapolate=*/false);
225  const auto &col1SamplePoints = samples_.at(unsigned(i));
226  const auto &col2SamplePoints = samples_.at(unsigned(i));
227  Scalar alpha = i - int(i);
228 
229  Scalar yMin =
230  alpha*std::get<1>(col1SamplePoints.front()) +
231  (1 - alpha)*std::get<1>(col2SamplePoints.front());
232 
233  Scalar yMax =
234  alpha*std::get<1>(col1SamplePoints.back()) +
235  (1 - alpha)*std::get<1>(col2SamplePoints.back());
236 
237  return yMin <= y && y <= yMax;
238  }
239 
246  Scalar eval(Scalar x, Scalar y, bool extrapolate = true) const
247  {
248 #ifndef NDEBUG
249  if (!extrapolate && !applies(x,y))
250  {
251  OPM_THROW(NumericalProblem,
252  "Attempt to get tabulated value for ("
253  << x << ", " << y
254  << ") on table");
255  };
256 #endif
257 
258  Scalar alpha = xToI(x, extrapolate);
259  size_t i = std::max<size_t>(0, std::min(numX() - 2, static_cast<size_t>(alpha)));
260  alpha -= i;
261 
262  Scalar beta1 = yToJ(i, y, extrapolate);
263  Scalar beta2 = yToJ(i + 1, y, extrapolate);
264 
265  size_t j1 = std::max<size_t>(0, std::min(numY(i) - 2, static_cast<size_t>(beta1)));
266  size_t j2 = std::max<size_t>(0, std::min(numY(i + 1) - 2, static_cast<size_t>(beta2)));
267 
268  beta1 -= j1;
269  beta2 -= j2;
270 
271  // bi-linear interpolation
272  Scalar s1 = valueAt(i, j1)*(1.0 - beta1) + valueAt(i, j1 + 1)*beta1;
273  Scalar s2 = valueAt(i + 1, j2)*(1.0 - beta2) + valueAt(i + 1, j2 + 1)*beta2;
274  return s1*(1.0 - alpha) + s2*alpha;
275  }
276 
283  template <class Evaluation>
284  Evaluation eval(const Evaluation& x, const Evaluation& y, bool extrapolate=false) const
285  {
286 #ifndef NDEBUG
287  if (!extrapolate && !applies(x.value, y.value)) {
288  OPM_THROW(NumericalProblem,
289  "Attempt to get undefined table value (" << x << ", " << y << ")");
290  };
291 #endif
292 
293  // bi-linear interpolation: first, calculate the x and y indices in the lookup
294  // table ...
295  Evaluation alpha = Evaluation::createConstant(xToI(x.value, extrapolate));
296  unsigned i = static_cast<unsigned>(
297  std::max<int>(0, std::min<int>(static_cast<int>(numX() - 2),
298  static_cast<int>(alpha.value))));
299  alpha -= i;
300 
301  Evaluation beta1;
302  Evaluation beta2;
303 
304  beta1.value = yToJ(i, y.value, extrapolate);
305  beta2.value = yToJ(i + 1, y.value, extrapolate);
306 
307  unsigned j1 = static_cast<unsigned>(
308  std::max(0, std::min<int>(static_cast<int>(numY(i) - 2),
309  static_cast<int>(beta1.value))));
310  unsigned j2 = static_cast<unsigned>(
311  std::max(0, std::min<int>(static_cast<int>(numY(i + 1) - 2),
312  static_cast<int>(beta2.value))));
313 
314  beta1.value -= j1;
315  beta2.value -= j2;
316 
317  // set the correct derivatives of alpha and the betas
318  for (unsigned varIdx = 0; varIdx < x.derivatives.size(); ++varIdx) {
319  alpha.derivatives[varIdx] = x.derivatives[varIdx]/(xAt(i + 1) - xAt(i));
320 
321  beta1.derivatives[varIdx] = y.derivatives[varIdx]/(yAt(i, j1 + 1) - yAt(i, j1));
322  beta2.derivatives[varIdx] = y.derivatives[varIdx]/(yAt(i + 1, j2 + 1) - yAt(i + 1, j2));
323  }
324 
325  Valgrind::CheckDefined(alpha);
326  Valgrind::CheckDefined(beta1);
327  Valgrind::CheckDefined(beta2);
328 
329  // ... then evaluate the two function values for the same y value ...
330  Evaluation s1, s2;
331  s1 = valueAt(i, j1)*(1.0 - beta1) + valueAt(i, j1 + 1)*beta1;
332  s2 = valueAt(i + 1, j2)*(1.0 - beta2) + valueAt(i + 1, j2 + 1)*beta2;
333 
336 
337  // ... and finally combine them using x the position
338  Evaluation result;
339  result = s1*(1.0 - alpha) + s2*alpha;
340  Valgrind::CheckDefined(result);
341 
342  return result;
343  }
344 
350  size_t appendXPos(Scalar nextX)
351  {
352  if (xPos_.empty() || xPos_.back() < nextX) {
353  xPos_.push_back(nextX);
354  samples_.resize(xPos_.size());
355  return xPos_.size() - 1;
356  }
357  else if (xPos_.front() > nextX) {
358  // this is slow, but so what?
359  xPos_.insert(xPos_.begin(), nextX);
360  samples_.insert(samples_.begin(), std::vector<SamplePoint>());
361  return 0;
362  }
363  OPM_THROW(std::invalid_argument,
364  "Sampling points should be specified either monotonically "
365  "ascending or descending.");
366  }
367 
373  size_t appendSamplePoint(size_t i, Scalar y, Scalar value)
374  {
375  assert(0 <= i && i < numX());
376 
377  Scalar x = iToX(i);
378  if (samples_[i].empty() || std::get<1>(samples_[i].back()) < y) {
379  samples_[i].push_back(SamplePoint(x, y, value));
380  return samples_[i].size() - 1;
381  }
382  else if (std::get<1>(samples_[i].front()) > y) {
383  // slow, but we still don't care...
384  samples_[i].insert(samples_[i].begin(), SamplePoint(x, y, value));
385  return 0;
386  }
387 
388  OPM_THROW(std::invalid_argument,
389  "Sampling points should be specified either monotonically "
390  "ascending or descending.");
391  }
392 
399  void print(std::ostream &os = std::cout) const
400  {
401  Scalar x0 = xMin();
402  Scalar x1 = xMax();
403  int m = numX();
404 
405  Scalar y0 = 1e100;
406  Scalar y1 = -1e100;
407  int n = 0;
408  for (int i = 0; i < m; ++ i) {
409  y0 = std::min(y0, yMin(i));
410  y1 = std::max(y1, yMax(i));
411  n = std::max(n, numY(i));
412  }
413 
414  m *= 3;
415  n *= 3;
416  for (int i = 0; i <= m; ++i) {
417  Scalar x = x0 + (x1 - x0)*i/m;
418  for (int j = 0; j <= n; ++j) {
419  Scalar y = y0 + (y1 - y0)*j/n;
420  os << x << " " << y << " " << eval(x, y) << "\n";
421  }
422  os << "\n";
423  }
424  }
425 
426 private:
427  // the vector which contains the values of the sample points
428  // f(x_i, y_j). don't use this directly, use getSamplePoint(i,j)
429  // instead!
430  std::vector<std::vector<SamplePoint> > samples_;
431 
432  // the position of each vertical line on the x-axis
433  std::vector<Scalar> xPos_;
434 };
435 } // namespace Opm
436 
437 #endif
Scalar xMin() const
Returns the minimum of the X coordinate of the sampling points.
Definition: UniformXTabulated2DFunction.hpp:64
UniformXTabulated2DFunction()
Definition: UniformXTabulated2DFunction.hpp:58
bool CheckDefined(const T &value OPM_UNUSED)
Make valgrind complain if any of the memory occupied by an object is undefined.
Definition: Valgrind.hpp:74
size_t numX() const
Returns the number of sampling points in X direction.
Definition: UniformXTabulated2DFunction.hpp:94
Definition: Air_Mesitylene.hpp:31
Scalar valueAt(size_t i, size_t j) const
Returns the value of a sampling point.
Definition: UniformXTabulated2DFunction.hpp:88
size_t appendSamplePoint(size_t i, Scalar y, Scalar value)
Append a sample point.
Definition: UniformXTabulated2DFunction.hpp:373
Some templates to wrap the valgrind client request macros.
Evaluation< Scalar, VarSetTag, numVars > max(const Evaluation< Scalar, VarSetTag, numVars > &x1, const Evaluation< Scalar, VarSetTag, numVars > &x2)
Definition: Math.hpp:114
size_t numY(size_t i) const
Returns the number of sampling points in Y direction a given column.
Definition: UniformXTabulated2DFunction.hpp:112
size_t appendXPos(Scalar nextX)
Set the x-position of a vertical line.
Definition: UniformXTabulated2DFunction.hpp:350
Evaluation eval(const Evaluation &x, const Evaluation &y, bool extrapolate=false) const
Evaluate the function at a given (x,y) position.
Definition: UniformXTabulated2DFunction.hpp:284
bool applies(Scalar x, Scalar y) const
Returns true iff a coordinate lies in the tabulated range.
Definition: UniformXTabulated2DFunction.hpp:219
Scalar iToX(size_t i) const
Return the position on the x-axis of the i-th interval.
Definition: UniformXTabulated2DFunction.hpp:118
Scalar yMax(size_t i) const
Returns the maximum of the Y coordinate of the sampling points for a given column.
Definition: UniformXTabulated2DFunction.hpp:106
Scalar xAt(size_t i) const
Returns the value of the X coordinate of the sampling points.
Definition: UniformXTabulated2DFunction.hpp:76
Evaluation< Scalar, VarSetTag, numVars > min(const Evaluation< Scalar, VarSetTag, numVars > &x1, const Evaluation< Scalar, VarSetTag, numVars > &x2)
Definition: Math.hpp:61
#define OPM_OPTIM_UNUSED
Definition: Unused.hpp:42
Scalar eval(Scalar x, Scalar y, bool extrapolate=true) const
Evaluate the function at a given (x,y) position.
Definition: UniformXTabulated2DFunction.hpp:246
Scalar xToI(Scalar x, OPM_OPTIM_UNUSED bool extrapolate=false) const
Return the interval index of a given position on the x-axis.
Definition: UniformXTabulated2DFunction.hpp:144
void print(std::ostream &os=std::cout) const
Print the table for debugging purposes.
Definition: UniformXTabulated2DFunction.hpp:399
Implements a scalar function that depends on two variables and which is sampled uniformly in the X di...
Definition: UniformXTabulated2DFunction.hpp:53
Provides the OPM_UNUSED macro.
Scalar yMin(size_t i) const
Returns the minimum of the Y coordinate of the sampling points for a given column.
Definition: UniformXTabulated2DFunction.hpp:100
Scalar jToY(size_t i, size_t j) const
Return the position on the y-axis of the j-th interval.
Definition: UniformXTabulated2DFunction.hpp:128
Scalar xMax() const
Returns the maximum of the X coordinate of the sampling points.
Definition: UniformXTabulated2DFunction.hpp:70
Scalar yAt(size_t i, size_t j) const
Returns the value of the Y coordinate of a sampling point.
Definition: UniformXTabulated2DFunction.hpp:82
Scalar yToJ(size_t i, Scalar y, OPM_OPTIM_UNUSED bool extrapolate=false) const
Return the interval index of a given position on the y-axis.
Definition: UniformXTabulated2DFunction.hpp:185
A traits class which provides basic mathematical functions for arbitrary scalar floating point values...