#2125 Curve Calculations : Add tools used to interpolate two curves

This commit is contained in:
Magne Sjaastad 2017-11-17 13:18:39 +01:00
parent 5cbaf3c1eb
commit 8ee0ddb317
3 changed files with 517 additions and 2 deletions

View File

@ -19,7 +19,10 @@
#include "RigCurveDataTools.h"
#include <QDateTime>
#include <cmath> // Needed for HUGE_VAL on Linux
#include <set>
//--------------------------------------------------------------------------------------------------
@ -85,6 +88,7 @@ std::vector<std::pair<size_t, size_t>> RigCurveDataTools::computePolyLineStartSt
return lineStartAndStopIndices;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -102,3 +106,221 @@ bool RigCurveDataTools::isValidValue(double value, bool removeNegativeValues)
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RigCurveDataTools::isValidValue(double value)
{
if (value == HUGE_VAL || value == -HUGE_VAL || value != value)
{
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigCurveDataInterpolationTools::RigCurveDataInterpolationTools(const std::vector<double>& valuesA,
const std::vector<QDateTime>& timeStepsA,
const std::vector<double>& valuesB,
const std::vector<QDateTime>& timeStepsB)
: m_valuesA(valuesA),
m_timeStepsA(timeStepsA),
m_valuesB(valuesB),
m_timeStepsB(timeStepsB)
{
computeInterpolatedValues();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigCurveDataTools::CurveIntervals RigCurveDataInterpolationTools::validIntervals() const
{
return m_curveIntervals;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::tuple<QDateTime, double, double>> RigCurveDataInterpolationTools::interpolatedCurveData() const
{
return m_interpolatedValues;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigCurveDataInterpolationTools::computeInterpolatedValues()
{
if (m_valuesA.size() != m_timeStepsA.size() || m_valuesB.size() != m_timeStepsB.size())
{
return;
}
const bool removeNegativeValues = false;
auto validIntervalsA = RigCurveDataTools::calculateIntervalsOfValidValues(m_valuesA, removeNegativeValues);
std::vector<std::pair<QDateTime, QDateTime>> validTimeStepsA;
for (const auto& interval : validIntervalsA)
{
validTimeStepsA.push_back(std::make_pair(m_timeStepsA[interval.first], m_timeStepsA[interval.second]));
}
auto validIntervalsB = RigCurveDataTools::calculateIntervalsOfValidValues(m_valuesB, removeNegativeValues);
for (const auto& interval : validIntervalsB)
{
const QDateTime& from = m_timeStepsB[interval.first];
const QDateTime& to = m_timeStepsB[interval.second];
auto intervals = intersectingValidIntervals(from, to, validTimeStepsA);
for (const auto& i : intervals)
{
std::set<QDateTime> validTimeSteps;
// Add all time steps from curve A inside interval
for (const auto& d : m_timeStepsA)
{
if (i.first <= d && d <= i.second)
{
validTimeSteps.insert(d);
}
}
// Add all time steps from curve B inside interval
for (const auto& d : m_timeStepsB)
{
if (i.first <= d && d <= i.second)
{
validTimeSteps.insert(d);
}
}
size_t firstIndex = m_interpolatedValues.size();
for (const auto& dt : validTimeSteps)
{
double valueA = RigCurveDataInterpolationTools::interpolatedValue(dt, m_valuesA, m_timeStepsA);
double valueB = RigCurveDataInterpolationTools::interpolatedValue(dt, m_valuesB, m_timeStepsB);
m_interpolatedValues.push_back(std::make_tuple(dt, valueA, valueB));
}
size_t lastIndex = m_interpolatedValues.size() - 1;
m_curveIntervals.push_back(std::make_pair(firstIndex, lastIndex));
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::pair<QDateTime, QDateTime>> RigCurveDataInterpolationTools::intersectingValidIntervals(const QDateTime& a,
const QDateTime& b,
const std::vector<std::pair<QDateTime, QDateTime>>& intervals)
{
std::vector<std::pair<QDateTime, QDateTime>> validIntervals;
for (const auto& interval : intervals)
{
const QDateTime& c = interval.first;
const QDateTime& d = interval.second;
if (d < a)
{
continue;
}
if (b < c)
{
// We assume the intervals are increasing, and all other intervals are larger
break;
}
if (c <= a)
{
if (b <= d)
{
validIntervals.push_back(std::make_pair(a, b));
}
else
{
validIntervals.push_back(std::make_pair(a, d));
}
}
else
{
if (b <= d)
{
validIntervals.push_back(std::make_pair(c, b));
}
else
{
validIntervals.push_back(std::make_pair(c, d));
}
}
}
return validIntervals;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigCurveDataInterpolationTools::interpolatedValue(const QDateTime& dt, const std::vector<double>& values, const std::vector<QDateTime>& timeSteps)
{
if (values.size() != timeSteps.size()) return HUGE_VAL;
for (size_t firstI = 0; firstI < timeSteps.size(); firstI++)
{
size_t secondI = firstI + 1;
if (secondI == timeSteps.size())
{
if (timeSteps[firstI] == dt)
{
return values[firstI];
}
}
if (secondI < timeSteps.size() &&
timeSteps[firstI] <= dt &&
timeSteps[secondI] > dt)
{
const double& firstValue = values[firstI];
const double& secondValue = values[secondI];
bool isFirstValid = RigCurveDataTools::isValidValue(firstValue);
bool isSecondValid = RigCurveDataTools::isValidValue(secondValue);
if (!isFirstValid && !isSecondValid)
{
CVF_ASSERT(false);
return HUGE_VAL;
}
if (!isFirstValid) return secondValue;
if (!isSecondValid) return firstValue;
double firstDiff = timeSteps[firstI].secsTo(dt);
double secondDiff = dt.secsTo(timeSteps[secondI]);
double firstWeight = secondDiff / (firstDiff + secondDiff);
double secondWeight = firstDiff / (firstDiff + secondDiff);
double val = (firstValue * firstWeight) + (secondValue * secondWeight);
CVF_ASSERT(RigCurveDataTools::isValidValue(val));
return val;
}
}
return HUGE_VAL;
}

View File

@ -23,6 +23,10 @@
#include <cstddef>
#include <vector>
#include <utility>
#include <tuple>
class QDateTime;
//==================================================================================================
@ -54,7 +58,53 @@ public:
}
static std::vector<std::pair<size_t, size_t>> computePolyLineStartStopIndices(const CurveIntervals& intervals);
private:
public:
// Helper methods, available as public to be able to access from unit tests
static bool isValidValue(double value, bool removeNegativeValues);
static bool isValidValue(double value);
};
//==================================================================================================
///
//==================================================================================================
class RigCurveDataInterpolationTools
{
public:
RigCurveDataInterpolationTools(const std::vector<double>& valuesA,
const std::vector<QDateTime>& timeStepsA,
const std::vector<double>& valuesB,
const std::vector<QDateTime>& timeStepsB);
RigCurveDataTools::CurveIntervals validIntervals() const;
std::vector<std::tuple<QDateTime, double, double>> interpolatedCurveData() const;
public:
// Helper methods, available as public to be able to access from unit tests
static std::vector<std::pair<QDateTime, QDateTime>> intersectingValidIntervals(const QDateTime& from,
const QDateTime& to,
const std::vector<std::pair<QDateTime, QDateTime>>& intervals);
static double interpolatedValue(const QDateTime& dt,
const std::vector<double>& values,
const std::vector<QDateTime>& timeSteps);
private:
void computeInterpolatedValues();
private:
const std::vector<double>& m_valuesA;
const std::vector<QDateTime>& m_timeStepsA;
const std::vector<double>& m_valuesB;
const std::vector<QDateTime>& m_timeStepsB;
std::vector<std::tuple<QDateTime, double, double>> m_interpolatedValues;
RigCurveDataTools::CurveIntervals m_curveIntervals;
};

View File

@ -3,6 +3,8 @@
#include "RigCurveDataTools.h"
#include <cmath> // Needed for HUGE_VAL on Linux
#include "QDateTime"
#include <numeric>
//--------------------------------------------------------------------------------------------------
@ -49,3 +51,244 @@ TEST(RimWellLogExtractionCurveImplTest, StripOffHugeValAtEndsAndInteriorOfVector
EXPECT_EQ(5, static_cast<int>(valuesIntervals[1].first));
EXPECT_EQ(6, static_cast<int>(valuesIntervals[1].second));
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(RimWellLogExtractionCurveImplTest, DateOverlap)
{
std::vector<std::pair<int, int>> dayIntervalsA { {5, 7}, {9, 12}, {15, 15}, {20, 30} , {30, 31}};
std::vector<std::pair<int, int>> dayIntervalsB { {3, 5}, {8, 13}, {15, 15}, {21, 22}, {25, 27}};
QDateTime startDate;
std::vector<std::pair<QDateTime, QDateTime>> intervalsA;
for (const auto& interval : dayIntervalsA)
{
intervalsA.push_back(std::make_pair(startDate.addDays(interval.first), startDate.addDays(interval.second)));
}
std::vector<std::pair<QDateTime, QDateTime>> intervalsB;
for (const auto& interval : dayIntervalsB)
{
intervalsB.push_back(std::make_pair(startDate.addDays(interval.first), startDate.addDays(interval.second)));
}
std::vector<std::pair<int, int>> allDayIntervals;
for (const auto& intervalA : intervalsA)
{
auto intersecting = RigCurveDataInterpolationTools::intersectingValidIntervals(intervalA.first, intervalA.second, intervalsB);
for (const auto& i : intersecting)
{
allDayIntervals.push_back(std::make_pair(startDate.daysTo(i.first), startDate.daysTo(i.second)));
}
}
EXPECT_EQ(5, static_cast<int>(allDayIntervals.size()));
EXPECT_EQ(5, allDayIntervals[0].first);
EXPECT_EQ(5, allDayIntervals[0].second);
EXPECT_EQ( 9, allDayIntervals[1].first);
EXPECT_EQ(12, allDayIntervals[1].second);
EXPECT_EQ(15, allDayIntervals[2].first);
EXPECT_EQ(15, allDayIntervals[2].second);
EXPECT_EQ(21, allDayIntervals[3].first);
EXPECT_EQ(22, allDayIntervals[3].second);
EXPECT_EQ(25, allDayIntervals[4].first);
EXPECT_EQ(27, allDayIntervals[4].second);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(RimWellLogExtractionCurveImplTest, TestDateInterpolation)
{
std::vector<double> values{ 2.0, 3.5, 5.0, 6.0};
std::vector<int> days{ 1, 5, 10, 15};
QDateTime startDate;
std::vector<QDateTime> timeSteps;
for (const auto& day : days)
{
timeSteps.push_back(startDate.addDays(day));
}
{
QDateTime dt = startDate.addDays(1);
double val = RigCurveDataInterpolationTools::interpolatedValue(dt, values, timeSteps);
EXPECT_EQ(2.0, val);
}
{
QDateTime dt = startDate.addDays(0);
double val = RigCurveDataInterpolationTools::interpolatedValue(dt, values, timeSteps);
EXPECT_EQ(HUGE_VAL, val);
}
{
QDateTime dt = startDate.addDays(20);
double val = RigCurveDataInterpolationTools::interpolatedValue(dt, values, timeSteps);
EXPECT_EQ(HUGE_VAL, val);
}
{
QDateTime dt = startDate.addDays(3);
double val = RigCurveDataInterpolationTools::interpolatedValue(dt, values, timeSteps);
EXPECT_EQ(2.75, val);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(RimWellLogExtractionCurveImplTest, ExtractIntervalsWithSameTimeSteps)
{
std::vector<double> valuesA { HUGE_VAL, 1.0, HUGE_VAL, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, HUGE_VAL };
std::vector<double> valuesB { 10, 20, 30, 40, 45, HUGE_VAL, HUGE_VAL, 5.0, 6.0, HUGE_VAL };
EXPECT_EQ(valuesA.size(), valuesB.size());
std::vector<int> days(10);
std::iota(days.begin(), days.end(), 10);
QDateTime startDate;
std::vector<QDateTime> timeSteps;
for (const auto& day : days)
{
timeSteps.push_back(startDate.addDays(day));
}
RigCurveDataInterpolationTools interpolate(valuesA, timeSteps, valuesB, timeSteps);
auto values = interpolate.interpolatedCurveData();
auto intervals = interpolate.validIntervals();
EXPECT_EQ(5, static_cast<int>(values.size()));
EXPECT_EQ(3, static_cast<int>(intervals.size()));
EXPECT_EQ( 1.0, std::get<1>(values[0]));
EXPECT_EQ(20.0, std::get<2>(values[0]));
EXPECT_EQ( 2.0, std::get<1>(values[1]));
EXPECT_EQ(40.0, std::get<2>(values[1]));
EXPECT_EQ( 2.5, std::get<1>(values[2]));
EXPECT_EQ(45.0, std::get<2>(values[2]));
EXPECT_EQ(5.0, std::get<1>(values[3]));
EXPECT_EQ(5.0, std::get<2>(values[3]));
EXPECT_EQ(6.0, std::get<1>(values[4]));
EXPECT_EQ(6.0, std::get<2>(values[4]));
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(RimWellLogExtractionCurveImplTest, ExtractIntervalsWithSameTimeStepsOneComplete)
{
std::vector<double> valuesA { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 };
std::vector<double> valuesB { 10, 20, 30, HUGE_VAL, 50, HUGE_VAL, 70 };
EXPECT_EQ(valuesA.size(), valuesB.size());
std::vector<int> days(7);
std::iota(days.begin(), days.end(), 10);
QDateTime startDate;
std::vector<QDateTime> timeSteps;
for (const auto& day : days)
{
timeSteps.push_back(startDate.addDays(day));
}
RigCurveDataInterpolationTools interpolate(valuesA, timeSteps, valuesB, timeSteps);
auto values = interpolate.interpolatedCurveData();
auto intervals = interpolate.validIntervals();
EXPECT_EQ(5, static_cast<int>(values.size()));
EXPECT_EQ(3, static_cast<int>(intervals.size()));
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(RimWellLogExtractionCurveImplTest, ExtractIntervalsWithSameTimeStepsBothComplete)
{
std::vector<double> valuesA{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 };
std::vector<double> valuesB{ 10, 20, 30, 40, 50, 60, 70 };
EXPECT_EQ(valuesA.size(), valuesB.size());
std::vector<int> days(7);
std::iota(days.begin(), days.end(), 10);
QDateTime startDate;
std::vector<QDateTime> timeSteps;
for (const auto& day : days)
{
timeSteps.push_back(startDate.addDays(day));
}
RigCurveDataInterpolationTools interpolate(valuesA, timeSteps, valuesB, timeSteps);
auto values = interpolate.interpolatedCurveData();
auto intervals = interpolate.validIntervals();
EXPECT_EQ(7, static_cast<int>(values.size()));
EXPECT_EQ(1, static_cast<int>(intervals.size()));
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(RimWellLogExtractionCurveImplTest, OverlappintTimes)
{
std::vector<double> valuesA{ 1, 2, 3, 4, 5 };
std::vector<double> valuesB{ 10, 20, 30, 40, 50 };
EXPECT_EQ(valuesA.size(), valuesB.size());
std::vector<int> daysA{ 0, 10, 11, 15, 20 };
std::vector<int> daysB{ 1, 2, 3, 5, 7 };
QDateTime startDate;
std::vector<QDateTime> timeStepsA;
for (const auto& day : daysA)
{
timeStepsA.push_back(startDate.addDays(day));
}
std::vector<QDateTime> timeStepsB;
for (const auto& day : daysB)
{
timeStepsB.push_back(startDate.addDays(day));
}
RigCurveDataInterpolationTools interpolate(valuesA, timeStepsA, valuesB, timeStepsB);
auto values = interpolate.interpolatedCurveData();
auto intervals = interpolate.validIntervals();
EXPECT_EQ(5, static_cast<int>(values.size()));
EXPECT_EQ(1, static_cast<int>(intervals.size()));
}