mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
Merge pull request #4674 from OPM/feature-ensemble-rft
#4634 and #4635: Merge feature-ensemble-rft branch
This commit is contained in:
commit
2795855621
@ -409,7 +409,7 @@ void RimWellLogRftCurve::onLoadDataAndUpdate(bool updateParentPlot)
|
||||
|
||||
if (tvDepthVector.size() != measuredDepthVector.size())
|
||||
{
|
||||
measuredDepthVector = interpolatedMeasuredDepthValuesFromObservedData(tvDepthVector);
|
||||
measuredDepthVector = interpolatedMeasuredDepthValuesFromWellPathOrObservedData(tvDepthVector);
|
||||
if (measuredDepthVector.size() == tvDepthVector.size())
|
||||
{
|
||||
m_derivingMDFromObservedData = true;
|
||||
@ -900,23 +900,34 @@ std::vector<double> RimWellLogRftCurve::measuredDepthValues()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double> RimWellLogRftCurve::interpolatedMeasuredDepthValuesFromObservedData(const std::vector<double>& tvDepthValues)
|
||||
std::vector<double> RimWellLogRftCurve::interpolatedMeasuredDepthValuesFromWellPathOrObservedData(const std::vector<double>& tvDepthValues)
|
||||
{
|
||||
RimProject* proj = RiaApplication::instance()->project();
|
||||
RimWellPath* wellPath = proj->wellPathByName(m_wellName);
|
||||
|
||||
std::vector<double> interpolatedMdValues;
|
||||
if (m_observedFmuRftData)
|
||||
if (wellPath)
|
||||
{
|
||||
const std::vector<double>& mdValuesOfWellPath = wellPath->wellPathGeometry()->measureDepths();
|
||||
std::vector<double> tvdValuesOfWellPath = wellPath->wellPathGeometry()->trueVerticalDepths();
|
||||
|
||||
interpolatedMdValues = RigWellPathGeometryTools::interpolateMdFromTvd(mdValuesOfWellPath, tvdValuesOfWellPath, tvDepthValues);
|
||||
CVF_ASSERT(interpolatedMdValues.size() == tvDepthValues.size());
|
||||
}
|
||||
else if (m_observedFmuRftData)
|
||||
{
|
||||
RifReaderRftInterface* reader = m_observedFmuRftData->rftReader();
|
||||
if (reader)
|
||||
{
|
||||
std::vector<double> tvdValuesOfObbservedData;
|
||||
std::vector<double> mdValuesOfObbservedData;
|
||||
std::vector<double> tvdValuesOfObservedData;
|
||||
std::vector<double> mdValuesOfObservedData;
|
||||
|
||||
RifEclipseRftAddress tvdAddress(m_wellName(), m_timeStep, RifEclipseRftAddress::TVD);
|
||||
RifEclipseRftAddress mdAddress(m_wellName(), m_timeStep, RifEclipseRftAddress::MD);
|
||||
|
||||
reader->values(tvdAddress, &tvdValuesOfObbservedData);
|
||||
reader->values(mdAddress, &mdValuesOfObbservedData);
|
||||
interpolatedMdValues = RigWellPathGeometryTools::interpolateMdFromTvd(mdValuesOfObbservedData, tvdValuesOfObbservedData, tvDepthValues);
|
||||
reader->values(tvdAddress, &tvdValuesOfObservedData);
|
||||
reader->values(mdAddress, &mdValuesOfObservedData);
|
||||
interpolatedMdValues = RigWellPathGeometryTools::interpolateMdFromTvd(mdValuesOfObservedData, tvdValuesOfObservedData, tvDepthValues);
|
||||
CVF_ASSERT(interpolatedMdValues.size() == tvDepthValues.size());
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ private:
|
||||
std::vector<double> xValues();
|
||||
std::vector<double> tvDepthValues();
|
||||
std::vector<double> measuredDepthValues();
|
||||
std::vector<double> interpolatedMeasuredDepthValuesFromObservedData(const std::vector<double>& tvDepthValues);
|
||||
std::vector<double> interpolatedMeasuredDepthValuesFromWellPathOrObservedData(const std::vector<double>& tvDepthValues);
|
||||
|
||||
private:
|
||||
caf::PdmPtrField<RimEclipseResultCase*> m_eclipseResultCase;
|
||||
|
@ -408,3 +408,16 @@ const std::vector<double>& RigWellPath::measureDepths() const
|
||||
return m_measuredDepths;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double> RigWellPath::trueVerticalDepths() const
|
||||
{
|
||||
std::vector<double> tvds;
|
||||
for (const cvf::Vec3d& point : m_wellPathPoints)
|
||||
{
|
||||
tvds.push_back(std::fabs(point.z()));
|
||||
}
|
||||
return tvds;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ public:
|
||||
|
||||
const std::vector<cvf::Vec3d>& wellPathPoints() const;
|
||||
const std::vector<double>& measureDepths() const;
|
||||
std::vector<double> trueVerticalDepths() const;
|
||||
|
||||
RigWellPath();
|
||||
void setDatumElevation(double value);
|
||||
|
@ -94,52 +94,89 @@ std::vector<double> RigWellPathGeometryTools::interpolateMdFromTvd(const std::ve
|
||||
std::vector<double> interpolatedMdValues;
|
||||
interpolatedMdValues.reserve(tvdValuesToInterpolateFrom.size());
|
||||
|
||||
std::vector<double>::const_iterator last_it = originalTvdValues.begin();
|
||||
for (std::vector<double>::const_iterator it = tvdValuesToInterpolateFrom.begin(); it != tvdValuesToInterpolateFrom.end(); ++it)
|
||||
{
|
||||
double tvdValue = *it;
|
||||
double sign = 0.0;
|
||||
if (it != tvdValuesToInterpolateFrom.begin())
|
||||
{
|
||||
sign = *it - *(it - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
sign = *(it + 1) - *it;
|
||||
}
|
||||
if (std::fabs(sign) < 1.0e-8)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sign /= std::fabs(sign);
|
||||
QwtSpline spline = createSpline(originalMdValues, originalTvdValues);
|
||||
std::vector<int> segmentStartIndices = findSplineSegmentsContainingRoots(spline, tvdValuesToInterpolateFrom);
|
||||
|
||||
auto current_it = last_it;
|
||||
// Is incrementing current_it taking us closer to the TVD value we want?
|
||||
while (current_it != originalTvdValues.end())
|
||||
for (size_t i = 0; i < segmentStartIndices.size(); ++i)
|
||||
{
|
||||
double currentTVDValue = tvdValuesToInterpolateFrom[i];
|
||||
double startMD = spline.points().front().x();
|
||||
double endMD = spline.points().back().y();
|
||||
if (segmentStartIndices[i] != -1)
|
||||
{
|
||||
if (*current_it * sign >= tvdValue * sign)
|
||||
int startIndex = segmentStartIndices[i];
|
||||
int endIndex = startIndex + 1;
|
||||
|
||||
// Search interval for best MD value
|
||||
startMD = spline.points()[startIndex].x();
|
||||
endMD = spline.points().back().y();
|
||||
|
||||
if (endIndex < spline.points().size())
|
||||
{
|
||||
if (!interpolatedMdValues.empty())
|
||||
{
|
||||
double mdDiff = 0.0;
|
||||
if (interpolatedMdValues.size() > 1)
|
||||
{
|
||||
mdDiff = interpolatedMdValues[i - 1] - interpolatedMdValues[i - 2];
|
||||
}
|
||||
startMD = std::max(startMD, interpolatedMdValues.back() + 0.1 * mdDiff);
|
||||
}
|
||||
endMD = spline.points()[endIndex].x();
|
||||
}
|
||||
}
|
||||
double mdValue = solveForX(spline, startMD, endMD, currentTVDValue);
|
||||
interpolatedMdValues.push_back(mdValue);
|
||||
}
|
||||
return interpolatedMdValues;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<int> RigWellPathGeometryTools::findSplineSegmentsContainingRoots(const QwtSpline& spline, const std::vector<double>& tvdValuesToInterpolateFrom)
|
||||
{
|
||||
std::vector<int> segmentStartIndices;
|
||||
segmentStartIndices.reserve(tvdValuesToInterpolateFrom.size());
|
||||
|
||||
int lastSplineStartIndex = 0;
|
||||
for (double tvdValue : tvdValuesToInterpolateFrom)
|
||||
{
|
||||
int currentSplineStartIndex = lastSplineStartIndex;
|
||||
|
||||
bool foundMatch = false;
|
||||
// Increment current_it until we find an interval containing our TVD
|
||||
while (currentSplineStartIndex < spline.points().size() - 2)
|
||||
{
|
||||
double diffCurrent = spline.points()[currentSplineStartIndex].y() - tvdValue;
|
||||
if (std::abs(diffCurrent) < 1.0e-8) // Current is matching the point
|
||||
{
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
auto next_it = current_it + 1;
|
||||
if (next_it != originalTvdValues.end())
|
||||
{
|
||||
double originalDataSign = (*next_it - *current_it);
|
||||
originalDataSign /= std::fabs(originalDataSign);
|
||||
if (originalDataSign * sign < 0.0)
|
||||
break;
|
||||
}
|
||||
current_it = next_it;
|
||||
}
|
||||
int nextStartIndex = currentSplineStartIndex + 1;
|
||||
|
||||
int valueIndex = static_cast<int>(current_it - originalTvdValues.begin());
|
||||
double mdValue = linearInterpolation(originalTvdValues, originalMdValues, valueIndex, tvdValue);
|
||||
interpolatedMdValues.push_back(mdValue);
|
||||
last_it = current_it;
|
||||
double diffNext = spline.points()[nextStartIndex].y() - tvdValue;
|
||||
if (diffCurrent * diffNext < 0.0) // One is above, the other is below
|
||||
{
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
currentSplineStartIndex = nextStartIndex;
|
||||
}
|
||||
if (foundMatch)
|
||||
{
|
||||
segmentStartIndices.push_back(currentSplineStartIndex);
|
||||
lastSplineStartIndex = currentSplineStartIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
segmentStartIndices.push_back(-1);
|
||||
}
|
||||
}
|
||||
return interpolatedMdValues;
|
||||
|
||||
return segmentStartIndices;
|
||||
}
|
||||
|
||||
std::vector<cvf::Vec3d> RigWellPathGeometryTools::interpolateUndefinedNormals(const cvf::Vec3d& planeNormal,
|
||||
@ -224,20 +261,93 @@ cvf::Vec3d RigWellPathGeometryTools::estimateDominantDirectionInXYPlane(const st
|
||||
return directionSum.getNormalized();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Golden-section minimization: https://en.wikipedia.org/wiki/Golden-section_search
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigWellPathGeometryTools::solveForX(const QwtSpline& spline, double minX, double maxX, double y)
|
||||
{
|
||||
const double phi = (1.0 + std::sqrt(5.0)) / 2.0;
|
||||
const double tol = 1.0e-8;
|
||||
|
||||
double a = minX, b = maxX;
|
||||
double c = b - (b - a) / phi;
|
||||
double d = a + (b - a) / phi;
|
||||
|
||||
double fa = spline.value(a) - y;
|
||||
double fb = spline.value(b) - y;
|
||||
double fc = spline.value(c) - y;
|
||||
double fd = spline.value(d) - y;
|
||||
|
||||
for (int n = 0; n < 100; ++n)
|
||||
{
|
||||
if (std::fabs(c - d) < tol)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (std::fabs(fc) < std::fabs(fd))
|
||||
{
|
||||
b = d;
|
||||
fb = fd;
|
||||
d = c;
|
||||
fd = fc;
|
||||
c = b - (b - a) / phi;
|
||||
fc = spline.value(c) - y;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = c;
|
||||
fa = fc;
|
||||
c = d;
|
||||
fc = fd;
|
||||
d = a + (b - a) / phi;
|
||||
fd = spline.value(d) - y;
|
||||
}
|
||||
}
|
||||
return (a + b) / 2.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigWellPathGeometryTools::linearInterpolation(const std::vector<double>& xValues, const std::vector<double>& yValues, int valueIndex, double x)
|
||||
QwtSpline RigWellPathGeometryTools::createSpline(const std::vector<double>& originalMdValues, const std::vector<double>& originalTvdValues)
|
||||
{
|
||||
int N = (int) xValues.size() - 1;
|
||||
int i = cvf::Math::clamp(valueIndex, 1, N);
|
||||
|
||||
double x1 = xValues[i - 1];
|
||||
double x2 = xValues[i];
|
||||
double y1 = yValues[i - 1];
|
||||
double y2 = yValues[i];
|
||||
QPolygonF polygon;
|
||||
for (size_t i = 0; i < originalMdValues.size(); ++i)
|
||||
{
|
||||
polygon << QPointF(originalMdValues[i], originalTvdValues[i]);
|
||||
}
|
||||
QwtSplineCurveFitter curveFitter;
|
||||
QPolygonF splinePoints = curveFitter.fitCurve(polygon);
|
||||
|
||||
double M = (y2 - y1) / (x2 - x1);
|
||||
// Extend spline from 0.0 to a large value for MD
|
||||
// This is to force a specific and known extrapolation.
|
||||
// Otherwise we get an undefined and unknown extrapolation.
|
||||
{
|
||||
double x1 = splinePoints[0].x();
|
||||
double x2 = splinePoints[1].x();
|
||||
double y1 = splinePoints[0].y();
|
||||
double y2 = splinePoints[1].y();
|
||||
double M = (y2 - y1) / (x2 - x1);
|
||||
|
||||
return M * (x - x1) + y1;
|
||||
QPointF startPoint(0.0f, M * (0.0f - x1) + y1);
|
||||
splinePoints.push_front(startPoint);
|
||||
}
|
||||
{
|
||||
int N = splinePoints.size() - 1;
|
||||
double x1 = splinePoints[N - 1].x();
|
||||
double x2 = splinePoints[N].x();
|
||||
double y1 = splinePoints[N - 1].y();
|
||||
double y2 = splinePoints[N].y();
|
||||
double M = (y2 - y1) / (x2 - x1);
|
||||
double endX = 2.0 * splinePoints[N].x();
|
||||
|
||||
QPointF endPoint(endX, M * (endX - x1) + y1);
|
||||
splinePoints.push_back(endPoint);
|
||||
}
|
||||
|
||||
QwtSpline spline;
|
||||
spline.setPoints(splinePoints);
|
||||
|
||||
return spline;
|
||||
}
|
||||
|
@ -18,15 +18,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cvfBase.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <qwt_curve_fitter.h>
|
||||
#include <qwt_spline.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RigWellPath;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigWellPathGeometryTools
|
||||
{
|
||||
@ -38,16 +41,20 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
static std::vector<cvf::Vec3d> calculateLineSegmentNormals(const std::vector<cvf::Vec3d>& vertices,
|
||||
double angle);
|
||||
static std::vector<double> interpolateMdFromTvd(const std::vector<double>& originalMdValues, const std::vector<double>& originalTvdValues, const std::vector<double>& tvdValuesToInterpolateFrom);
|
||||
static std::vector<cvf::Vec3d> calculateLineSegmentNormals(const std::vector<cvf::Vec3d>& vertices, double angle);
|
||||
static std::vector<double> interpolateMdFromTvd(const std::vector<double>& originalMdValues,
|
||||
const std::vector<double>& originalTvdValues,
|
||||
const std::vector<double>& tvdValuesToInterpolateFrom);
|
||||
|
||||
private:
|
||||
static std::vector<cvf::Vec3d> interpolateUndefinedNormals(const cvf::Vec3d& planeNormal,
|
||||
static std::vector<cvf::Vec3d> interpolateUndefinedNormals(const cvf::Vec3d& planeNormal,
|
||||
const std::vector<cvf::Vec3d>& normals,
|
||||
const std::vector<cvf::Vec3d>& vertices);
|
||||
static cvf::Vec3d estimateDominantDirectionInXYPlane(const std::vector<cvf::Vec3d>& vertices);
|
||||
static cvf::Vec3d estimateDominantDirectionInXYPlane(const std::vector<cvf::Vec3d>& vertices);
|
||||
|
||||
static double linearInterpolation(const std::vector<double>& xValues, const std::vector<double>& yValues, int valueIndex, double x);
|
||||
static double solveForX(const QwtSpline& spline, double minX, double maxX, double y);
|
||||
|
||||
static QwtSpline createSpline(const std::vector<double>& originalMdValues, const std::vector<double>& originalTvdValues);
|
||||
static std::vector<int> findSplineSegmentsContainingRoots(const QwtSpline& spline,
|
||||
const std::vector<double>& tvdValuesToInterpolateFrom);
|
||||
};
|
||||
|
@ -20,9 +20,13 @@
|
||||
|
||||
#include "RigWellPathGeometryTools.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
|
||||
#define TOLERANCE 1.0e-7
|
||||
#define SOLVER_TOLERANCE
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@ -36,7 +40,7 @@ TEST(RigWellPathGeometryTools, VerticalPath)
|
||||
EXPECT_EQ(fullTVDValues.size(), fullMDValues.size());
|
||||
for (size_t i = 0; i < fullTVDValues.size(); ++i)
|
||||
{
|
||||
EXPECT_DOUBLE_EQ(fullTVDValues[i], fullMDValues[i]);
|
||||
EXPECT_NEAR(fullTVDValues[i], fullMDValues[i], TOLERANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,12 +49,13 @@ TEST(RigWellPathGeometryTools, LinearPath)
|
||||
std::vector<double> mdValues = {100, 500, 1000};
|
||||
std::vector<double> tvdValues = {50, 250, 500};
|
||||
std::vector<double> fullTVDValues = {50, 100, 150, 200, 250, 300, 350, 400, 450, 500};
|
||||
|
||||
std::vector<double> fullMDValues = RigWellPathGeometryTools::interpolateMdFromTvd(mdValues, tvdValues, fullTVDValues);
|
||||
|
||||
EXPECT_EQ(fullTVDValues.size(), fullMDValues.size());
|
||||
for (size_t i = 0; i < fullTVDValues.size(); ++i)
|
||||
{
|
||||
EXPECT_DOUBLE_EQ(2.0*fullTVDValues[i], fullMDValues[i]);
|
||||
EXPECT_NEAR(2.0*fullTVDValues[i], fullMDValues[i], TOLERANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,11 +78,15 @@ TEST(RigWellPathGeometryTools, QuadraticPath)
|
||||
{
|
||||
fullTvdValues.push_back(quadraticFunction(md));
|
||||
}
|
||||
|
||||
std::vector<double> estimatedFullMDValues = RigWellPathGeometryTools::interpolateMdFromTvd(mdValues, tvdValues, fullTvdValues);
|
||||
EXPECT_EQ(estimatedFullMDValues.size(), fullMDValues.size());
|
||||
for (size_t i = 0; i < estimatedFullMDValues.size(); ++i)
|
||||
{
|
||||
EXPECT_DOUBLE_EQ(fullMDValues[i], estimatedFullMDValues[i]);
|
||||
if (std::find(mdValues.begin(), mdValues.end(), estimatedFullMDValues[i]) != mdValues.end())
|
||||
{
|
||||
EXPECT_NEAR(fullMDValues[i], estimatedFullMDValues[i], TOLERANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,6 +96,32 @@ double cubicFunction(double x)
|
||||
}
|
||||
|
||||
TEST(RigWellPathGeometryTools, CubicPath)
|
||||
{
|
||||
std::vector<double> mdValues = {100, 300, 700, 1000};
|
||||
std::vector<double> tvdValues;
|
||||
for (double md : mdValues)
|
||||
{
|
||||
tvdValues.push_back(cubicFunction(md));
|
||||
}
|
||||
std::vector<double> fullMDValues = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
|
||||
std::vector<double> fullTvdValues;
|
||||
for (double md : fullMDValues)
|
||||
{
|
||||
fullTvdValues.push_back(cubicFunction(md));
|
||||
}
|
||||
|
||||
std::vector<double> estimatedFullMDValues = RigWellPathGeometryTools::interpolateMdFromTvd(mdValues, tvdValues, fullTvdValues);
|
||||
EXPECT_EQ(estimatedFullMDValues.size(), fullMDValues.size());
|
||||
for (size_t i = 0; i < estimatedFullMDValues.size(); ++i)
|
||||
{
|
||||
if (std::find(mdValues.begin(), mdValues.end(), estimatedFullMDValues[i]) != mdValues.end())
|
||||
{
|
||||
EXPECT_NEAR(fullMDValues[i], estimatedFullMDValues[i], TOLERANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RigWellPathGeometryTools, CubicPathPoorSampling)
|
||||
{
|
||||
std::vector<double> mdValues = {100, 300, 600, 1000};
|
||||
std::vector<double> tvdValues;
|
||||
@ -100,10 +135,14 @@ TEST(RigWellPathGeometryTools, CubicPath)
|
||||
{
|
||||
fullTvdValues.push_back(cubicFunction(md));
|
||||
}
|
||||
|
||||
std::vector<double> estimatedFullMDValues = RigWellPathGeometryTools::interpolateMdFromTvd(mdValues, tvdValues, fullTvdValues);
|
||||
EXPECT_EQ(estimatedFullMDValues.size(), fullMDValues.size());
|
||||
for (size_t i = 0; i < estimatedFullMDValues.size(); ++i)
|
||||
{
|
||||
EXPECT_DOUBLE_EQ(fullMDValues[i], estimatedFullMDValues[i]);
|
||||
if (std::find(mdValues.begin(), mdValues.end(), estimatedFullMDValues[i]) != mdValues.end())
|
||||
{
|
||||
EXPECT_NEAR(fullMDValues[i], estimatedFullMDValues[i], TOLERANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user