diff --git a/ApplicationLibCode/Application/RiaSummaryCurveDefinition.cpp b/ApplicationLibCode/Application/RiaSummaryCurveDefinition.cpp index b8c83e19cc..424c5f67b3 100644 --- a/ApplicationLibCode/Application/RiaSummaryCurveDefinition.cpp +++ b/ApplicationLibCode/Application/RiaSummaryCurveDefinition.cpp @@ -41,11 +41,11 @@ RiaSummaryCurveDefinition::RiaSummaryCurveDefinition() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RiaSummaryCurveDefinition::RiaSummaryCurveDefinition( RimSummaryCase* summaryCase, - const RifEclipseSummaryAddress& summaryAddress, +RiaSummaryCurveDefinition::RiaSummaryCurveDefinition( RimSummaryCase* summaryCaseY, + const RifEclipseSummaryAddress& summaryAddressY, bool isEnsembleCurve ) - : m_summaryCaseY( summaryCase ) - , m_summaryAddressY( summaryAddress ) + : m_summaryCaseY( summaryCaseY ) + , m_summaryAddressY( summaryAddressY ) , m_summaryCaseX( nullptr ) , m_summaryAddressX( RifEclipseSummaryAddress::timeAddress() ) , m_ensemble( nullptr ) @@ -56,9 +56,9 @@ RiaSummaryCurveDefinition::RiaSummaryCurveDefinition( RimSummaryCase* //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RiaSummaryCurveDefinition::RiaSummaryCurveDefinition( RimSummaryCaseCollection* ensemble, const RifEclipseSummaryAddress& summaryAddress ) +RiaSummaryCurveDefinition::RiaSummaryCurveDefinition( RimSummaryCaseCollection* ensemble, const RifEclipseSummaryAddress& summaryAddressY ) : m_summaryCaseY( nullptr ) - , m_summaryAddressY( summaryAddress ) + , m_summaryAddressY( summaryAddressY ) , m_summaryCaseX( nullptr ) , m_summaryAddressX( RifEclipseSummaryAddress::timeAddress() ) , m_ensemble( ensemble ) diff --git a/ApplicationLibCode/Application/RiaSummaryCurveDefinition.h b/ApplicationLibCode/Application/RiaSummaryCurveDefinition.h index 6b8eff67c1..b59a3c4b76 100644 --- a/ApplicationLibCode/Application/RiaSummaryCurveDefinition.h +++ b/ApplicationLibCode/Application/RiaSummaryCurveDefinition.h @@ -38,8 +38,8 @@ class RiaSummaryCurveDefinition { public: RiaSummaryCurveDefinition(); - explicit RiaSummaryCurveDefinition( RimSummaryCase* summaryCase, const RifEclipseSummaryAddress& summaryAddress, bool isEnsembleCurve ); - explicit RiaSummaryCurveDefinition( RimSummaryCaseCollection* ensemble, const RifEclipseSummaryAddress& summaryAddress ); + explicit RiaSummaryCurveDefinition( RimSummaryCase* summaryCaseY, const RifEclipseSummaryAddress& summaryAddressY, bool isEnsembleCurve ); + explicit RiaSummaryCurveDefinition( RimSummaryCaseCollection* ensemble, const RifEclipseSummaryAddress& summaryAddressY ); // Y Axis RimSummaryCase* summaryCaseY() const; diff --git a/ApplicationLibCode/FileInterface/CMakeLists_files.cmake b/ApplicationLibCode/FileInterface/CMakeLists_files.cmake index 8e0da3e48f..6b140d4b0a 100644 --- a/ApplicationLibCode/FileInterface/CMakeLists_files.cmake +++ b/ApplicationLibCode/FileInterface/CMakeLists_files.cmake @@ -45,7 +45,6 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RifSummaryCaseRestartSelector.h ${CMAKE_CURRENT_LIST_DIR}/RifCaseRealizationParametersReader.h ${CMAKE_CURRENT_LIST_DIR}/RifFileParseTools.h - ${CMAKE_CURRENT_LIST_DIR}/RifEnsembleStatisticsReader.h ${CMAKE_CURRENT_LIST_DIR}/RifReaderEnsembleStatisticsRft.h ${CMAKE_CURRENT_LIST_DIR}/RifDerivedEnsembleReader.h ${CMAKE_CURRENT_LIST_DIR}/RifActiveCellsReader.h @@ -136,7 +135,6 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RifSummaryCaseRestartSelector.cpp ${CMAKE_CURRENT_LIST_DIR}/RifCaseRealizationParametersReader.cpp ${CMAKE_CURRENT_LIST_DIR}/RifFileParseTools.cpp - ${CMAKE_CURRENT_LIST_DIR}/RifEnsembleStatisticsReader.cpp ${CMAKE_CURRENT_LIST_DIR}/RifDerivedEnsembleReader.cpp ${CMAKE_CURRENT_LIST_DIR}/RifActiveCellsReader.cpp ${CMAKE_CURRENT_LIST_DIR}/RifCsvDataTableFormatter.cpp diff --git a/ApplicationLibCode/FileInterface/RifEnsembleStatisticsReader.cpp b/ApplicationLibCode/FileInterface/RifEnsembleStatisticsReader.cpp deleted file mode 100644 index 28dcf3ddbc..0000000000 --- a/ApplicationLibCode/FileInterface/RifEnsembleStatisticsReader.cpp +++ /dev/null @@ -1,116 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2017- Statoil ASA -// -// ResInsight is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY -// WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. -// -// See the GNU General Public License at -// for more details. -// -///////////////////////////////////////////////////////////////////////////////// - -#include "RifEnsembleStatisticsReader.h" - -#include "RimEnsembleCurveSet.h" -#include "RimEnsembleStatisticsCase.h" -#include "RimSummaryCaseCollection.h" - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RifEnsembleStatisticsReader::RifEnsembleStatisticsReader( RimEnsembleStatisticsCase* ensStatCase ) -{ - CVF_ASSERT( ensStatCase ); - - m_ensembleStatCase = ensStatCase; - - m_allResultAddresses = - std::set( { RifEclipseSummaryAddress::ensembleStatisticsAddress( ENSEMBLE_STAT_P10_QUANTITY_NAME, "" ), - RifEclipseSummaryAddress::ensembleStatisticsAddress( ENSEMBLE_STAT_P50_QUANTITY_NAME, "" ), - RifEclipseSummaryAddress::ensembleStatisticsAddress( ENSEMBLE_STAT_P90_QUANTITY_NAME, "" ), - RifEclipseSummaryAddress::ensembleStatisticsAddress( ENSEMBLE_STAT_MEAN_QUANTITY_NAME, "" ) } ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::vector RifEnsembleStatisticsReader::timeSteps( const RifEclipseSummaryAddress& resultAddress ) const -{ - if ( !validateAddress( resultAddress ) ) return {}; - return m_ensembleStatCase->timeSteps(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RifEnsembleStatisticsReader::values( const RifEclipseSummaryAddress& resultAddress, std::vector* values ) const -{ - if ( !validateAddress( resultAddress ) ) return false; - - const std::vector* sourceData = nullptr; - auto quantityName = resultAddress.ensembleStatisticsVectorName(); - - if ( quantityName == ENSEMBLE_STAT_P10_QUANTITY_NAME ) - sourceData = &m_ensembleStatCase->p10(); - else if ( quantityName == ENSEMBLE_STAT_P50_QUANTITY_NAME ) - sourceData = &m_ensembleStatCase->p50(); - else if ( quantityName == ENSEMBLE_STAT_P90_QUANTITY_NAME ) - sourceData = &m_ensembleStatCase->p90(); - else if ( quantityName == ENSEMBLE_STAT_MEAN_QUANTITY_NAME ) - sourceData = &m_ensembleStatCase->mean(); - - if ( !sourceData ) return false; - - values->clear(); - values->reserve( sourceData->size() ); - for ( auto val : *sourceData ) - values->push_back( val ); - return true; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::string RifEnsembleStatisticsReader::unitName( const RifEclipseSummaryAddress& resultAddress ) const -{ - std::string retval; - - // The stat case does not have a unit set, so pick up the unit from one of the input cases, if possible - auto cases = m_ensembleStatCase->curveSet()->summaryCaseCollection()->allSummaryCases(); - if ( cases.size() > 0 ) - { - QString qName = QString::fromStdString( resultAddress.vectorName() ); - std::string orgQName = qName.split( ":" )[1].toStdString(); - - RifEclipseSummaryAddress address = RifEclipseSummaryAddress( resultAddress ); - address.setVectorName( orgQName ); - - retval = cases[0]->summaryReader()->unitName( address ); - } - - return retval; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RiaDefines::EclipseUnitSystem RifEnsembleStatisticsReader::unitSystem() const -{ - return m_ensembleStatCase->unitSystem(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RifEnsembleStatisticsReader::validateAddress( const RifEclipseSummaryAddress& address ) const -{ - return address.category() == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_ENSEMBLE_STATISTICS && - !address.vectorName().empty(); -} diff --git a/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.cpp b/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.cpp index d95122ad4c..404f0388f3 100644 --- a/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.cpp @@ -77,18 +77,16 @@ void RimDataSourceSteppingTools::modifyCurrentIndex( caf::PdmValueField* bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& oldValue, const QVariant& newValue, RifEclipseSummaryAddressDefines::SummaryCategory category, - RifEclipseSummaryAddress* adr ) + RifEclipseSummaryAddress& adr ) { - if ( !adr ) return false; - if ( category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_REGION ) { int oldInt = oldValue.toInt(); int newInt = newValue.toInt(); - if ( adr->regionNumber() == oldInt ) + if ( adr.regionNumber() == oldInt ) { - adr->setRegion( newInt ); + adr.setRegion( newInt ); return true; } @@ -98,9 +96,9 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& int oldInt = oldValue.toInt(); int newInt = newValue.toInt(); - if ( adr->aquiferNumber() == oldInt ) + if ( adr.aquiferNumber() == oldInt ) { - adr->setAquiferNumber( newInt ); + adr.setAquiferNumber( newInt ); return true; } @@ -110,9 +108,9 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& std::string oldString = oldValue.toString().toStdString(); std::string newString = newValue.toString().toStdString(); - if ( adr->groupName() == oldString ) + if ( adr.groupName() == oldString ) { - adr->setGroupName( newString ); + adr.setGroupName( newString ); return true; } @@ -122,9 +120,9 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& std::string oldString = oldValue.toString().toStdString(); std::string newString = newValue.toString().toStdString(); - if ( adr->networkName() == oldString ) + if ( adr.networkName() == oldString ) { - adr->setNetworkName( newString ); + adr.setNetworkName( newString ); return true; } @@ -134,9 +132,9 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& std::string oldString = oldValue.toString().toStdString(); std::string newString = newValue.toString().toStdString(); - if ( adr->wellName() == oldString ) + if ( adr.wellName() == oldString ) { - adr->setWellName( newString ); + adr.setWellName( newString ); return true; } @@ -146,9 +144,9 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& { std::string oldString = oldValue.toString().toStdString(); std::string newString = newValue.toString().toStdString(); - if ( adr->blockAsString() == oldString ) + if ( adr.blockAsString() == oldString ) { - adr->setCellIjk( newString ); + adr.setCellIjk( newString ); return true; } @@ -157,11 +155,11 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& { std::string oldString = oldValue.toString().toStdString(); std::string newString = newValue.toString().toStdString(); - if ( adr->formatUiTextRegionToRegion() == oldString ) + if ( adr.formatUiTextRegionToRegion() == oldString ) { auto [region1, region2] = RifEclipseSummaryAddress::regionToRegionPairFromUiText( newString ); - adr->setRegion( region1 ); - adr->setRegion2( region2 ); + adr.setRegion( region1 ); + adr.setRegion2( region2 ); return true; } @@ -170,9 +168,9 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& { int oldInt = oldValue.toInt(); int newInt = newValue.toInt(); - if ( adr->wellSegmentNumber() == oldInt ) + if ( adr.wellSegmentNumber() == oldInt ) { - adr->setWellSegmentNumber( newInt ); + adr.setWellSegmentNumber( newInt ); return true; } @@ -184,48 +182,14 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -bool RimDataSourceSteppingTools::updateHistoryAndSummaryQuantityIfMatching( const QVariant& oldValue, - const QVariant& newValue, - RifEclipseSummaryAddress* adr ) -{ - if ( !adr ) return false; - - std::string oldString = oldValue.toString().toStdString(); - std::string newString = newValue.toString().toStdString(); - - if ( adr->vectorName() == oldString ) - { - adr->setVectorName( newString ); - - return true; - } - - std::string correspondingOldString = RiaSummaryAddressAnalyzer::correspondingHistorySummaryCurveName( oldString ); - std::string correspondingNewString = RiaSummaryAddressAnalyzer::correspondingHistorySummaryCurveName( newString ); - - if ( adr->vectorName() == correspondingOldString ) - { - adr->setVectorName( correspondingNewString ); - - return true; - } - - return false; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RimDataSourceSteppingTools::updateQuantityIfMatching( const QVariant& oldValue, const QVariant& newValue, RifEclipseSummaryAddress* adr ) +bool RimDataSourceSteppingTools::updateQuantityIfMatching( const QVariant& oldValue, const QVariant& newValue, RifEclipseSummaryAddress& adr ) { - if ( !adr ) return false; - std::string oldString = oldValue.toString().toStdString(); std::string newString = newValue.toString().toStdString(); - if ( adr->vectorName() == oldString ) + if ( adr.vectorName() == oldString ) { - adr->setVectorName( newString ); + adr.setVectorName( newString ); return true; } diff --git a/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.h b/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.h index 79ee1ce209..7dca9aaef1 100644 --- a/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.h +++ b/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.h @@ -39,9 +39,7 @@ class RimDataSourceSteppingTools static bool updateAddressIfMatching( const QVariant& oldValue, const QVariant& newValue, RifEclipseSummaryAddressDefines::SummaryCategory category, - RifEclipseSummaryAddress* adr ); + RifEclipseSummaryAddress& adr ); - static bool updateHistoryAndSummaryQuantityIfMatching( const QVariant& oldValue, const QVariant& newValue, RifEclipseSummaryAddress* adr ); - - static bool updateQuantityIfMatching( const QVariant& oldValue, const QVariant& newValue, RifEclipseSummaryAddress* adr ); + static bool updateQuantityIfMatching( const QVariant& oldValue, const QVariant& newValue, RifEclipseSummaryAddress& adr ); }; diff --git a/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.cpp b/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.cpp index facb9e1bc8..4c90fe6590 100644 --- a/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.cpp @@ -292,7 +292,7 @@ void RimSummaryCalculation::substituteVariables( std::vector +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimEnsembleCrossPlotStatisticsCase.h" + +#include "RiaSummaryTools.h" +#include "RiaTimeHistoryCurveResampler.h" + +#include "RigStatisticsMath.h" + +#include "RimEnsembleCurveSet.h" +#include "RimEnsembleStatisticsCase.h" +#include "RimSummaryCaseCollection.h" + +#include +#include +#include + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimEnsembleCrossPlotStatisticsCase::RimEnsembleCrossPlotStatisticsCase() +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimEnsembleCrossPlotStatisticsCase::values( const RifEclipseSummaryAddress& resultAddress, std::vector* values ) const +{ + if ( m_adrX.isValid() ) + { + auto stringToTest = resultAddress.vectorName(); + auto it = stringToTest.find( m_adrX.vectorName() ); + if ( it != std::string::npos ) + { + *values = m_binnedXValues; + return true; + } + } + + auto quantityName = resultAddress.ensembleStatisticsVectorName(); + + if ( quantityName == ENSEMBLE_STAT_P10_QUANTITY_NAME ) + *values = m_p10Data; + else if ( quantityName == ENSEMBLE_STAT_P50_QUANTITY_NAME ) + *values = m_p50Data; + else if ( quantityName == ENSEMBLE_STAT_P90_QUANTITY_NAME ) + *values = m_p90Data; + else if ( quantityName == ENSEMBLE_STAT_MEAN_QUANTITY_NAME ) + *values = m_meanData; + + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RimEnsembleCrossPlotStatisticsCase::unitName( const RifEclipseSummaryAddress& resultAddress ) const +{ + if ( m_firstSummaryCase && m_firstSummaryCase->summaryReader() ) + { + return m_firstSummaryCase->summaryReader()->unitName( resultAddress ); + } + + return "Ensemble Cross Plot - Undefined Unit"; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimEnsembleCrossPlotStatisticsCase::caseName() const +{ + return "Ensemble Statistics"; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleCrossPlotStatisticsCase::createSummaryReaderInterface() +{ + // Nothing to do, as RimEnsembleCrossPlotStatisticsCase inherits RifSummaryReaderInterface +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifSummaryReaderInterface* RimEnsembleCrossPlotStatisticsCase::summaryReader() +{ + return this; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleCrossPlotStatisticsCase::calculate( const std::vector& sumCases, + const RifEclipseSummaryAddress& inputAddressX, + const RifEclipseSummaryAddress& inputAddressY, + bool includeIncompleteCurves, + int binCount, + int sampleCountThreshold ) +{ + if ( !inputAddressX.isValid() || !inputAddressY.isValid() ) return; + if ( sumCases.empty() ) return; + + clearData(); + + // Use first summary case to get unit system and other meta data + m_firstSummaryCase = sumCases.front(); + + m_adrX = inputAddressX; + m_adrY = inputAddressY; + + std::vector> pairs; + + auto [minTimeStep, maxTimeStep] = RimEnsembleStatisticsCase::findMinMaxTimeStep( sumCases, inputAddressX ); + RiaDefines::DateTimePeriod period = RimEnsembleStatisticsCase::findBestResamplingPeriod( minTimeStep, maxTimeStep ); + + for ( const auto& sumCase : sumCases ) + { + const auto& reader = sumCase->summaryReader(); + if ( reader ) + { + const std::vector& timeSteps = reader->timeSteps( inputAddressX ); + + std::vector valuesX; + reader->values( inputAddressX, &valuesX ); + if ( valuesX.empty() ) continue; + + std::vector valuesY; + reader->values( inputAddressY, &valuesY ); + if ( valuesY.empty() ) continue; + + if ( !includeIncompleteCurves && timeSteps.size() != valuesX.size() ) continue; + if ( !includeIncompleteCurves && timeSteps.size() != valuesY.size() ) continue; + + auto [resampledTimeStepsX, resampledValuesX] = + RiaSummaryTools::resampledValuesForPeriod( inputAddressX, timeSteps, valuesX, period ); + + auto [resampledTimeStepsY, resampledValuesY] = + RiaSummaryTools::resampledValuesForPeriod( inputAddressY, timeSteps, valuesY, period ); + + size_t minimumCount = std::min( resampledValuesX.size(), resampledValuesY.size() ); + + for ( size_t i = 0; i < minimumCount; i++ ) + { + pairs.emplace_back( std::make_pair( resampledValuesX[i], resampledValuesY[i] ) ); + } + } + } + + // Sort on X values + std::sort( pairs.begin(), pairs.end(), []( const auto& lhs, const auto& rhs ) { return lhs.first < rhs.first; } ); + + const auto p = std::minmax_element( pairs.begin(), pairs.end() ); + auto minX = p.first->first; + auto maxX = p.second->first; + auto rangeX = maxX - minX; + auto deltaRangeX = rangeX / binCount; + + double currentX = minX; + + std::vector binnedYValues; + for ( auto v : pairs ) + { + if ( v.first < currentX + deltaRangeX ) + { + binnedYValues.emplace_back( v.second ); + } + else + { + // Add statistics for current bin if sample count is above threshold + // TODO: Add option to skip bin if unique realization count is below threshold + + if ( static_cast( binnedYValues.size() ) > sampleCountThreshold ) + { + double p10, p50, p90, mean; + RigStatisticsMath::calculateStatisticsCurves( binnedYValues, &p10, &p50, &p90, &mean, RigStatisticsMath::PercentileStyle::SWITCHED ); + m_p10Data.push_back( p10 ); + m_p50Data.push_back( p50 ); + m_p90Data.push_back( p90 ); + m_meanData.push_back( mean ); + + m_binnedXValues.emplace_back( currentX ); + } + + currentX += deltaRangeX; + binnedYValues.clear(); + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimEnsembleCrossPlotStatisticsCase::hasP10Data() const +{ + return !m_p10Data.empty(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimEnsembleCrossPlotStatisticsCase::hasP50Data() const +{ + return !m_p50Data.empty(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimEnsembleCrossPlotStatisticsCase::hasP90Data() const +{ + return !m_p90Data.empty(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimEnsembleCrossPlotStatisticsCase::hasMeanData() const +{ + return !m_meanData.empty(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiaDefines::EclipseUnitSystem RimEnsembleCrossPlotStatisticsCase::unitSystem() const +{ + if ( m_firstSummaryCase && m_firstSummaryCase->summaryReader() ) + { + return m_firstSummaryCase->summaryReader()->unitSystem(); + } + + return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimEnsembleCrossPlotStatisticsCase::timeSteps( const RifEclipseSummaryAddress& resultAddress ) const +{ + if ( m_firstSummaryCase && m_firstSummaryCase->summaryReader() ) + { + return m_firstSummaryCase->summaryReader()->timeSteps( resultAddress ); + } + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleCrossPlotStatisticsCase::clearData() +{ + m_binnedXValues.clear(); + m_p10Data.clear(); + m_p50Data.clear(); + m_p90Data.clear(); + m_meanData.clear(); + m_firstSummaryCase = nullptr; +} diff --git a/ApplicationLibCode/FileInterface/RifEnsembleStatisticsReader.h b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCrossPlotStatisticsCase.h similarity index 51% rename from ApplicationLibCode/FileInterface/RifEnsembleStatisticsReader.h rename to ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCrossPlotStatisticsCase.h index b19554ceed..e3ced3bbd4 100644 --- a/ApplicationLibCode/FileInterface/RifEnsembleStatisticsReader.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCrossPlotStatisticsCase.h @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2017- Statoil ASA +// Copyright (C) 2023- Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,28 +18,57 @@ #pragma once +#include "RiaDateTimeDefines.h" +#include "RiaDefines.h" + #include "RifSummaryReaderInterface.h" -class RimEnsembleStatisticsCase; +#include "RimSummaryCase.h" + class RifEclipseSummaryAddress; //================================================================================================== /// //================================================================================================== -class RifEnsembleStatisticsReader : public RifSummaryReaderInterface +class RimEnsembleCrossPlotStatisticsCase : public RimSummaryCase, public RifSummaryReaderInterface { public: - RifEnsembleStatisticsReader( RimEnsembleStatisticsCase* ensStatCase ); + RimEnsembleCrossPlotStatisticsCase(); + + void calculate( const std::vector& sumCases, + const RifEclipseSummaryAddress& inputAddressX, + const RifEclipseSummaryAddress& inputAddressY, + bool includeIncompleteCurves, + int binCount, + int sampleCountThreshold ); + + bool hasP10Data() const; + bool hasP50Data() const; + bool hasP90Data() const; + bool hasMeanData() const; + + QString caseName() const override; + void createSummaryReaderInterface() override; + RifSummaryReaderInterface* summaryReader() override; + RiaDefines::EclipseUnitSystem unitSystem() const override; std::vector timeSteps( const RifEclipseSummaryAddress& resultAddress ) const override; bool values( const RifEclipseSummaryAddress& resultAddress, std::vector* values ) const override; std::string unitName( const RifEclipseSummaryAddress& resultAddress ) const override; - RiaDefines::EclipseUnitSystem unitSystem() const override; - private: - bool validateAddress( const RifEclipseSummaryAddress& address ) const; + void clearData(); private: - RimEnsembleStatisticsCase* m_ensembleStatCase; + std::vector m_p10Data; + std::vector m_p50Data; + std::vector m_p90Data; + std::vector m_meanData; + + RifEclipseSummaryAddress m_adrX; + RifEclipseSummaryAddress m_adrY; + + std::vector m_binnedXValues; + + caf::PdmPointer m_firstSummaryCase; }; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp index 814057567c..5aacf58197 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp @@ -30,8 +30,6 @@ #include "RimSummaryCalculationCollection.h" #include "SummaryPlotCommands/RicSummaryPlotEditorUi.h" -#include "RifEnsembleStatisticsReader.h" - #include "RimCustomObjectiveFunction.h" #include "RimCustomObjectiveFunctionCollection.h" #include "RimDerivedEnsembleCaseCollection.h" @@ -43,10 +41,12 @@ #include "RimEnsembleStatisticsCase.h" #include "RimObjectiveFunction.h" #include "RimObjectiveFunctionTools.h" +#include "RimPlotAxisProperties.h" #include "RimPlotAxisPropertiesInterface.h" #include "RimProject.h" #include "RimRegularLegendConfig.h" #include "RimSummaryAddress.h" +#include "RimSummaryAddressSelector.h" #include "RimSummaryCalculationCollection.h" #include "RimSummaryCase.h" #include "RimSummaryCaseCollection.h" @@ -139,6 +139,19 @@ RimEnsembleCurveSet::RimEnsembleCurveSet() CAF_PDM_InitFieldNoDefault( &m_resampling, "Resampling", "Resampling" ); + // X Axis + CAF_PDM_InitField( &m_xAxisType, + "HorizontalAxisType", + caf::AppEnum( RiaDefines::HorizontalAxisType::TIME ), + "Axis Type" ); + + CAF_PDM_InitFieldNoDefault( &m_xAddressSelector, "XAddressSelector", "" ); + m_xAddressSelector = new RimSummaryAddressSelector; + m_xAddressSelector.uiCapability()->setUiTreeHidden( true ); + m_xAddressSelector.uiCapability()->setUiTreeChildrenHidden( true ); + + m_xAddressSelector->addressChanged.connect( this, &RimEnsembleCurveSet::onXAxisAddressChanged ); + CAF_PDM_InitField( &m_colorMode, "ColorMode", caf::AppEnum( ColorMode::SINGLE_COLOR_WITH_ALPHA ), "Coloring Mode" ); CAF_PDM_InitField( &m_colorForRealizations, "Color", RiaColorTools::textColor3f(), "Color" ); @@ -228,9 +241,8 @@ RimEnsembleCurveSet::RimEnsembleCurveSet() m_summaryAddressNameTools = new RimSummaryCurveAutoName; - m_ensembleStatCase.reset( new RimEnsembleStatisticsCase( this ) ); - m_ensembleStatCase->createSummaryReaderInterface(); - m_ensembleStatCase->createRftReaderInterface(); + m_ensembleStatCaseY.reset( new RimEnsembleStatisticsCase() ); + m_ensembleStatCaseXY.reset( new RimEnsembleCrossPlotStatisticsCase() ); m_disableStatisticCurves = false; m_isCurveSetFiltered = false; @@ -406,6 +418,31 @@ void RimEnsembleCurveSet::setSummaryAddress( RifEclipseSummaryAddress address ) m_objectiveValuesSummaryAddresses.push_back( summaryAddress ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleCurveSet::setCurveAddress( RiaSummaryCurveAddress address ) +{ + setSummaryAddress( address.summaryAddressY() ); + setSummaryAddressX( address.summaryAddressX() ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleCurveSet::setSummaryAddressX( RifEclipseSummaryAddress address ) +{ + m_xAddressSelector->setAddress( address ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimEnsembleCurveSet::isXAxisSummaryVector() const +{ + return m_xAxisType() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -429,7 +466,12 @@ RifEclipseSummaryAddress RimEnsembleCurveSet::summaryAddress() const //-------------------------------------------------------------------------------------------------- RiaSummaryCurveAddress RimEnsembleCurveSet::curveAddress() const { - return RiaSummaryCurveAddress( summaryAddress(), RifEclipseSummaryAddress::timeAddress() ); + if ( m_xAxisType() == RiaDefines::HorizontalAxisType::TIME ) + { + return RiaSummaryCurveAddress( RifEclipseSummaryAddress::timeAddress(), summaryAddress() ); + } + + return RiaSummaryCurveAddress( m_xAddressSelector->summaryAddress(), summaryAddress() ); } //-------------------------------------------------------------------------------------------------- @@ -678,6 +720,42 @@ void RimEnsembleCurveSet::fieldChangedByUi( const caf::PdmFieldHandle* changedFi updateTextInPlot = true; } + else if ( &m_xAxisType == changedField ) + { + if ( m_xAxisType() == RiaDefines::HorizontalAxisType::TIME ) + { + // TODO: How to handle this? + // m_xPlotAxisProperties = plot->timeAxisProperties(); + } + else if ( isXAxisSummaryVector() ) + { + if ( !m_xAddressSelector->ensemble() ) + { + m_xAddressSelector->setEnsemble( summaryCaseCollection() ); + m_xAddressSelector->setAddress( summaryAddress() ); + } + + if ( !m_xAddressSelector->plotAxisProperties() ) + { + RiuPlotAxis plotAxis = RiuPlotAxis::defaultBottomForSummaryVectors(); + if ( auto axis = plot->axisPropertiesForPlotAxis( plotAxis ) ) + { + m_xAddressSelector->setPlotAxisProperties( axis ); + } + else + { + RimPlotAxisProperties* newPlotAxisProperties = + plot->addNewAxisProperties( RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM, "Bottom Axis" ); + plot->updateConnectedEditors(); + + m_xAddressSelector->setPlotAxisProperties( newPlotAxisProperties ); + } + } + } + plot->updateAxes(); + plot->updatePlotTitle(); + updateAllCurves(); + } else if ( changedField == &m_resampling ) { updateAllCurves(); @@ -914,8 +992,10 @@ void RimEnsembleCurveSet::defineObjectEditorAttribute( QString uiConfigName, caf //-------------------------------------------------------------------------------------------------- void RimEnsembleCurveSet::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { + m_yValuesSummaryAddressUiField = m_yValuesSummaryAddress->address(); + { - caf::PdmUiGroup* curveDataGroup = uiOrdering.addNewGroup( "Summary Vector Y" ); + caf::PdmUiGroup* curveDataGroup = uiOrdering.addNewGroup( "Summary Vector" ); curveDataGroup->add( &m_yValuesSummaryCaseCollection ); curveDataGroup->add( &m_yValuesSummaryAddressUiField ); curveDataGroup->add( &m_yPushButtonSelectSummaryAddress, { false, 1, 0 } ); @@ -923,6 +1003,16 @@ void RimEnsembleCurveSet::defineUiOrdering( QString uiConfigName, caf::PdmUiOrde curveDataGroup->add( &m_plotAxisProperties ); } + { + caf::PdmUiGroup* curveDataGroup = uiOrdering.addNewGroup( "Summary Vector X Axis" ); + curveDataGroup->add( &m_xAxisType ); + + if ( isXAxisSummaryVector() ) + { + m_xAddressSelector->uiOrdering( uiConfigName, *curveDataGroup ); + } + } + appendColorGroup( uiOrdering ); { @@ -986,6 +1076,14 @@ void RimEnsembleCurveSet::onCustomObjectiveFunctionChanged( const caf::SignalEmi updateObjectiveFunctionLegend(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleCurveSet::onXAxisAddressChanged( const caf::SignalEmitter* emitter ) +{ + updateAllCurves(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -1790,8 +1888,6 @@ void RimEnsembleCurveSet::updateEnsembleCurves( const std::vectorhideEnsembleCurves() ) return; - setLeftOrRightAxisY( axisY() ); - RimSummaryAddress* addr = m_yValuesSummaryAddress(); if ( plot && addr->address().category() != RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_INVALID ) { @@ -1817,6 +1913,14 @@ void RimEnsembleCurveSet::updateEnsembleCurves( const std::vectorsetLeftOrRightAxisY( axisY() ); + if ( isXAxisSummaryVector() ) + { + curve->setAxisTypeX( RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ); + curve->setSummaryCaseX( sumCase ); + curve->setSummaryAddressX( m_xAddressSelector->summaryAddress() ); + if ( m_xAddressSelector->plotAxisProperties() ) + curve->setTopOrBottomAxisX( m_xAddressSelector->plotAxisProperties()->plotAxis() ); + } newSummaryCurves.push_back( curve ); } @@ -1877,22 +1981,70 @@ void RimEnsembleCurveSet::updateStatisticsCurves( const std::vectorallSummaryCases(); } - m_ensembleStatCase->calculate( statCases, m_statistics->includeIncompleteCurves() ); + + if ( isXAxisSummaryVector() ) + { + m_ensembleStatCaseXY->calculate( statCases, + m_xAddressSelector->summaryAddress(), + summaryAddress(), + m_statistics->includeIncompleteCurves(), + m_statistics->crossPlotCurvesBinCount(), + m_statistics->crossPlotCurvesSampleCountThresholdPerBin() ); + } + else + { + m_ensembleStatCaseY->calculate( statCases, summaryAddress(), m_statistics->includeIncompleteCurves() ); + } } - std::vector addresses; + std::vector addresses; if ( m_statistics->isActive() ) { - RifEclipseSummaryAddress dataAddress = m_yValuesSummaryAddress->address(); + if ( isXAxisSummaryVector() ) + { + RifEclipseSummaryAddress dataAddressY = m_yValuesSummaryAddress->address(); + RifEclipseSummaryAddress dataAddressX = m_xAddressSelector->summaryAddress(); - if ( m_statistics->showP10Curve() && m_ensembleStatCase->hasP10Data() ) - addresses.push_back( SAddr::ensembleStatisticsAddress( ENSEMBLE_STAT_P10_QUANTITY_NAME, dataAddress.vectorName() ) ); - if ( m_statistics->showP50Curve() && m_ensembleStatCase->hasP50Data() ) - addresses.push_back( SAddr::ensembleStatisticsAddress( ENSEMBLE_STAT_P50_QUANTITY_NAME, dataAddress.vectorName() ) ); - if ( m_statistics->showP90Curve() && m_ensembleStatCase->hasP90Data() ) - addresses.push_back( SAddr::ensembleStatisticsAddress( ENSEMBLE_STAT_P90_QUANTITY_NAME, dataAddress.vectorName() ) ); - if ( m_statistics->showMeanCurve() && m_ensembleStatCase->hasMeanData() ) - addresses.push_back( SAddr::ensembleStatisticsAddress( ENSEMBLE_STAT_MEAN_QUANTITY_NAME, dataAddress.vectorName() ) ); + auto getStatisticsAddress = []( const std::string& statisticsVectorName, + const RifEclipseSummaryAddress& addrX, + const RifEclipseSummaryAddress& addrY ) -> RiaSummaryCurveAddress + { + auto xStatAddress = SAddr::ensembleStatisticsAddress( statisticsVectorName, addrX.vectorName() ); + auto yStatAddress = SAddr::ensembleStatisticsAddress( statisticsVectorName, addrY.vectorName() ); + + return RiaSummaryCurveAddress( xStatAddress, yStatAddress ); + }; + + if ( m_statistics->showP10Curve() && m_ensembleStatCaseXY->hasP10Data() ) + addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P10_QUANTITY_NAME, dataAddressX, dataAddressY ) ); + if ( m_statistics->showP50Curve() && m_ensembleStatCaseXY->hasP50Data() ) + addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P50_QUANTITY_NAME, dataAddressX, dataAddressY ) ); + if ( m_statistics->showP90Curve() && m_ensembleStatCaseXY->hasP90Data() ) + addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P90_QUANTITY_NAME, dataAddressX, dataAddressY ) ); + if ( m_statistics->showMeanCurve() && m_ensembleStatCaseXY->hasMeanData() ) + addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_MEAN_QUANTITY_NAME, dataAddressX, dataAddressY ) ); + } + else + { + RifEclipseSummaryAddress dataAddressY = m_yValuesSummaryAddress->address(); + + auto getStatisticsAddress = []( const std::string& statisticsVectorName, const RifEclipseSummaryAddress& addrY ) -> RiaSummaryCurveAddress + { + auto xStatAddress = RifEclipseSummaryAddress::timeAddress(); + auto yStatAddress = SAddr::ensembleStatisticsAddress( statisticsVectorName, addrY.vectorName() ); + + return RiaSummaryCurveAddress( xStatAddress, yStatAddress ); + }; + + if ( m_statistics->showP10Curve() && m_ensembleStatCaseY->hasP10Data() ) + addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P10_QUANTITY_NAME, dataAddressY ) ); + if ( m_statistics->showP50Curve() && m_ensembleStatCaseY->hasP50Data() ) + addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P50_QUANTITY_NAME, dataAddressY ) ); + if ( m_statistics->showP90Curve() && m_ensembleStatCaseY->hasP90Data() ) + addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P90_QUANTITY_NAME, dataAddressY ) ); + if ( m_statistics->showMeanCurve() && m_ensembleStatCaseY->hasMeanData() ) + addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_MEAN_QUANTITY_NAME, dataAddressY ) ); + } } deleteStatisticsCurves(); @@ -1900,6 +2052,13 @@ void RimEnsembleCurveSet::updateStatisticsCurves( const std::vector(); if ( plot && plot->plotWidget() ) { + RimSummaryCase* summaryCase = nullptr; + + if ( isXAxisSummaryVector() ) + summaryCase = m_ensembleStatCaseXY.get(); + else + summaryCase = m_ensembleStatCaseY.get(); + for ( auto address : addresses ) { auto curve = new RimSummaryCurve(); @@ -1908,19 +2067,28 @@ void RimEnsembleCurveSet::updateStatisticsCurves( const std::vectorsetColor( m_statistics->color() ); curve->setResampling( m_resampling() ); - auto symbol = statisticsCurveSymbolFromAddress( address ); + auto symbol = statisticsCurveSymbolFromAddress( address.summaryAddressY() ); curve->setSymbol( symbol ); curve->setSymbolSize( statisticsCurveSymbolSize( symbol ) ); curve->setSymbolSkipDistance( 150 ); if ( m_statistics->showCurveLabels() ) { - curve->setSymbolLabel( QString::fromStdString( address.ensembleStatisticsVectorName() ) ); + curve->setSymbolLabel( QString::fromStdString( address.summaryAddressY().ensembleStatisticsVectorName() ) ); } curve->setLineStyle( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID ); - curve->setSummaryCaseY( m_ensembleStatCase.get() ); - curve->setSummaryAddressYAndApplyInterpolation( address ); + curve->setSummaryCaseY( summaryCase ); + curve->setSummaryAddressYAndApplyInterpolation( address.summaryAddressY() ); curve->setLeftOrRightAxisY( axisY() ); + if ( isXAxisSummaryVector() ) + { + curve->setAxisTypeX( RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ); + curve->setSummaryCaseX( summaryCase ); + curve->setSummaryAddressX( address.summaryAddressX() ); + if ( m_xAddressSelector->plotAxisProperties() ) + curve->setTopOrBottomAxisX( m_xAddressSelector->plotAxisProperties()->plotAxis() ); + } + curve->setShowInLegend( m_statistics->showStatisticsCurveLegends() ); curve->updateCurveVisibility(); @@ -2058,7 +2226,9 @@ bool RimEnsembleCurveSet::isFiltered() const //-------------------------------------------------------------------------------------------------- bool RimEnsembleCurveSet::hasP10Data() const { - return m_ensembleStatCase->hasP10Data(); + if ( isXAxisSummaryVector() ) return m_ensembleStatCaseXY->hasP10Data(); + + return m_ensembleStatCaseY->hasP10Data(); } //-------------------------------------------------------------------------------------------------- @@ -2066,7 +2236,9 @@ bool RimEnsembleCurveSet::hasP10Data() const //-------------------------------------------------------------------------------------------------- bool RimEnsembleCurveSet::hasP50Data() const { - return m_ensembleStatCase->hasP50Data(); + if ( isXAxisSummaryVector() ) return m_ensembleStatCaseXY->hasP50Data(); + + return m_ensembleStatCaseY->hasP50Data(); } //-------------------------------------------------------------------------------------------------- @@ -2074,7 +2246,9 @@ bool RimEnsembleCurveSet::hasP50Data() const //-------------------------------------------------------------------------------------------------- bool RimEnsembleCurveSet::hasP90Data() const { - return m_ensembleStatCase->hasP90Data(); + if ( isXAxisSummaryVector() ) return m_ensembleStatCaseXY->hasP90Data(); + + return m_ensembleStatCaseY->hasP90Data(); } //-------------------------------------------------------------------------------------------------- @@ -2082,7 +2256,9 @@ bool RimEnsembleCurveSet::hasP90Data() const //-------------------------------------------------------------------------------------------------- bool RimEnsembleCurveSet::hasMeanData() const { - return m_ensembleStatCase->hasMeanData(); + if ( isXAxisSummaryVector() ) return m_ensembleStatCaseXY->hasMeanData(); + + return m_ensembleStatCaseY->hasMeanData(); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h index 75113758d5..ce41861608 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h @@ -25,7 +25,9 @@ #include "RiaDateTimeDefines.h" #include "RiaPlotDefines.h" #include "RiaSummaryCurveAddress.h" +#include "RiaSummaryDefines.h" +#include "RimEnsembleCrossPlotStatisticsCase.h" #include "RimEnsembleCurveSetColorManager.h" #include "RimEnsembleCurveSetInterface.h" #include "RimTimeStepFilter.h" @@ -64,6 +66,7 @@ class RiuSummaryVectorSelectionDialog; class RiuPlotWidget; class RiuPlotCurve; class RimPlotAxisPropertiesInterface; +class RimSummaryAddressSelector; class QwtPlot; class QwtPlotCurve; @@ -111,6 +114,7 @@ class RimEnsembleCurveSet : public caf::PdmObject, public RimEnsembleCurveSetInt void deleteCurve( RimSummaryCurve* curve ); void setSummaryAddress( RifEclipseSummaryAddress address ); + void setCurveAddress( RiaSummaryCurveAddress address ); void setSummaryAddressAndStatisticsFlag( RifEclipseSummaryAddress address ); RifEclipseSummaryAddress summaryAddress() const; RiaSummaryCurveAddress curveAddress() const; @@ -215,10 +219,14 @@ class RimEnsembleCurveSet : public caf::PdmObject, public RimEnsembleCurveSetInt void onObjectiveFunctionChanged( const caf::SignalEmitter* emitter ); void onCustomObjectiveFunctionChanged( const caf::SignalEmitter* emitter ); + void onXAxisAddressChanged( const caf::SignalEmitter* emitter ); void setTransparentCurveColor(); void onColorTagClicked( const SignalEmitter* emitter, size_t index ); + void setSummaryAddressX( RifEclipseSummaryAddress address ); + bool isXAxisSummaryVector() const; + private: caf::PdmField m_showCurves; caf::PdmChildArrayField m_curves; @@ -231,6 +239,9 @@ class RimEnsembleCurveSet : public caf::PdmObject, public RimEnsembleCurveSetInt caf::PdmField m_yPushButtonSelectSummaryAddress; caf::PdmField m_resampling; + caf::PdmField> m_xAxisType; + caf::PdmChildField m_xAddressSelector; + caf::PdmField m_colorMode; caf::PdmField m_mainEnsembleColor; caf::PdmField m_colorForRealizations; @@ -270,7 +281,8 @@ class RimEnsembleCurveSet : public caf::PdmObject, public RimEnsembleCurveSetInt QPointer m_filterOverlayFrame; QPointer m_objectiveFunctionOverlayFrame; - std::unique_ptr m_ensembleStatCase; + std::unique_ptr m_ensembleStatCaseY; + std::unique_ptr m_ensembleStatCaseXY; std::unique_ptr m_analyserOfSelectedCurveDefs; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatistics.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatistics.cpp index f6fbbff5b8..79a9715281 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatistics.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatistics.cpp @@ -46,6 +46,12 @@ RimEnsembleStatistics::RimEnsembleStatistics( RimEnsembleCurveSetInterface* pare CAF_PDM_InitField( &m_showCurveLabels, "ShowCurveLabels", true, "Show Curve Labels" ); CAF_PDM_InitField( &m_includeIncompleteCurves, "IncludeIncompleteCurves", false, "Include Incomplete Curves" ); + CAF_PDM_InitField( &m_crossPlotCurvesBinCount, "CrossPlotCurvesBinCount", 100, "Bin Count" ); + CAF_PDM_InitField( &m_crossPlotCurvesStatisticsSampleCountThresholdPerBin, + "CrossPlotCurvesStatisticsSampleCountThresholdPerBin", + 100, + "Sample Threshold per Bin" ); + CAF_PDM_InitField( &m_warningLabel, "WarningLabel", QString( "Warning: Ensemble time range mismatch" ), "" ); CAF_PDM_InitField( &m_color, "Color", RiaColorTools::textColor3f(), "Color" ); @@ -79,6 +85,22 @@ void RimEnsembleStatistics::setShowStatisticsCurves( bool show ) m_active = show; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +int RimEnsembleStatistics::crossPlotCurvesBinCount() const +{ + return m_crossPlotCurvesBinCount; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +int RimEnsembleStatistics::crossPlotCurvesSampleCountThresholdPerBin() const +{ + return m_crossPlotCurvesStatisticsSampleCountThresholdPerBin; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -124,27 +146,23 @@ void RimEnsembleStatistics::showColorField( bool show ) //-------------------------------------------------------------------------------------------------- void RimEnsembleStatistics::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { - if ( changedField == &m_active || changedField == &m_basedOnFilteredCases || changedField == &m_showP10Curve || - changedField == &m_showP50Curve || changedField == &m_showP90Curve || changedField == &m_showMeanCurve || - changedField == &m_showCurveLabels || changedField == &m_color || changedField == &m_includeIncompleteCurves || - changedField == &m_showStatisticsCurveLegends ) + if ( changedField == &m_hideEnsembleCurves ) { auto curveSet = m_parentCurveSet; if ( !curveSet ) return; - curveSet->updateStatisticsCurves(); + curveSet->updateAllCurves(); - // Trigger update of tree view editor for ensemble curve set as they depend on these fields - if ( changedField == &m_active || changedField == &m_basedOnFilteredCases || changedField == &m_color ) curveSet->updateEditors(); + return; } - if ( changedField == &m_hideEnsembleCurves ) - { - auto curveSet = m_parentCurveSet; - if ( !curveSet ) return; + auto curveSet = m_parentCurveSet; + if ( !curveSet ) return; - curveSet->updateAllCurves(); - } + curveSet->updateStatisticsCurves(); + + // Trigger update of tree view editor for ensemble curve set as they depend on these fields + if ( changedField == &m_active || changedField == &m_basedOnFilteredCases || changedField == &m_color ) curveSet->updateEditors(); } //-------------------------------------------------------------------------------------------------- @@ -162,6 +180,10 @@ void RimEnsembleStatistics::defineUiOrdering( QString uiConfigName, caf::PdmUiOr uiOrdering.add( &m_includeIncompleteCurves ); uiOrdering.add( &m_showCurveLabels ); + auto crossPlotGroup = uiOrdering.addNewGroup( "Cross Plot" ); + crossPlotGroup->add( &m_crossPlotCurvesBinCount ); + crossPlotGroup->add( &m_crossPlotCurvesStatisticsSampleCountThresholdPerBin ); + if ( m_showColorField ) uiOrdering.add( &m_color ); auto group = uiOrdering.addNewGroup( "Curves" ); diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatistics.h b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatistics.h index a95381a6fd..d4702fb2ac 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatistics.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatistics.h @@ -52,6 +52,9 @@ class RimEnsembleStatistics : public caf::PdmObject bool includeIncompleteCurves() const { return m_includeIncompleteCurves; } + int crossPlotCurvesBinCount() const; + int crossPlotCurvesSampleCountThresholdPerBin() const; + void disableP10Curve( bool disable ); void disableP50Curve( bool disable ); void disableP90Curve( bool disable ); @@ -76,6 +79,10 @@ class RimEnsembleStatistics : public caf::PdmObject caf::PdmField m_showCurveLabels; caf::PdmField m_includeIncompleteCurves; + // Ensemble cross plot settings + caf::PdmField m_crossPlotCurvesBinCount; + caf::PdmField m_crossPlotCurvesStatisticsSampleCountThresholdPerBin; + caf::PdmField m_warningLabel; bool m_showColorField; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp index 7839b868a1..ec659d422a 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp @@ -18,8 +18,6 @@ #include "RimEnsembleStatisticsCase.h" -#include "RifEnsembleStatisticsReader.h" - #include "RiaSummaryTools.h" #include "RiaTimeHistoryCurveResampler.h" @@ -40,9 +38,8 @@ //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimEnsembleStatisticsCase::RimEnsembleStatisticsCase( RimEnsembleCurveSet* curveSet ) +RimEnsembleStatisticsCase::RimEnsembleStatisticsCase() { - m_curveSet = curveSet; } //-------------------------------------------------------------------------------------------------- @@ -56,83 +53,97 @@ const std::vector& RimEnsembleStatisticsCase::timeSteps() const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -const std::vector& RimEnsembleStatisticsCase::p10() const +std::vector RimEnsembleStatisticsCase::timeSteps( const RifEclipseSummaryAddress& resultAddress ) const { - return m_p10Data; + return m_timeSteps; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -const std::vector& RimEnsembleStatisticsCase::p50() const +bool RimEnsembleStatisticsCase::hasP10Data() const { - return m_p50Data; + return !m_p10Data.empty(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -const std::vector& RimEnsembleStatisticsCase::p90() const +bool RimEnsembleStatisticsCase::hasP50Data() const { - return m_p90Data; + return !m_p50Data.empty(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -const std::vector& RimEnsembleStatisticsCase::mean() const +bool RimEnsembleStatisticsCase::hasP90Data() const { - return m_meanData; + return !m_p90Data.empty(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -QString RimEnsembleStatisticsCase::caseName() const +bool RimEnsembleStatisticsCase::hasMeanData() const { - return "Ensemble Statistics"; + return !m_meanData.empty(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimEnsembleStatisticsCase::createSummaryReaderInterface() +bool RimEnsembleStatisticsCase::values( const RifEclipseSummaryAddress& resultAddress, std::vector* values ) const { - m_statisticsReader.reset( new RifEnsembleStatisticsReader( this ) ); + auto quantityName = resultAddress.ensembleStatisticsVectorName(); + + if ( quantityName == ENSEMBLE_STAT_P10_QUANTITY_NAME ) + *values = m_p10Data; + else if ( quantityName == ENSEMBLE_STAT_P50_QUANTITY_NAME ) + *values = m_p50Data; + else if ( quantityName == ENSEMBLE_STAT_P90_QUANTITY_NAME ) + *values = m_p90Data; + else if ( quantityName == ENSEMBLE_STAT_MEAN_QUANTITY_NAME ) + *values = m_meanData; + + return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RifSummaryReaderInterface* RimEnsembleStatisticsCase::summaryReader() +std::string RimEnsembleStatisticsCase::unitName( const RifEclipseSummaryAddress& resultAddress ) const { - if ( !m_statisticsReader ) + if ( m_firstSummaryCase && m_firstSummaryCase->summaryReader() ) { - createSummaryReaderInterface(); + return m_firstSummaryCase->summaryReader()->unitName( resultAddress ); } - return m_statisticsReader.get(); + + return "Ensemble Statistics Case - Undefined Unit"; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -const RimEnsembleCurveSet* RimEnsembleStatisticsCase::curveSet() const +QString RimEnsembleStatisticsCase::caseName() const { - return m_curveSet; + return "Ensemble Statistics"; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimEnsembleStatisticsCase::calculate( const std::vector& sumCases, bool includeIncompleteCurves ) +void RimEnsembleStatisticsCase::createSummaryReaderInterface() { - auto inputAddress = m_curveSet->summaryAddress(); - if ( m_statisticsReader && inputAddress.isValid() ) - { - const std::vector& validCases = validSummaryCases( sumCases, inputAddress, includeIncompleteCurves ); + // Nothing to do here as RimEnsembleStatisticsCase inherits from RifSummaryReaderInterface +} - calculate( validCases, inputAddress, includeIncompleteCurves ); - } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifSummaryReaderInterface* RimEnsembleStatisticsCase::summaryReader() +{ + return this; } //-------------------------------------------------------------------------------------------------- @@ -142,15 +153,18 @@ void RimEnsembleStatisticsCase::calculate( const std::vector& s const RifEclipseSummaryAddress& inputAddress, bool includeIncompleteCurves ) { - std::vector allTimeSteps; - std::vector> caseAndTimeStepValues; - + clearData(); if ( !inputAddress.isValid() ) return; + if ( sumCases.empty() ) return; - auto [minTimeStep, maxTimeStep] = findMinMaxTimeStep( sumCases, inputAddress ); + // Use first summary case to get unit system and other meta data + m_firstSummaryCase = sumCases.front(); + auto [minTimeStep, maxTimeStep] = findMinMaxTimeStep( sumCases, inputAddress ); RiaDefines::DateTimePeriod period = findBestResamplingPeriod( minTimeStep, maxTimeStep ); + std::vector allTimeSteps; + std::vector> caseAndTimeStepValues; caseAndTimeStepValues.reserve( sumCases.size() ); for ( const auto& sumCase : sumCases ) { @@ -172,7 +186,6 @@ void RimEnsembleStatisticsCase::calculate( const std::vector& s } } - clearData(); m_timeSteps = allTimeSteps; for ( size_t timeStepIndex = 0; timeStepIndex < allTimeSteps.size(); timeStepIndex++ ) @@ -202,10 +215,11 @@ void RimEnsembleStatisticsCase::calculate( const std::vector& s //-------------------------------------------------------------------------------------------------- RiaDefines::EclipseUnitSystem RimEnsembleStatisticsCase::unitSystem() const { - if ( m_curveSet ) + if ( m_firstSummaryCase && m_firstSummaryCase->summaryReader() ) { - return m_curveSet->summaryCaseCollection()->unitSystem(); + return m_firstSummaryCase->summaryReader()->unitSystem(); } + return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN; } @@ -219,6 +233,7 @@ void RimEnsembleStatisticsCase::clearData() m_p50Data.clear(); m_p90Data.clear(); m_meanData.clear(); + m_firstSummaryCase = nullptr; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.h b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.h index 662023bac4..47fc986fd3 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.h @@ -21,43 +21,38 @@ #include "RiaDateTimeDefines.h" #include "RiaDefines.h" +#include "RifSummaryReaderInterface.h" + #include "RimSummaryCase.h" -class RifEnsembleStatisticsReader; -class RimEnsembleCurveSet; class RifEclipseSummaryAddress; //================================================================================================== /// //================================================================================================== -class RimEnsembleStatisticsCase : public RimSummaryCase +class RimEnsembleStatisticsCase : public RimSummaryCase, public RifSummaryReaderInterface { public: - RimEnsembleStatisticsCase( RimEnsembleCurveSet* curveSet ); + RimEnsembleStatisticsCase(); const std::vector& timeSteps() const; - const std::vector& p10() const; - const std::vector& p50() const; - const std::vector& p90() const; - const std::vector& mean() const; - bool hasP10Data() const { return !m_p10Data.empty(); } - bool hasP50Data() const { return !m_p50Data.empty(); } - bool hasP90Data() const { return !m_p90Data.empty(); } - bool hasMeanData() const { return !m_meanData.empty(); } + bool hasP10Data() const; + bool hasP50Data() const; + bool hasP90Data() const; + bool hasMeanData() const; - QString caseName() const override; - void createSummaryReaderInterface() override; - RifSummaryReaderInterface* summaryReader() override; + QString caseName() const override; + void createSummaryReaderInterface() override; + RifSummaryReaderInterface* summaryReader() override; + RiaDefines::EclipseUnitSystem unitSystem() const override; - const RimEnsembleCurveSet* curveSet() const; + void calculate( const std::vector& sumCases, const RifEclipseSummaryAddress& inputAddress, bool includeIncompleteCurves ); - void calculate( const std::vector& sumCases, bool includeIncompleteCurves ); - RiaDefines::EclipseUnitSystem unitSystem() const; + std::vector timeSteps( const RifEclipseSummaryAddress& resultAddress ) const override; + bool values( const RifEclipseSummaryAddress& resultAddress, std::vector* values ) const override; + std::string unitName( const RifEclipseSummaryAddress& resultAddress ) const override; -private: - void calculate( const std::vector& sumCases, const RifEclipseSummaryAddress& inputAddress, bool includeIncompleteCurves ); - void clearData(); static std::vector validSummaryCases( const std::vector& allSumCases, const RifEclipseSummaryAddress& inputAddress, bool includeIncompleteCurves ); @@ -66,12 +61,14 @@ class RimEnsembleStatisticsCase : public RimSummaryCase static RiaDefines::DateTimePeriod findBestResamplingPeriod( time_t minTimeStep, time_t maxTimeStep ); private: - std::unique_ptr m_statisticsReader; - RimEnsembleCurveSet* m_curveSet; + void clearData(); +private: std::vector m_timeSteps; std::vector m_p10Data; std::vector m_p50Data; std::vector m_p90Data; std::vector m_meanData; + + caf::PdmPointer m_firstSummaryCase; }; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressSelector.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressSelector.cpp new file mode 100644 index 0000000000..c4e5a10cf7 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressSelector.cpp @@ -0,0 +1,360 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimSummaryAddressSelector.h" + +#include "RiaResultNames.h" +#include "RiaSummaryCurveDefinition.h" +#include "RiaSummaryTools.h" + +#include "RifSummaryReaderInterface.h" + +#include "RimPlotAxisPropertiesInterface.h" +#include "RimProject.h" +#include "RimSummaryAddress.h" +#include "RimSummaryCase.h" +#include "RimSummaryCaseCollection.h" +#include "RimSummaryPlot.h" + +#include "RiuSummaryVectorSelectionDialog.h" + +#include "cafPdmUiLineEditor.h" +#include "cafPdmUiPushButtonEditor.h" + +CAF_PDM_SOURCE_INIT( RimSummaryAddressSelector, "RimSummaryAddressSelector" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryAddressSelector::RimSummaryAddressSelector() + : addressChanged( this ) + +{ + CAF_PDM_InitFieldNoDefault( &m_summaryCase, "SummaryCase", "Case" ); + m_summaryCase.uiCapability()->setUiTreeChildrenHidden( true ); + m_summaryCase.uiCapability()->setAutoAddingOptionFromValue( false ); + + CAF_PDM_InitFieldNoDefault( &m_summaryCaseCollection, "SummaryCaseCollection", "Ensemble" ); + m_summaryCaseCollection.uiCapability()->setUiTreeChildrenHidden( true ); + m_summaryCaseCollection.uiCapability()->setAutoAddingOptionFromValue( false ); + + CAF_PDM_InitFieldNoDefault( &m_summaryAddressUiField, "summaryAddressUiField", "Vector" ); + m_summaryAddressUiField.xmlCapability()->disableIO(); + m_summaryAddressUiField.uiCapability()->setUiEditorTypeName( caf::PdmUiLineEditor::uiEditorTypeName() ); + + CAF_PDM_InitFieldNoDefault( &m_summaryAddress, "SummaryAddress", "Summary Address" ); + m_summaryAddress.uiCapability()->setUiTreeHidden( true ); + m_summaryAddress.uiCapability()->setUiTreeChildrenHidden( true ); + + CAF_PDM_InitFieldNoDefault( &m_pushButtonSelectSummaryAddress, "SelectAddress", "" ); + caf::PdmUiPushButtonEditor::configureEditorForField( &m_pushButtonSelectSummaryAddress ); + m_pushButtonSelectSummaryAddress.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); + m_pushButtonSelectSummaryAddress = false; + + CAF_PDM_InitFieldNoDefault( &m_plotAxisProperties, "PlotAxisProperties", "Axis" ); + + m_summaryAddress = new RimSummaryAddress; + + CAF_PDM_InitFieldNoDefault( &m_resamplingPeriod, "Resampling", "Resampling" ); + + m_dataSource = SummaryDataSource::SINGLE_CASE; + m_showDataSource = true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::setSummaryCase( RimSummaryCase* summaryCase ) +{ + m_summaryCase = summaryCase; + m_dataSource = SummaryDataSource::SINGLE_CASE; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::setEnsemble( RimSummaryCaseCollection* ensemble ) +{ + m_summaryCaseCollection = ensemble; + m_dataSource = SummaryDataSource::ENSEMBLE; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::setAddress( const RifEclipseSummaryAddress& address ) +{ + m_summaryAddress->setAddress( address ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::setResamplingPeriod( RiaDefines::DateTimePeriodEnum resampling ) +{ + m_resamplingPeriod = resampling; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::setPlotAxisProperties( RimPlotAxisPropertiesInterface* plotAxisProperties ) +{ + m_plotAxisProperties = plotAxisProperties; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::setShowDataSource( bool enable ) +{ + m_showDataSource = enable; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryCase* RimSummaryAddressSelector::summaryCase() const +{ + return m_summaryCase(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryCaseCollection* RimSummaryAddressSelector::ensemble() const +{ + return m_summaryCaseCollection(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifEclipseSummaryAddress RimSummaryAddressSelector::summaryAddress() const +{ + return m_summaryAddress()->address(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiaDefines::DateTimePeriodEnum RimSummaryAddressSelector::resamplingPeriod() const +{ + return m_resamplingPeriod(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimPlotAxisPropertiesInterface* RimSummaryAddressSelector::plotAxisProperties() const +{ + return m_plotAxisProperties(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) +{ + if ( changedField == &m_pushButtonSelectSummaryAddress ) + { + RiuSummaryVectorSelectionDialog dlg( nullptr ); + + if ( m_dataSource == SummaryDataSource::SINGLE_CASE ) + { + dlg.hideEnsembles(); + dlg.setCaseAndAddress( m_summaryCase(), m_summaryAddress->address() ); + } + else if ( m_dataSource == SummaryDataSource::ENSEMBLE ) + { + dlg.hideSummaryCases(); + dlg.setEnsembleAndAddress( m_summaryCaseCollection(), m_summaryAddress->address() ); + } + + if ( dlg.exec() == QDialog::Accepted ) + { + auto curveSelection = dlg.curveSelection(); + if ( !curveSelection.empty() ) + { + m_summaryCase = curveSelection[0].summaryCaseY(); + m_summaryCaseCollection = curveSelection[0].ensemble(); + auto addr = curveSelection[0].summaryAddressY(); + m_summaryAddress->setAddress( addr ); + m_summaryAddressUiField = addr; + } + } + + m_pushButtonSelectSummaryAddress = false; + } + else if ( changedField == &m_summaryAddressUiField ) + { + m_summaryAddress->setAddress( m_summaryAddressUiField() ); + } + + addressChanged.send(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +auto createOptionsForSummaryCase = []() -> QList +{ + RimProject* proj = RimProject::current(); + std::vector cases = proj->allSummaryCases(); + + auto options = RiaSummaryTools::optionsForSummaryCases( cases ); + if ( !options.empty() ) + { + options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) ); + } + + return options; +}; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +auto createOptionsForEnsemble = []() -> QList +{ + QList options; + + RimProject* proj = RimProject::current(); + std::vector groups = proj->summaryGroups(); + + for ( RimSummaryCaseCollection* group : groups ) + { + if ( group->isEnsemble() ) options.push_back( caf::PdmOptionItemInfo( group->name(), group ) ); + } + + options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) ); + return options; +}; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +auto createOptionsForAddresses = []( const std::set& allAddresses ) -> QList +{ + QList options; + + for ( auto& address : allAddresses ) + { + if ( address.isErrorResult() ) continue; + + std::string name = address.uiText(); + QString s = QString::fromStdString( name ); + options.push_back( caf::PdmOptionItemInfo( s, QVariant::fromValue( address ) ) ); + } + + options.push_front( caf::PdmOptionItemInfo( RiaResultNames::undefinedResultName(), QVariant::fromValue( RifEclipseSummaryAddress() ) ) ); + return options; +}; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RimSummaryAddressSelector::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) +{ + if ( fieldNeedingOptions == &m_summaryCase ) + { + return createOptionsForSummaryCase(); + } + + if ( fieldNeedingOptions == &m_summaryCaseCollection ) + { + return createOptionsForEnsemble(); + } + + if ( fieldNeedingOptions == &m_summaryAddressUiField ) + { + std::set addresses; + if ( m_dataSource == SummaryDataSource::SINGLE_CASE && m_summaryCase() ) + { + RifSummaryReaderInterface* reader = m_summaryCase()->summaryReader(); + if ( reader ) + { + addresses = reader->allResultAddresses(); + } + } + else if ( m_dataSource == SummaryDataSource::ENSEMBLE && m_summaryCaseCollection() ) + { + addresses = m_summaryCaseCollection()->ensembleSummaryAddresses(); + } + + return createOptionsForAddresses( addresses ); + } + + if ( fieldNeedingOptions == &m_plotAxisProperties ) + { + if ( auto plot = firstAncestorOrThisOfTypeAsserted() ) + { + QList options; + for ( auto axis : plot->plotAxes() ) + { + options.push_back( caf::PdmOptionItemInfo( axis->objectName(), axis ) ); + } + + return options; + } + } + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) +{ + if ( m_showDataSource ) + { + if ( m_dataSource == SummaryDataSource::SINGLE_CASE ) + { + uiOrdering.add( &m_summaryCase ); + } + else + { + uiOrdering.add( &m_summaryCaseCollection ); + } + } + + // Update the UI field, as this is not serialized to file + m_summaryAddressUiField = m_summaryAddress->address(); + + uiOrdering.add( &m_summaryAddressUiField, { true, 2, 1 } ); + uiOrdering.add( &m_pushButtonSelectSummaryAddress, { false, 1, 0 } ); + uiOrdering.add( &m_resamplingPeriod, { true, 3, 1 } ); + uiOrdering.add( &m_plotAxisProperties, { true, 3, 1 } ); + + uiOrdering.skipRemainingFields( true ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryAddressSelector::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) +{ + if ( &m_pushButtonSelectSummaryAddress == field ) + { + auto attrib = dynamic_cast( attribute ); + if ( attrib ) + { + attrib->m_buttonText = "..."; + } + } +} diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressSelector.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressSelector.h new file mode 100644 index 0000000000..e3270471a8 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressSelector.h @@ -0,0 +1,82 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RiaDateTimeDefines.h" + +#include "RifEclipseSummaryAddress.h" +#include "RifEclipseSummaryAddressQMetaType.h" + +#include "cafPdmChildField.h" +#include "cafPdmField.h" +#include "cafPdmObject.h" +#include "cafPdmPtrField.h" + +class RimSummaryCase; +class RimSummaryCaseCollection; +class RimSummaryAddress; +class RimPlotAxisPropertiesInterface; + +class RimSummaryAddressSelector : public caf::PdmObject +{ + CAF_PDM_HEADER_INIT; + +public: + enum class SummaryDataSource + { + SINGLE_CASE, + ENSEMBLE + }; + + caf::Signal<> addressChanged; + +public: + RimSummaryAddressSelector(); + + void setSummaryCase( RimSummaryCase* summaryCase ); + void setEnsemble( RimSummaryCaseCollection* ensemble ); + void setAddress( const RifEclipseSummaryAddress& address ); + void setResamplingPeriod( RiaDefines::DateTimePeriodEnum resampling ); + void setPlotAxisProperties( RimPlotAxisPropertiesInterface* plotAxisProperties ); + void setShowDataSource( bool enable ); + + RimSummaryCase* summaryCase() const; + RimSummaryCaseCollection* ensemble() const; + RifEclipseSummaryAddress summaryAddress() const; + RiaDefines::DateTimePeriodEnum resamplingPeriod() const; + RimPlotAxisPropertiesInterface* plotAxisProperties() const; + +private: + void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; + QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; + void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; + void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override; + +private: + caf::PdmPtrField m_summaryCase; + caf::PdmPtrField m_summaryCaseCollection; + caf::PdmChildField m_summaryAddress; + caf::PdmField m_summaryAddressUiField; + caf::PdmField m_pushButtonSelectSummaryAddress; + caf::PdmPtrField m_plotAxisProperties; + caf::PdmField m_resamplingPeriod; + + SummaryDataSource m_dataSource; + bool m_showDataSource; +}; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp index 8be1d14e73..6f7891bd7a 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp @@ -525,7 +525,7 @@ QList RimSummaryCurve::calculateValueOptions( const caf: options = RiaSummaryTools::optionsForSummaryCases( cases ); - if ( options.size() > 0 ) + if ( !options.empty() ) { options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) ); } @@ -643,38 +643,53 @@ void RimSummaryCurve::onLoadDataAndUpdate( bool updateParentPlot ) if ( m_xAxisType == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ) { - auto curveValuesX = valuesX(); - auto curveTimeStepsX = timeStepsX(); + if ( m_xValuesSummaryAddress()->address().category() == SummaryCategory::SUMMARY_ENSEMBLE_STATISTICS ) + { + std::vector curveValuesX; + std::vector curveValuesY; - auto curveTimeStepsY = timeStepsY(); + // Read x and y values from ensemble statistics (not time steps are read) + RifSummaryReaderInterface* reader = m_xValuesSummaryCase()->summaryReader(); - if ( curveValuesY.empty() || curveValuesX.empty() ) - { - shouldPopulateViewWithEmptyData = true; + reader->values( m_xValuesSummaryAddress->address(), &curveValuesX ); + reader->values( m_yValuesSummaryAddress->address(), &curveValuesY ); + + setSamplesFromXYValues( curveValuesX, curveValuesY, useLogarithmicScale ); } else { - RiaTimeHistoryCurveMerger curveMerger; - curveMerger.addCurveData( curveTimeStepsX, curveValuesX ); - curveMerger.addCurveData( curveTimeStepsY, curveValuesY ); - curveMerger.computeInterpolatedValues(); + auto curveValuesX = valuesX(); + auto curveTimeStepsX = timeStepsX(); + auto curveTimeStepsY = timeStepsY(); - if ( curveMerger.allXValues().size() > 0 ) + if ( curveValuesY.empty() || curveValuesX.empty() ) { - setSamplesFromXYValues( curveMerger.interpolatedYValuesForAllXValues( 0 ), - curveMerger.interpolatedYValuesForAllXValues( 1 ), - useLogarithmicScale ); + shouldPopulateViewWithEmptyData = true; } else { - shouldPopulateViewWithEmptyData = true; + RiaTimeHistoryCurveMerger curveMerger; + curveMerger.addCurveData( curveTimeStepsX, curveValuesX ); + curveMerger.addCurveData( curveTimeStepsY, curveValuesY ); + curveMerger.computeInterpolatedValues(); + + if ( !curveMerger.allXValues().empty() ) + { + setSamplesFromXYValues( curveMerger.interpolatedYValuesForAllXValues( 0 ), + curveMerger.interpolatedYValuesForAllXValues( 1 ), + useLogarithmicScale ); + } + else + { + shouldPopulateViewWithEmptyData = true; + } } } } else { std::vector curveTimeStepsY = timeStepsY(); - if ( plot->timeAxisProperties() && curveTimeStepsY.size() > 0 && curveTimeStepsY.size() == curveValuesY.size() ) + if ( plot->timeAxisProperties() && !curveTimeStepsY.empty() && curveTimeStepsY.size() == curveValuesY.size() ) { if ( plot->timeAxisProperties()->timeMode() == RimSummaryTimeAxisProperties::DATE ) { @@ -732,7 +747,7 @@ void RimSummaryCurve::onLoadDataAndUpdate( bool updateParentPlot ) double timeScale = plot->timeAxisProperties()->fromTimeTToDisplayUnitScale(); std::vector timeFromSimulationStart; - if ( curveTimeStepsY.size() ) + if ( !curveTimeStepsY.empty() ) { time_t startDate = curveTimeStepsY[0]; for ( const auto& date : curveTimeStepsY ) @@ -1193,7 +1208,7 @@ void RimSummaryCurve::fieldChangedByUi( const caf::PdmFieldHandle* changedField, if ( dlg.exec() == QDialog::Accepted ) { auto curveSelection = dlg.curveSelection(); - if ( curveSelection.size() > 0 ) + if ( !curveSelection.empty() ) { m_yValuesSummaryCase = curveSelection[0].summaryCaseY(); m_yValuesSummaryAddress->setAddress( curveSelection[0].summaryAddressY() ); @@ -1227,7 +1242,7 @@ void RimSummaryCurve::fieldChangedByUi( const caf::PdmFieldHandle* changedField, if ( dlg.exec() == QDialog::Accepted ) { auto curveSelection = dlg.curveSelection(); - if ( curveSelection.size() > 0 ) + if ( !curveSelection.empty() ) { m_xValuesSummaryCase = curveSelection[0].summaryCaseY(); m_xValuesSummaryAddress->setAddress( curveSelection[0].summaryAddressY() ); @@ -1255,7 +1270,7 @@ void RimSummaryCurve::fieldChangedByUi( const caf::PdmFieldHandle* changedField, curveMerger.addCurveData( curveTimeStepsY, curveValuesY ); curveMerger.computeInterpolatedValues(); - if ( curveMerger.validIntervalsForAllXValues().size() == 0 ) + if ( curveMerger.validIntervalsForAllXValues().empty() ) { QString description; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotAxisFormatter.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotAxisFormatter.cpp index 7901cfd200..c51f80dba7 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotAxisFormatter.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotAxisFormatter.cpp @@ -270,8 +270,17 @@ QString RimSummaryPlotAxisFormatter::autoAxisTitle() const for ( const RiaSummaryCurveDefinition& curveDef : m_curveDefinitions ) { - std::string unitText; - RifEclipseSummaryAddress sumAddress = curveDef.summaryAddressY(); + RifEclipseSummaryAddress sumAddress; + if ( m_axisProperties->plotAxis().isHorizontal() ) + { + sumAddress = curveDef.summaryAddressX(); + } + else + { + sumAddress = curveDef.summaryAddressY(); + } + + std::string unitText; if ( curveDef.ensemble() ) { std::vector sumCases = curveDef.ensemble()->allSummaryCases(); @@ -285,8 +294,7 @@ QString RimSummaryPlotAxisFormatter::autoAxisTitle() const RimSummaryCase* sumCase = curveDef.summaryCaseY(); if ( m_axisProperties->plotAxis().isHorizontal() ) { - sumCase = curveDef.summaryCaseX(); - sumAddress = curveDef.summaryAddressX(); + sumCase = curveDef.summaryCaseX(); } if ( sumCase && sumCase->summaryReader() ) diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotSourceStepping.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotSourceStepping.cpp index 47a20b25f4..c61555ba28 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotSourceStepping.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotSourceStepping.cpp @@ -329,6 +329,7 @@ void RimSummaryPlotSourceStepping::fieldChangedByUi( const caf::PdmFieldHandle* if ( dataSourceSteppingObject() ) curves = dataSourceSteppingObject()->allCurves( m_sourceSteppingType ); bool isAutoZoomAllowed = false; + bool doZoomAll = false; if ( changedField == &m_stepDimension ) { @@ -361,6 +362,25 @@ void RimSummaryPlotSourceStepping::fieldChangedByUi( const caf::PdmFieldHandle* bool triggerLoadDataAndUpdate = false; + auto updateEnsembleAddresses = [&doZoomAll, &oldValue, &newValue]( const std::vector& curveSets ) + { + for ( auto curveSet : curveSets ) + { + auto curveAdr = curveSet->curveAddress(); + + auto yAddressToModify = curveAdr.summaryAddressY(); + RimDataSourceSteppingTools::updateQuantityIfMatching( oldValue, newValue, yAddressToModify ); + + auto xAddressToModify = curveAdr.summaryAddressX(); + RimDataSourceSteppingTools::updateQuantityIfMatching( oldValue, newValue, xAddressToModify ); + + curveSet->setCurveAddress( RiaSummaryCurveAddress( xAddressToModify, yAddressToModify ) ); + curveSet->updateConnectedEditors(); + + doZoomAll = true; + } + }; + if ( changedField == &m_summaryCase ) { if ( m_summaryCase() ) @@ -431,11 +451,7 @@ void RimSummaryPlotSourceStepping::fieldChangedByUi( const caf::PdmFieldHandle* if ( dataSourceSteppingObject() ) { - for ( auto curveSet : dataSourceSteppingObject()->curveSets() ) - { - auto adr = curveSet->summaryAddress(); - if ( RimDataSourceSteppingTools::updateQuantityIfMatching( oldValue, newValue, &adr ) ) curveSet->setSummaryAddress( adr ); - } + updateEnsembleAddresses( dataSourceSteppingObject()->curveSets() ); } m_vectorName.uiCapability()->updateConnectedEditors(); @@ -486,26 +502,21 @@ void RimSummaryPlotSourceStepping::fieldChangedByUi( const caf::PdmFieldHandle* if ( isYAxisStepping() ) { RifEclipseSummaryAddress adr = curve->summaryAddressY(); - RimDataSourceSteppingTools::updateAddressIfMatching( oldValue, newValue, summaryCategoryToModify, &adr ); + RimDataSourceSteppingTools::updateAddressIfMatching( oldValue, newValue, summaryCategoryToModify, adr ); curve->setSummaryAddressY( adr ); } if ( isXAxisStepping() ) { RifEclipseSummaryAddress adr = curve->summaryAddressX(); - RimDataSourceSteppingTools::updateAddressIfMatching( oldValue, newValue, summaryCategoryToModify, &adr ); + RimDataSourceSteppingTools::updateAddressIfMatching( oldValue, newValue, summaryCategoryToModify, adr ); curve->setSummaryAddressX( adr ); } } if ( dataSourceSteppingObject() ) { - for ( auto curveSet : dataSourceSteppingObject()->curveSets() ) - { - auto adr = curveSet->summaryAddress(); - RimDataSourceSteppingTools::updateAddressIfMatching( oldValue, newValue, summaryCategoryToModify, &adr ); - curveSet->setSummaryAddress( adr ); - } + updateEnsembleAddresses( dataSourceSteppingObject()->curveSets() ); } triggerLoadDataAndUpdate = true; @@ -522,7 +533,15 @@ void RimSummaryPlotSourceStepping::fieldChangedByUi( const caf::PdmFieldHandle* summaryMultiPlot->updatePlots(); summaryMultiPlot->updatePlotTitles(); - if ( isAutoZoomAllowed ) summaryMultiPlot->zoomAllYAxes(); + if ( doZoomAll ) + { + summaryMultiPlot->zoomAll(); + } + else if ( isAutoZoomAllowed ) + { + // The time axis can be zoomed and will be used for all plots. Do not zoom time axis in this case. + summaryMultiPlot->zoomAllYAxes(); + } RiuPlotMainWindow* mainPlotWindow = RiaGuiApplication::instance()->mainPlotWindow(); mainPlotWindow->updateMultiPlotToolBar(); @@ -1261,13 +1280,13 @@ void RimSummaryPlotSourceStepping::updateVectorNameInCurves( std::vectorsummaryAddressY(); - if ( RimDataSourceSteppingTools::updateQuantityIfMatching( oldValue, newValue, &adr ) ) curve->setSummaryAddressY( adr ); + if ( RimDataSourceSteppingTools::updateQuantityIfMatching( oldValue, newValue, adr ) ) curve->setSummaryAddressY( adr ); } if ( isXAxisStepping() ) { auto adr = curve->summaryAddressX(); - if ( RimDataSourceSteppingTools::updateQuantityIfMatching( oldValue, newValue, &adr ) ) curve->setSummaryAddressX( adr ); + if ( RimDataSourceSteppingTools::updateQuantityIfMatching( oldValue, newValue, adr ) ) curve->setSummaryAddressX( adr ); } if ( m_autoUpdateAppearance )