mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
230 lines
8.3 KiB
C++
230 lines
8.3 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2020 Equinor ASA
|
|
//
|
|
// ResInsight 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.
|
|
//
|
|
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
|
|
// for more details.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "RiaInterpolationTools.h"
|
|
|
|
#include "cafAssert.h"
|
|
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <limits>
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool almostEqual( double a, double b, double maxRelDiff = std::numeric_limits<double>::epsilon() * 128 )
|
|
{
|
|
// Calculate the difference.
|
|
double diff = std::fabs( a - b );
|
|
double fabsa = std::fabs( a );
|
|
double fabsb = std::fabs( b );
|
|
// Find the largest
|
|
double largest = ( fabsb > fabsa ) ? fabsb : fabsa;
|
|
return ( diff <= largest * maxRelDiff );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RiaInterpolationTools::linear( const std::vector<double>& x, const std::vector<double>& y, double value, ExtrapolationMode extrapolationMode )
|
|
{
|
|
CAF_ASSERT( x.size() == y.size() );
|
|
|
|
// Handle cases with only one data point.
|
|
if ( x.size() <= 1 )
|
|
{
|
|
return std::numeric_limits<double>::infinity();
|
|
}
|
|
|
|
// Find the lower boundary
|
|
bool found = false;
|
|
int lowerIndex = 0;
|
|
for ( int i = 0; i < static_cast<int>( x.size() - 1 ); i++ )
|
|
{
|
|
if ( x[i] <= value && x[i + 1] >= value )
|
|
{
|
|
lowerIndex = i;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
// Value is outside of the defined range
|
|
if ( !found )
|
|
{
|
|
// Check if we are just outside the boundaries
|
|
if ( almostEqual( value, x[0] ) )
|
|
return y[0];
|
|
else if ( almostEqual( value, x[x.size() - 1] ) )
|
|
return y[x.size() - 1];
|
|
|
|
if ( extrapolationMode == ExtrapolationMode::CLOSEST )
|
|
{
|
|
return extrapolateClosestValue( x, y, value );
|
|
}
|
|
else if ( extrapolationMode == ExtrapolationMode::TREND )
|
|
{
|
|
return extrapolate( x, y, value );
|
|
}
|
|
|
|
CAF_ASSERT( extrapolationMode == ExtrapolationMode::NONE );
|
|
return std::numeric_limits<double>::infinity();
|
|
}
|
|
|
|
int upperIndex = lowerIndex + 1;
|
|
|
|
double lowerX = x[lowerIndex];
|
|
double lowerY = y[lowerIndex];
|
|
double upperX = x[upperIndex];
|
|
double upperY = y[upperIndex];
|
|
|
|
double deltaY = upperY - lowerY;
|
|
double deltaX = upperX - lowerX;
|
|
|
|
return lowerY + ( ( value - lowerX ) / deltaX ) * deltaY;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RiaInterpolationTools::extrapolate( const std::vector<double>& x, const std::vector<double>& y, double value )
|
|
{
|
|
return y[0] + ( value - x[0] ) / ( x[1] - x[0] ) * ( y[1] - y[0] );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RiaInterpolationTools::extrapolateClosestValue( const std::vector<double>& x, const std::vector<double>& y, double value )
|
|
{
|
|
if ( value <= x[0] )
|
|
return y[0];
|
|
else
|
|
return y[x.size() - 1];
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
int RiaInterpolationTools::findNextDataPoint( const std::vector<double>& values, int index )
|
|
{
|
|
for ( size_t i = index; i < values.size(); i++ )
|
|
{
|
|
if ( values[i] != std::numeric_limits<double>::infinity() ) return static_cast<int>( i );
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
int RiaInterpolationTools::findPreviousDataPoint( const std::vector<double>& values, int index )
|
|
{
|
|
CAF_ASSERT( index >= 0 );
|
|
|
|
for ( int i = index; i >= 0; i-- )
|
|
{
|
|
if ( values[i] != std::numeric_limits<double>::infinity() ) return static_cast<int>( i );
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
int RiaInterpolationTools::extrapolateRange( int start, int end, int firstPoint, int lastPoint, const std::vector<double>& x, std::vector<double>& y )
|
|
{
|
|
std::vector<double> xs = { x[firstPoint], x[lastPoint] };
|
|
std::vector<double> ys = { y[firstPoint], y[lastPoint] };
|
|
for ( int index = start; index < end; index++ )
|
|
{
|
|
// Avoid excessive extrapolation when points are very close
|
|
if ( almostEqual( xs[0], xs[1] ) || almostEqual( ys[0], ys[1] ) )
|
|
y[index] = ys[0];
|
|
else
|
|
y[index] = extrapolate( xs, ys, x[index] );
|
|
}
|
|
|
|
return end;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
int RiaInterpolationTools::interpolateRange( int start, int end, int firstPoint, int lastPoint, const std::vector<double>& x, std::vector<double>& y )
|
|
{
|
|
CAF_ASSERT( start <= end );
|
|
|
|
std::vector<double> xs = { x[firstPoint], x[lastPoint] };
|
|
std::vector<double> ys = { y[firstPoint], y[lastPoint] };
|
|
for ( int index = start; index < end; index++ )
|
|
{
|
|
y[index] = RiaInterpolationTools::linear( xs, ys, x[index] );
|
|
}
|
|
|
|
return end;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RiaInterpolationTools::interpolateMissingValues( const std::vector<double>& x, std::vector<double>& y )
|
|
{
|
|
CAF_ASSERT( x.size() == y.size() );
|
|
|
|
int index = 0;
|
|
|
|
// Previous index which is not inf
|
|
int prevSetIndex = -1;
|
|
|
|
while ( index < static_cast<int>( y.size() ) )
|
|
{
|
|
// Missing values are inf in the input data
|
|
if ( y[index] == std::numeric_limits<double>::infinity() )
|
|
{
|
|
// Find the next index with a value
|
|
int nextSetIndex = findNextDataPoint( y, index + 1 );
|
|
|
|
if ( prevSetIndex == -1 )
|
|
{
|
|
// The first value is inf: need to find next two valid points and extrapolate
|
|
int nextSetIndex2 = findNextDataPoint( y, nextSetIndex + 1 );
|
|
index = extrapolateRange( index, nextSetIndex, nextSetIndex, nextSetIndex2, x, y );
|
|
}
|
|
else if ( nextSetIndex == -1 )
|
|
{
|
|
// The last value is inf: extrapolate from two last data points
|
|
int prevSetIndex2 = findPreviousDataPoint( y, prevSetIndex - 1 );
|
|
index = extrapolateRange( index, (int)y.size(), prevSetIndex2, prevSetIndex, x, y );
|
|
}
|
|
else if ( nextSetIndex != static_cast<int>( y.size() ) )
|
|
{
|
|
// The missing values somewhere between non-inf data: interpolate all the values
|
|
index = interpolateRange( index, nextSetIndex, prevSetIndex, nextSetIndex, x, y );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Nothing to do for the values which are not missing
|
|
prevSetIndex = index;
|
|
++index;
|
|
}
|
|
}
|
|
}
|