Tabulated1DFunction.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) 2015 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 */
25 #ifndef OPM_TABULATED_1D_FUNCTION_HPP
26 #define OPM_TABULATED_1D_FUNCTION_HPP
27 
28 #include <opm/common/ErrorMacros.hpp>
29 #include <opm/common/Exceptions.hpp>
31 
32 #include <algorithm>
33 #include <cassert>
34 #include <iostream>
35 #include <tuple>
36 #include <vector>
37 
38 namespace Opm {
43 template <class Scalar>
45 {
46 public:
53  {}
54 
62  template <class ScalarArrayX, class ScalarArrayY>
63  Tabulated1DFunction(size_t nSamples,
64  const ScalarArrayX &x,
65  const ScalarArrayY &y,
66  bool sortInputs = false)
67  { this->setXYArrays(nSamples, x, y, sortInputs); }
68 
77  template <class ScalarContainer>
78  Tabulated1DFunction(const ScalarContainer &x,
79  const ScalarContainer &y,
80  bool sortInputs = false)
81  { this->setXYContainers(x, y, sortInputs); }
82 
89  template <class PointContainer>
90  Tabulated1DFunction(const PointContainer &points,
91  bool sortInputs = false)
92  { this->setContainerOfTuples(points, sortInputs); }
93 
99  template <class ScalarArrayX, class ScalarArrayY>
100  void setXYArrays(size_t nSamples,
101  const ScalarArrayX &x,
102  const ScalarArrayY &y,
103  bool sortInputs = false)
104  {
105  assert(nSamples > 1);
106 
107  resizeArrays_(nSamples);
108  for (size_t i = 0; i < nSamples; ++i) {
109  xValues_[i] = x[i];
110  yValues_[i] = y[i];
111  }
112 
113  if (sortInputs)
114  sortInput_();
115  else if (xValues_[0] > xValues_[numSamples() - 1])
116  reverseSamplingPoints_();
117  }
118 
124  template <class ScalarContainerX, class ScalarContainerY>
125  void setXYContainers(const ScalarContainerX &x,
126  const ScalarContainerY &y,
127  bool sortInputs = false)
128  {
129  assert(x.size() == y.size());
130  assert(x.size() > 1);
131 
132  resizeArrays_(x.size());
133  std::copy(x.begin(), x.end(), xValues_.begin());
134  std::copy(y.begin(), y.end(), yValues_.begin());
135 
136  if (sortInputs)
137  sortInput_();
138  else if (xValues_[0] > xValues_[numSamples() - 1])
139  reverseSamplingPoints_();
140  }
141 
145  template <class PointArray>
146  void setArrayOfPoints(size_t nSamples,
147  const PointArray &points,
148  bool sortInputs = false)
149  {
150  // a linear function with less than two sampling points? what an incredible
151  // bad idea!
152  assert(nSamples > 1);
153 
154  resizeArrays_(nSamples);
155  for (size_t i = 0; i < nSamples; ++i) {
156  xValues_[i] = points[i][0];
157  yValues_[i] = points[i][1];
158  }
159 
160  if (sortInputs)
161  sortInput_();
162  else if (xValues_[0] > xValues_[numSamples() - 1])
163  reverseSamplingPoints_();
164  }
165 
180  template <class XYContainer>
181  void setContainerOfTuples(const XYContainer &points,
182  bool sortInputs = false)
183  {
184  // a linear function with less than two sampling points? what an incredible
185  // bad idea!
186  assert(points.size() > 1);
187 
188  resizeArrays_(points.size());
189  typename XYContainer::const_iterator it = points.begin();
190  typename XYContainer::const_iterator endIt = points.end();
191  for (int i = 0; it != endIt; ++i, ++it) {
192  xValues_[i] = std::get<0>(*it);
193  yValues_[i] = std::get<1>(*it);
194  }
195 
196  if (sortInputs)
197  sortInput_();
198  else if (xValues_[0] > xValues_[numSamples() - 1])
199  reverseSamplingPoints_();
200  }
201 
205  size_t numSamples() const
206  { return xValues_.size(); }
207 
211  Scalar xMin() const
212  { return xValues_[0]; }
213 
217  Scalar xMax() const
218  { return xValues_[numSamples() - 1]; }
219 
223  Scalar xAt(size_t i) const
224  { return xValues_[i]; }
225 
229  Scalar valueAt(size_t i) const
230  { return yValues_[i]; }
231 
235  bool applies(Scalar x) const
236  { return xValues_[0] <= x && x <= xValues_[numSamples() - 1]; }
237 
247  Scalar eval(Scalar x, bool extrapolate=false) const
248  {
249  size_t segIdx;
250  if (extrapolate && x < xValues_.front())
251  segIdx = 0;
252  else if (extrapolate && x > xValues_.back())
253  segIdx = numSamples() - 2;
254  else {
255  assert(xValues_.front() <= x && x <= xValues_.back());
256  segIdx = findSegmentIndex_(x);
257  }
258 
259  Scalar x0 = xValues_[segIdx];
260  Scalar x1 = xValues_[segIdx + 1];
261 
262  Scalar y0 = yValues_[segIdx];
263  Scalar y1 = yValues_[segIdx + 1];
264 
265  return y0 + (y1 - y0)*(x - x0)/(x1 - x0);
266  }
267 
277  template <class Evaluation>
278  Evaluation eval(const Evaluation& x, bool extrapolate=false) const
279  {
280  size_t segIdx;
281  if (extrapolate && x.value < xValues_.front())
282  segIdx = 0;
283  else if (extrapolate && x.value > xValues_.back())
284  segIdx = numSamples() - 2;
285  else {
286  assert(xValues_.front() <= x.value && x.value <= xValues_.back());
287  segIdx = findSegmentIndex_(x.value);
288  }
289 
290  Scalar x0 = xValues_[segIdx];
291  Scalar x1 = xValues_[segIdx + 1];
292 
293  Scalar y0 = yValues_[segIdx];
294  Scalar y1 = yValues_[segIdx + 1];
295 
296  Scalar m = (y1 - y0)/(x1 - x0);
297 
298  Evaluation result;
299  result.value = y0 + m*(x.value - x0);
300  for (unsigned varIdx = 0; varIdx < result.derivatives.size(); ++varIdx)
301  result.derivatives[varIdx] = m*x.derivatives[varIdx];
302 
303  return result;
304  }
305 
317  Scalar evalDerivative(Scalar x, bool /*extrapolate*/=false) const
318  {
319  int segIdx = findSegmentIndex_(x);
320 
321  return evalDerivative(x, segIdx);
322  }
323 
338  Scalar evalSecondDerivative(OPM_OPTIM_UNUSED Scalar x, OPM_OPTIM_UNUSED bool extrapolate=false) const
339  {
340  assert(extrapolate || applies(x));
341  return 0.0;
342  }
343 
358  Scalar evalThirdDerivative(OPM_OPTIM_UNUSED Scalar x, OPM_OPTIM_UNUSED bool extrapolate=false) const
359  {
360  assert(extrapolate || applies(x));
361  return 0.0;
362  }
363 
372  int monotonic(Scalar x0, Scalar x1, bool extrapolate OPM_OPTIM_UNUSED = false) const
373  {
374  assert(x0 != x1);
375 
376  // make sure that x0 is smaller than x1
377  if (x0 > x1)
378  std::swap(x0, x1);
379 
380  assert(x0 < x1);
381 
382  int r = 3;
383  if (x0 < xMin()) {
384  assert(extrapolate);
385 
386  x0 = xMin();
387  };
388 
389  size_t i = findSegmentIndex_(x0);
390  if (xValues_[i + 1] >= x1) {
391  // interval is fully contained within a single function
392  // segment
393  updateMonotonicity_(i, r);
394  return r;
395  }
396 
397  // the first segment overlaps with the specified interval
398  // partially
399  updateMonotonicity_(i, r);
400  ++ i;
401 
402  // make sure that the segments which are completly in the
403  // interval [x0, x1] all exhibit the same monotonicity.
404  size_t iEnd = findSegmentIndex_(x1);
405  for (; i < iEnd - 1; ++i) {
406  updateMonotonicity_(i, r);
407  if (!r)
408  return 0;
409  }
410 
411  // if the user asked for a part of the function which is
412  // extrapolated, we need to check the slope at the function's
413  // endpoint
414  if (x1 > xMax()) {
415  assert(extrapolate);
416 
417  Scalar m = evalDerivative_(xMax(), /*segmentIdx=*/numSamples() - 2);
418  if (m < 0)
419  return (r < 0 || r==3)?-1:0;
420  else if (m > 0)
421  return (r > 0 || r==3)?1:0;
422 
423  return r;
424  }
425 
426  // check for the last segment
427  updateMonotonicity_(iEnd, r);
428 
429  return r;
430  }
431 
436  int monotonic() const
437  { return monotonic(xMin(), xMax()); }
438 
455  void printCSV(Scalar xi0, Scalar xi1, int k, std::ostream &os = std::cout) const
456  {
457  Scalar x0 = std::min(xi0, xi1);
458  Scalar x1 = std::max(xi0, xi1);
459  const int n = numSamples() - 1;
460  for (int i = 0; i <= k; ++i) {
461  double x = i*(x1 - x0)/k + x0;
462  double y;
463  double dy_dx;
464  if (!applies(x)) {
465  if (x < xValues_[0]) {
466  dy_dx = evalDerivative(xValues_[0]);
467  y = (x - xValues_[0])*dy_dx + yValues_[0];
468  }
469  else if (x > xValues_[n]) {
470  dy_dx = evalDerivative(xValues_[n]);
471  y = (x - xValues_[n])*dy_dx + yValues_[n];
472  }
473  else {
474  OPM_THROW(std::runtime_error,
475  "The sampling points given to a function must be sorted by their x value!");
476  }
477  }
478  else {
479  y = eval(x);
480  dy_dx = evalDerivative(x);
481  }
482 
483  os << x << " " << y << " " << dy_dx << "\n";
484  }
485  }
486 
487 private:
488  size_t findSegmentIndex_(Scalar x) const
489  {
490  // we need at least two sampling points!
491  assert(xValues_.size() >= 2);
492 
493  if (x <= xValues_[1])
494  return 0;
495  else if (x >= xValues_[xValues_.size() - 2])
496  return xValues_.size() - 2;
497  else {
498  // bisection
499  size_t segmentIdx = 1;
500  size_t upperIdx = xValues_.size() - 2;
501  while (segmentIdx + 1 < upperIdx) {
502  size_t pivotIdx = (segmentIdx + upperIdx) / 2;
503  if (x < xValues_[pivotIdx])
504  upperIdx = pivotIdx;
505  else
506  segmentIdx = pivotIdx;
507  }
508 
509  assert(xValues_[segmentIdx] <= x);
510  assert(x <= xValues_[segmentIdx + 1]);
511  return segmentIdx;
512  }
513  }
514 
515  Scalar evalDerivative_(OPM_UNUSED Scalar x, int segIdx) const
516  {
517  Scalar x0 = xValues_[segIdx];
518  Scalar x1 = xValues_[segIdx + 1];
519 
520  Scalar y0 = yValues_[segIdx];
521  Scalar y1 = yValues_[segIdx + 1];
522 
523  return (y1 - y0)/(x1 - x0);
524  }
525 
526  // returns the monotonicity of a segment
527  //
528  // The return value have the following meaning:
529  //
530  // 3: function is constant within interval [x0, x1]
531  // 1: function is monotonously increasing in the specified interval
532  // 9: function is not monotonic in the specified interval
533  // -1: function is monotonously decreasing in the specified interval
534  int updateMonotonicity_(int i, int &r) const
535  {
536  if (yValues_[i] < yValues_[i + 1]) {
537  // monotonically increasing?
538  if (r == 3 || r == 1)
539  r = 1;
540  else
541  r = 0;
542  return 1;
543  }
544  else if (yValues_[i] > yValues_[i + 1]) {
545  // monotonically decreasing?
546  if (r == 3 || r == -1)
547  r = -1;
548  else
549  r = 0;
550  return -1;
551  }
552 
553  return 3;
554  }
555 
559  struct ComparatorX_
560  {
561  ComparatorX_(const std::vector<Scalar> &x)
562  : x_(x)
563  {}
564 
565  bool operator ()(size_t idxA, size_t idxB) const
566  { return x_.at(idxA) < x_.at(idxB); }
567 
568  const std::vector<Scalar> &x_;
569  };
570 
574  void sortInput_()
575  {
576  size_t n = numSamples();
577 
578  // create a vector containing 0...n-1
579  std::vector<unsigned> idxVector(n);
580  for (unsigned i = 0; i < n; ++i)
581  idxVector[i] = i;
582 
583  // sort the indices according to the x values of the sample
584  // points
585  ComparatorX_ cmp(xValues_);
586  std::sort(idxVector.begin(), idxVector.end(), cmp);
587 
588  // reorder the sample points
589  std::vector<Scalar> tmpX(n), tmpY(n);
590  for (size_t i = 0; i < idxVector.size(); ++ i) {
591  tmpX[i] = xValues_[idxVector[i]];
592  tmpY[i] = yValues_[idxVector[i]];
593  }
594  xValues_ = tmpX;
595  yValues_ = tmpY;
596  }
597 
602  void reverseSamplingPoints_()
603  {
604  // reverse the arrays
605  size_t n = numSamples();
606  for (size_t i = 0; i <= (n - 1)/2; ++i) {
607  std::swap(xValues_[i], xValues_[n - i - 1]);
608  std::swap(yValues_[i], yValues_[n - i - 1]);
609  }
610  }
611 
615  void resizeArrays_(size_t nSamples)
616  {
617  xValues_.resize(nSamples);
618  yValues_.resize(nSamples);
619  }
620 
621  std::vector<Scalar> xValues_;
622  std::vector<Scalar> yValues_;
623 };
624 } // namespace Opm
625 
626 #endif
void printCSV(Scalar xi0, Scalar xi1, int k, std::ostream &os=std::cout) const
Prints k tuples of the format (x, y, dx/dy, isMonotonic) to stdout.
Definition: Tabulated1DFunction.hpp:455
void setContainerOfTuples(const XYContainer &points, bool sortInputs=false)
Set the sampling points of the piecewise linear function using a STL-compatible container of tuple-li...
Definition: Tabulated1DFunction.hpp:181
Definition: Air_Mesitylene.hpp:31
Scalar evalDerivative(Scalar x, bool=false) const
Evaluate the spline's derivative at a given position.
Definition: Tabulated1DFunction.hpp:317
Scalar eval(Scalar x, bool extrapolate=false) const
Evaluate the spline at a given position.
Definition: Tabulated1DFunction.hpp:247
bool applies(Scalar x) const
Return true iff the given x is in range [x1, xn].
Definition: Tabulated1DFunction.hpp:235
void setXYArrays(size_t nSamples, const ScalarArrayX &x, const ScalarArrayY &y, bool sortInputs=false)
Set the sampling points for the piecewise linear function.
Definition: Tabulated1DFunction.hpp:100
Evaluation< Scalar, VarSetTag, numVars > max(const Evaluation< Scalar, VarSetTag, numVars > &x1, const Evaluation< Scalar, VarSetTag, numVars > &x2)
Definition: Math.hpp:114
int monotonic(Scalar x0, Scalar x1, bool extrapolate OPM_OPTIM_UNUSED=false) const
Returns 1 if the function is monotonically increasing, -1 if the function is mononously decreasing an...
Definition: Tabulated1DFunction.hpp:372
Scalar evalThirdDerivative(OPM_OPTIM_UNUSED Scalar x, OPM_OPTIM_UNUSED bool extrapolate=false) const
Evaluate the function's third derivative at a given position.
Definition: Tabulated1DFunction.hpp:358
Scalar xMin() const
Return the x value of the leftmost sampling point.
Definition: Tabulated1DFunction.hpp:211
void setXYContainers(const ScalarContainerX &x, const ScalarContainerY &y, bool sortInputs=false)
Set the sampling points for the piecewise linear function.
Definition: Tabulated1DFunction.hpp:125
int monotonic() const
Same as monotonic(x0, x1), but with the entire range of the function as interval. ...
Definition: Tabulated1DFunction.hpp:436
Evaluation eval(const Evaluation &x, bool extrapolate=false) const
Evaluate the spline at a given position.
Definition: Tabulated1DFunction.hpp:278
Scalar xAt(size_t i) const
Return the x value of the a sample point with a given index.
Definition: Tabulated1DFunction.hpp:223
Evaluation< Scalar, VarSetTag, numVars > min(const Evaluation< Scalar, VarSetTag, numVars > &x1, const Evaluation< Scalar, VarSetTag, numVars > &x2)
Definition: Math.hpp:61
#define OPM_UNUSED
Definition: Unused.hpp:32
Scalar xMax() const
Return the x value of the rightmost sampling point.
Definition: Tabulated1DFunction.hpp:217
Scalar evalSecondDerivative(OPM_OPTIM_UNUSED Scalar x, OPM_OPTIM_UNUSED bool extrapolate=false) const
Evaluate the function's second derivative at a given position.
Definition: Tabulated1DFunction.hpp:338
#define OPM_OPTIM_UNUSED
Definition: Unused.hpp:42
size_t numSamples() const
Returns the number of sampling points.
Definition: Tabulated1DFunction.hpp:205
Scalar valueAt(size_t i) const
Return the value of the a sample point with a given index.
Definition: Tabulated1DFunction.hpp:229
Tabulated1DFunction(const PointContainer &points, bool sortInputs=false)
Convenience constructor for a piecewise linear function.
Definition: Tabulated1DFunction.hpp:90
void setArrayOfPoints(size_t nSamples, const PointArray &points, bool sortInputs=false)
Set the sampling points for the piecewise linear function.
Definition: Tabulated1DFunction.hpp:146
Tabulated1DFunction(size_t nSamples, const ScalarArrayX &x, const ScalarArrayY &y, bool sortInputs=false)
Convenience constructor for a piecewise linear function.
Definition: Tabulated1DFunction.hpp:63
Provides the OPM_UNUSED macro.
Implements a linearly interpolated scalar function that depends on one variable.
Definition: Tabulated1DFunction.hpp:44
Tabulated1DFunction(const ScalarContainer &x, const ScalarContainer &y, bool sortInputs=false)
Convenience constructor for a piecewise linear function.
Definition: Tabulated1DFunction.hpp:78
Tabulated1DFunction()
Default constructor for a piecewise linear function.
Definition: Tabulated1DFunction.hpp:52