From 7a782cec665b16b46b79a0ae2d3540cf2184a1c5 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Mon, 4 Sep 2023 10:08:30 +0200 Subject: [PATCH] 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 --- .../Application/Tools/RiaExtractionTools.cpp | 48 +++ .../Application/Tools/RiaExtractionTools.h | 11 +- .../Commands/RicWellLogTools.cpp | 4 +- .../FileInterface/RifDataSourceForRftPlt.cpp | 32 -- .../FileInterface/RifDataSourceForRftPlt.h | 4 +- .../FileInterface/RifReaderEclipseRft.cpp | 30 +- .../FileInterface/RifReaderEclipseRft.h | 3 +- .../RifReaderEnsembleStatisticsRft.cpp | 155 ++++++--- .../RifReaderEnsembleStatisticsRft.h | 18 +- .../FileInterface/RifReaderOpmRft.cpp | 20 +- .../FileInterface/RifReaderOpmRft.h | 2 +- .../FileInterface/RifReaderRftInterface.cpp | 106 +++++- .../FileInterface/RifReaderRftInterface.h | 8 +- .../Flow/RimWellPlotTools.cpp | 50 ++- .../ProjectDataModel/Flow/RimWellPlotTools.h | 3 + .../ProjectDataModel/Flow/RimWellPltPlot.cpp | 24 +- .../Flow/RimWellRftEnsembleCurveSet.cpp | 66 ++++ .../Flow/RimWellRftEnsembleCurveSet.h | 15 + .../ProjectDataModel/Flow/RimWellRftPlot.cpp | 49 ++- .../ProjectDataModel/Flow/RimWellRftPlot.h | 7 +- .../Summary/RimSummaryCaseCollection.cpp | 17 +- .../Summary/RimSummaryCaseCollection.h | 16 +- .../RimWellLogCurveCommonDataSource.cpp | 2 +- .../WellLog/RimWellLogRftCurve.cpp | 312 ++++-------------- .../WellLog/RimWellLogRftCurve.h | 26 +- .../RigWellLogExtractor.cpp | 23 ++ .../ReservoirDataModel/RigWellLogExtractor.h | 2 + 27 files changed, 626 insertions(+), 427 deletions(-) diff --git a/ApplicationLibCode/Application/Tools/RiaExtractionTools.cpp b/ApplicationLibCode/Application/Tools/RiaExtractionTools.cpp index 151762004b..7e66cdb34d 100644 --- a/ApplicationLibCode/Application/Tools/RiaExtractionTools.cpp +++ b/ApplicationLibCode/Application/Tools/RiaExtractionTools.cpp @@ -18,11 +18,16 @@ #include "RiaExtractionTools.h" +#include "RiaSimWellBranchTools.h" + #include "RigWellPath.h" + #include "RimEclipseCase.h" #include "RimMainPlotCollection.h" #include "RimSimWellInView.h" +#include "RimTools.h" #include "RimWellLogPlotCollection.h" +#include "RimWellPathCollection.h" //-------------------------------------------------------------------------------------------------- /// @@ -37,6 +42,22 @@ RigEclipseWellLogExtractor* RiaExtractionTools::findOrCreateWellLogExtractor( Ri return wlPlotCollection->findOrCreateExtractor( wellPath, eclipseCase ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigEclipseWellLogExtractor* RiaExtractionTools::findOrCreateWellLogExtractor( const QString& wellName, RimEclipseCase* eclipseCase ) +{ + if ( !eclipseCase ) return nullptr; + + if ( auto wellPathCollection = RimTools::wellPathCollection() ) + { + auto wellPath = wellPathCollection->tryFindMatchingWellPath( wellName ); + return RiaExtractionTools::findOrCreateWellLogExtractor( wellPath, eclipseCase ); + } + + return nullptr; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -71,6 +92,33 @@ RigEclipseWellLogExtractor* RiaExtractionTools::findOrCreateSimWellExtractor( co return wlPlotCollection->findOrCreateSimWellExtractor( simWell->name, caseUserDescription, wellPathGeom, eclipseCase->eclipseCaseData() ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigEclipseWellLogExtractor* RiaExtractionTools::findOrCreateSimWellExtractor( RimEclipseCase* eclipseCase, + const QString& simWellName, + bool useBranchDetection, + int branchIndex ) +{ + if ( !eclipseCase ) return nullptr; + + auto wlPlotCollection = wellLogPlotCollection(); + if ( !wlPlotCollection ) return nullptr; + + std::vector wellPaths = RiaSimWellBranchTools::simulationWellBranches( simWellName, useBranchDetection ); + if ( wellPaths.empty() ) return nullptr; + + branchIndex = RiaSimWellBranchTools::clampBranchIndex( simWellName, branchIndex, useBranchDetection ); + if ( branchIndex >= static_cast( wellPaths.size() ) ) return nullptr; + + auto wellPathBranch = wellPaths[branchIndex]; + + return wlPlotCollection->findOrCreateSimWellExtractor( simWellName, + QString( "Find or create sim well extractor" ), + wellPathBranch, + eclipseCase->eclipseCaseData() ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Application/Tools/RiaExtractionTools.h b/ApplicationLibCode/Application/Tools/RiaExtractionTools.h index bdfa1d6a42..6b8a2af641 100644 --- a/ApplicationLibCode/Application/Tools/RiaExtractionTools.h +++ b/ApplicationLibCode/Application/Tools/RiaExtractionTools.h @@ -36,11 +36,18 @@ class QString; //================================================================================================== namespace RiaExtractionTools { +RimWellLogPlotCollection* wellLogPlotCollection(); + +// Eclipse +RigEclipseWellLogExtractor* findOrCreateWellLogExtractor( const QString& wellName, RimEclipseCase* eclipseCase ); RigEclipseWellLogExtractor* findOrCreateWellLogExtractor( RimWellPath* wellPath, RimEclipseCase* eclipseCase ); + +// GeoMech RigGeoMechWellLogExtractor* findOrCreateWellLogExtractor( RimWellPath* wellPath, RimGeoMechCase* geomCase, int partId = 0 ); +// Simulation wells RigEclipseWellLogExtractor* findOrCreateSimWellExtractor( const RimSimWellInView* simWell, const RigWellPath* wellPathGeom ); - -RimWellLogPlotCollection* wellLogPlotCollection(); +RigEclipseWellLogExtractor* + findOrCreateSimWellExtractor( RimEclipseCase* eclipseCase, const QString& simWellName, bool useBranchDetection, int branchIndex ); }; // namespace RiaExtractionTools diff --git a/ApplicationLibCode/Commands/RicWellLogTools.cpp b/ApplicationLibCode/Commands/RicWellLogTools.cpp index 75be613379..a77bcd1c97 100644 --- a/ApplicationLibCode/Commands/RicWellLogTools.cpp +++ b/ApplicationLibCode/Commands/RicWellLogTools.cpp @@ -356,7 +356,7 @@ RimWellLogRftCurve* RicWellLogTools::addRftCurve( RimWellLogTrack* plotTrack, co if ( simWell && resultCase ) { - curve->setEclipseResultCase( resultCase ); + curve->setEclipseCase( resultCase ); curve->setDefaultAddress( simWell->name() ); plotTrack->setFormationCase( resultCase ); @@ -364,7 +364,7 @@ RimWellLogRftCurve* RicWellLogTools::addRftCurve( RimWellLogTrack* plotTrack, co } else if ( resultCase ) { - curve->setEclipseResultCase( resultCase ); + curve->setEclipseCase( resultCase ); auto wellNames = resultCase->rftReader()->wellNames(); if ( !wellNames.empty() ) diff --git a/ApplicationLibCode/FileInterface/RifDataSourceForRftPlt.cpp b/ApplicationLibCode/FileInterface/RifDataSourceForRftPlt.cpp index 26ff0d08d0..97d47541b7 100644 --- a/ApplicationLibCode/FileInterface/RifDataSourceForRftPlt.cpp +++ b/ApplicationLibCode/FileInterface/RifDataSourceForRftPlt.cpp @@ -18,7 +18,6 @@ #include "RifDataSourceForRftPlt.h" -#include "RifReaderEclipseRft.h" #include "RigEclipseCaseData.h" #include "RimEclipseCase.h" @@ -230,37 +229,6 @@ auto RifDataSourceForRftPlt::operator<=>( const RifDataSourceForRftPlt& addr2 ) } } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RifReaderRftInterface* RifDataSourceForRftPlt::rftReader() const -{ - if ( m_sourceType == SourceType::GRID_MODEL_CELL_DATA || m_sourceType == SourceType::RFT_SIM_WELL_DATA ) - { - // TODO: Consider changing to RimEclipseResultCase to avoid casting - auto eclResCase = dynamic_cast( eclCase() ); - - if ( eclResCase ) return eclResCase->rftReader(); - } - else if ( m_sourceType == SourceType::SUMMARY_RFT ) - { - if ( m_summaryCase ) return m_summaryCase->rftReader(); - } - else if ( m_sourceType == SourceType::ENSEMBLE_RFT ) - { - if ( m_ensemble ) return m_ensemble->rftStatisticsReader(); - } - else if ( m_sourceType == SourceType::OBSERVED_FMU_RFT ) - { - if ( m_observedFmuRftData ) return m_observedFmuRftData->rftReader(); - } - else if ( m_sourceType == SourceType::OBSERVED_PRESSURE_DEPTH ) - { - if ( m_pressureDepthData ) return m_pressureDepthData->rftReader(); - } - return nullptr; -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/FileInterface/RifDataSourceForRftPlt.h b/ApplicationLibCode/FileInterface/RifDataSourceForRftPlt.h index fab7278ce5..a65b789dd1 100644 --- a/ApplicationLibCode/FileInterface/RifDataSourceForRftPlt.h +++ b/ApplicationLibCode/FileInterface/RifDataSourceForRftPlt.h @@ -31,7 +31,6 @@ class RimWellLogFile; class RimEclipseCase; -class RifReaderRftInterface; class RimSummaryCase; class RimSummaryCaseCollection; class RimObservedFmuRftData; @@ -64,8 +63,7 @@ class RifDataSourceForRftPlt RifDataSourceForRftPlt( RimObservedFmuRftData* observedFmuRftData ); RifDataSourceForRftPlt( RimPressureDepthData* pressureDepthData ); - SourceType sourceType() const; - RifReaderRftInterface* rftReader() const; + SourceType sourceType() const; RimEclipseCase* eclCase() const; RimSummaryCase* summaryCase() const; diff --git a/ApplicationLibCode/FileInterface/RifReaderEclipseRft.cpp b/ApplicationLibCode/FileInterface/RifReaderEclipseRft.cpp index 1d55aae4dc..4d344cddf4 100644 --- a/ApplicationLibCode/FileInterface/RifReaderEclipseRft.cpp +++ b/ApplicationLibCode/FileInterface/RifReaderEclipseRft.cpp @@ -246,19 +246,17 @@ void RifReaderEclipseRft::values( const RifEclipseRftAddress& rftAddress, std::v //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RifReaderEclipseRft::cellIndices( const RifEclipseRftAddress& rftAddress, std::vector* indices ) +std::vector RifReaderEclipseRft::cellIndices( const QString& wellName, const QDateTime& timeStep ) { - CVF_ASSERT( indices ); - if ( !m_ecl_rft_file ) { open(); } - indices->clear(); + int index = indexFromAddress( wellName, timeStep ); + if ( index < 0 ) return {}; - int index = indexFromAddress( rftAddress ); - if ( index < 0 ) return; + std::vector indices; ecl_rft_node_type* node = ecl_rft_file_iget_node( m_ecl_rft_file, index ); @@ -268,8 +266,10 @@ void RifReaderEclipseRft::cellIndices( const RifEclipseRftAddress& rftAddress, s ecl_rft_node_iget_ijk( node, cellIdx, &i, &j, &k ); caf::VecIjk ijk( (size_t)i, (size_t)j, (size_t)k ); - indices->push_back( ijk ); + indices.push_back( ijk ); } + + return indices; } //-------------------------------------------------------------------------------------------------- @@ -453,3 +453,19 @@ int RifReaderEclipseRft::indexFromAddress( const RifEclipseRftAddress& rftAddres return -1; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +int RifReaderEclipseRft::indexFromAddress( const QString& wellName, const QDateTime& timeStep ) const +{ + for ( const auto& [rftAddr, nodeIdx] : m_rftAddressToLibeclNodeIdx ) + { + if ( rftAddr.wellName() == wellName && rftAddr.timeStep() == timeStep ) + { + return nodeIdx; + } + } + + return -1; +} diff --git a/ApplicationLibCode/FileInterface/RifReaderEclipseRft.h b/ApplicationLibCode/FileInterface/RifReaderEclipseRft.h index 8edda46215..a78080f5fd 100644 --- a/ApplicationLibCode/FileInterface/RifReaderEclipseRft.h +++ b/ApplicationLibCode/FileInterface/RifReaderEclipseRft.h @@ -45,7 +45,7 @@ class RifReaderEclipseRft : public RifReaderRftInterface, public cvf::Object std::set eclipseRftAddresses() override; void values( const RifEclipseRftAddress& rftAddress, std::vector* values ) override; - void cellIndices( const RifEclipseRftAddress& rftAddress, std::vector* indices ) override; + std::vector cellIndices( const QString& wellName, const QDateTime& timeStep ) override; std::set availableTimeSteps( const QString& wellName ) override; std::set availableTimeSteps( const QString& wellName, @@ -60,6 +60,7 @@ class RifReaderEclipseRft : public RifReaderRftInterface, public cvf::Object private: void open(); int indexFromAddress( const RifEclipseRftAddress& rftAddress ) const; + int indexFromAddress( const QString& wellName, const QDateTime& timeStep ) const; private: // NOLINTBEGIN(modernize-use-using) diff --git a/ApplicationLibCode/FileInterface/RifReaderEnsembleStatisticsRft.cpp b/ApplicationLibCode/FileInterface/RifReaderEnsembleStatisticsRft.cpp index 4d029c62df..dde3745b28 100644 --- a/ApplicationLibCode/FileInterface/RifReaderEnsembleStatisticsRft.cpp +++ b/ApplicationLibCode/FileInterface/RifReaderEnsembleStatisticsRft.cpp @@ -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 RifReaderEnsembleStatisticsRft::eclipseRftAddresses() { + if ( !m_summaryCaseCollection ) return {}; + std::set allAddresses; for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() ) { @@ -78,7 +83,10 @@ std::set RifReaderEnsembleStatisticsRft::eclipseRftAddress //-------------------------------------------------------------------------------------------------- void RifReaderEnsembleStatisticsRft::values( const RifEclipseRftAddress& rftAddress, std::vector* 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 RifReaderEnsembleStatisticsRft::availableTimeSteps( const QString& wellName ) { + if ( !m_summaryCaseCollection ) return {}; + std::set allTimeSteps; for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() ) { @@ -116,6 +126,8 @@ std::set RifReaderEnsembleStatisticsRft::availableTimeSteps( const QS std::set RifReaderEnsembleStatisticsRft::availableTimeSteps( const QString& wellName, const RifEclipseRftAddress::RftWellLogChannelType& wellLogChannelName ) { + if ( !m_summaryCaseCollection ) return {}; + std::set allTimeSteps; for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() ) { @@ -135,6 +147,8 @@ std::set RifReaderEnsembleStatisticsRft::availableTimeSteps( const QString& wellName, const std::set& relevantChannels ) { + if ( !m_summaryCaseCollection ) return {}; + std::set allTimeSteps; for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() ) { @@ -152,6 +166,8 @@ std::set //-------------------------------------------------------------------------------------------------- std::set RifReaderEnsembleStatisticsRft::availableWellLogChannels( const QString& wellName ) { + if ( !m_summaryCaseCollection ) return {}; + std::set allWellLogChannels; for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() ) { @@ -170,6 +186,8 @@ std::set RifReaderEnsembleStatistic //-------------------------------------------------------------------------------------------------- std::set RifReaderEnsembleStatisticsRft::wellNames() { + if ( !m_summaryCaseCollection ) return {}; + std::set allWellNames; for ( auto summaryCase : m_summaryCaseCollection->allSummaryCases() ) { @@ -185,47 +203,110 @@ std::set 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 ); - - 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 ); - - RiaCurveMerger curveMerger; - - RiaWeightedMeanCalculator dataSetSizeCalc; - - for ( RimSummaryCase* summaryCase : m_summaryCaseCollection->allSummaryCases() ) + if ( !m_summaryCaseCollection ) return; + + using ChannelType = RifEclipseRftAddress::RftWellLogChannelType; + RifEclipseRftAddress pressAddress = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::PRESSURE ); + RifEclipseRftAddress tvdAddress = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::TVD ); + + 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 curveMerger; + RiaWeightedMeanCalculator dataSetSizeCalc; + std::vector tvdDepthsForFirstRftCase; + + for ( RimSummaryCase* summaryCase : m_summaryCaseCollection->allSummaryCases() ) { - std::vector depths; + auto reader = summaryCase->rftReader(); + if ( !reader ) continue; + std::vector pressures; - reader->values( depthAddress, &depths ); reader->values( pressAddress, &pressures ); - if ( !depths.empty() && !pressures.empty() ) + + if ( tvdDepthsForFirstRftCase.empty() ) + { + reader->values( tvdAddress, &tvdDepthsForFirstRftCase ); + } + + std::vector measuredDepths = reader->computeMeasuredDepth( wellName, timeStep, extractor ); + + if ( !measuredDepths.empty() && !pressures.empty() ) { - dataSetSizeCalc.addValueAndWeight( depths.size(), 1.0 ); - curveMerger.addCurveData( depths, pressures ); + dataSetSizeCalc.addValueAndWeight( measuredDepths.size(), 1.0 ); + curveMerger.addCurveData( measuredDepths, pressures ); } } + + 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; + } } - curveMerger.computeInterpolatedValues( false ); + 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 curveMerger; + RiaWeightedMeanCalculator dataSetSizeCalc; + RifEclipseRftAddress tvdAddress = RifEclipseRftAddress::createAddress( wellName, timeStep, ChannelType::TVD ); + + for ( RimSummaryCase* summaryCase : m_summaryCaseCollection->allSummaryCases() ) + { + auto reader = summaryCase->rftReader(); + if ( !reader ) continue; + + std::vector pressures; + reader->values( pressAddress, &pressures ); + + std::vector tvdDepths; + reader->values( tvdAddress, &tvdDepths ); + + if ( !tvdDepths.empty() && !pressures.empty() ) + { + dataSetSizeCalc.addValueAndWeight( tvdDepths.size(), 1.0 ); + curveMerger.addCurveData( tvdDepths, pressures ); + } + } - clearData( wellName, timeStep ); + 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& curveMerger, + RiaWeightedMeanCalculator& 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& 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(); ) { diff --git a/ApplicationLibCode/FileInterface/RifReaderEnsembleStatisticsRft.h b/ApplicationLibCode/FileInterface/RifReaderEnsembleStatisticsRft.h index 3b95d8ed59..8647f38493 100644 --- a/ApplicationLibCode/FileInterface/RifReaderEnsembleStatisticsRft.h +++ b/ApplicationLibCode/FileInterface/RifReaderEnsembleStatisticsRft.h @@ -20,14 +20,18 @@ #include "RifReaderEclipseRft.h" +#include "RiaCurveMerger.h" +#include "RiaWeightedMeanCalculator.h" + #include "cvfObject.h" class RimSummaryCaseCollection; +class RimEclipseCase; class RifReaderEnsembleStatisticsRft : public RifReaderRftInterface, public cvf::Object { public: - RifReaderEnsembleStatisticsRft( const RimSummaryCaseCollection* summaryCaseCollection ); + RifReaderEnsembleStatisticsRft( const RimSummaryCaseCollection* summaryCaseCollection, RimEclipseCase* eclipseCase ); std::set eclipseRftAddresses() override; void values( const RifEclipseRftAddress& rftAddress, std::vector* values ) override; @@ -41,11 +45,19 @@ class RifReaderEnsembleStatisticsRft : public RifReaderRftInterface, public cvf: std::set wellNames() override; private: - void calculateStatistics( const RifEclipseRftAddress& rftAddress ); - void clearData( const QString& wellName, const QDateTime& timeStep ); + void calculateStatistics( const QString& wellName, const QDateTime& timeStep ); + + void extractStatisticsFromCurveMerger( const QString& wellName, + const QDateTime& timeStep, + RifEclipseRftAddress depthAddress, + RiaCurveMerger& curveMerger, + RiaWeightedMeanCalculator& dataSetSizeCalc ); + + void clearCache( const QString& wellName, const QDateTime& timeStep ); private: const RimSummaryCaseCollection* m_summaryCaseCollection; + RimEclipseCase* m_eclipseCase; std::map> m_cachedValues; }; diff --git a/ApplicationLibCode/FileInterface/RifReaderOpmRft.cpp b/ApplicationLibCode/FileInterface/RifReaderOpmRft.cpp index 48d885006a..f3ba7d0b53 100644 --- a/ApplicationLibCode/FileInterface/RifReaderOpmRft.cpp +++ b/ApplicationLibCode/FileInterface/RifReaderOpmRft.cpp @@ -272,40 +272,44 @@ std::set RifReaderOpmRft::wellNames() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RifReaderOpmRft::cellIndices( const RifEclipseRftAddress& rftAddress, std::vector* indices ) +std::vector RifReaderOpmRft::cellIndices( const QString& wellName, const QDateTime& timeStep ) { - if ( !openFiles() ) return; + if ( !openFiles() ) return {}; - auto wellName = rftAddress.wellName().toStdString(); + auto stdWellName = wellName.toStdString(); - auto date = rftAddress.timeStep().date(); + auto date = timeStep.date(); int y = date.year(); int m = date.month(); int d = date.day(); + std::vector indices; + try { auto resultNameI = "CONIPOS"; - auto dataI = m_opm_rft->getRft( resultNameI, wellName, y, m, d ); + auto dataI = m_opm_rft->getRft( resultNameI, stdWellName, y, m, d ); auto resultNameJ = "CONJPOS"; - auto dataJ = m_opm_rft->getRft( resultNameJ, wellName, y, m, d ); + auto dataJ = m_opm_rft->getRft( resultNameJ, stdWellName, y, m, d ); auto resultNameK = "CONKPOS"; - auto dataK = m_opm_rft->getRft( resultNameK, wellName, y, m, d ); + auto dataK = m_opm_rft->getRft( resultNameK, stdWellName, y, m, d ); if ( !dataI.empty() && ( dataI.size() == dataJ.size() ) && ( dataI.size() == dataK.size() ) ) { for ( size_t n = 0; n < dataI.size(); n++ ) { // NB: Transform to zero-based cell indices - indices->push_back( caf::VecIjk( dataI[n] - 1, dataJ[n] - 1, dataK[n] - 1 ) ); + indices.push_back( caf::VecIjk( dataI[n] - 1, dataJ[n] - 1, dataK[n] - 1 ) ); } } } catch ( ... ) { } + + return indices; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/FileInterface/RifReaderOpmRft.h b/ApplicationLibCode/FileInterface/RifReaderOpmRft.h index 66e6951f6f..acb8c2da7f 100644 --- a/ApplicationLibCode/FileInterface/RifReaderOpmRft.h +++ b/ApplicationLibCode/FileInterface/RifReaderOpmRft.h @@ -50,7 +50,7 @@ class RifReaderOpmRft : public RifReaderRftInterface, public cvf::Object std::set availableWellLogChannels( const QString& wellName ) override; std::set wellNames() override; - void cellIndices( const RifEclipseRftAddress& rftAddress, std::vector* indices ) override; + std::vector cellIndices( const QString& wellName, const QDateTime& timeStep ) override; std::map branchIdsAndOneBasedIndices( const QString& wellName, const QDateTime& timeStep, RiaDefines::RftBranchType branchType ); diff --git a/ApplicationLibCode/FileInterface/RifReaderRftInterface.cpp b/ApplicationLibCode/FileInterface/RifReaderRftInterface.cpp index ddb8e8a808..b143877668 100644 --- a/ApplicationLibCode/FileInterface/RifReaderRftInterface.cpp +++ b/ApplicationLibCode/FileInterface/RifReaderRftInterface.cpp @@ -15,8 +15,17 @@ // for more details. // ///////////////////////////////////////////////////////////////////////////////// + #include "RifReaderRftInterface.h" +#include "RigEclipseCaseData.h" +#include "RigEclipseWellLogExtractor.h" +#include "RigMainGrid.h" +#include "RigWellPath.h" +#include "RigWellPathGeometryTools.h" + +#include "cafVecIjk.h" + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -34,9 +43,104 @@ std::set RifReaderRftInterface::eclipseRftAddresses( const return matchingAddresses; } +//-------------------------------------------------------------------------------------------------- +// Compute average measured depth for cell based on grid intersections for cells. If the well path geometry do not contain measured depth +// for a grid cell, the measured depth is estimated based on existing geometry for the well path. +//-------------------------------------------------------------------------------------------------- +std::vector + RifReaderRftInterface::computeMeasuredDepth( const QString& wellName, const QDateTime& timeStep, RigEclipseWellLogExtractor* eclExtractor ) +{ + if ( !eclExtractor ) return {}; + if ( !eclExtractor->caseData() ) return {}; + + if ( eclExtractor->cellIntersectionMDs().empty() ) return {}; + + auto mainGrid = eclExtractor->caseData()->mainGrid(); + if ( !mainGrid ) return {}; + + std::vector avgMeasuredDepthForCells; + std::vector tvdValuesToEstimate; + + auto cellIjk = cellIndices( wellName, timeStep ); + for ( const caf::VecIjk& ijk : cellIjk ) + { + auto globalCellIndex = mainGrid->cellIndexFromIJK( ijk.i(), ijk.j(), ijk.k() ); + + auto avgMd = eclExtractor->averageMdForCell( globalCellIndex ); + if ( avgMd.has_value() ) + { + avgMeasuredDepthForCells.push_back( avgMd.value() ); + } + else + { + // The RFT cell is not part of cells intersected by well path + // Use the TVD of cell center to estimate measured depth + + avgMeasuredDepthForCells.push_back( std::numeric_limits::infinity() ); + auto center = mainGrid->cell( globalCellIndex ).center(); + tvdValuesToEstimate.push_back( -center.z() ); + } + } + + if ( !tvdValuesToEstimate.empty() ) + { + auto wellPathMd = eclExtractor->wellPathGeometry()->measuredDepths(); + auto wellPathTvd = eclExtractor->wellPathGeometry()->trueVerticalDepths(); + + // Estimate measured depth for cells that do not have measured depth + auto estimatedMeasuredDepth = RigWellPathGeometryTools::interpolateMdFromTvd( wellPathMd, wellPathTvd, tvdValuesToEstimate ); + + double previousMd = std::numeric_limits::infinity(); + + // Replace infinity MD values with estimated MD values based on well path geometry + // previousMd contains the last known measured depth value as we move along the well path + + size_t estimatedIndex = 0; + for ( auto& measuredDepth : avgMeasuredDepthForCells ) + { + if ( measuredDepth == std::numeric_limits::infinity() ) + { + // No measured depth for cell is found, try to estimate MD based on MD for previous cell + if ( estimatedIndex < estimatedMeasuredDepth.size() ) + { + double estimatedMd = estimatedMeasuredDepth[estimatedIndex++]; + if ( previousMd != std::numeric_limits::infinity() ) + { + if ( previousMd < estimatedMd ) + { + // The estimated MD is larger than previous MD, use the estimated MD + measuredDepth = estimatedMd; + } + else + { + // The estimated MD is smaller than previous MD, use the previous MD + 1.0 + measuredDepth = previousMd + 1.0; + } + } + else + { + // We do not have a valid previous MD, use the estimated MD + measuredDepth = estimatedMd; + } + } + else + { + measuredDepth = previousMd + 1.0; + } + } + + // Assign the current MD as previous MD to be used for next estimated MD + previousMd = measuredDepth; + } + } + + return avgMeasuredDepthForCells; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RifReaderRftInterface::cellIndices( const RifEclipseRftAddress& rftAddress, std::vector* indices ) +std::vector RifReaderRftInterface::cellIndices( const QString& wellName, const QDateTime& timeStep ) { + return {}; } diff --git a/ApplicationLibCode/FileInterface/RifReaderRftInterface.h b/ApplicationLibCode/FileInterface/RifReaderRftInterface.h index 4f009532b2..d9b7c59abd 100644 --- a/ApplicationLibCode/FileInterface/RifReaderRftInterface.h +++ b/ApplicationLibCode/FileInterface/RifReaderRftInterface.h @@ -31,6 +31,8 @@ namespace caf class VecIjk; }; +class RigEclipseWellLogExtractor; + class RifReaderRftInterface { public: @@ -46,5 +48,9 @@ class RifReaderRftInterface virtual std::set availableWellLogChannels( const QString& wellName ) = 0; virtual std::set wellNames() = 0; - virtual void cellIndices( const RifEclipseRftAddress& rftAddress, std::vector* indices ); + // To be moved into Rig structures + std::vector computeMeasuredDepth( const QString& wellName, const QDateTime& timeStep, RigEclipseWellLogExtractor* extractor ); + + // TODO: Move to protected or private + virtual std::vector cellIndices( const QString& wellName, const QDateTime& timeStep ); }; diff --git a/ApplicationLibCode/ProjectDataModel/Flow/RimWellPlotTools.cpp b/ApplicationLibCode/ProjectDataModel/Flow/RimWellPlotTools.cpp index 1d7ed96f1f..49f32ce8d7 100644 --- a/ApplicationLibCode/ProjectDataModel/Flow/RimWellPlotTools.cpp +++ b/ApplicationLibCode/ProjectDataModel/Flow/RimWellPlotTools.cpp @@ -597,6 +597,18 @@ std::set RimWellPlotTools::availableSimWellTimesteps( RimEclipseCase* return availebleTimeSteps; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifReaderRftInterface* RimWellPlotTools::rftReaderInterface( RimEclipseCase* eclipseCase ) +{ + auto eclResCase = dynamic_cast( eclipseCase ); + + if ( eclResCase ) return eclResCase->rftReader(); + + return nullptr; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -608,7 +620,7 @@ RiaRftPltCurveDefinition RimWellPlotTools::curveDefFromCurve( const RimWellLogCu if ( rftCurve != nullptr ) { - RimEclipseResultCase* rftCase = dynamic_cast( rftCurve->eclipseResultCase() ); + RimEclipseResultCase* rftCase = dynamic_cast( rftCurve->eclipseCase() ); RimSummaryCase* rftSummaryCase = rftCurve->summaryCase(); RimSummaryCaseCollection* rftEnsemble = rftCurve->ensemble(); RimObservedFmuRftData* rftFmuData = rftCurve->observedFmuRftData(); @@ -739,9 +751,10 @@ std::set for ( const RifDataSourceForRftPlt& addr : selectedSourcesExpanded ) { - if ( addr.sourceType() == RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA && addr.rftReader() ) + auto rftReader = rftReaderInterface( addr.eclCase() ); + if ( addr.sourceType() == RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA && rftReader ) { - std::set rftTimes = addr.rftReader()->availableTimeSteps( simWellName, interestingRFTResults ); + std::set rftTimes = rftReader->availableTimeSteps( simWellName, interestingRFTResults ); for ( const QDateTime& time : rftTimes ) { if ( selectedTimeStepSet.count( time ) ) @@ -1149,21 +1162,26 @@ std::map> } } } + + hasObservedData = !observedTimeStepsWithSources.empty(); } if ( hasRftData ) { for ( const auto& source : selSources ) { - if ( source.sourceType() == RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA && source.rftReader() ) + auto rftReader = rftReaderInterface( source.eclCase() ); + if ( source.sourceType() == RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA && rftReader ) { - std::set rftTimes = source.rftReader()->availableTimeSteps( simWellName, interestingRFTResults ); + std::set rftTimes = rftReader->availableTimeSteps( simWellName, interestingRFTResults ); for ( const QDateTime& date : rftTimes ) { rftTimeStepsWithSources[date].insert( source ); } } } + + hasRftData = !rftTimeStepsWithSources.empty(); } if ( hasGridData ) @@ -1181,6 +1199,8 @@ std::map> } } } + + hasGridData = !gridTimestepsWithSources.empty(); } if ( hasSummaryRftData ) @@ -1198,6 +1218,8 @@ std::map> } } } + + hasSummaryRftData = !summaryRftTimeStepsWithSources.empty(); } if ( hasEnsembleData ) @@ -1214,34 +1236,36 @@ std::map> } } } + + hasEnsembleData = !ensembleTimeStepsWithSources.empty(); } // If we have a time baseline add the equal or adjacent grid timesteps - std::map> timestepsToShowWithSources; - std::map>* timeBaseline = nullptr; + std::map> timestepsToShowWithSources; + std::map> timeBaseline; if ( hasObservedData ) { - timeBaseline = &observedTimeStepsWithSources; + timeBaseline = observedTimeStepsWithSources; } else if ( hasRftData ) { - timeBaseline = &rftTimeStepsWithSources; + timeBaseline = rftTimeStepsWithSources; } else if ( hasSummaryRftData ) { - timeBaseline = &summaryRftTimeStepsWithSources; + timeBaseline = summaryRftTimeStepsWithSources; } else if ( hasEnsembleData ) { - timeBaseline = &ensembleTimeStepsWithSources; + timeBaseline = ensembleTimeStepsWithSources; } - if ( timeBaseline ) + if ( !timeBaseline.empty() ) { std::set baseTimeSteps; - for ( const auto& dateSourceSetPair : *timeBaseline ) + for ( const auto& dateSourceSetPair : timeBaseline ) baseTimeSteps.insert( dateSourceSetPair.first ); std::set rftTimeSteps; diff --git a/ApplicationLibCode/ProjectDataModel/Flow/RimWellPlotTools.h b/ApplicationLibCode/ProjectDataModel/Flow/RimWellPlotTools.h index 120c79cdd5..1516fe43a8 100644 --- a/ApplicationLibCode/ProjectDataModel/Flow/RimWellPlotTools.h +++ b/ApplicationLibCode/ProjectDataModel/Flow/RimWellPlotTools.h @@ -43,6 +43,7 @@ class RimPressureDepthData; class RiuWellRftPlot; class RigEclipseCaseData; class RigEclipseResultAddress; +class RifReaderRftInterface; //================================================================================================== /// @@ -153,4 +154,6 @@ class RimWellPlotTools static std::set findMatchingOrAdjacentTimeSteps( const std::set& baseTimeLine, const std::set& availableTimeSteps ); static std::set availableSimWellTimesteps( RimEclipseCase* eclCase, const QString& simWellName, bool addFirstReportTimeStep ); + + static RifReaderRftInterface* rftReaderInterface( RimEclipseCase* eclipseCase ); }; diff --git a/ApplicationLibCode/ProjectDataModel/Flow/RimWellPltPlot.cpp b/ApplicationLibCode/ProjectDataModel/Flow/RimWellPltPlot.cpp index 4d757452d7..8d17f04811 100644 --- a/ApplicationLibCode/ProjectDataModel/Flow/RimWellPltPlot.cpp +++ b/ApplicationLibCode/ProjectDataModel/Flow/RimWellPltPlot.cpp @@ -295,20 +295,16 @@ class RigRftResultPointCalculator : public RigResultPointCalculator public: RigRftResultPointCalculator( const QString& wellPathName, RimEclipseResultCase* eclCase, QDateTime m_timeStep ) { - RifEclipseRftAddress gasRateAddress = RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ), - m_timeStep, - RifEclipseRftAddress::RftWellLogChannelType::GRAT ); - RifEclipseRftAddress oilRateAddress = RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ), - m_timeStep, - RifEclipseRftAddress::RftWellLogChannelType::ORAT ); - RifEclipseRftAddress watRateAddress = RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ), - m_timeStep, - RifEclipseRftAddress::RftWellLogChannelType::WRAT ); - - std::vector rftIndices; - eclCase->rftReader()->cellIndices( gasRateAddress, &rftIndices ); - if ( rftIndices.empty() ) eclCase->rftReader()->cellIndices( oilRateAddress, &rftIndices ); - if ( rftIndices.empty() ) eclCase->rftReader()->cellIndices( watRateAddress, &rftIndices ); + const auto wellNameForRft = RimWellPlotTools::simWellName( wellPathName ); + + RifEclipseRftAddress gasRateAddress = + RifEclipseRftAddress::createAddress( wellNameForRft, m_timeStep, RifEclipseRftAddress::RftWellLogChannelType::GRAT ); + RifEclipseRftAddress oilRateAddress = + RifEclipseRftAddress::createAddress( wellNameForRft, m_timeStep, RifEclipseRftAddress::RftWellLogChannelType::ORAT ); + RifEclipseRftAddress watRateAddress = + RifEclipseRftAddress::createAddress( wellNameForRft, m_timeStep, RifEclipseRftAddress::RftWellLogChannelType::WRAT ); + + std::vector rftIndices = eclCase->rftReader()->cellIndices( wellNameForRft, m_timeStep ); if ( rftIndices.empty() ) return; std::vector gasRates; diff --git a/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftEnsembleCurveSet.cpp b/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftEnsembleCurveSet.cpp index d58ed1cf06..55a431292a 100644 --- a/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftEnsembleCurveSet.cpp +++ b/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftEnsembleCurveSet.cpp @@ -18,10 +18,14 @@ #include "RimWellRftEnsembleCurveSet.h" +#include "RifReaderEnsembleStatisticsRft.h" + +#include "RimEclipseCase.h" #include "RimEnsembleCurveSetColorManager.h" #include "RimRegularLegendConfig.h" #include "RimSummaryCase.h" #include "RimSummaryCaseCollection.h" +#include "RimTools.h" #include "RimWellRftPlot.h" #include "RiuQwtPlotWidget.h" @@ -59,6 +63,9 @@ RimWellRftEnsembleCurveSet::RimWellRftEnsembleCurveSet() m_ensembleLegendConfig = new RimRegularLegendConfig(); m_ensembleLegendConfig->setColorLegend( RimRegularLegendConfig::mapToColorLegend( RimEnsembleCurveSetColorManager::DEFAULT_ENSEMBLE_COLOR_RANGE ) ); + + CAF_PDM_InitFieldNoDefault( &m_eclipseCase, "EclipseResultCase", "Eclipse Result Case" ); + m_eclipseCase.uiCapability()->setUiHidden( true ); } //-------------------------------------------------------------------------------------------------- @@ -76,9 +83,14 @@ RimSummaryCaseCollection* RimWellRftEnsembleCurveSet::ensemble() const return m_ensemble; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- void RimWellRftEnsembleCurveSet::setEnsemble( RimSummaryCaseCollection* ensemble ) { m_ensemble = ensemble; + + clearEnsembleStatistics(); } //-------------------------------------------------------------------------------------------------- @@ -150,6 +162,22 @@ std::vector RimWellRftEnsembleCurveSet::parametersWithVariation() const return std::vector( paramSet.begin(), paramSet.end() ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellRftEnsembleCurveSet::clearEnsembleStatistics() +{ + m_statisticsEclipseRftReader = new RifReaderEnsembleStatisticsRft( m_ensemble(), m_eclipseCase() ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellRftEnsembleCurveSet::initAfterRead() +{ + clearEnsembleStatistics(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -177,6 +205,31 @@ RigEnsembleParameter::Type RimWellRftEnsembleCurveSet::currentEnsembleParameterT return RigEnsembleParameter::TYPE_NONE; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellRftEnsembleCurveSet::setEclipseCase( RimEclipseCase* eclipseCase ) +{ + m_eclipseCase = eclipseCase; + clearEnsembleStatistics(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimEclipseCase* RimWellRftEnsembleCurveSet::eclipseCase() const +{ + return m_eclipseCase(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifReaderRftInterface* RimWellRftEnsembleCurveSet::statisticsEclipseRftReader() +{ + return m_statisticsEclipseRftReader.p(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -188,6 +241,10 @@ void RimWellRftEnsembleCurveSet::fieldChangedByUi( const caf::PdmFieldHandle* ch rftPlot->syncCurvesFromUiSelection(); rftPlot->updateConnectedEditors(); } + else if ( changedField == &m_eclipseCase ) + { + clearEnsembleStatistics(); + } } //-------------------------------------------------------------------------------------------------- @@ -203,6 +260,12 @@ QList RimWellRftEnsembleCurveSet::calculateValueOptions( options.push_back( caf::PdmOptionItemInfo( param, param ) ); } } + else if ( fieldNeedingOptions == &m_eclipseCase ) + { + RimTools::caseOptionItems( &options ); + + options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) ); + } return options; } @@ -218,6 +281,9 @@ void RimWellRftEnsembleCurveSet::defineUiOrdering( QString uiConfigName, caf::Pd { colorsGroup->add( &m_ensembleParameter ); } + + uiOrdering.add( &m_eclipseCase ); + uiOrdering.skipRemainingFields( true ); } diff --git a/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftEnsembleCurveSet.h b/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftEnsembleCurveSet.h index c27ec7f44c..9ee889010e 100644 --- a/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftEnsembleCurveSet.h +++ b/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftEnsembleCurveSet.h @@ -32,6 +32,9 @@ class RiuCvfOverlayItemWidget; class RimSummaryCaseCollection; +class RimEclipseCase; +class RifReaderEnsembleStatisticsRft; +class RifReaderRftInterface; class RimWellRftEnsembleCurveSet : public caf::PdmObject { @@ -57,6 +60,11 @@ class RimWellRftEnsembleCurveSet : public caf::PdmObject RimRegularLegendConfig* legendConfig(); RigEnsembleParameter::Type currentEnsembleParameterType() const; + void setEclipseCase( RimEclipseCase* eclipseCase ); + RimEclipseCase* eclipseCase() const; + + RifReaderRftInterface* statisticsEclipseRftReader(); + protected: void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; @@ -69,11 +77,18 @@ class RimWellRftEnsembleCurveSet : public caf::PdmObject private: QString ensembleName() const; std::vector parametersWithVariation() const; + void clearEnsembleStatistics(); private: + caf::PdmPtrField m_eclipseCase; caf::PdmPtrField m_ensemble; caf::PdmProxyValueField m_ensembleName; caf::PdmField m_ensembleColorMode; caf::PdmField m_ensembleParameter; caf::PdmChildField m_ensembleLegendConfig; + + cvf::ref m_statisticsEclipseRftReader; + +protected: + void initAfterRead() override; }; diff --git a/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftPlot.cpp b/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftPlot.cpp index 9413523552..16476d303d 100644 --- a/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftPlot.cpp +++ b/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftPlot.cpp @@ -59,6 +59,7 @@ #include "RimWellPathCollection.h" #include "RimWellPlotTools.h" #include "RimWellPltPlot.h" +#include "RimWellRftEnsembleCurveSet.h" #include "RiuAbstractLegendFrame.h" #include "RiuAbstractOverlayContentFrame.h" @@ -123,6 +124,10 @@ RimWellRftPlot::RimWellRftPlot() m_selectedTimeSteps.uiCapability()->setAutoAddingOptionFromValue( false ); CAF_PDM_InitFieldNoDefault( &m_ensembleCurveSets, "EnsembleCurveSets", "Ensemble Curve Sets" ); + CAF_PDM_InitFieldNoDefault( &m_ensembleCurveSetEclipseCase, + "EclipseResultCase", + "Grid Model For MD", + "Grid model used to compute measured depth using well path geometry" ); // TODO: may want to support TRUE_VERTICAL_DEPTH_RKB in the future // It was developed for regular well log plots and requires some more work for RFT plots. @@ -496,7 +501,7 @@ void RimWellRftPlot::updateCurvesInPlot( const std::setaddCurve( curve ); auto rftCase = curveDefToAdd.address().eclCase(); - curve->setEclipseResultCase( dynamic_cast( rftCase ) ); + curve->setEclipseCase( rftCase ); RifEclipseRftAddress address = RifEclipseRftAddress::createAddress( simWellName, curveDefToAdd.timeStep(), @@ -534,8 +539,8 @@ void RimWellRftPlot::updateCurvesInPlot( const std::setaddCurve( curve ); - auto rftCase = curveDefToAdd.address().summaryCase(); - curve->setSummaryCase( rftCase ); + auto summaryCase = curveDefToAdd.address().summaryCase(); + curve->setSummaryCase( summaryCase ); curve->setEnsemble( curveDefToAdd.address().ensemble() ); curve->setObservedFmuRftData( findObservedFmuData( m_wellPathNameOrSimWellName, curveDefToAdd.timeStep() ) ); RifEclipseRftAddress address = RifEclipseRftAddress::createAddress( m_wellPathNameOrSimWellName, @@ -545,7 +550,13 @@ void RimWellRftPlot::updateCurvesInPlot( const std::setsetEclipseResultCase( dynamic_cast( curveDefToAdd.address().eclCase() ) ); + auto eclipeCase = curveDefToAdd.address().eclCase(); + if ( curveDefToAdd.address().ensemble() ) + { + auto curveSet = findEnsembleCurveSet( curveDefToAdd.address().ensemble() ); + if ( curveSet ) eclipeCase = curveSet->eclipseCase(); + } + curve->setEclipseCase( eclipeCase ); double zValue = 1.0; if ( !curveDefToAdd.address().ensemble() ) @@ -565,9 +576,11 @@ void RimWellRftPlot::updateCurvesInPlot( const std::set rftAddresses = - ensemble->rftStatisticsReader()->eclipseRftAddresses( m_wellPathNameOrSimWellName, curveDefToAdd.timeStep() ); + curveSet->statisticsEclipseRftReader()->eclipseRftAddresses( m_wellPathNameOrSimWellName, curveDefToAdd.timeStep() ); for ( const auto& rftAddress : rftAddresses ) { if ( rftAddress.wellLogChannel() == RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_P50 ) @@ -584,6 +597,7 @@ void RimWellRftPlot::updateCurvesInPlot( const std::setaddCurve( curve ); curve->setEnsemble( ensemble ); + curve->setEclipseCase( curveSet->eclipseCase() ); curve->setRftAddress( rftAddress ); curve->setObservedFmuRftData( findObservedFmuData( m_wellPathNameOrSimWellName, curveDefToAdd.timeStep() ) ); curve->setZOrder( RiuQwtPlotCurveDefines::zDepthForIndex( RiuQwtPlotCurveDefines::ZIndex::Z_ENSEMBLE_STAT_CURVE ) ); @@ -795,6 +809,13 @@ QList RimWellRftPlot::calculateValueOptions( const caf:: options = RiaSimWellBranchTools::valueOptionsForBranchIndexField( simulationWellBranches ); } + else if ( fieldNeedingOptions == &m_ensembleCurveSetEclipseCase ) + { + RimTools::caseOptionItems( &options ); + + options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) ); + } + return options; } @@ -968,6 +989,17 @@ void RimWellRftPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, updateFormationsOnPlot(); syncCurvesFromUiSelection(); } + else if ( changedField == &m_ensembleCurveSetEclipseCase ) + { + for ( RimWellRftEnsembleCurveSet* curveSet : m_ensembleCurveSets() ) + { + curveSet->setEclipseCase( m_ensembleCurveSetEclipseCase ); + } + + createEnsembleCurveSets(); + updateFormationsOnPlot(); + syncCurvesFromUiSelection(); + } } //-------------------------------------------------------------------------------------------------- @@ -1016,6 +1048,11 @@ void RimWellRftPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& caf::PdmUiGroup* sourcesGroup = uiOrdering.addNewGroupWithKeyword( "Sources", "Sources" ); sourcesGroup->add( &m_selectedSources ); + if ( !m_ensembleCurveSets.empty() ) + { + uiOrdering.add( &m_ensembleCurveSetEclipseCase ); + } + caf::PdmUiGroup* timeStepsGroup = uiOrdering.addNewGroupWithKeyword( "Time Steps", "TimeSteps" ); timeStepsGroup->add( &m_selectedTimeSteps ); diff --git a/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftPlot.h b/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftPlot.h index 3cdfc8130b..168ff2f8d6 100644 --- a/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftPlot.h +++ b/ApplicationLibCode/ProjectDataModel/Flow/RimWellRftPlot.h @@ -92,6 +92,8 @@ class RimWellRftPlot : public RimWellLogPlot bool showErrorBarsForObservedData() const; void onLegendDefinitionChanged(); + RimWellRftEnsembleCurveSet* findEnsembleCurveSet( RimSummaryCaseCollection* ensemble ) const; + protected: void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName ) override; @@ -138,7 +140,6 @@ class RimWellRftPlot : public RimWellLogPlot std::vector selectedEnsembles() const; void createEnsembleCurveSets(); - RimWellRftEnsembleCurveSet* findEnsembleCurveSet( RimSummaryCaseCollection* ensemble ) const; private: friend class RimWellRftEnsembleCurveSet; @@ -153,7 +154,9 @@ class RimWellRftPlot : public RimWellLogPlot caf::PdmField> m_selectedSources; caf::PdmField> m_selectedTimeSteps; - caf::PdmChildArrayField m_ensembleCurveSets; + caf::PdmChildArrayField m_ensembleCurveSets; + caf::PdmPtrField m_ensembleCurveSetEclipseCase; + std::map> m_ensembleLegendFrames; std::map m_dataSourceColors; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp index 0a9354fc81..ce0fa8369c 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp @@ -28,6 +28,9 @@ #include "RicfCommandObject.h" +#include "RifReaderRftInterface.h" +#include "RifSummaryReaderInterface.h" + #include "RimAnalysisPlotDataEntry.h" #include "RimDerivedEnsembleCaseCollection.h" #include "RimEnsembleCurveSet.h" @@ -36,10 +39,6 @@ #include "RimSummaryCalculationCollection.h" #include "RimSummaryCase.h" -#include "RifReaderEclipseRft.h" -#include "RifReaderEnsembleStatisticsRft.h" -#include "RifSummaryReaderInterface.h" - #include "cafPdmFieldScriptingCapability.h" #include "cafPdmUiTreeOrdering.h" @@ -135,8 +134,6 @@ RimSummaryCaseCollection::RimSummaryCaseCollection() m_dataVectorFolders->uiCapability()->setUiTreeHidden( true ); m_dataVectorFolders.xmlCapability()->disableIO(); - m_statisticsEclipseRftReader = new RifReaderEnsembleStatisticsRft( this ); - m_commonAddressCount = 0; } @@ -406,14 +403,6 @@ std::set RimSummaryCaseCollection::rftTimeStepsForWell( const QString return allTimeSteps; } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RifReaderRftInterface* RimSummaryCaseCollection::rftStatisticsReader() -{ - return m_statisticsEclipseRftReader.p(); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h index 6dcdd38369..725f7d376a 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h @@ -39,8 +39,6 @@ #include #include -class RifReaderRftInterface; -class RifReaderEnsembleStatisticsRft; class RimSummaryCase; class RimSummaryAddressCollection; class RiaSummaryAddressAnalyzer; @@ -74,12 +72,11 @@ class RimSummaryCaseCollection : public caf::PdmObject virtual std::set ensembleSummaryAddresses() const; virtual std::set ensembleTimeSteps() const; - std::set wellsWithRftData() const; - std::set rftTimeStepsForWell( const QString& wellName ) const; - RifReaderRftInterface* rftStatisticsReader(); - void setEnsembleId( int ensembleId ); - int ensembleId() const; - bool hasEnsembleParameters() const; + std::set wellsWithRftData() const; + std::set rftTimeStepsForWell( const QString& wellName ) const; + void setEnsembleId( int ensembleId ); + int ensembleId() const; + bool hasEnsembleParameters() const; std::vector variationSortedEnsembleParameters( bool excludeNoVariation = false ) const; std::vector> correlationSortedEnsembleParameters( const RifEclipseSummaryAddress& address ) const; @@ -147,8 +144,7 @@ class RimSummaryCaseCollection : public caf::PdmObject caf::PdmField m_isEnsemble; caf::PdmChildField m_dataVectorFolders; - cvf::ref m_statisticsEclipseRftReader; - caf::PdmField m_ensembleId; + caf::PdmField m_ensembleId; size_t m_commonAddressCount; // if different address count among cases, set to 0 diff --git a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCurveCommonDataSource.cpp b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCurveCommonDataSource.cpp index cedb8c041b..608ba3dc67 100644 --- a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCurveCommonDataSource.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCurveCommonDataSource.cpp @@ -382,7 +382,7 @@ void RimWellLogCurveCommonDataSource::analyseCurvesAndTracks( const std::vector< else if ( rftCurve ) { if ( rftCurve->summaryCase() ) m_uniqueSummaryCases.insert( rftCurve->summaryCase() ); - if ( rftCurve->eclipseResultCase() ) m_uniqueCases.insert( rftCurve->eclipseResultCase() ); + if ( rftCurve->eclipseCase() ) m_uniqueCases.insert( rftCurve->eclipseCase() ); m_uniqueWellNames.insert( rftCurve->wellName() ); auto adr = rftCurve->rftAddress(); diff --git a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.cpp b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.cpp index 6fcccbd1d6..6b01a74ac3 100644 --- a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.cpp @@ -22,6 +22,7 @@ #include "RiaColorTools.h" #include "RiaDefines.h" #include "RiaEclipseUnitTools.h" +#include "RiaExtractionTools.h" #include "RiaQDateTimeTools.h" #include "RiaResultNames.h" #include "RiaRftDefines.h" @@ -44,7 +45,6 @@ #include "RimDepthTrackPlot.h" #include "RimEclipseResultCase.h" #include "RimFileSummaryCase.h" -#include "RimMainPlotCollection.h" #include "RimObservedFmuRftData.h" #include "RimPressureDepthData.h" #include "RimProject.h" @@ -53,7 +53,6 @@ #include "RimSummaryCaseCollection.h" #include "RimTools.h" #include "RimWellLogPlot.h" -#include "RimWellLogPlotCollection.h" #include "RimWellLogTrack.h" #include "RimWellPath.h" #include "RimWellPlotTools.h" @@ -152,8 +151,8 @@ RimWellLogRftCurve::RimWellLogRftCurve() { CAF_PDM_InitObject( "Well Log RFT Curve", RimWellLogCurve::wellLogCurveIconName() ); - CAF_PDM_InitFieldNoDefault( &m_eclipseResultCase, "CurveEclipseResultCase", "Eclipse Result Case" ); - m_eclipseResultCase.uiCapability()->setUiTreeChildrenHidden( true ); + CAF_PDM_InitFieldNoDefault( &m_eclipseCase, "CurveEclipseResultCase", "Eclipse Result Case" ); + m_eclipseCase.uiCapability()->setUiTreeChildrenHidden( true ); CAF_PDM_InitFieldNoDefault( &m_summaryCase, "CurveSummaryCase", "Summary Case" ); m_summaryCase.uiCapability()->setUiTreeChildrenHidden( true ); @@ -265,17 +264,17 @@ void RimWellLogRftCurve::setSegmentBranchType( RiaDefines::RftBranchType branchT //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimWellLogRftCurve::setEclipseResultCase( RimEclipseResultCase* eclipseResultCase ) +void RimWellLogRftCurve::setEclipseCase( RimEclipseCase* eclipseCase ) { - m_eclipseResultCase = eclipseResultCase; + m_eclipseCase = eclipseCase; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimEclipseResultCase* RimWellLogRftCurve::eclipseResultCase() const +RimEclipseCase* RimWellLogRftCurve::eclipseCase() const { - return m_eclipseResultCase; + return m_eclipseCase; } //-------------------------------------------------------------------------------------------------- @@ -527,9 +526,9 @@ std::map RimWellLogRftCurve::createCurveNameKeyValueMap() cons variableValueMap[RiaDefines::namingVariableResultType()] = "RFT"; QString caseText; - if ( m_eclipseResultCase ) + if ( m_eclipseCase ) { - caseText = m_eclipseResultCase->caseUserDescription(); + caseText = m_eclipseCase->caseUserDescription(); } else if ( m_summaryCase && m_ensemble ) // Summary RFT curves have both ensemble and summary set { @@ -649,7 +648,7 @@ void RimWellLogRftCurve::onLoadDataAndUpdate( bool updateParentPlot ) RimPlotCurve::updateCurvePresentation( updateParentPlot ); - DerivedMDSource derivedMDSource = DerivedMDSource::NO_SOURCE; + QString axisPrefixText; if ( m_autoCheckStateBasedOnCurveData() || isChecked() ) { @@ -659,7 +658,7 @@ void RimWellLogRftCurve::onLoadDataAndUpdate( bool updateParentPlot ) bool showErrorBarsInObservedData = rftPlot ? rftPlot->showErrorBarsForObservedData() : false; m_showErrorBars = showErrorBarsInObservedData; - std::vector measuredDepthVector = measuredDepthValues(); + std::vector measuredDepthVector = measuredDepthValues( axisPrefixText ); std::vector tvDepthVector = tvDepthValues(); std::vector values = xValues(); std::vector errors = errorValues(); @@ -682,13 +681,13 @@ void RimWellLogRftCurve::onLoadDataAndUpdate( bool updateParentPlot ) } RiaDefines::EclipseUnitSystem unitSystem = RiaDefines::EclipseUnitSystem::UNITS_METRIC; - if ( m_eclipseResultCase ) + if ( m_eclipseCase ) { // TODO: If no grid data, but only RFT data is loaded, we do not have any way to // detect unit - if ( m_eclipseResultCase->eclipseCaseData() ) + if ( m_eclipseCase->eclipseCaseData() ) { - unitSystem = m_eclipseResultCase->eclipseCaseData()->unitsType(); + unitSystem = m_eclipseCase->eclipseCaseData()->unitsType(); } } else if ( m_summaryCase ) @@ -718,19 +717,14 @@ void RimWellLogRftCurve::onLoadDataAndUpdate( bool updateParentPlot ) if ( tvDepthVector.size() != measuredDepthVector.size() ) { - if ( deriveMeasuredDepthValuesFromWellPath( tvDepthVector, measuredDepthVector ) ) + if ( deriveMeasuredDepthFromObservedData( tvDepthVector, measuredDepthVector ) ) { - derivedMDSource = DerivedMDSource::WELL_PATH; - } - else if ( deriveMeasuredDepthFromObservedData( tvDepthVector, measuredDepthVector ) ) - { - derivedMDSource = DerivedMDSource::OBSERVED_DATA; + axisPrefixText = "OBS/"; } } if ( tvDepthVector.size() != measuredDepthVector.size() ) { - derivedMDSource = DerivedMDSource::NO_SOURCE; measuredDepthVector = tvDepthVector; } @@ -783,23 +777,7 @@ void RimWellLogRftCurve::onLoadDataAndUpdate( bool updateParentPlot ) RiuQwtPlotWidget* viewer = wellLogTrack->viewer(); if ( viewer ) { - QString text; - - if ( derivedMDSource != DerivedMDSource::NO_SOURCE ) - { - if ( derivedMDSource == DerivedMDSource::WELL_PATH ) - { - text = "WELL/" + wellLogPlot->depthAxisTitle(); - } - else - { - text = "OBS/" + wellLogPlot->depthAxisTitle(); - } - } - else // Standard depth title set from plot - { - text = wellLogPlot->depthAxisTitle(); - } + QString text = axisPrefixText + wellLogPlot->depthAxisTitle(); viewer->setAxisTitleText( wellLogPlot->depthAxis(), text ); } @@ -852,7 +830,7 @@ void RimWellLogRftCurve::defineUiOrdering( QString uiConfigName, caf::PdmUiOrder RimPlotCurve::updateFieldUiState(); caf::PdmUiGroup* curveDataGroup = uiOrdering.addNewGroup( "Curve Data" ); - curveDataGroup->add( &m_eclipseResultCase ); + curveDataGroup->add( &m_eclipseCase ); curveDataGroup->add( &m_summaryCase ); curveDataGroup->add( &m_wellName ); curveDataGroup->add( &m_timeStep ); @@ -894,7 +872,7 @@ QList RimWellLogRftCurve::calculateValueOptions( const c if ( !options.empty() ) return options; RifReaderRftInterface* reader = rftReader(); - if ( fieldNeedingOptions == &m_eclipseResultCase ) + if ( fieldNeedingOptions == &m_eclipseCase ) { RimTools::caseOptionItems( &options ); @@ -954,12 +932,10 @@ QList RimWellLogRftCurve::calculateValueOptions( const c //-------------------------------------------------------------------------------------------------- void RimWellLogRftCurve::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { - m_idxInWellPathToIdxInRftFile.clear(); - bool loadData = false; RimWellLogCurve::fieldChangedByUi( changedField, oldValue, newValue ); - if ( changedField == &m_eclipseResultCase ) + if ( changedField == &m_eclipseCase ) { m_timeStep = QDateTime(); m_wellName = ""; @@ -1027,11 +1003,6 @@ std::vector RimWellLogRftCurve::perPointLabels() const //-------------------------------------------------------------------------------------------------- RifReaderRftInterface* RimWellLogRftCurve::rftReader() const { - if ( m_eclipseResultCase() ) - { - return m_eclipseResultCase()->rftReader(); - } - if ( m_summaryCase() ) // Summary RFT curves have both summary and ensemble set. Prioritize summary for reader. { return m_summaryCase()->rftReader(); @@ -1039,7 +1010,14 @@ RifReaderRftInterface* RimWellLogRftCurve::rftReader() const if ( m_ensemble() ) { - return m_ensemble()->rftStatisticsReader(); + auto wellLogPlot = firstAncestorOrThisOfType(); + auto curveSet = wellLogPlot->findEnsembleCurveSet( m_ensemble ); + if ( curveSet ) + { + return curveSet->statisticsEclipseRftReader(); + } + + return nullptr; } if ( m_observedFmuRftData() ) @@ -1052,6 +1030,11 @@ RifReaderRftInterface* RimWellLogRftCurve::rftReader() const return m_pressureDepthData()->rftReader(); } + if ( auto resultCase = dynamic_cast( m_eclipseCase() ) ) + { + return resultCase->rftReader(); + } + return nullptr; } @@ -1060,121 +1043,18 @@ RifReaderRftInterface* RimWellLogRftCurve::rftReader() const //-------------------------------------------------------------------------------------------------- RigEclipseWellLogExtractor* RimWellLogRftCurve::extractor() { - RifReaderRftInterface* reader = rftReader(); - if ( !reader ) return nullptr; - - auto mainPlotCollection = firstAncestorOrThisOfTypeAsserted(); - - RimWellLogPlotCollection* wellLogCollection = mainPlotCollection->wellLogPlotCollection(); - if ( !wellLogCollection ) return nullptr; - - RigEclipseWellLogExtractor* eclExtractor = nullptr; - RimProject* proj = RimProject::current(); RimWellPath* wellPath = proj->wellPathFromSimWellName( m_wellName() ); - eclExtractor = wellLogCollection->findOrCreateExtractor( wellPath, m_eclipseResultCase ); - - if ( !eclExtractor && m_eclipseResultCase ) - { - QString simWellName = RimWellPlotTools::simWellName( m_wellName ); - std::vector wellPaths = RiaSimWellBranchTools::simulationWellBranches( simWellName, m_branchDetection ); - if ( wellPaths.empty() ) return nullptr; - - m_branchIndex = RiaSimWellBranchTools::clampBranchIndex( simWellName, m_branchIndex, m_branchDetection ); - - auto wellPathBranch = wellPaths[m_branchIndex]; - - eclExtractor = wellLogCollection->findOrCreateSimWellExtractor( simWellName, - QString( "Find or create sim well extractor" ), - wellPathBranch, - m_eclipseResultCase->eclipseCaseData() ); - } - - return eclExtractor; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RimWellLogRftCurve::createWellPathIdxToRftFileIdxMapping() -{ - if ( !m_idxInWellPathToIdxInRftFile.empty() ) - { - return true; - } - - RigEclipseWellLogExtractor* eclExtractor = extractor(); - - if ( !eclExtractor ) return false; - - std::vector intersections = eclExtractor->cellIntersectionInfosAlongWellPath(); - if ( intersections.empty() ) return false; - - std::map globCellIndicesToIndexInWell; - - for ( size_t idx = 0; idx < intersections.size(); idx++ ) - { - globCellIndicesToIndexInWell[intersections[idx].globCellIndex] = idx; - } - - RifEclipseRftAddress depthAddress = - RifEclipseRftAddress::createAddress( m_wellName(), m_timeStep, RifEclipseRftAddress::RftWellLogChannelType::TVD ); - std::vector rftIndices; - if ( !rftReader() ) return false; - - rftReader()->cellIndices( depthAddress, &rftIndices ); - - const RigMainGrid* mainGrid = eclExtractor->caseData()->mainGrid(); - - for ( size_t idx = 0; idx < rftIndices.size(); idx++ ) - { - caf::VecIjk ijkIndex = rftIndices[idx]; - size_t globalCellIndex = mainGrid->cellIndexFromIJK( ijkIndex.i(), ijkIndex.j(), ijkIndex.k() ); - - if ( globCellIndicesToIndexInWell.find( globalCellIndex ) != globCellIndicesToIndexInWell.end() ) - { - m_idxInWellPathToIdxInRftFile[globCellIndicesToIndexInWell[globalCellIndex]] = idx; - } - } - - return true; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -size_t RimWellLogRftCurve::rftFileIndex( size_t wellPathIndex ) -{ - if ( m_idxInWellPathToIdxInRftFile.empty() ) - { - createWellPathIdxToRftFileIdxMapping(); - } - - if ( m_idxInWellPathToIdxInRftFile.find( wellPathIndex ) == m_idxInWellPathToIdxInRftFile.end() ) - { - return cvf::UNDEFINED_SIZE_T; - } - return m_idxInWellPathToIdxInRftFile[wellPathIndex]; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::vector RimWellLogRftCurve::sortedIndicesInRftFile() -{ - if ( m_idxInWellPathToIdxInRftFile.empty() ) - { - createWellPathIdxToRftFileIdxMapping(); - } - - std::vector indices; - for ( auto& it : m_idxInWellPathToIdxInRftFile ) + // The well path extractor has the best geometrical representation, so use this if found + if ( auto wellPathExtractor = RiaExtractionTools::findOrCreateWellLogExtractor( wellPath, m_eclipseCase ) ) { - indices.push_back( it.second ); + return wellPathExtractor; } - return indices; + // Use sim well extractor as fallback + QString simWellName = RimWellPlotTools::simWellName( m_wellName ); + return RiaExtractionTools::findOrCreateSimWellExtractor( m_eclipseCase(), simWellName, m_branchDetection(), m_branchIndex() ); } //-------------------------------------------------------------------------------------------------- @@ -1202,23 +1082,6 @@ std::vector RimWellLogRftCurve::xValues() auto address = RifEclipseRftAddress::createAddress( m_wellName(), m_timeStep, m_wellLogChannelName() ); reader->values( address, &values ); - - bool wellPathExists = createWellPathIdxToRftFileIdxMapping(); - - if ( wellPathExists ) - { - std::vector valuesSorted; - - for ( size_t idx : sortedIndicesInRftFile() ) - { - if ( idx < values.size() ) - { - valuesSorted.push_back( ( values.at( idx ) ) ); - } - } - - std::swap( valuesSorted, values ); - } } if ( m_scaleFactor() != 1.0 ) @@ -1260,51 +1123,35 @@ std::vector RimWellLogRftCurve::tvDepthValues() if ( !reader ) return values; + RifEclipseRftAddress depthAddress = + RifEclipseRftAddress::createAddress( m_wellName(), m_timeStep, RifEclipseRftAddress::RftWellLogChannelType::TVD ); + if ( m_rftDataType() == RftDataType::RFT_SEGMENT_DATA ) { - auto depthAddress = RifEclipseRftAddress::createBranchSegmentAddress( m_wellName(), - m_timeStep, - RiaDefines::segmentTvdDepthResultName(), - segmentBranchIndex(), - m_segmentBranchType() ); - - reader->values( depthAddress, &values ); - return values; + depthAddress = RifEclipseRftAddress::createBranchSegmentAddress( m_wellName(), + m_timeStep, + RiaDefines::segmentTvdDepthResultName(), + segmentBranchIndex(), + m_segmentBranchType() ); } - auto depthAddress = RifEclipseRftAddress::createAddress( m_wellName(), m_timeStep, RifEclipseRftAddress::RftWellLogChannelType::TVD ); reader->values( depthAddress, &values ); - bool wellPathExists = createWellPathIdxToRftFileIdxMapping(); - - if ( wellPathExists ) - { - std::vector valuesSorted; - - for ( size_t idx : sortedIndicesInRftFile() ) - { - if ( idx < values.size() ) - { - valuesSorted.push_back( ( values.at( idx ) ) ); - } - } - - return valuesSorted; - } - return values; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -std::vector RimWellLogRftCurve::measuredDepthValues() +std::vector RimWellLogRftCurve::measuredDepthValues( QString& prefixText ) { if ( m_rftDataType() == RftDataType::RFT_SEGMENT_DATA ) { RifReaderRftInterface* reader = rftReader(); if ( reader ) { + prefixText = "SEGMENT/"; + return RimRftTools::seglenstValues( reader, m_wellName(), m_timeStep, segmentBranchIndex(), m_segmentBranchType() ); } return {}; @@ -1320,6 +1167,8 @@ std::vector RimWellLogRftCurve::measuredDepthValues() RifEclipseRftAddress depthAddress = RifEclipseRftAddress::createAddress( m_wellName(), m_timeStep, RifEclipseRftAddress::RftWellLogChannelType::MD ); reader->values( depthAddress, &values ); + + prefixText = "OBS/"; return values; } @@ -1329,59 +1178,24 @@ std::vector RimWellLogRftCurve::measuredDepthValues() return {}; } - std::vector measuredDepthForCells; - RigEclipseWellLogExtractor* eclExtractor = extractor(); + if ( !eclExtractor ) return {}; - if ( !eclExtractor ) return measuredDepthForCells; - - std::vector measuredDepthForIntersections = eclExtractor->cellIntersectionMDs(); - - if ( measuredDepthForIntersections.empty() ) - { - return measuredDepthForCells; - } - - std::vector globCellIndices = eclExtractor->intersectedCellsGlobIdx(); - - for ( size_t i = 0; i < globCellIndices.size() - 1; i = i + 2 ) - { - double sum = measuredDepthForIntersections[i] + measuredDepthForIntersections[i + 1]; - - measuredDepthForCells.push_back( sum / 2.0 ); - } - - std::vector measuredDepthForCellsWhichHasRftData; - - for ( size_t i = 0; i < measuredDepthForCells.size(); i++ ) + if ( auto reader = rftReader() ) { - if ( rftFileIndex( i ) != cvf::UNDEFINED_SIZE_T ) - { - measuredDepthForCellsWhichHasRftData.push_back( measuredDepthForCells[i] ); - } - } + prefixText = "WELL/"; - return measuredDepthForCellsWhichHasRftData; -} + RifEclipseRftAddress depthAddress = + RifEclipseRftAddress::createAddress( m_wellName(), m_timeStep, RifEclipseRftAddress::RftWellLogChannelType::MD ); -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RimWellLogRftCurve::deriveMeasuredDepthValuesFromWellPath( const std::vector& tvDepthValues, std::vector& derivedMDValues ) -{ - RimProject* proj = RimProject::current(); - RimWellPath* wellPath = proj->wellPathByName( m_wellName ); + std::vector values; + reader->values( depthAddress, &values ); + if ( !values.empty() ) return values; - if ( wellPath && wellPath->wellPathGeometry() ) - { - const std::vector& mdValuesOfWellPath = wellPath->wellPathGeometry()->measuredDepths(); - const std::vector& tvdValuesOfWellPath = wellPath->wellPathGeometry()->trueVerticalDepths(); + return rftReader()->computeMeasuredDepth( m_wellName(), m_timeStep(), eclExtractor ); + }; - derivedMDValues = RigWellPathGeometryTools::interpolateMdFromTvd( mdValuesOfWellPath, tvdValuesOfWellPath, tvDepthValues ); - CVF_ASSERT( derivedMDValues.size() == tvDepthValues.size() ); - return true; - } - return false; + return {}; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.h b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.h index 3a9da8bfc8..2bb460fc26 100644 --- a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.h +++ b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.h @@ -33,7 +33,7 @@ class RifReaderRftInterface; class RigEclipseWellLogExtractor; -class RimEclipseResultCase; +class RimEclipseCase; class RimObservedFmuRftData; class RimSummaryCase; class RimSummaryCaseCollection; @@ -55,15 +55,6 @@ class RimWellLogRftCurve : public RimWellLogCurve RFT_SEGMENT_DATA }; -private: - enum class DerivedMDSource - { - NO_SOURCE, - WELL_PATH, - SEGMENT, - OBSERVED_DATA - }; - public: RimWellLogRftCurve(); ~RimWellLogRftCurve() override; @@ -80,8 +71,8 @@ class RimWellLogRftCurve : public RimWellLogCurve void setSegmentBranchIndex( int branchIndex ); void setSegmentBranchType( RiaDefines::RftBranchType branchType ); - void setEclipseResultCase( RimEclipseResultCase* eclipseResultCase ); - RimEclipseResultCase* eclipseResultCase() const; + void setEclipseCase( RimEclipseCase* eclipseCase ); + RimEclipseCase* eclipseCase() const; void setSummaryCase( RimSummaryCase* summaryCase ); RimSummaryCase* summaryCase() const; @@ -127,19 +118,15 @@ class RimWellLogRftCurve : public RimWellLogCurve RigEclipseWellLogExtractor* extractor(); - bool createWellPathIdxToRftFileIdxMapping(); - size_t rftFileIndex( size_t wellPathIndex ); - std::vector sortedIndicesInRftFile(); - void updateWellChannelNameAndTimeStep(); + void updateWellChannelNameAndTimeStep(); std::map createCurveNameKeyValueMap() const; std::vector xValues(); std::vector errorValues(); std::vector tvDepthValues(); - std::vector measuredDepthValues(); + std::vector measuredDepthValues( QString& prefixText ); - bool deriveMeasuredDepthValuesFromWellPath( const std::vector& tvDepthValues, std::vector& derivedMDValues ); bool deriveMeasuredDepthFromObservedData( const std::vector& tvDepthValues, std::vector& derivedMDValues ); int segmentBranchIndex() const; @@ -147,7 +134,7 @@ class RimWellLogRftCurve : public RimWellLogCurve static bool isSegmentResult( const QString& resultName ); private: - caf::PdmPtrField m_eclipseResultCase; + caf::PdmPtrField m_eclipseCase; caf::PdmPtrField m_summaryCase; caf::PdmPtrField m_ensemble; caf::PdmPtrField m_observedFmuRftData; @@ -166,6 +153,5 @@ class RimWellLogRftCurve : public RimWellLogCurve caf::PdmField m_segmentBranchIndex; caf::PdmField> m_segmentBranchType; - std::map m_idxInWellPathToIdxInRftFile; caf::PdmField> m_wellLogChannelName; }; diff --git a/ApplicationLibCode/ReservoirDataModel/RigWellLogExtractor.cpp b/ApplicationLibCode/ReservoirDataModel/RigWellLogExtractor.cpp index b0220894ab..0fa93db0c1 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWellLogExtractor.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigWellLogExtractor.cpp @@ -155,6 +155,29 @@ void RigWellLogExtractor::resampleIntersections( double maxDistanceBetweenInters m_intersectedCellFaces = resampledIntersectedCellFaces; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::optional RigWellLogExtractor::averageMdForCell( size_t cellIndex ) const +{ + std::vector indicesForCell; + + for ( size_t i = 0; i < m_intersectedCellsGlobIdx.size(); i++ ) + { + if ( m_intersectedCellsGlobIdx[i] == cellIndex ) indicesForCell.emplace_back( i ); + } + + if ( indicesForCell.empty() ) return std::nullopt; + + double sum = 0.0; + for ( const auto& i : indicesForCell ) + { + sum += m_intersectionMeasuredDepths[i]; + } + + return sum / static_cast( indicesForCell.size() ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ReservoirDataModel/RigWellLogExtractor.h b/ApplicationLibCode/ReservoirDataModel/RigWellLogExtractor.h index e9b24619bd..ff9dfe48ac 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWellLogExtractor.h +++ b/ApplicationLibCode/ReservoirDataModel/RigWellLogExtractor.h @@ -73,6 +73,8 @@ class RigWellLogExtractor : public cvf::Object void resampleIntersections( double maxDistanceBetweenIntersections ); + std::optional averageMdForCell( size_t cellIndex ) const; + protected: static void insertIntersectionsInMap( const std::vector& intersections, cvf::Vec3d p1,