changed: remove some generic utility classes
moved to opm-common
This commit is contained in:
parent
6c9365ac17
commit
9603fbd57f
@ -74,7 +74,6 @@ list (APPEND MAIN_SOURCE_FILES
|
|||||||
opm/core/transport/reorder/TransportSolverTwophaseReorder.cpp
|
opm/core/transport/reorder/TransportSolverTwophaseReorder.cpp
|
||||||
opm/core/transport/reorder/reordersequence.cpp
|
opm/core/transport/reorder/reordersequence.cpp
|
||||||
opm/core/transport/reorder/tarjan.c
|
opm/core/transport/reorder/tarjan.c
|
||||||
opm/core/utility/MonotCubicInterpolator.cpp
|
|
||||||
opm/core/utility/VelocityInterpolation.cpp
|
opm/core/utility/VelocityInterpolation.cpp
|
||||||
opm/core/utility/WachspressCoord.cpp
|
opm/core/utility/WachspressCoord.cpp
|
||||||
opm/core/utility/compressedToCartesian.cpp
|
opm/core/utility/compressedToCartesian.cpp
|
||||||
@ -97,7 +96,6 @@ list (APPEND TEST_SOURCE_FILES
|
|||||||
tests/test_dgbasis.cpp
|
tests/test_dgbasis.cpp
|
||||||
tests/test_cubic.cpp
|
tests/test_cubic.cpp
|
||||||
tests/test_flowdiagnostics.cpp
|
tests/test_flowdiagnostics.cpp
|
||||||
tests/test_nonuniformtablelinear.cpp
|
|
||||||
tests/test_parallelistlinformation.cpp
|
tests/test_parallelistlinformation.cpp
|
||||||
tests/test_sparsevector.cpp
|
tests/test_sparsevector.cpp
|
||||||
tests/test_velocityinterpolation.cpp
|
tests/test_velocityinterpolation.cpp
|
||||||
@ -255,18 +253,13 @@ list (APPEND PUBLIC_HEADER_FILES
|
|||||||
opm/core/transport/reorder/tarjan.h
|
opm/core/transport/reorder/tarjan.h
|
||||||
opm/core/utility/CompressedPropertyAccess.hpp
|
opm/core/utility/CompressedPropertyAccess.hpp
|
||||||
opm/core/utility/initHydroCarbonState.hpp
|
opm/core/utility/initHydroCarbonState.hpp
|
||||||
opm/core/utility/MonotCubicInterpolator.hpp
|
|
||||||
opm/core/utility/NonuniformTableLinear.hpp
|
|
||||||
opm/core/utility/RegionMapping.hpp
|
opm/core/utility/RegionMapping.hpp
|
||||||
opm/core/utility/RootFinders.hpp
|
|
||||||
opm/core/utility/SparseVector.hpp
|
|
||||||
opm/core/utility/UniformTableLinear.hpp
|
opm/core/utility/UniformTableLinear.hpp
|
||||||
opm/core/utility/VelocityInterpolation.hpp
|
opm/core/utility/VelocityInterpolation.hpp
|
||||||
opm/core/utility/WachspressCoord.hpp
|
opm/core/utility/WachspressCoord.hpp
|
||||||
opm/core/utility/buildUniformMonotoneTable.hpp
|
opm/core/utility/buildUniformMonotoneTable.hpp
|
||||||
opm/core/utility/compressedToCartesian.hpp
|
opm/core/utility/compressedToCartesian.hpp
|
||||||
opm/core/utility/extractPvtTableIndex.hpp
|
opm/core/utility/extractPvtTableIndex.hpp
|
||||||
opm/core/utility/linearInterpolation.hpp
|
|
||||||
opm/core/utility/miscUtilities.hpp
|
opm/core/utility/miscUtilities.hpp
|
||||||
opm/core/utility/miscUtilitiesBlackoil.hpp
|
opm/core/utility/miscUtilitiesBlackoil.hpp
|
||||||
opm/core/utility/miscUtilities_impl.hpp
|
opm/core/utility/miscUtilities_impl.hpp
|
||||||
|
@ -1,733 +0,0 @@
|
|||||||
/*
|
|
||||||
MonotCubicInterpolator
|
|
||||||
Copyright (C) 2006 Statoil ASA
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU General Public License
|
|
||||||
as published by the Free Software Foundation; either version 2
|
|
||||||
of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
@file MonotCubicInterpolator.C
|
|
||||||
@brief Represents one dimensional function f with single valued argument x
|
|
||||||
|
|
||||||
Class to represent a one-dimensional function with single-valued
|
|
||||||
argument. Cubic interpolation, preserving the monotonicity of the
|
|
||||||
discrete known function values
|
|
||||||
|
|
||||||
@see MonotCubicInterpolator.h for further documentation.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include <opm/core/utility/MonotCubicInterpolator.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
SOME DISCUSSION ON DATA STORAGE:
|
|
||||||
|
|
||||||
Internal data structure of points and values:
|
|
||||||
|
|
||||||
vector(s):
|
|
||||||
- Easier coding
|
|
||||||
- Faster vector operations when setting up derivatives.
|
|
||||||
- sorting slightly more complex.
|
|
||||||
- insertion of further values bad.
|
|
||||||
|
|
||||||
vector<double,double>
|
|
||||||
- easy sorting
|
|
||||||
- code complexity almost as for map.
|
|
||||||
- insertion of additional values bad
|
|
||||||
|
|
||||||
vector over struct datapoint { x, f, d}
|
|
||||||
- nice code
|
|
||||||
- not as sortable, insertion is cumbersome.
|
|
||||||
|
|
||||||
** This is used currently: **
|
|
||||||
map<double, double> one for (x,f) and one for (x,d)
|
|
||||||
- Naturally sorted on x-values (done by the map-construction)
|
|
||||||
- Slower to set up, awkward loop coding (?)
|
|
||||||
- easy to add more points.
|
|
||||||
- easier to just add code to linear interpolation code
|
|
||||||
- x-data is duplicated, but that memory waste is
|
|
||||||
unlikely to represent a serious issue.
|
|
||||||
|
|
||||||
map<double, <double, double> >
|
|
||||||
- naturally couples x-value, f-value and d-value
|
|
||||||
- even more unreadable(??) code?
|
|
||||||
- higher skills needed by programmer.
|
|
||||||
|
|
||||||
|
|
||||||
MONOTONE CUBIC INTERPOLATION:
|
|
||||||
|
|
||||||
It is a local algorithm. Adding one point only incur recomputation
|
|
||||||
of values in a neighbourhood of the point (in the interval getting
|
|
||||||
divided).
|
|
||||||
|
|
||||||
NOTE: We do not currently make use of this local fact. Upon
|
|
||||||
insertion of a new data pair, everything is recomputed. Revisit
|
|
||||||
this when needed.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
namespace Opm
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
MonotCubicInterpolator(const vector<double> & x, const vector<double> & f) {
|
|
||||||
if (x.size() != f.size()) {
|
|
||||||
throw("Unable to constuct MonotCubicInterpolator from vectors.") ;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the contents of the input vectors to our map of values.
|
|
||||||
vector<double>::const_iterator posx, posf;
|
|
||||||
for (posx = x.begin(), posf = f.begin(); posx != x.end(); ++posx, ++posf) {
|
|
||||||
data[*posx] = *posf ;
|
|
||||||
}
|
|
||||||
|
|
||||||
computeInternalFunctionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
read(const std::string & datafilename, int xColumn, int fColumn)
|
|
||||||
{
|
|
||||||
data.clear() ;
|
|
||||||
ddata.clear() ;
|
|
||||||
|
|
||||||
ifstream datafile_fs(datafilename.c_str());
|
|
||||||
if (!datafile_fs) {
|
|
||||||
return false ;
|
|
||||||
}
|
|
||||||
|
|
||||||
string linestring;
|
|
||||||
while (!datafile_fs.eof()) {
|
|
||||||
getline(datafile_fs, linestring);
|
|
||||||
|
|
||||||
// Replace commas with space:
|
|
||||||
string::size_type pos = 0;
|
|
||||||
while ( (pos = linestring.find(",", pos)) != string::npos ) {
|
|
||||||
// cout << "Found comma at position " << pos << endl;
|
|
||||||
linestring.replace(pos, 1, " ");
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
stringstream strs(linestring);
|
|
||||||
int columnindex = 0;
|
|
||||||
std::vector<double> value;
|
|
||||||
if (linestring.size() > 0 && linestring.at(0) != '#') {
|
|
||||||
while (!(strs.rdstate() & std::ios::failbit)) {
|
|
||||||
double tmp;
|
|
||||||
strs >> tmp;
|
|
||||||
value.push_back(tmp);
|
|
||||||
columnindex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (columnindex >= (max(xColumn, fColumn))) {
|
|
||||||
data[value[xColumn-1]] = value[fColumn-1] ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
datafile_fs.close();
|
|
||||||
|
|
||||||
if (data.size() == 0) {
|
|
||||||
return false ;
|
|
||||||
}
|
|
||||||
|
|
||||||
computeInternalFunctionData();
|
|
||||||
return true ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
addPair(double newx, double newf) {
|
|
||||||
if (std::isnan(newx) || std::isinf(newx) || std::isnan(newf) || std::isinf(newf)) {
|
|
||||||
throw("MonotCubicInterpolator: addPair() received inf/nan input.");
|
|
||||||
}
|
|
||||||
data[newx] = newf ;
|
|
||||||
|
|
||||||
// In a critical application, we should only update the
|
|
||||||
// internal function data for the offended interval,
|
|
||||||
// not for all function values over again.
|
|
||||||
computeInternalFunctionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
double
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
evaluate(double x) const {
|
|
||||||
|
|
||||||
if (std::isnan(x) || std::isinf(x)) {
|
|
||||||
throw("MonotCubicInterpolator: evaluate() received inf/nan input.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// xf becomes the first (xdata,fdata) pair where xdata >= x
|
|
||||||
map<double,double>::const_iterator xf_iterator = data.lower_bound(x);
|
|
||||||
|
|
||||||
// First check if we must extrapolate:
|
|
||||||
if (xf_iterator == data.begin()) {
|
|
||||||
if (data.begin()->first == x) { // Just on the interval limit
|
|
||||||
return data.begin()->second;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Constant extrapolation (!!)
|
|
||||||
return data.begin()->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (xf_iterator == data.end()) {
|
|
||||||
// Constant extrapolation (!!)
|
|
||||||
return data.rbegin()->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Ok, we have x_min < x < x_max
|
|
||||||
|
|
||||||
pair<double,double> xf2 = *xf_iterator;
|
|
||||||
pair<double,double> xf1 = *(--xf_iterator);
|
|
||||||
// we now have: xf2.first > x
|
|
||||||
|
|
||||||
// Linear interpolation if derivative data is not available:
|
|
||||||
if (ddata.size() != data.size()) {
|
|
||||||
double finterp = xf1.second +
|
|
||||||
(xf2.second - xf1.second) / (xf2.first - xf1.first)
|
|
||||||
* (x - xf1.first);
|
|
||||||
return finterp;
|
|
||||||
}
|
|
||||||
else { // Do Cubic Hermite spline
|
|
||||||
double t = (x - xf1.first)/(xf2.first - xf1.first); // t \in [0,1]
|
|
||||||
double h = xf2.first - xf1.first;
|
|
||||||
double finterp
|
|
||||||
= xf1.second * H00(t)
|
|
||||||
+ ddata[xf1.first] * H10(t) * h
|
|
||||||
+ xf2.second * H01(t)
|
|
||||||
+ ddata[xf2.first] * H11(t) * h ;
|
|
||||||
return finterp;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// double
|
|
||||||
// MonotCubicInterpolator::
|
|
||||||
// evaluate(double x, double& errorestimate_output) {
|
|
||||||
// cout << "Error: errorestimate not implemented" << endl;
|
|
||||||
// throw("error estimate not implemented");
|
|
||||||
// return x;
|
|
||||||
// }
|
|
||||||
|
|
||||||
vector<double>
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
get_xVector() const
|
|
||||||
{
|
|
||||||
map<double,double>::const_iterator xf_iterator = data.begin();
|
|
||||||
|
|
||||||
vector<double> outputvector;
|
|
||||||
outputvector.reserve(data.size());
|
|
||||||
for (xf_iterator = data.begin(); xf_iterator != data.end(); ++xf_iterator) {
|
|
||||||
outputvector.push_back(xf_iterator->first);
|
|
||||||
}
|
|
||||||
return outputvector;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
vector<double>
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
get_fVector() const
|
|
||||||
{
|
|
||||||
|
|
||||||
map<double,double>::const_iterator xf_iterator = data.begin();
|
|
||||||
|
|
||||||
vector<double> outputvector;
|
|
||||||
outputvector.reserve(data.size());
|
|
||||||
for (xf_iterator = data.begin(); xf_iterator != data.end(); ++xf_iterator) {
|
|
||||||
outputvector.push_back(xf_iterator->second);
|
|
||||||
}
|
|
||||||
return outputvector;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
string
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
toString() const
|
|
||||||
{
|
|
||||||
const int precision = 20;
|
|
||||||
std::string dataString;
|
|
||||||
std::stringstream dataStringStream;
|
|
||||||
for (map<double,double>::const_iterator it = data.begin();
|
|
||||||
it != data.end(); ++it) {
|
|
||||||
dataStringStream << setprecision(precision) << it->first;
|
|
||||||
dataStringStream << '\t';
|
|
||||||
dataStringStream << setprecision(precision) << it->second;
|
|
||||||
dataStringStream << '\n';
|
|
||||||
}
|
|
||||||
dataStringStream << "Derivative values:" << endl;
|
|
||||||
for (map<double,double>::const_iterator it = ddata.begin();
|
|
||||||
it != ddata.end(); ++it) {
|
|
||||||
dataStringStream << setprecision(precision) << it->first;
|
|
||||||
dataStringStream << '\t';
|
|
||||||
dataStringStream << setprecision(precision) << it->second;
|
|
||||||
dataStringStream << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataStringStream.str();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pair<double,double>
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
getMissingX() const
|
|
||||||
{
|
|
||||||
if( data.size() < 2) {
|
|
||||||
throw("MonotCubicInterpolator::getMissingX() only one datapoint.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for biggest difference value in function-datavalues:
|
|
||||||
|
|
||||||
map<double,double>::const_iterator maxfDiffPair_iterator = data.begin();;
|
|
||||||
double maxfDiffValue = 0;
|
|
||||||
|
|
||||||
map<double,double>::const_iterator xf_iterator;
|
|
||||||
map<double,double>::const_iterator xf_next_iterator;
|
|
||||||
|
|
||||||
for (xf_iterator = data.begin(), xf_next_iterator = ++(data.begin());
|
|
||||||
xf_next_iterator != data.end();
|
|
||||||
++xf_iterator, ++xf_next_iterator) {
|
|
||||||
double absfDiff = fabs((double)(*xf_next_iterator).second
|
|
||||||
- (double)(*xf_iterator).second);
|
|
||||||
if (absfDiff > maxfDiffValue) {
|
|
||||||
maxfDiffPair_iterator = xf_iterator;
|
|
||||||
maxfDiffValue = absfDiff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double newXvalue = ((*maxfDiffPair_iterator).first + ((*(++maxfDiffPair_iterator)).first))/2;
|
|
||||||
return make_pair(newXvalue, maxfDiffValue);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pair<double,double>
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
getMaximumF() const {
|
|
||||||
if (data.size() <= 1) {
|
|
||||||
throw ("MonotCubicInterpolator::getMaximumF() empty data.") ;
|
|
||||||
}
|
|
||||||
if (strictlyIncreasing)
|
|
||||||
return *data.rbegin();
|
|
||||||
else if (strictlyDecreasing)
|
|
||||||
return *data.begin();
|
|
||||||
else {
|
|
||||||
pair<double,double> maxf = *data.rbegin() ;
|
|
||||||
map<double,double>::const_iterator xf_iterator;
|
|
||||||
for (xf_iterator = data.begin() ; xf_iterator != data.end(); ++xf_iterator) {
|
|
||||||
if (xf_iterator->second > maxf.second) {
|
|
||||||
maxf = *xf_iterator ;
|
|
||||||
} ;
|
|
||||||
}
|
|
||||||
return maxf ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pair<double,double>
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
getMinimumF() const {
|
|
||||||
if (data.size() <= 1) {
|
|
||||||
throw ("MonotCubicInterpolator::getMinimumF() empty data.") ;
|
|
||||||
}
|
|
||||||
if (strictlyIncreasing)
|
|
||||||
return *data.begin();
|
|
||||||
else if (strictlyDecreasing) {
|
|
||||||
return *data.rbegin();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pair<double,double> minf = *data.rbegin() ;
|
|
||||||
map<double,double>::const_iterator xf_iterator;
|
|
||||||
for (xf_iterator = data.begin() ; xf_iterator != data.end(); ++xf_iterator) {
|
|
||||||
if (xf_iterator->second < minf.second) {
|
|
||||||
minf = *xf_iterator ;
|
|
||||||
} ;
|
|
||||||
}
|
|
||||||
return minf ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
computeInternalFunctionData() const {
|
|
||||||
|
|
||||||
/* The contents of this function is meaningless if there is only one datapoint */
|
|
||||||
if (data.size() <= 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We do not check the caching flag if we are instructed
|
|
||||||
to do this computation */
|
|
||||||
|
|
||||||
/* We compute monotoneness and directions by assuming
|
|
||||||
monotoneness, and setting to false if the function is not for
|
|
||||||
some value */
|
|
||||||
|
|
||||||
map<double,double>::const_iterator xf_iterator;
|
|
||||||
map<double,double>::const_iterator xf_next_iterator;
|
|
||||||
|
|
||||||
|
|
||||||
strictlyMonotone = true; // We assume this is true, and will set to false if not
|
|
||||||
monotone = true;
|
|
||||||
strictlyDecreasing = true;
|
|
||||||
decreasing = true;
|
|
||||||
strictlyIncreasing = true;
|
|
||||||
increasing = true;
|
|
||||||
|
|
||||||
// Increasing or decreasing??
|
|
||||||
xf_iterator = data.begin();
|
|
||||||
xf_next_iterator = ++(data.begin());
|
|
||||||
/* Cater for non-strictness, search for direction for monotoneness */
|
|
||||||
while (xf_next_iterator != data.end() &&
|
|
||||||
xf_iterator->second == xf_next_iterator->second) {
|
|
||||||
/* Ok, equal values, this is not strict. */
|
|
||||||
strictlyMonotone = false;
|
|
||||||
strictlyIncreasing = false;
|
|
||||||
strictlyDecreasing = false;
|
|
||||||
|
|
||||||
++xf_iterator;
|
|
||||||
++xf_next_iterator;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (xf_next_iterator != data.end()) {
|
|
||||||
|
|
||||||
if ( xf_iterator->second > xf_next_iterator->second) {
|
|
||||||
// Ok, decreasing, check monotoneness:
|
|
||||||
strictlyDecreasing = true;// if strictlyMonotone == false, this one should not be trusted anyway
|
|
||||||
decreasing = true;
|
|
||||||
strictlyIncreasing = false;
|
|
||||||
increasing = false;
|
|
||||||
while(++xf_iterator, ++xf_next_iterator != data.end()) {
|
|
||||||
if ((*xf_iterator).second < (*xf_next_iterator).second) {
|
|
||||||
monotone = false;
|
|
||||||
strictlyMonotone = false;
|
|
||||||
strictlyDecreasing = false; // meaningless now
|
|
||||||
break; // out of while loop
|
|
||||||
}
|
|
||||||
if ((*xf_iterator).second <= (*xf_next_iterator).second) {
|
|
||||||
strictlyMonotone = false;
|
|
||||||
strictlyDecreasing = false; // meaningless now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (xf_iterator->second < xf_next_iterator->second) {
|
|
||||||
// Ok, assume increasing, check monotoneness:
|
|
||||||
strictlyDecreasing = false;
|
|
||||||
strictlyIncreasing = true;
|
|
||||||
decreasing = false;
|
|
||||||
increasing = true;
|
|
||||||
while(++xf_iterator, ++xf_next_iterator != data.end()) {
|
|
||||||
if ((*xf_iterator).second > (*xf_next_iterator).second) {
|
|
||||||
monotone = false;
|
|
||||||
strictlyMonotone = false;
|
|
||||||
strictlyIncreasing = false; // meaningless now
|
|
||||||
break; // out of while loop
|
|
||||||
}
|
|
||||||
if ((*xf_iterator).second >= (*xf_next_iterator).second) {
|
|
||||||
strictlyMonotone = false;
|
|
||||||
strictlyIncreasing = false; // meaningless now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// first two values must be equal if we get
|
|
||||||
// here, but that should have been taken care
|
|
||||||
// of by the while loop above.
|
|
||||||
throw("Programming logic error.") ;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
computeSimpleDerivatives();
|
|
||||||
|
|
||||||
|
|
||||||
// If our input data is monotone, we can do monotone cubic
|
|
||||||
// interpolation, so adjust the derivatives if so.
|
|
||||||
//
|
|
||||||
// If input data is not monotone, we should not touch
|
|
||||||
// the derivatives, as this code should reduce to a
|
|
||||||
// standard cubic interpolation algorithm.
|
|
||||||
if (monotone) {
|
|
||||||
adjustDerivativesForMonotoneness();
|
|
||||||
}
|
|
||||||
|
|
||||||
strictlyMonotoneCached = true;
|
|
||||||
monotoneCached = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if the function curve is flat (zero derivative) at the
|
|
||||||
// endpoints, chop off endpoint data points if that is the case.
|
|
||||||
//
|
|
||||||
// The notion of "flat" is determined by the input parameter "epsilon"
|
|
||||||
// Values whose difference are less than epsilon are regarded as equal.
|
|
||||||
//
|
|
||||||
// This is implemented to be able to obtain a strictly monotone
|
|
||||||
// curve from a data set that is strictly monotone except at the
|
|
||||||
// endpoints.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// The data points
|
|
||||||
// (1,3), (2,3), (3,4), (4,5), (5,5), (6,5)
|
|
||||||
// will become
|
|
||||||
// (2,3), (3,4), (4,5)
|
|
||||||
//
|
|
||||||
// Assumes at least 3 datapoints. If less than three, this function is a noop.
|
|
||||||
void
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
chopFlatEndpoints(const double epsilon) {
|
|
||||||
|
|
||||||
if (getSize() < 3) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
map<double,double>::iterator xf_iterator;
|
|
||||||
map<double,double>::iterator xf_next_iterator;
|
|
||||||
|
|
||||||
// Clear flags:
|
|
||||||
strictlyMonotoneCached = false;
|
|
||||||
monotoneCached = false;
|
|
||||||
|
|
||||||
// Chop left end:
|
|
||||||
xf_iterator = data.begin();
|
|
||||||
xf_next_iterator = ++(data.begin());
|
|
||||||
// Erase data points that are similar to its right value from the left end.
|
|
||||||
while ((xf_next_iterator != data.end()) &&
|
|
||||||
(fabs(xf_iterator->second - xf_next_iterator->second) < epsilon )) {
|
|
||||||
xf_next_iterator++;
|
|
||||||
data.erase(xf_iterator);
|
|
||||||
xf_iterator++;
|
|
||||||
}
|
|
||||||
|
|
||||||
xf_iterator = data.end();
|
|
||||||
xf_iterator--; // (data.end() points beyond the last element)
|
|
||||||
xf_next_iterator = xf_iterator;
|
|
||||||
xf_next_iterator--;
|
|
||||||
// Erase data points that are similar to its left value from the right end.
|
|
||||||
while ((xf_next_iterator != data.begin()) &&
|
|
||||||
(fabs(xf_iterator->second - xf_next_iterator->second) < epsilon )) {
|
|
||||||
xf_next_iterator--;
|
|
||||||
data.erase(xf_iterator);
|
|
||||||
xf_iterator--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finished chopping, so recompute function data:
|
|
||||||
computeInternalFunctionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// If function is monotone, but not strictly monotone,
|
|
||||||
// this function will remove datapoints from intervals
|
|
||||||
// with zero derivative so that the curves become
|
|
||||||
// strictly monotone.
|
|
||||||
//
|
|
||||||
// Example
|
|
||||||
// The data points
|
|
||||||
// (1,2), (2,3), (3,4), (4,4), (5,5), (6,6)
|
|
||||||
// will become
|
|
||||||
// (1,2), (2,3), (3,4), (5,5), (6,6)
|
|
||||||
//
|
|
||||||
// Assumes at least two datapoints, if one or zero datapoint, this is a noop.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
void
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
shrinkFlatAreas(const double epsilon) {
|
|
||||||
|
|
||||||
if (getSize() < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
map<double,double>::iterator xf_iterator;
|
|
||||||
map<double,double>::iterator xf_next_iterator;
|
|
||||||
|
|
||||||
|
|
||||||
// Nothing to do if we already are strictly monotone
|
|
||||||
if (isStrictlyMonotone()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refuse to change a curve that is not monotone.
|
|
||||||
if (!isMonotone()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear flags, they are not to be trusted after we modify the
|
|
||||||
// data
|
|
||||||
strictlyMonotoneCached = false;
|
|
||||||
monotoneCached = false;
|
|
||||||
|
|
||||||
// Iterate through data values, if two data pairs
|
|
||||||
// have equal values, delete one of the data pair.
|
|
||||||
// Do not trust the source code on which data point is being
|
|
||||||
// removed (x-values of equal y-points might be averaged in the future)
|
|
||||||
xf_iterator = data.begin();
|
|
||||||
xf_next_iterator = ++(data.begin());
|
|
||||||
|
|
||||||
while (xf_next_iterator != data.end()) {
|
|
||||||
//cout << xf_iterator->first << "," << xf_iterator->second << " " << xf_next_iterator->first << "," << xf_next_iterator->second << "\n";
|
|
||||||
if (fabs(xf_iterator->second - xf_next_iterator->second) < epsilon ) {
|
|
||||||
//cout << "erasing data pair" << xf_next_iterator->first << " " << xf_next_iterator->second << "\n";
|
|
||||||
map <double,double>::iterator xf_tobedeleted_iterator = xf_next_iterator;
|
|
||||||
xf_next_iterator++;
|
|
||||||
data.erase(xf_tobedeleted_iterator);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
xf_iterator++;
|
|
||||||
xf_next_iterator++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
computeSimpleDerivatives() const {
|
|
||||||
|
|
||||||
ddata.clear();
|
|
||||||
|
|
||||||
// Do endpoints first:
|
|
||||||
map<double,double>::const_iterator xf_prev_iterator;
|
|
||||||
map<double,double>::const_iterator xf_iterator;
|
|
||||||
map<double,double>::const_iterator xf_next_iterator;
|
|
||||||
double diff;
|
|
||||||
|
|
||||||
// Leftmost interval:
|
|
||||||
xf_iterator = data.begin();
|
|
||||||
xf_next_iterator = ++(data.begin());
|
|
||||||
diff =
|
|
||||||
(xf_next_iterator->second - xf_iterator->second) /
|
|
||||||
(xf_next_iterator->first - xf_iterator->first);
|
|
||||||
ddata[xf_iterator->first] = diff ;
|
|
||||||
|
|
||||||
// Rightmost interval:
|
|
||||||
xf_iterator = --(--(data.end()));
|
|
||||||
xf_next_iterator = --(data.end());
|
|
||||||
diff =
|
|
||||||
(xf_next_iterator->second - xf_iterator->second) /
|
|
||||||
(xf_next_iterator->first - xf_iterator->first);
|
|
||||||
ddata[xf_next_iterator->first] = diff ;
|
|
||||||
|
|
||||||
// If we have more than two intervals, loop over internal points:
|
|
||||||
if (data.size() > 2) {
|
|
||||||
|
|
||||||
map<double,double>::const_iterator intpoint;
|
|
||||||
for (intpoint = ++data.begin(); intpoint != --data.end(); ++intpoint) {
|
|
||||||
/*
|
|
||||||
diff = (f2 - f1)/(x2-x1)/w + (f3-f1)/(x3-x2)/2
|
|
||||||
|
|
||||||
average of the forward and backward difference.
|
|
||||||
Weights are equal, should we weigh with h_i?
|
|
||||||
*/
|
|
||||||
|
|
||||||
map<double,double>::const_iterator lastpoint = intpoint; --lastpoint;
|
|
||||||
map<double,double>::const_iterator nextpoint = intpoint; ++nextpoint;
|
|
||||||
|
|
||||||
diff = (nextpoint->second - intpoint->second)/
|
|
||||||
(2*(nextpoint->first - intpoint->first))
|
|
||||||
+
|
|
||||||
(intpoint->second - lastpoint->second) /
|
|
||||||
(2*(intpoint->first - lastpoint->first));
|
|
||||||
|
|
||||||
ddata[intpoint->first] = diff ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
adjustDerivativesForMonotoneness() const {
|
|
||||||
map<double,double>::const_iterator point, dpoint;
|
|
||||||
|
|
||||||
/* Loop over all intervals, ie. loop over all points and look
|
|
||||||
at the interval to the right of the point */
|
|
||||||
for (point = data.begin(), dpoint = ddata.begin();
|
|
||||||
point != --data.end();
|
|
||||||
++point, ++dpoint) {
|
|
||||||
map<double,double>::const_iterator nextpoint, nextdpoint;
|
|
||||||
nextpoint = point; ++nextpoint;
|
|
||||||
nextdpoint = dpoint; ++nextdpoint;
|
|
||||||
|
|
||||||
double delta =
|
|
||||||
(nextpoint->second - point->second) /
|
|
||||||
(nextpoint->first - point->first);
|
|
||||||
if (fabs(delta) < 1e-14) {
|
|
||||||
ddata[point->first] = 0.0;
|
|
||||||
ddata[nextpoint->first] = 0.0;
|
|
||||||
} else {
|
|
||||||
double alpha = ddata[point->first] / delta;
|
|
||||||
double beta = ddata[nextpoint->first] / delta;
|
|
||||||
|
|
||||||
if (! isMonotoneCoeff(alpha, beta)) {
|
|
||||||
double tau = 3/sqrt(alpha*alpha + beta*beta);
|
|
||||||
|
|
||||||
ddata[point->first] = tau*alpha*delta;
|
|
||||||
ddata[nextpoint->first] = tau*beta*delta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MonotCubicInterpolator::
|
|
||||||
scaleData(double factor) {
|
|
||||||
map<double,double>::iterator it , itd ;
|
|
||||||
if (data.size() == ddata.size()) {
|
|
||||||
for (it = data.begin() , itd = ddata.begin() ; it != data.end() ; ++it , ++itd) {
|
|
||||||
it->second *= factor ;
|
|
||||||
itd->second *= factor ;
|
|
||||||
} ;
|
|
||||||
} else {
|
|
||||||
for (it = data.begin() ; it != data.end() ; ++it ) {
|
|
||||||
it->second *= factor ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Opm
|
|
@ -1,583 +0,0 @@
|
|||||||
/* -*-C++-*- */
|
|
||||||
|
|
||||||
#ifndef _MONOTCUBICINTERPOLATOR_H
|
|
||||||
#define _MONOTCUBICINTERPOLATOR_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
/*
|
|
||||||
MonotCubicInterpolator
|
|
||||||
Copyright (C) 2006 Statoil ASA
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU General Public License
|
|
||||||
as published by the Free Software Foundation; either version 2
|
|
||||||
of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Opm
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
Class to represent a one-dimensional function f with single-valued
|
|
||||||
argument x. The function is represented by a table of function
|
|
||||||
values. Interpolation between table values is cubic and monotonicity
|
|
||||||
preserving if input values are monotonous.
|
|
||||||
|
|
||||||
Outside x_min and x_max, the class will extrapolate using the
|
|
||||||
constant f(x_min) or f(x_max).
|
|
||||||
|
|
||||||
Extra functionality:
|
|
||||||
- Can return (x_1+x_2)/2 where x_1 and x_2 are such that
|
|
||||||
abs(f(x_1) - f(x_2)) is maximized. This is used to determine where
|
|
||||||
one should calculate a new value for increased accuracy in the
|
|
||||||
current function
|
|
||||||
|
|
||||||
Monotonicity preserving cubic interpolation algorithm is taken
|
|
||||||
from Fritsch and Carlson, "Monotone piecewise cubic interpolation",
|
|
||||||
SIAM J. Numer. Anal. 17, 238--246, no. 2,
|
|
||||||
|
|
||||||
$Id$
|
|
||||||
|
|
||||||
Algorithm also described here:
|
|
||||||
http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
|
|
||||||
|
|
||||||
|
|
||||||
@author Håvard Berland <havb (at) statoil.com>, December 2006
|
|
||||||
@brief Represents one dimensional function f with single valued argument x that can be interpolated using monotone cubic interpolation
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class MonotCubicInterpolator {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param datafilename A datafile with the x values and the corresponding f(x) values
|
|
||||||
|
|
||||||
Accepts a filename as input and parses this file for
|
|
||||||
two-column floating point data, interpreting the data as
|
|
||||||
representing function values x and f(x).
|
|
||||||
|
|
||||||
Ignores all lines not conforming to \<whitespace\>\<float\>\<whitespace\>\<float\>\<whatever\>\<newline\>
|
|
||||||
*/
|
|
||||||
MonotCubicInterpolator(const std::string & datafilename)
|
|
||||||
{
|
|
||||||
if (!read(datafilename)) {
|
|
||||||
throw("Unable to constuct MonotCubicInterpolator from file.") ;
|
|
||||||
} ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param datafilename A datafile with the x values and the corresponding f(x) values
|
|
||||||
|
|
||||||
Accepts a filename as input and parses this file for
|
|
||||||
two-column floating point data, interpreting the data as
|
|
||||||
representing function values x and f(x).
|
|
||||||
|
|
||||||
Ignores all lines not conforming to \<whitespace\>\<float\>\<whitespace\>\<float\>\<whatever\>\<newline\>
|
|
||||||
|
|
||||||
All commas in the file will be treated as spaces when parsing.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
MonotCubicInterpolator(const char* datafilename)
|
|
||||||
{
|
|
||||||
if (!read(std::string(datafilename))) {
|
|
||||||
throw("Unable to constuct MonotCubicInterpolator from file.") ;
|
|
||||||
} ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param datafilename data file
|
|
||||||
@param XColumn x values
|
|
||||||
@param fColumn f values
|
|
||||||
|
|
||||||
Accepts a filename as input, and parses the chosen columns in
|
|
||||||
that file.
|
|
||||||
*/
|
|
||||||
MonotCubicInterpolator(const char* datafilename, int xColumn, int fColumn)
|
|
||||||
{
|
|
||||||
if (!read(std::string(datafilename),xColumn,fColumn)) {
|
|
||||||
throw("Unable to constuct MonotCubicInterpolator from file.") ;
|
|
||||||
} ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param datafilename data file
|
|
||||||
@param XColumn x values
|
|
||||||
@param fColumn f values
|
|
||||||
|
|
||||||
Accepts a filename as input, and parses the chosen columns in
|
|
||||||
that file.
|
|
||||||
*/
|
|
||||||
MonotCubicInterpolator(const std::string & datafilename, int xColumn, int fColumn)
|
|
||||||
{
|
|
||||||
if (!read(datafilename,xColumn,fColumn)) {
|
|
||||||
throw("Unable to constuct MonotCubicInterpolator from file.") ;
|
|
||||||
} ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param x vector of x values
|
|
||||||
@param f vector of corresponding f values
|
|
||||||
|
|
||||||
Accepts two equal-length vectors as input for constructing
|
|
||||||
the interpolation object. First vector is the x-values, the
|
|
||||||
second vector is the function values
|
|
||||||
*/
|
|
||||||
MonotCubicInterpolator(const std::vector<double> & x ,
|
|
||||||
const std::vector<double> & f);
|
|
||||||
|
|
||||||
/**
|
|
||||||
No input, an empty function object is created.
|
|
||||||
|
|
||||||
This object must be treated with care until
|
|
||||||
populated.
|
|
||||||
*/
|
|
||||||
MonotCubicInterpolator() { }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param datafilename A datafile with the x values and the corresponding f(x) values
|
|
||||||
|
|
||||||
Accepts a filename as input and parses this file for
|
|
||||||
two-column floating point data, interpreting the data as
|
|
||||||
representing function values x and f(x).
|
|
||||||
|
|
||||||
returns true on success
|
|
||||||
|
|
||||||
All commas in file will be treated as spaces when parsing
|
|
||||||
|
|
||||||
Ignores all lines not conforming to \<whitespace\>\<float\>\<whitespace\>\<float\>\<whatever\>\<newline\>
|
|
||||||
*/
|
|
||||||
bool read(const std::string & datafilename) {
|
|
||||||
return read(datafilename,1,2) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param datafilename data file
|
|
||||||
@param XColumn x values
|
|
||||||
@param fColumn f values
|
|
||||||
|
|
||||||
Accepts a filename as input, and parses the chosen columns in
|
|
||||||
that file.
|
|
||||||
*/
|
|
||||||
bool read(const std::string & datafilename, int xColumn, int fColumn) ;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param x x value
|
|
||||||
|
|
||||||
Returns f(x) for given x (input). Interpolates (monotone cubic
|
|
||||||
or linearly) if necessary.
|
|
||||||
|
|
||||||
Extrapolates using the constants f(x_min) or f(x_max) if
|
|
||||||
input x is outside (x_min, x_max)
|
|
||||||
|
|
||||||
@return f(x) for a given x
|
|
||||||
*/
|
|
||||||
double operator () (double x) const { return evaluate(x) ; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param x x value
|
|
||||||
|
|
||||||
Returns f(x) for given x (input). Interpolates (monotone cubic
|
|
||||||
or linearly) if necessary.
|
|
||||||
|
|
||||||
Extrapolates using the constants f(x_min) or f(x_max) if
|
|
||||||
input x is outside (x_min, x_max)
|
|
||||||
|
|
||||||
@return f(x) for a given x
|
|
||||||
*/
|
|
||||||
double evaluate(double x) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param x x value
|
|
||||||
@param errorestimate_output
|
|
||||||
|
|
||||||
Returns f(x) and an error estimate for given x (input).
|
|
||||||
|
|
||||||
Interpolates (linearly) if necessary.
|
|
||||||
|
|
||||||
Throws an exception if extrapolation would be necessary for
|
|
||||||
evaluation. We do not want to do extrapolation (yet).
|
|
||||||
|
|
||||||
The error estimate for x1 < x < x2 is
|
|
||||||
(x2 - x1)^2/8 * f''(x) where f''(x) is evaluated using
|
|
||||||
the stencil (1 -2 1) using either (x0, x1, x2) or (x1, x2, x3);
|
|
||||||
|
|
||||||
Throws an exception if the table contains only two x-values.
|
|
||||||
|
|
||||||
NOT IMPLEMENTED YET!
|
|
||||||
*/
|
|
||||||
double evaluate(double x, double & errorestimate_output ) const ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Minimum x-value, returns both x and f in a pair.
|
|
||||||
|
|
||||||
@return minimum x value
|
|
||||||
@return f(minimum x value)
|
|
||||||
*/
|
|
||||||
std::pair<double,double> getMinimumX() const {
|
|
||||||
// Easy since the data is sorted on x:
|
|
||||||
return *data.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Maximum x-value, returns both x and f in a pair.
|
|
||||||
|
|
||||||
@return maximum x value
|
|
||||||
@return f(maximum x value)
|
|
||||||
*/
|
|
||||||
std::pair<double,double> getMaximumX() const {
|
|
||||||
// Easy since the data is sorted on x:
|
|
||||||
return *data.rbegin();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Maximum f-value, returns both x and f in a pair.
|
|
||||||
|
|
||||||
@return x value corresponding to maximum f value
|
|
||||||
@return maximum f value
|
|
||||||
*/
|
|
||||||
std::pair<double,double> getMaximumF() const ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Minimum f-value, returns both x and f in a pair
|
|
||||||
|
|
||||||
@return x value corresponding to minimal f value
|
|
||||||
@return minimum f value
|
|
||||||
*/
|
|
||||||
std::pair<double,double> getMinimumF() const ;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Provide a copy of the x-data as a vector
|
|
||||||
|
|
||||||
Unspecified order, but corresponds to get_fVector.
|
|
||||||
|
|
||||||
@return x values as a vector
|
|
||||||
*/
|
|
||||||
std::vector<double> get_xVector() const ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Provide a copy of tghe function data as a vector
|
|
||||||
|
|
||||||
Unspecified order, but corresponds to get_xVector
|
|
||||||
|
|
||||||
@return f values as a vector
|
|
||||||
|
|
||||||
*/
|
|
||||||
std::vector<double> get_fVector() const ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param factor Scaling constant
|
|
||||||
|
|
||||||
Scale all the function value data by a constant
|
|
||||||
*/
|
|
||||||
void scaleData(double factor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Determines if the current function-value-data is strictly
|
|
||||||
monotone. This is a utility function for outsiders if they want
|
|
||||||
to invert the data for example.
|
|
||||||
|
|
||||||
@return True if f(x) is strictly monotone, else False
|
|
||||||
*/
|
|
||||||
bool isStrictlyMonotone() {
|
|
||||||
|
|
||||||
/* Use cached value if it can be trusted */
|
|
||||||
if (strictlyMonotoneCached) {
|
|
||||||
return strictlyMonotone;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
computeInternalFunctionData();
|
|
||||||
return strictlyMonotone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Determines if the current function-value-data is monotone.
|
|
||||||
|
|
||||||
@return True if f(x) is monotone, else False
|
|
||||||
*/
|
|
||||||
bool isMonotone() const {
|
|
||||||
if (monotoneCached) {
|
|
||||||
return monotone;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
computeInternalFunctionData();
|
|
||||||
return monotone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
Determines if the current function-value-data is strictly
|
|
||||||
increasing. This is a utility function for outsiders if they want
|
|
||||||
to invert the data for example.
|
|
||||||
|
|
||||||
@return True if f(x) is strictly increasing, else False
|
|
||||||
*/
|
|
||||||
bool isStrictlyIncreasing() {
|
|
||||||
|
|
||||||
/* Use cached value if it can be trusted */
|
|
||||||
if (strictlyMonotoneCached) {
|
|
||||||
return (strictlyMonotone && strictlyIncreasing);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
computeInternalFunctionData();
|
|
||||||
return (strictlyMonotone && strictlyIncreasing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Determines if the current function-value-data is monotone and increasing.
|
|
||||||
|
|
||||||
@return True if f(x) is monotone and increasing, else False
|
|
||||||
*/
|
|
||||||
bool isMonotoneIncreasing() const {
|
|
||||||
if (monotoneCached) {
|
|
||||||
return (monotone && increasing);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
computeInternalFunctionData();
|
|
||||||
return (monotone && increasing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
Determines if the current function-value-data is strictly
|
|
||||||
decreasing. This is a utility function for outsiders if they want
|
|
||||||
to invert the data for example.
|
|
||||||
|
|
||||||
@return True if f(x) is strictly decreasing, else False
|
|
||||||
*/
|
|
||||||
bool isStrictlyDecreasing() {
|
|
||||||
|
|
||||||
/* Use cached value if it can be trusted */
|
|
||||||
if (strictlyMonotoneCached) {
|
|
||||||
return (strictlyMonotone && strictlyDecreasing);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
computeInternalFunctionData();
|
|
||||||
return (strictlyMonotone && strictlyDecreasing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Determines if the current function-value-data is monotone and decreasing
|
|
||||||
|
|
||||||
@return True if f(x) is monotone and decreasing, else False
|
|
||||||
*/
|
|
||||||
bool isMonotoneDecreasing() const {
|
|
||||||
if (monotoneCached) {
|
|
||||||
return (monotone && decreasing);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
computeInternalFunctionData();
|
|
||||||
return (monotone && decreasing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
@param newx New x point
|
|
||||||
@param newf New f(x) point
|
|
||||||
|
|
||||||
Adds a new datapoint to the function.
|
|
||||||
|
|
||||||
This causes all the derivatives at all points of the functions
|
|
||||||
to be recomputed and then adjusted for monotone cubic
|
|
||||||
interpolation. If this function ever enters a critical part of
|
|
||||||
any code, the locality of the algorithm for monotone adjustment
|
|
||||||
must be exploited.
|
|
||||||
|
|
||||||
*/
|
|
||||||
void addPair(double newx, double newf);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Returns an x-value that is believed to yield the best
|
|
||||||
improvement in global accuracy for the interpolation if
|
|
||||||
computed.
|
|
||||||
|
|
||||||
Searches for the largest jump in f-values, and returns a x
|
|
||||||
value being the average of the two x-values representing the
|
|
||||||
f-value-jump.
|
|
||||||
|
|
||||||
@return New x value beleived to yield the best improvement in global accuracy
|
|
||||||
@return Maximal difference
|
|
||||||
*/
|
|
||||||
std::pair<double,double> getMissingX() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Constructs a string containing the data in a table
|
|
||||||
|
|
||||||
@return a string containing the data in a table
|
|
||||||
*/
|
|
||||||
std::string toString() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
@return Number of datapoint pairs in this object
|
|
||||||
*/
|
|
||||||
int getSize() const {
|
|
||||||
return data.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Checks if the function curve is flat at the endpoints, chop off
|
|
||||||
endpoint data points if that is the case.
|
|
||||||
|
|
||||||
The notion of "flat" is determined by the input parameter "epsilon"
|
|
||||||
Values whose difference are less than epsilon are regarded as equal.
|
|
||||||
|
|
||||||
This is implemented to be able to obtain a strictly monotone
|
|
||||||
curve from a data set that is strictly monotone except at the
|
|
||||||
endpoints.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
The data points
|
|
||||||
(1,3), (2,3), (3,4), (4,5), (5,5), (6,5)
|
|
||||||
will become
|
|
||||||
(2,3), (3,4), (4,5)
|
|
||||||
|
|
||||||
Assumes at least 3 datapoints. If less than three, this function is a noop.
|
|
||||||
*/
|
|
||||||
void chopFlatEndpoints(const double);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Wrapper function for chopFlatEndpoints(const double)
|
|
||||||
providing a default epsilon parameter
|
|
||||||
*/
|
|
||||||
void chopFlatEndpoints() {
|
|
||||||
chopFlatEndpoints(1e-14);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
If function is monotone, but not strictly monotone,
|
|
||||||
this function will remove datapoints from intervals
|
|
||||||
with zero derivative so that the curve become
|
|
||||||
strictly monotone.
|
|
||||||
|
|
||||||
Example
|
|
||||||
The data points
|
|
||||||
(1,2), (2,3), (3,4), (4,4), (5,5), (6,6)
|
|
||||||
will become
|
|
||||||
(1,2), (2,3), (3,4), (5,5), (6,6)
|
|
||||||
|
|
||||||
Assumes at least two datapoints, if one or zero datapoint, this is a noop.
|
|
||||||
*/
|
|
||||||
void shrinkFlatAreas(const double);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Wrapper function for shrinkFlatAreas(const double)
|
|
||||||
providing a default epsilon parameter
|
|
||||||
*/
|
|
||||||
void shrinkFlatAreas() {
|
|
||||||
shrinkFlatAreas(1e-14);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// Data structure to store x- and f-values
|
|
||||||
std::map<double, double> data;
|
|
||||||
|
|
||||||
// Data structure to store x- and d-values
|
|
||||||
mutable std::map<double, double> ddata;
|
|
||||||
|
|
||||||
|
|
||||||
// Storage containers for precomputed interpolation data
|
|
||||||
// std::vector<double> dvalues; // derivatives in Hermite interpolation.
|
|
||||||
|
|
||||||
// Flag to determine whether the boolean strictlyMonotone can be
|
|
||||||
// trusted.
|
|
||||||
mutable bool strictlyMonotoneCached;
|
|
||||||
mutable bool monotoneCached; /* only monotone, not stricly montone */
|
|
||||||
|
|
||||||
mutable bool strictlyMonotone;
|
|
||||||
mutable bool monotone;
|
|
||||||
|
|
||||||
// if strictlyMonotone is true (and can be trusted), the two next are meaningful
|
|
||||||
mutable bool strictlyDecreasing;
|
|
||||||
mutable bool strictlyIncreasing;
|
|
||||||
mutable bool decreasing;
|
|
||||||
mutable bool increasing;
|
|
||||||
|
|
||||||
|
|
||||||
/* Hermite basis functions, t \in [0,1] ,
|
|
||||||
notation from:
|
|
||||||
http://en.wikipedia.org/w/index.php?title=Cubic_Hermite_spline&oldid=84495502
|
|
||||||
*/
|
|
||||||
|
|
||||||
double H00(double t) const {
|
|
||||||
return 2*t*t*t - 3*t*t + 1;
|
|
||||||
}
|
|
||||||
double H10(double t) const {
|
|
||||||
return t*t*t - 2*t*t + t;
|
|
||||||
}
|
|
||||||
double H01(double t) const {
|
|
||||||
return -2*t*t*t + 3*t*t;
|
|
||||||
}
|
|
||||||
double H11(double t) const {
|
|
||||||
return t*t*t - t*t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void computeInternalFunctionData() const ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Computes initial derivative values using centered (second order) difference
|
|
||||||
for internal datapoints, and one-sided derivative for endpoints
|
|
||||||
|
|
||||||
The internal datastructure map<double,double> ddata is populated by this method.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void computeSimpleDerivatives() const ;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Adjusts the derivative values (ddata) so that we can guarantee that
|
|
||||||
the resulting piecewise Hermite polymial is monotone. This is
|
|
||||||
done according to the algorithm of Fritsch and Carlsson 1980,
|
|
||||||
see Section 4, especially the two last lines.
|
|
||||||
*/
|
|
||||||
void adjustDerivativesForMonotoneness() const ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Checks if the coefficient alpha and beta is in
|
|
||||||
the region that guarantees monotoneness of the
|
|
||||||
derivative values they represent
|
|
||||||
|
|
||||||
See Fritsch and Carlson 1980, Lemma 2,
|
|
||||||
alternatively Step 5 in Wikipedia's article
|
|
||||||
on Monotone cubic interpolation.
|
|
||||||
*/
|
|
||||||
bool isMonotoneCoeff(double alpha, double beta) const {
|
|
||||||
if ((alpha*alpha + beta*beta) <= 9) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Opm
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,208 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2009, 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
|
||||||
Copyright 2009, 2010, 2011, 2012 Statoil ASA.
|
|
||||||
|
|
||||||
This file is part of the Open Porous Media project (OPM).
|
|
||||||
|
|
||||||
OPM is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
OPM is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED
|
|
||||||
#define OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <exception>
|
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <opm/common/ErrorMacros.hpp>
|
|
||||||
#include <opm/core/utility/linearInterpolation.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Opm
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief This class uses linear interpolation to compute the value
|
|
||||||
/// (and its derivative) of a function f sampled at possibly
|
|
||||||
/// nonuniform points. If values outside the domain are sought,
|
|
||||||
/// values will be extrapolated linearly.
|
|
||||||
/// @tparam T the range type of the function (should be an algebraic ring type)
|
|
||||||
template<typename T>
|
|
||||||
class NonuniformTableLinear
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// @brief Default constructor.
|
|
||||||
NonuniformTableLinear();
|
|
||||||
|
|
||||||
/// @brief Construct from vectors of x and y values.
|
|
||||||
/// @param x_values vector of domain values
|
|
||||||
/// @param y_values vector of corresponding range values.
|
|
||||||
template<class XContainer, class YContainer>
|
|
||||||
NonuniformTableLinear(const XContainer& x_values,
|
|
||||||
const YContainer& y_values);
|
|
||||||
|
|
||||||
/// @brief Get the domain.
|
|
||||||
/// @return the domain as a pair of doubles.
|
|
||||||
std::pair<double, double> domain();
|
|
||||||
|
|
||||||
/// @brief Rescale the domain.
|
|
||||||
/// @param new_domain the new domain as a pair of doubles.
|
|
||||||
void rescaleDomain(std::pair<double, double> new_domain);
|
|
||||||
|
|
||||||
/// @brief Evaluate the value at x.
|
|
||||||
/// @param x a domain value
|
|
||||||
/// @return f(x)
|
|
||||||
double operator()(const double x) const;
|
|
||||||
|
|
||||||
/// @brief Evaluate the derivative at x.
|
|
||||||
/// @param x a domain value
|
|
||||||
/// @return f'(x)
|
|
||||||
double derivative(const double x) const;
|
|
||||||
|
|
||||||
/// @brief Evaluate the inverse at y. Requires T to be a double.
|
|
||||||
/// @param y a range value
|
|
||||||
/// @return f^{-1}(y)
|
|
||||||
double inverse(const double y) const;
|
|
||||||
|
|
||||||
/// @brief Equality operator.
|
|
||||||
/// @param other another NonuniformTableLinear.
|
|
||||||
/// @return true if they are represented exactly alike.
|
|
||||||
bool operator==(const NonuniformTableLinear& other) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::vector<double> x_values_;
|
|
||||||
std::vector<T> y_values_;
|
|
||||||
mutable std::vector<T> x_values_reversed_;
|
|
||||||
mutable std::vector<T> y_values_reversed_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// A utility function
|
|
||||||
/// @brief Detect if a sequence is nondecreasing.
|
|
||||||
/// @tparam FI a forward iterator whose value type has operator< defined.
|
|
||||||
/// @param beg start of sequence
|
|
||||||
/// @param end one-beyond-end of sequence
|
|
||||||
/// @return false if there exists two consecutive values (v1, v2) in the sequence
|
|
||||||
/// for which v2 < v1, else returns true.
|
|
||||||
template <typename FI>
|
|
||||||
bool isNondecreasing(const FI beg, const FI end)
|
|
||||||
{
|
|
||||||
if (beg == end) return true;
|
|
||||||
FI it = beg;
|
|
||||||
++it;
|
|
||||||
FI prev = beg;
|
|
||||||
for (; it != end; ++it, ++prev) {
|
|
||||||
if (*it < *prev) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Member implementations.
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline
|
|
||||||
NonuniformTableLinear<T>
|
|
||||||
::NonuniformTableLinear()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
template<class XContainer, class YContainer>
|
|
||||||
inline
|
|
||||||
NonuniformTableLinear<T>
|
|
||||||
::NonuniformTableLinear(const XContainer& x_column,
|
|
||||||
const YContainer& y_column)
|
|
||||||
: x_values_( x_column.begin() , x_column.end()),
|
|
||||||
y_values_( y_column.begin() , y_column.end())
|
|
||||||
{
|
|
||||||
assert(isNondecreasing(x_values_.begin(), x_values_.end()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline std::pair<double, double>
|
|
||||||
NonuniformTableLinear<T>
|
|
||||||
::domain()
|
|
||||||
{
|
|
||||||
return std::make_pair(x_values_[0], x_values_.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void
|
|
||||||
NonuniformTableLinear<T>
|
|
||||||
::rescaleDomain(std::pair<double, double> new_domain)
|
|
||||||
{
|
|
||||||
const double a = x_values_[0];
|
|
||||||
const double b = x_values_.back();
|
|
||||||
const double c = new_domain.first;
|
|
||||||
const double d = new_domain.second;
|
|
||||||
// x in [a, b] -> x in [c, d]
|
|
||||||
for (int i = 0; i < int(x_values_.size()); ++i) {
|
|
||||||
x_values_[i] = (x_values_[i] - a)*(d - c)/(b - a) + c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline double
|
|
||||||
NonuniformTableLinear<T>
|
|
||||||
::operator()(const double x) const
|
|
||||||
{
|
|
||||||
return Opm::linearInterpolation(x_values_, y_values_, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline double
|
|
||||||
NonuniformTableLinear<T>
|
|
||||||
::derivative(const double x) const
|
|
||||||
{
|
|
||||||
return Opm::linearInterpolationDerivative(x_values_, y_values_, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline double
|
|
||||||
NonuniformTableLinear<T>
|
|
||||||
::inverse(const double y) const
|
|
||||||
{
|
|
||||||
if (y_values_.front() < y_values_.back()) {
|
|
||||||
return Opm::linearInterpolation(y_values_, x_values_, y);
|
|
||||||
} else {
|
|
||||||
if (y_values_reversed_.empty()) {
|
|
||||||
y_values_reversed_ = y_values_;
|
|
||||||
std::reverse(y_values_reversed_.begin(), y_values_reversed_.end());
|
|
||||||
assert(isNondecreasing(y_values_reversed_.begin(), y_values_reversed_.end()));
|
|
||||||
x_values_reversed_ = x_values_;
|
|
||||||
std::reverse(x_values_reversed_.begin(), x_values_reversed_.end());
|
|
||||||
}
|
|
||||||
return Opm::linearInterpolation(y_values_reversed_, x_values_reversed_, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline bool
|
|
||||||
NonuniformTableLinear<T>
|
|
||||||
::operator==(const NonuniformTableLinear<T>& other) const
|
|
||||||
{
|
|
||||||
return x_values_ == other.x_values_
|
|
||||||
&& y_values_ == other.y_values_;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Opm
|
|
||||||
|
|
||||||
#endif // OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED
|
|
@ -1,340 +0,0 @@
|
|||||||
//===========================================================================
|
|
||||||
//
|
|
||||||
// File: RootFinders.hpp
|
|
||||||
//
|
|
||||||
// Created: Thu May 6 19:59:42 2010
|
|
||||||
//
|
|
||||||
// Author(s): Atgeirr F Rasmussen <atgeirr@sintef.no>
|
|
||||||
// Jostein R Natvig <jostein.r.natvig@sintef.no>
|
|
||||||
//
|
|
||||||
// $Date$
|
|
||||||
//
|
|
||||||
// $Revision$
|
|
||||||
//
|
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
|
||||||
Copyright 2010 Statoil ASA.
|
|
||||||
|
|
||||||
This file is part of the Open Porous Media project (OPM).
|
|
||||||
|
|
||||||
OPM is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
OPM is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OPM_ROOTFINDERS_HEADER
|
|
||||||
#define OPM_ROOTFINDERS_HEADER
|
|
||||||
|
|
||||||
#include <opm/common/ErrorMacros.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <limits>
|
|
||||||
#include <cmath>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace Opm
|
|
||||||
{
|
|
||||||
|
|
||||||
struct ThrowOnError
|
|
||||||
{
|
|
||||||
static double handleBracketingFailure(const double x0, const double x1, const double f0, const double f1)
|
|
||||||
{
|
|
||||||
OPM_THROW(std::runtime_error, "Error in parameters, zero not bracketed: [a, b] = ["
|
|
||||||
<< x0 << ", " << x1 << "] f(a) = " << f0 << " f(b) = " << f1);
|
|
||||||
return -1e100; // Never reached.
|
|
||||||
}
|
|
||||||
static double handleTooManyIterations(const double x0, const double x1, const int maxiter)
|
|
||||||
{
|
|
||||||
OPM_THROW(std::runtime_error, "Maximum number of iterations exceeded: " << maxiter << "\n"
|
|
||||||
<< "Current interval is [" << std::min(x0, x1) << ", "
|
|
||||||
<< std::max(x0, x1) << "]");
|
|
||||||
return -1e100; // Never reached.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct WarnAndContinueOnError
|
|
||||||
{
|
|
||||||
static double handleBracketingFailure(const double x0, const double x1, const double f0, const double f1)
|
|
||||||
{
|
|
||||||
OPM_REPORT;
|
|
||||||
std::cerr << "Error in parameters, zero not bracketed: [a, b] = ["
|
|
||||||
<< x0 << ", " << x1 << "] f(a) = " << f0 << " f(b) = " << f1
|
|
||||||
<< "";
|
|
||||||
return std::fabs(f0) < std::fabs(f1) ? x0 : x1;
|
|
||||||
}
|
|
||||||
static double handleTooManyIterations(const double x0, const double x1, const int maxiter)
|
|
||||||
{
|
|
||||||
OPM_REPORT;
|
|
||||||
std::cerr << "Maximum number of iterations exceeded: " << maxiter
|
|
||||||
<< ", current interval is [" << std::min(x0, x1) << ", "
|
|
||||||
<< std::max(x0, x1) << "]";
|
|
||||||
return 0.5*(x0 + x1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct ContinueOnError
|
|
||||||
{
|
|
||||||
static double handleBracketingFailure(const double x0, const double x1, const double f0, const double f1)
|
|
||||||
{
|
|
||||||
return std::fabs(f0) < std::fabs(f1) ? x0 : x1;
|
|
||||||
}
|
|
||||||
static double handleTooManyIterations(const double x0, const double x1, const int /*maxiter*/)
|
|
||||||
{
|
|
||||||
return 0.5*(x0 + x1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <class ErrorPolicy = ThrowOnError>
|
|
||||||
class RegulaFalsi
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
|
|
||||||
/// Implements a modified regula falsi method as described in
|
|
||||||
/// "Improved algorithms of Illinois-type for the numerical
|
|
||||||
/// solution of nonlinear equations"
|
|
||||||
/// by J. A. Ford.
|
|
||||||
/// Current variant is the 'Pegasus' method.
|
|
||||||
template <class Functor>
|
|
||||||
inline static double solve(const Functor& f,
|
|
||||||
const double a,
|
|
||||||
const double b,
|
|
||||||
const int max_iter,
|
|
||||||
const double tolerance,
|
|
||||||
int& iterations_used)
|
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
const double macheps = numeric_limits<double>::epsilon();
|
|
||||||
const double eps = tolerance + macheps*max(max(fabs(a), fabs(b)), 1.0);
|
|
||||||
|
|
||||||
double x0 = a;
|
|
||||||
double x1 = b;
|
|
||||||
double f0 = f(x0);
|
|
||||||
const double epsF = tolerance + macheps*max(fabs(f0), 1.0);
|
|
||||||
if (fabs(f0) < epsF) {
|
|
||||||
return x0;
|
|
||||||
}
|
|
||||||
double f1 = f(x1);
|
|
||||||
if (fabs(f1) < epsF) {
|
|
||||||
return x1;
|
|
||||||
}
|
|
||||||
if (f0*f1 > 0.0) {
|
|
||||||
return ErrorPolicy::handleBracketingFailure(a, b, f0, f1);
|
|
||||||
}
|
|
||||||
iterations_used = 0;
|
|
||||||
// In every iteraton, x1 is the last point computed,
|
|
||||||
// and x0 is the last point computed that makes it a bracket.
|
|
||||||
while (fabs(x1 - x0) >= 1e-9*eps) {
|
|
||||||
double xnew = regulaFalsiStep(x0, x1, f0, f1);
|
|
||||||
double fnew = f(xnew);
|
|
||||||
// cout << "xnew = " << xnew << " fnew = " << fnew << endl;
|
|
||||||
++iterations_used;
|
|
||||||
if (iterations_used > max_iter) {
|
|
||||||
return ErrorPolicy::handleTooManyIterations(x0, x1, max_iter);
|
|
||||||
}
|
|
||||||
if (fabs(fnew) < epsF) {
|
|
||||||
return xnew;
|
|
||||||
}
|
|
||||||
// Now we must check which point we must replace.
|
|
||||||
if ((fnew > 0.0) == (f0 > 0.0)) {
|
|
||||||
// We must replace x0.
|
|
||||||
x0 = x1;
|
|
||||||
f0 = f1;
|
|
||||||
} else {
|
|
||||||
// We must replace x1, this is the case where
|
|
||||||
// the modification to regula falsi kicks in,
|
|
||||||
// by modifying f0.
|
|
||||||
// 1. The classic Illinois method
|
|
||||||
// const double gamma = 0.5;
|
|
||||||
// @afr: The next two methods do not work??!!?
|
|
||||||
// 2. The method called 'Method 3' in the paper.
|
|
||||||
// const double phi0 = f1/f0;
|
|
||||||
// const double phi1 = fnew/f1;
|
|
||||||
// const double gamma = 1.0 - phi1/(1.0 - phi0);
|
|
||||||
// 3. The method called 'Method 4' in the paper.
|
|
||||||
// const double phi0 = f1/f0;
|
|
||||||
// const double phi1 = fnew/f1;
|
|
||||||
// const double gamma = 1.0 - phi0 - phi1;
|
|
||||||
// cout << "phi0 = " << phi0 <<" phi1 = " << phi1 <<
|
|
||||||
// " gamma = " << gamma << endl;
|
|
||||||
// 4. The 'Pegasus' method
|
|
||||||
const double gamma = f1/(f1 + fnew);
|
|
||||||
f0 *= gamma;
|
|
||||||
}
|
|
||||||
x1 = xnew;
|
|
||||||
f1 = fnew;
|
|
||||||
}
|
|
||||||
return 0.5*(x0 + x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Implements a modified regula falsi method as described in
|
|
||||||
/// "Improved algorithms of Illinois-type for the numerical
|
|
||||||
/// solution of nonlinear equations"
|
|
||||||
/// by J. A. Ford.
|
|
||||||
/// Current variant is the 'Pegasus' method.
|
|
||||||
/// This version takes an extra parameter for the initial guess.
|
|
||||||
template <class Functor>
|
|
||||||
inline static double solve(const Functor& f,
|
|
||||||
const double initial_guess,
|
|
||||||
const double a,
|
|
||||||
const double b,
|
|
||||||
const int max_iter,
|
|
||||||
const double tolerance,
|
|
||||||
int& iterations_used)
|
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
const double macheps = numeric_limits<double>::epsilon();
|
|
||||||
const double eps = tolerance + macheps*max(max(fabs(a), fabs(b)), 1.0);
|
|
||||||
|
|
||||||
double f_initial = f(initial_guess);
|
|
||||||
const double epsF = tolerance + macheps*max(fabs(f_initial), 1.0);
|
|
||||||
if (fabs(f_initial) < epsF) {
|
|
||||||
return initial_guess;
|
|
||||||
}
|
|
||||||
double x0 = a;
|
|
||||||
double x1 = b;
|
|
||||||
double f0 = f_initial;
|
|
||||||
double f1 = f_initial;
|
|
||||||
if (x0 != initial_guess) {
|
|
||||||
f0 = f(x0);
|
|
||||||
if (fabs(f0) < epsF) {
|
|
||||||
return x0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (x1 != initial_guess) {
|
|
||||||
f1 = f(x1);
|
|
||||||
if (fabs(f1) < epsF) {
|
|
||||||
return x1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (f0*f_initial < 0.0) {
|
|
||||||
x1 = initial_guess;
|
|
||||||
f1 = f_initial;
|
|
||||||
} else {
|
|
||||||
x0 = initial_guess;
|
|
||||||
f0 = f_initial;
|
|
||||||
}
|
|
||||||
if (f0*f1 > 0.0) {
|
|
||||||
return ErrorPolicy::handleBracketingFailure(a, b, f0, f1);
|
|
||||||
}
|
|
||||||
iterations_used = 0;
|
|
||||||
// In every iteraton, x1 is the last point computed,
|
|
||||||
// and x0 is the last point computed that makes it a bracket.
|
|
||||||
while (fabs(x1 - x0) >= 1e-9*eps) {
|
|
||||||
double xnew = regulaFalsiStep(x0, x1, f0, f1);
|
|
||||||
double fnew = f(xnew);
|
|
||||||
// cout << "xnew = " << xnew << " fnew = " << fnew << endl;
|
|
||||||
++iterations_used;
|
|
||||||
if (iterations_used > max_iter) {
|
|
||||||
return ErrorPolicy::handleTooManyIterations(x0, x1, max_iter);
|
|
||||||
}
|
|
||||||
if (fabs(fnew) < epsF) {
|
|
||||||
return xnew;
|
|
||||||
}
|
|
||||||
// Now we must check which point we must replace.
|
|
||||||
if ((fnew > 0.0) == (f0 > 0.0)) {
|
|
||||||
// We must replace x0.
|
|
||||||
x0 = x1;
|
|
||||||
f0 = f1;
|
|
||||||
} else {
|
|
||||||
// We must replace x1, this is the case where
|
|
||||||
// the modification to regula falsi kicks in,
|
|
||||||
// by modifying f0.
|
|
||||||
// 1. The classic Illinois method
|
|
||||||
// const double gamma = 0.5;
|
|
||||||
// @afr: The next two methods do not work??!!?
|
|
||||||
// 2. The method called 'Method 3' in the paper.
|
|
||||||
// const double phi0 = f1/f0;
|
|
||||||
// const double phi1 = fnew/f1;
|
|
||||||
// const double gamma = 1.0 - phi1/(1.0 - phi0);
|
|
||||||
// 3. The method called 'Method 4' in the paper.
|
|
||||||
// const double phi0 = f1/f0;
|
|
||||||
// const double phi1 = fnew/f1;
|
|
||||||
// const double gamma = 1.0 - phi0 - phi1;
|
|
||||||
// cout << "phi0 = " << phi0 <<" phi1 = " << phi1 <<
|
|
||||||
// " gamma = " << gamma << endl;
|
|
||||||
// 4. The 'Pegasus' method
|
|
||||||
const double gamma = f1/(f1 + fnew);
|
|
||||||
f0 *= gamma;
|
|
||||||
}
|
|
||||||
x1 = xnew;
|
|
||||||
f1 = fnew;
|
|
||||||
}
|
|
||||||
return 0.5*(x0 + x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
inline static double regulaFalsiStep(const double a,
|
|
||||||
const double b,
|
|
||||||
const double fa,
|
|
||||||
const double fb)
|
|
||||||
{
|
|
||||||
assert(fa*fb < 0.0);
|
|
||||||
return (b*fa - a*fb)/(fa - fb);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Attempts to find an interval bracketing a zero by successive
|
|
||||||
/// enlargement of search interval.
|
|
||||||
template <class Functor>
|
|
||||||
inline void bracketZero(const Functor& f,
|
|
||||||
const double x0,
|
|
||||||
const double dx,
|
|
||||||
double& a,
|
|
||||||
double& b)
|
|
||||||
{
|
|
||||||
const int max_iters = 100;
|
|
||||||
double f0 = f(x0);
|
|
||||||
double cur_dx = dx;
|
|
||||||
int i = 0;
|
|
||||||
for (; i < max_iters; ++i) {
|
|
||||||
double x = x0 + cur_dx;
|
|
||||||
double f_new = f(x);
|
|
||||||
if (f0*f_new <= 0.0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cur_dx = -2.0*cur_dx;
|
|
||||||
}
|
|
||||||
if (i == max_iters) {
|
|
||||||
OPM_THROW(std::runtime_error, "Could not bracket zero in " << max_iters << "iterations.");
|
|
||||||
}
|
|
||||||
if (cur_dx < 0.0) {
|
|
||||||
a = x0 + cur_dx;
|
|
||||||
b = i < 2 ? x0 : x0 + 0.25*cur_dx;
|
|
||||||
} else {
|
|
||||||
a = i < 2 ? x0 : x0 + 0.25*cur_dx;
|
|
||||||
b = x0 + cur_dx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Opm
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // OPM_ROOTFINDERS_HEADER
|
|
@ -1,198 +0,0 @@
|
|||||||
//===========================================================================
|
|
||||||
//
|
|
||||||
// File: SparseVector.hpp
|
|
||||||
//
|
|
||||||
// Created: Mon Jun 29 15:28:59 2009
|
|
||||||
//
|
|
||||||
// Author(s): Atgeirr F Rasmussen <atgeirr@sintef.no>
|
|
||||||
// Bård Skaflestad <bard.skaflestad@sintef.no>
|
|
||||||
//
|
|
||||||
// $Date$
|
|
||||||
//
|
|
||||||
// $Revision$
|
|
||||||
//
|
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
|
||||||
Copyright 2009, 2010 Statoil ASA.
|
|
||||||
|
|
||||||
This file is part of the Open Porous Media project (OPM).
|
|
||||||
|
|
||||||
OPM is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
OPM is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef OPM_SPARSEVECTOR_HEADER
|
|
||||||
#define OPM_SPARSEVECTOR_HEADER
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <numeric>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <opm/common/ErrorMacros.hpp>
|
|
||||||
|
|
||||||
namespace Opm
|
|
||||||
{
|
|
||||||
|
|
||||||
/// A SparseVector stores a vector with possibly many empty elements
|
|
||||||
/// as efficiently as possible.
|
|
||||||
/// It is supposed to behave similarly to a standard vector, but since
|
|
||||||
/// direct indexing is a O(log n) operation instead of O(1), we do not
|
|
||||||
/// supply it as operator[].
|
|
||||||
template <typename T>
|
|
||||||
class SparseVector
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Default constructor. Yields an empty SparseVector.
|
|
||||||
SparseVector()
|
|
||||||
: size_(0), default_elem_()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a SparseVector with a given size, but no nonzero
|
|
||||||
/// elements.
|
|
||||||
explicit SparseVector(int sz)
|
|
||||||
: size_(sz), default_elem_()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A constructor taking all the element data for the vector and their indices.
|
|
||||||
/// \param data_beg The start of the element data.
|
|
||||||
/// \param data_end One-beyond-end of the element data.
|
|
||||||
/// \param rowsize_beg The start of the index data.
|
|
||||||
/// \param rowsize_end One beyond the end of the index data.
|
|
||||||
template <typename DataIter, typename IntegerIter>
|
|
||||||
SparseVector(int sz,
|
|
||||||
DataIter data_beg, DataIter data_end,
|
|
||||||
IntegerIter index_beg, IntegerIter index_end)
|
|
||||||
: size_(sz), data_(data_beg, data_end), indices_(index_beg, index_end),
|
|
||||||
default_elem_()
|
|
||||||
{
|
|
||||||
#ifndef NDEBUG
|
|
||||||
OPM_ERROR_IF(sz < 0, "The size of a SparseVector must be non-negative");
|
|
||||||
OPM_ERROR_IF(indices_.size() != data_.size(), "The number of indices of a SparseVector must equal to the number of entries");
|
|
||||||
int last_index = -1;
|
|
||||||
int num_ind = indices_.size();
|
|
||||||
for (int i = 0; i < num_ind; ++i) {
|
|
||||||
int index = indices_[i];
|
|
||||||
if (index <= last_index || index >= sz) {
|
|
||||||
OPM_THROW(std::logic_error, "Error in SparseVector construction, index is nonincreasing or out of range.");
|
|
||||||
}
|
|
||||||
last_index = index;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Appends an element to the vector. Note that this function does not
|
|
||||||
/// increase the size() of the vector, it just adds another nonzero element.
|
|
||||||
/// Elements must be added in index order.
|
|
||||||
void addElement(const T& elem, int index)
|
|
||||||
{
|
|
||||||
assert(indices_.empty() || index > indices_.back());
|
|
||||||
assert(index < size_);
|
|
||||||
data_.push_back(elem);
|
|
||||||
indices_.push_back(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \return true if the vector has size 0.
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return size_ == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the size of the vector.
|
|
||||||
/// Recall that most or all of the vector may be default/zero.
|
|
||||||
int size() const
|
|
||||||
{
|
|
||||||
return size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of nonzero data elements.
|
|
||||||
int nonzeroSize() const
|
|
||||||
{
|
|
||||||
return data_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes the vector empty().
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
data_.clear();
|
|
||||||
indices_.clear();
|
|
||||||
size_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Equality.
|
|
||||||
bool operator==(const SparseVector& other) const
|
|
||||||
{
|
|
||||||
return size_ == other.size_ && data_ == other.data_ && indices_ == other.indices_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// O(log n) element access.
|
|
||||||
/// \param index the proper vector index
|
|
||||||
/// \return the element with the given index, or the default element if no element in
|
|
||||||
/// the vector has the given index.
|
|
||||||
const T& element(int index) const
|
|
||||||
{
|
|
||||||
#ifndef NDEBUG
|
|
||||||
OPM_ERROR_IF(index < 0, "The index of a SparseVector must be non-negative (is " << index << ")");
|
|
||||||
OPM_ERROR_IF(index >= size_, "The index of a SparseVector must be smaller than the maximum value (is " << index << ", max value: " << size_ <<")");
|
|
||||||
#endif
|
|
||||||
std::vector<int>::const_iterator lb = std::lower_bound(indices_.begin(), indices_.end(), index);
|
|
||||||
if (lb != indices_.end() && *lb == index) {
|
|
||||||
return data_[lb - indices_.begin()];
|
|
||||||
} else {
|
|
||||||
return default_elem_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// O(1) element access.
|
|
||||||
/// \param nzindex an index counting only nonzero elements.
|
|
||||||
/// \return the nzindex'th nonzero element.
|
|
||||||
const T& nonzeroElement(int nzindex) const
|
|
||||||
{
|
|
||||||
#ifndef NDEBUG
|
|
||||||
OPM_ERROR_IF(nzindex < 0, "The index of a SparseVector must be non-negative (is " << nzindex << ")");
|
|
||||||
OPM_ERROR_IF(nzindex >= nonzeroSize(), "The index of a SparseVector must be smaller than the maximum value (is " << nzindex << ", max value: " << nonzeroSize() <<")");
|
|
||||||
#endif
|
|
||||||
return data_[nzindex];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// O(1) index access.
|
|
||||||
/// \param nzindex an index counting only nonzero elements.
|
|
||||||
/// \return the index of the nzindex'th nonzero element.
|
|
||||||
int nonzeroIndex(int nzindex) const
|
|
||||||
{
|
|
||||||
assert(nzindex >= 0);
|
|
||||||
assert(nzindex < nonzeroSize());
|
|
||||||
return indices_[nzindex];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// The vectors data_ and indices_ are always the same size.
|
|
||||||
// The indices are supposed to be stored in increasing order,
|
|
||||||
// to be unique, and to be in [0, size_ - 1].
|
|
||||||
// default_elem_ is returned when a default element is requested.
|
|
||||||
int size_;
|
|
||||||
std::vector<T> data_;
|
|
||||||
std::vector<int> indices_;
|
|
||||||
T default_elem_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Opm
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // OPM_SPARSEVECTOR_HEADER
|
|
@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2009, 2010, 2013 SINTEF ICT, Applied Mathematics.
|
|
||||||
Copyright 2009, 2010 Statoil ASA.
|
|
||||||
|
|
||||||
This file is part of the Open Porous Media project (OPM).
|
|
||||||
|
|
||||||
OPM is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
OPM is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OPM_LINEARINTERPOLATION_HEADER_INCLUDED
|
|
||||||
#define OPM_LINEARINTERPOLATION_HEADER_INCLUDED
|
|
||||||
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace Opm
|
|
||||||
{
|
|
||||||
|
|
||||||
inline int tableIndex(const std::vector<double>& table, double x)
|
|
||||||
{
|
|
||||||
// Returns an index in an ordered table such that x is between
|
|
||||||
// table[j] and table[j+1]. If x is out of range, first or last
|
|
||||||
// interval is returned; Binary search.
|
|
||||||
int n = table.size() - 1;
|
|
||||||
if (n < 2) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int jl = 0;
|
|
||||||
int ju = n;
|
|
||||||
bool ascend = (table[n] > table[0]);
|
|
||||||
while (ju - jl > 1) {
|
|
||||||
int jm = (ju + jl)/2; // Compute a midpoint
|
|
||||||
if ( (x >= table[jm]) == ascend) {
|
|
||||||
jl = jm; // Replace lower limit
|
|
||||||
} else {
|
|
||||||
ju = jm; // Replace upper limit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return jl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline double linearInterpolationDerivative(const std::vector<double>& xv,
|
|
||||||
const std::vector<double>& yv, double x)
|
|
||||||
{
|
|
||||||
// Extrapolates if x is outside xv
|
|
||||||
int ix1 = tableIndex(xv, x);
|
|
||||||
int ix2 = ix1 + 1;
|
|
||||||
return (yv[ix2] - yv[ix1])/(xv[ix2] - xv[ix1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline double linearInterpolation(const std::vector<double>& xv,
|
|
||||||
const std::vector<double>& yv, double x)
|
|
||||||
{
|
|
||||||
// Extrapolates if x is outside xv
|
|
||||||
int ix1 = tableIndex(xv, x);
|
|
||||||
int ix2 = ix1 + 1;
|
|
||||||
return (yv[ix2] - yv[ix1])/(xv[ix2] - xv[ix1])*(x - xv[ix1]) + yv[ix1];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline double linearInterpolationNoExtrapolation(const std::vector<double>& xv,
|
|
||||||
const std::vector<double>& yv, double x)
|
|
||||||
{
|
|
||||||
// Return end values if x is outside xv
|
|
||||||
if (x < xv.front()) {
|
|
||||||
return yv.front();
|
|
||||||
}
|
|
||||||
if (x > xv.back()) {
|
|
||||||
return yv.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ix1 = tableIndex(xv, x);
|
|
||||||
int ix2 = ix1 + 1;
|
|
||||||
return (yv[ix2] - yv[ix1])/(xv[ix2] - xv[ix1])*(x - xv[ix1]) + yv[ix1];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline double linearInterpolation(const std::vector<double>& xv,
|
|
||||||
const std::vector<double>& yv,
|
|
||||||
double x, int& ix1)
|
|
||||||
{
|
|
||||||
// Extrapolates if x is outside xv
|
|
||||||
ix1 = tableIndex(xv, x);
|
|
||||||
int ix2 = ix1 + 1;
|
|
||||||
return (yv[ix2] - yv[ix1])/(xv[ix2] - xv[ix1])*(x - xv[ix1]) + yv[ix1];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Opm
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // OPM_LINEARINTERPOLATION_HEADER_INCLUDED
|
|
@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
|
||||||
Copyright 2009, 2010 Statoil ASA.
|
|
||||||
|
|
||||||
This file is part of The Open Reservoir Simulator Project (OpenRS).
|
|
||||||
|
|
||||||
OpenRS is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
OpenRS is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with OpenRS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#if defined(HAVE_DYNAMIC_BOOST_TEST)
|
|
||||||
#define BOOST_TEST_DYN_LINK
|
|
||||||
#endif
|
|
||||||
#define NVERBOSE // to suppress our messages when throwing
|
|
||||||
|
|
||||||
|
|
||||||
#define BOOST_TEST_MODULE NonuniformTableLinearTests
|
|
||||||
#include <boost/test/unit_test.hpp>
|
|
||||||
|
|
||||||
#include <opm/core/utility/NonuniformTableLinear.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(utility_functions)
|
|
||||||
{
|
|
||||||
// Test isNondecreasing().
|
|
||||||
using Opm::isNondecreasing;
|
|
||||||
double xva1[] = { -1.0, 2.0, 2.2, 3.0, 5.0 };
|
|
||||||
const int numvals1 = sizeof(xva1)/sizeof(xva1[0]);
|
|
||||||
BOOST_CHECK(isNondecreasing(xva1, xva1 + numvals1));
|
|
||||||
double xva2[] = { -1.0, 2.0, 2.0, 2.0, 5.0 };
|
|
||||||
const int numvals2 = sizeof(xva2)/sizeof(xva2[0]);
|
|
||||||
BOOST_CHECK(isNondecreasing(xva2, xva2 + numvals2));
|
|
||||||
double xva3[] = { -1.0, 2.0, 1.9, 3.0, 5.0 };
|
|
||||||
const int numvals3 = sizeof(xva3)/sizeof(xva3[0]);
|
|
||||||
BOOST_CHECK(!isNondecreasing(xva3, xva3 + numvals3));
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(table_operations)
|
|
||||||
{
|
|
||||||
// Make a simple table.
|
|
||||||
double xva[] = { -1.0, 2.0, 2.2, 3.0, 5.0 };
|
|
||||||
const int numvals = sizeof(xva)/sizeof(xva[0]);
|
|
||||||
std::vector<double> xv(xva, xva + numvals);
|
|
||||||
double yva[numvals] = { 1.0, 2.0, 3.0, 4.0, 2.0 };
|
|
||||||
std::vector<double> yv(yva, yva + numvals);
|
|
||||||
Opm::NonuniformTableLinear<double> t1(xv, yv);
|
|
||||||
Opm::NonuniformTableLinear<double> t1_copy1(xv, yv);
|
|
||||||
Opm::NonuniformTableLinear<double> t1_copy2(t1);
|
|
||||||
|
|
||||||
// Check equality.
|
|
||||||
BOOST_CHECK(t1 == t1_copy1);
|
|
||||||
BOOST_CHECK(t1 == t1_copy2);
|
|
||||||
|
|
||||||
// Check some evaluations.
|
|
||||||
for (int i = 0; i < numvals; ++i) {
|
|
||||||
BOOST_CHECK_EQUAL(t1(xv[i]), yv[i]);
|
|
||||||
}
|
|
||||||
BOOST_CHECK_EQUAL(t1(2.6), 3.5);
|
|
||||||
BOOST_CHECK_EQUAL(t1(4.0), 3.0);
|
|
||||||
BOOST_CHECK_EQUAL(t1.derivative(4.0), -1.0);
|
|
||||||
// Derivatives at endpoints.
|
|
||||||
BOOST_CHECK_CLOSE(t1.derivative(-1.0), 1.0/3.0, 1e-13);
|
|
||||||
BOOST_CHECK_CLOSE(t1.derivative(5.0), -1.0, 1e-13);
|
|
||||||
// Extrapolation of values.
|
|
||||||
BOOST_CHECK_CLOSE(t1(xv[0] - 1.0), 2.0/3.0, 1e-13);
|
|
||||||
BOOST_CHECK_CLOSE(t1(xv.back() + 1.0), 1.0, 1e-13);
|
|
||||||
|
|
||||||
// Domains.
|
|
||||||
BOOST_CHECK_EQUAL(t1.domain().first, xv[0]);
|
|
||||||
BOOST_CHECK_EQUAL(t1.domain().second, xv.back());
|
|
||||||
std::pair<double, double> new_domain(-100.0, 20.0);
|
|
||||||
t1.rescaleDomain(new_domain);
|
|
||||||
BOOST_CHECK_EQUAL(t1.domain().first, new_domain.first);
|
|
||||||
BOOST_CHECK_EQUAL(t1.domain().second, new_domain.second);
|
|
||||||
for (int i = 0; i < numvals; ++i) {
|
|
||||||
BOOST_CHECK_EQUAL(t1((xv[i] + 1.0)*20.0 - 100.0), yv[i]);
|
|
||||||
}
|
|
||||||
BOOST_CHECK_EQUAL(t1(0.0), 3.0);
|
|
||||||
BOOST_CHECK(std::fabs(t1.derivative(0.0) + 1.0/20.0) < 1e-11);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user