RFT ensemble refactoring

* Compute average MD for intersections with a cell
* Create extractor for simulation well
* Remove rftReader from RifDataSourceForRftPlt
* Add function compute measured depth for RFT cells based on well path geometry
* Move statistics reader to well ensemble curve set
* Make sure both TVD and MD are cached if possible
* Add selection of grid case to use for estimation of measured depth (MD)
Add "Grid Model For MD" where the user can select a grid model. This grid model is propagated to a hidden field in EnsembleCurveSet. The grid model is then applied to RifReaderEnsembleStatisticsRft owned by EnsembleCurveSet
This commit is contained in:
Magne Sjaastad
2023-09-04 10:08:30 +02:00
committed by GitHub
parent fe17b211b8
commit 7a782cec66
27 changed files with 627 additions and 428 deletions

View File

@@ -18,20 +18,23 @@
#include "RifReaderEnsembleStatisticsRft.h"
#include "RiaCurveMerger.h"
#include "RiaWeightedMeanCalculator.h"
#include "RiaExtractionTools.h"
#include "RigStatisticsMath.h"
#include "RimSummaryCase.h"
#include "RimSummaryCaseCollection.h"
#include "RimTools.h"
#include "cafAssert.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifReaderEnsembleStatisticsRft::RifReaderEnsembleStatisticsRft( const RimSummaryCaseCollection* summaryCaseCollection )
RifReaderEnsembleStatisticsRft::RifReaderEnsembleStatisticsRft( const RimSummaryCaseCollection* summaryCaseCollection,
RimEclipseCase* eclipseCase )
: m_summaryCaseCollection( summaryCaseCollection )
, m_eclipseCase( eclipseCase )
{
}
@@ -40,6 +43,8 @@ RifReaderEnsembleStatisticsRft::RifReaderEnsembleStatisticsRft( const RimSummary
//--------------------------------------------------------------------------------------------------
std::set<RifEclipseRftAddress> RifReaderEnsembleStatisticsRft::eclipseRftAddresses()
{
if ( !m_summaryCaseCollection ) return {};
std::set<RifEclipseRftAddress> allAddresses;
for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() )
{
@@ -78,7 +83,10 @@ std::set<RifEclipseRftAddress> RifReaderEnsembleStatisticsRft::eclipseRftAddress
//--------------------------------------------------------------------------------------------------
void RifReaderEnsembleStatisticsRft::values( const RifEclipseRftAddress& rftAddress, std::vector<double>* values )
{
CAF_ASSERT( rftAddress.wellLogChannel() == RifEclipseRftAddress::RftWellLogChannelType::TVD ||
if ( !m_summaryCaseCollection ) return;
CAF_ASSERT( rftAddress.wellLogChannel() == RifEclipseRftAddress::RftWellLogChannelType::MD ||
rftAddress.wellLogChannel() == RifEclipseRftAddress::RftWellLogChannelType::TVD ||
rftAddress.wellLogChannel() == RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_MEAN ||
rftAddress.wellLogChannel() == RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_P10 ||
rftAddress.wellLogChannel() == RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_P50 ||
@@ -88,7 +96,7 @@ void RifReaderEnsembleStatisticsRft::values( const RifEclipseRftAddress& rftAddr
auto it = m_cachedValues.find( rftAddress );
if ( it == m_cachedValues.end() )
{
calculateStatistics( rftAddress );
calculateStatistics( rftAddress.wellName(), rftAddress.timeStep() );
}
*values = m_cachedValues[rftAddress];
}
@@ -98,6 +106,8 @@ void RifReaderEnsembleStatisticsRft::values( const RifEclipseRftAddress& rftAddr
//--------------------------------------------------------------------------------------------------
std::set<QDateTime> RifReaderEnsembleStatisticsRft::availableTimeSteps( const QString& wellName )
{
if ( !m_summaryCaseCollection ) return {};
std::set<QDateTime> allTimeSteps;
for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() )
{
@@ -116,6 +126,8 @@ std::set<QDateTime> RifReaderEnsembleStatisticsRft::availableTimeSteps( const QS
std::set<QDateTime> RifReaderEnsembleStatisticsRft::availableTimeSteps( const QString& wellName,
const RifEclipseRftAddress::RftWellLogChannelType& wellLogChannelName )
{
if ( !m_summaryCaseCollection ) return {};
std::set<QDateTime> allTimeSteps;
for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() )
{
@@ -135,6 +147,8 @@ std::set<QDateTime>
RifReaderEnsembleStatisticsRft::availableTimeSteps( const QString& wellName,
const std::set<RifEclipseRftAddress::RftWellLogChannelType>& relevantChannels )
{
if ( !m_summaryCaseCollection ) return {};
std::set<QDateTime> allTimeSteps;
for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() )
{
@@ -152,6 +166,8 @@ std::set<QDateTime>
//--------------------------------------------------------------------------------------------------
std::set<RifEclipseRftAddress::RftWellLogChannelType> RifReaderEnsembleStatisticsRft::availableWellLogChannels( const QString& wellName )
{
if ( !m_summaryCaseCollection ) return {};
std::set<RifEclipseRftAddress::RftWellLogChannelType> allWellLogChannels;
for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() )
{
@@ -170,6 +186,8 @@ std::set<RifEclipseRftAddress::RftWellLogChannelType> RifReaderEnsembleStatistic
//--------------------------------------------------------------------------------------------------
std::set<QString> RifReaderEnsembleStatisticsRft::wellNames()
{
if ( !m_summaryCaseCollection ) return {};
std::set<QString> allWellNames;
for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() )
{
@@ -185,47 +203,110 @@ std::set<QString> RifReaderEnsembleStatisticsRft::wellNames()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifReaderEnsembleStatisticsRft::calculateStatistics( const RifEclipseRftAddress& rftAddress )
void RifReaderEnsembleStatisticsRft::calculateStatistics( const QString& wellName, const QDateTime& timeStep )
{
const QString& wellName = rftAddress.wellName();
const QDateTime& timeStep = rftAddress.timeStep();
RifEclipseRftAddress depthAddress =
RifEclipseRftAddress::createAddress( wellName, timeStep, RifEclipseRftAddress::RftWellLogChannelType::TVD );
RifEclipseRftAddress pressAddress =
RifEclipseRftAddress::createAddress( wellName, timeStep, RifEclipseRftAddress::RftWellLogChannelType::PRESSURE );
if ( !m_summaryCaseCollection ) return;
RifEclipseRftAddress p10Address =
RifEclipseRftAddress::createAddress( wellName, timeStep, RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_P10 );
RifEclipseRftAddress p50Address =
RifEclipseRftAddress::createAddress( wellName, timeStep, RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_P50 );
RifEclipseRftAddress p90Address =
RifEclipseRftAddress::createAddress( wellName, timeStep, RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_P90 );
RifEclipseRftAddress meanAddress =
RifEclipseRftAddress::createAddress( wellName, timeStep, RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_MEAN );
using ChannelType = RifEclipseRftAddress::RftWellLogChannelType;
RifEclipseRftAddress pressAddress = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::PRESSURE );
RifEclipseRftAddress tvdAddress = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::TVD );
RiaCurveMerger<double> curveMerger;
RiaWeightedMeanCalculator<size_t> dataSetSizeCalc;
for ( RimSummaryCase* summaryCase : m_summaryCaseCollection->allSummaryCases() )
RigEclipseWellLogExtractor* extractor = RiaExtractionTools::findOrCreateWellLogExtractor( wellName, m_eclipseCase );
if ( extractor )
{
RifReaderRftInterface* reader = summaryCase->rftReader();
if ( reader )
// Create a well log extractor if a well path and an Eclipse case is defined
// Use the extractor to compute measured depth for RFT cells
// The TVD values is extracted from the first summary case
RifEclipseRftAddress mdAddress = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::MD );
RiaCurveMerger<double> curveMerger;
RiaWeightedMeanCalculator<size_t> dataSetSizeCalc;
std::vector<double> tvdDepthsForFirstRftCase;
for ( RimSummaryCase* summaryCase : m_summaryCaseCollection->allSummaryCases() )
{
std::vector<double> depths;
auto reader = summaryCase->rftReader();
if ( !reader ) continue;
std::vector<double> pressures;
reader->values( depthAddress, &depths );
reader->values( pressAddress, &pressures );
if ( !depths.empty() && !pressures.empty() )
if ( tvdDepthsForFirstRftCase.empty() )
{
dataSetSizeCalc.addValueAndWeight( depths.size(), 1.0 );
curveMerger.addCurveData( depths, pressures );
reader->values( tvdAddress, &tvdDepthsForFirstRftCase );
}
std::vector<double> measuredDepths = reader->computeMeasuredDepth( wellName, timeStep, extractor );
if ( !measuredDepths.empty() && !pressures.empty() )
{
dataSetSizeCalc.addValueAndWeight( measuredDepths.size(), 1.0 );
curveMerger.addCurveData( measuredDepths, pressures );
}
}
}
curveMerger.computeInterpolatedValues( false );
clearData( wellName, timeStep );
extractStatisticsFromCurveMerger( wellName, timeStep, mdAddress, curveMerger, dataSetSizeCalc );
if ( m_cachedValues[mdAddress].size() == tvdDepthsForFirstRftCase.size() )
{
// The number of RFT cells can vary between realizations in some cases. TVD depth is only given if the number of RFT cells is
// identical between realizations.
m_cachedValues[tvdAddress] = tvdDepthsForFirstRftCase;
}
}
else
{
// Compute statistics based on TVD depths. No measured depth can be estimated.
// This concept works well for vertical wells, but does not work for horizontal wells.
RiaCurveMerger<double> curveMerger;
RiaWeightedMeanCalculator<size_t> dataSetSizeCalc;
RifEclipseRftAddress tvdAddress = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::TVD );
for ( RimSummaryCase* summaryCase : m_summaryCaseCollection->allSummaryCases() )
{
auto reader = summaryCase->rftReader();
if ( !reader ) continue;
std::vector<double> pressures;
reader->values( pressAddress, &pressures );
std::vector<double> tvdDepths;
reader->values( tvdAddress, &tvdDepths );
if ( !tvdDepths.empty() && !pressures.empty() )
{
dataSetSizeCalc.addValueAndWeight( tvdDepths.size(), 1.0 );
curveMerger.addCurveData( tvdDepths, pressures );
}
}
extractStatisticsFromCurveMerger( wellName, timeStep, tvdAddress, curveMerger, dataSetSizeCalc );
}
}
//--------------------------------------------------------------------------------------------------
/// Compute statistics for values, either based on measured depth or TVD
//--------------------------------------------------------------------------------------------------
void RifReaderEnsembleStatisticsRft::extractStatisticsFromCurveMerger( const QString& wellName,
const QDateTime& timeStep,
RifEclipseRftAddress depthAddress,
RiaCurveMerger<double>& curveMerger,
RiaWeightedMeanCalculator<size_t>& dataSetSizeCalc )
{
using ChannelType = RifEclipseRftAddress::RftWellLogChannelType;
CAF_ASSERT( depthAddress.wellLogChannel() == ChannelType::MD || depthAddress.wellLogChannel() == ChannelType::TVD );
auto p10Address = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::PRESSURE_P10 );
auto p50Address = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::PRESSURE_P50 );
auto p90Address = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::PRESSURE_P90 );
auto meanAddress = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::PRESSURE_MEAN );
clearCache( wellName, timeStep );
curveMerger.computeInterpolatedValues( false );
const std::vector<double>& allDepths = curveMerger.allXValues();
if ( !allDepths.empty() )
@@ -259,7 +340,7 @@ void RifReaderEnsembleStatisticsRft::calculateStatistics( const RifEclipseRftAdd
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifReaderEnsembleStatisticsRft::clearData( const QString& wellName, const QDateTime& timeStep )
void RifReaderEnsembleStatisticsRft::clearCache( const QString& wellName, const QDateTime& timeStep )
{
for ( auto it = m_cachedValues.begin(); it != m_cachedValues.end(); )
{