From 263e39b97b88012d10b1f58d9b0006228c30480d Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Sat, 30 Sep 2023 11:07:23 +0200 Subject: [PATCH] Add regression curves to ensemble statistics curves * Add statistics enum * Support ensemble statistics curve as data source for regression curves * Allow creation of regression curves from statistics curves * Make sure regression curves are updated after source curves * Add state to use full or user defined range for regression source data * Add isRegressionCurve * Make sure source stepping works when regression curves are present --- .../Tools/RiaSummaryAddressAnalyzer.cpp | 2 + ...icCreateRegressionAnalysisCurveFeature.cpp | 67 +++- .../RicCreateRegressionAnalysisCurveFeature.h | 2 + .../FileInterface/CMakeLists_files.cmake | 2 + .../RifEclipseSummaryAddressDefines.cpp | 75 ++++ .../RifEclipseSummaryAddressDefines.h | 24 +- .../RimContextCommandBuilder.cpp | 1 + .../RimEnsembleCrossPlotStatisticsCase.cpp | 10 +- .../Summary/RimEnsembleCurveSet.cpp | 36 +- .../Summary/RimEnsembleCurveSet.h | 11 +- .../Summary/RimEnsembleStatisticsCase.cpp | 8 +- .../Summary/RimSummaryCurve.cpp | 86 ++--- .../Summary/RimSummaryCurve.h | 2 + .../Summary/RimSummaryPlot.cpp | 13 + .../RimSummaryRegressionAnalysisCurve.cpp | 322 ++++++++++++++---- .../RimSummaryRegressionAnalysisCurve.h | 42 ++- 16 files changed, 561 insertions(+), 142 deletions(-) create mode 100644 ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.cpp diff --git a/ApplicationLibCode/Application/Tools/RiaSummaryAddressAnalyzer.cpp b/ApplicationLibCode/Application/Tools/RiaSummaryAddressAnalyzer.cpp index 87de016cd8..e293c82f03 100644 --- a/ApplicationLibCode/Application/Tools/RiaSummaryAddressAnalyzer.cpp +++ b/ApplicationLibCode/Application/Tools/RiaSummaryAddressAnalyzer.cpp @@ -469,6 +469,8 @@ void RiaSummaryAddressAnalyzer::computeQuantityNamesWithHistory() const //-------------------------------------------------------------------------------------------------- void RiaSummaryAddressAnalyzer::analyzeSingleAddress( const RifEclipseSummaryAddress& address ) { + if ( !address.isValid() ) return; + if ( address.category() == SummaryCategory::SUMMARY_TIME ) { m_onlyCrossPlotCurves = false; diff --git a/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.cpp b/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.cpp index f2387228c1..0abf8c56f2 100644 --- a/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.cpp +++ b/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.cpp @@ -18,12 +18,15 @@ #include "RicCreateRegressionAnalysisCurveFeature.h" +#include "RiaColorTools.h" #include "RiaSummaryTools.h" +#include "RimEnsembleCurveSet.h" #include "RimSummaryCurve.h" #include "RimSummaryMultiPlot.h" #include "RimSummaryPlot.h" #include "RimSummaryRegressionAnalysisCurve.h" + #include "RiuPlotMainWindowTools.h" #include "cafSelectionManagerTools.h" @@ -39,8 +42,7 @@ CAF_CMD_SOURCE_INIT( RicCreateRegressionAnalysisCurveFeature, "RicCreateRegressi //-------------------------------------------------------------------------------------------------- bool RicCreateRegressionAnalysisCurveFeature::isCommandEnabled() const { - RimSummaryPlot* selectedPlot = caf::firstAncestorOfTypeFromSelectedObject(); - return ( selectedPlot && !RiaSummaryTools::isSummaryCrossPlot( selectedPlot ) ); + return caf::firstAncestorOfTypeFromSelectedObject(); } //-------------------------------------------------------------------------------------------------- @@ -48,11 +50,20 @@ bool RicCreateRegressionAnalysisCurveFeature::isCommandEnabled() const //-------------------------------------------------------------------------------------------------- void RicCreateRegressionAnalysisCurveFeature::onActionTriggered( bool isChecked ) { - RimSummaryCurve* curve = caf::firstAncestorOfTypeFromSelectedObject(); - if ( curve ) + RimSummaryRegressionAnalysisCurve* newCurve = nullptr; + + if ( auto summaryCurve = caf::firstAncestorOfTypeFromSelectedObject() ) + { + newCurve = createRegressionAnalysisCurveAndAddToPlot( summaryCurve ); + } + + if ( auto curveSet = caf::firstAncestorOfTypeFromSelectedObject() ) { - RimSummaryRegressionAnalysisCurve* newCurve = createRegressionAnalysisCurveAndAddToPlot( curve ); + newCurve = createRegressionAnalysisCurveAndAddToPlot( curveSet ); + } + if ( newCurve ) + { RiuPlotMainWindowTools::showPlotMainWindow(); RiuPlotMainWindowTools::selectAsCurrentItem( newCurve ); } @@ -75,9 +86,7 @@ RimSummaryRegressionAnalysisCurve* { RimSummaryPlot* summaryPlot = caf::firstAncestorOfTypeFromSelectedObject(); - RimSummaryRegressionAnalysisCurve* newCurve = new RimSummaryRegressionAnalysisCurve(); - CVF_ASSERT( newCurve ); - + auto newCurve = new RimSummaryRegressionAnalysisCurve(); RiaSummaryTools::copyCurveDataSources( *newCurve, *sourceCurve ); newCurve->setColor( sourceCurve->color() ); @@ -88,7 +97,47 @@ RimSummaryRegressionAnalysisCurve* RiaSummaryTools::copyCurveAxisData( *newCurve, *sourceCurve ); - newCurve->updateDefaultValues(); + newCurve->loadDataAndUpdate( true ); + newCurve->updateConnectedEditors(); + + RimSummaryMultiPlot* summaryMultiPlot = summaryPlot->firstAncestorOrThisOfType(); + if ( summaryMultiPlot ) + { + summaryMultiPlot->updatePlotTitles(); + } + else + { + summaryPlot->updatePlotTitle(); + } + + summaryPlot->updateAllRequiredEditors(); + + return newCurve; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryRegressionAnalysisCurve* + RicCreateRegressionAnalysisCurveFeature::createRegressionAnalysisCurveAndAddToPlot( RimEnsembleCurveSet* sourceCurveSet ) +{ + RimSummaryPlot* summaryPlot = caf::firstAncestorOfTypeFromSelectedObject(); + + auto newCurve = new RimSummaryRegressionAnalysisCurve(); + + newCurve->setEnsembleCurveSet( sourceCurveSet ); + + auto color = RiaColorTools::fromQColorTo3f( sourceCurveSet->mainEnsembleColor() ); + newCurve->setColor( color ); + newCurve->setSymbol( RiuPlotCurveSymbol::PointSymbolEnum::SYMBOL_RECT ); + newCurve->setSymbolSkipDistance( 50 ); + + summaryPlot->addCurveAndUpdate( newCurve ); + + newCurve->setAxisTypeX( sourceCurveSet->xAxisType() ); + newCurve->setTopOrBottomAxisX( sourceCurveSet->axisX() ); + newCurve->setLeftOrRightAxisY( sourceCurveSet->axisY() ); + newCurve->loadDataAndUpdate( true ); newCurve->updateConnectedEditors(); diff --git a/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.h b/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.h index 19093302c4..265fc6ae28 100644 --- a/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.h +++ b/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.h @@ -24,6 +24,7 @@ class RimSummaryPlot; class RimSummaryCurve; +class RimEnsembleCurveSet; //================================================================================================== /// @@ -38,4 +39,5 @@ class RicCreateRegressionAnalysisCurveFeature : public caf::CmdFeature void setupActionLook( QAction* actionToSetup ) override; static RimSummaryRegressionAnalysisCurve* createRegressionAnalysisCurveAndAddToPlot( RimSummaryCurve* sourceCurve ); + static RimSummaryRegressionAnalysisCurve* createRegressionAnalysisCurveAndAddToPlot( RimEnsembleCurveSet* sourceCurveSet ); }; diff --git a/ApplicationLibCode/FileInterface/CMakeLists_files.cmake b/ApplicationLibCode/FileInterface/CMakeLists_files.cmake index 6b140d4b0a..c2d4e79bf6 100644 --- a/ApplicationLibCode/FileInterface/CMakeLists_files.cmake +++ b/ApplicationLibCode/FileInterface/CMakeLists_files.cmake @@ -88,6 +88,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RifInpExportTools.h ${CMAKE_CURRENT_LIST_DIR}/RifFaultReactivationModelExporter.h ${CMAKE_CURRENT_LIST_DIR}/RifThermalToStimPlanFractureXmlOutput.h + ${CMAKE_CURRENT_LIST_DIR}/RifEclipseSummaryAddressDefines.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -177,6 +178,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RifInpExportTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RifFaultReactivationModelExporter.cpp ${CMAKE_CURRENT_LIST_DIR}/RifThermalToStimPlanFractureXmlOutput.cpp + ${CMAKE_CURRENT_LIST_DIR}/RifEclipseSummaryAddressDefines.cpp ) list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.cpp b/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.cpp new file mode 100644 index 0000000000..7ebb9ca2cf --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.cpp @@ -0,0 +1,75 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// 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 "RifEclipseSummaryAddressDefines.h" + +#include "cafAppEnum.h" + +namespace caf +{ +template <> +void caf::AppEnum::setUp() +{ + addItem( RifEclipseSummaryAddressDefines::StatisticsType::NONE, "NONE", "None" ); + addItem( RifEclipseSummaryAddressDefines::StatisticsType::P10, "P10", "P10" ); + addItem( RifEclipseSummaryAddressDefines::StatisticsType::P50, "P50", "P50" ); + addItem( RifEclipseSummaryAddressDefines::StatisticsType::P90, "P90", "P90" ); + addItem( RifEclipseSummaryAddressDefines::StatisticsType::MEAN, "MEAN", "Mean" ); + setDefault( RifEclipseSummaryAddressDefines::StatisticsType::NONE ); +} +} // namespace caf + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifEclipseSummaryAddressDefines::statisticsNameP10() +{ + return statisticsTypeToString( StatisticsType::P10 ); +} +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifEclipseSummaryAddressDefines::statisticsNameP50() +{ + return statisticsTypeToString( StatisticsType::P50 ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifEclipseSummaryAddressDefines::statisticsNameP90() +{ + return statisticsTypeToString( StatisticsType::P90 ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifEclipseSummaryAddressDefines::statisticsNameMean() +{ + return statisticsTypeToString( StatisticsType::MEAN ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifEclipseSummaryAddressDefines::statisticsTypeToString( StatisticsType type ) +{ + caf::AppEnum enumType( type ); + return enumType.uiText().toStdString(); +} diff --git a/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.h b/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.h index 9a4772903a..a13978928b 100644 --- a/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.h +++ b/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.h @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 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 @@ -15,12 +15,10 @@ // for more details. // ///////////////////////////////////////////////////////////////////////////////// + #pragma once -#define ENSEMBLE_STAT_P10_QUANTITY_NAME "P10" -#define ENSEMBLE_STAT_P50_QUANTITY_NAME "P50" -#define ENSEMBLE_STAT_P90_QUANTITY_NAME "P90" -#define ENSEMBLE_STAT_MEAN_QUANTITY_NAME "MEAN" +#include //================================================================================================== // @@ -65,4 +63,20 @@ enum class SummaryIdentifierType INPUT_VECTOR_NAME, INPUT_ID }; + +enum class StatisticsType +{ + NONE, + P10, + P50, + P90, + MEAN +}; + +std::string statisticsNameP10(); +std::string statisticsNameP50(); +std::string statisticsNameP90(); +std::string statisticsNameMean(); +std::string statisticsTypeToString( StatisticsType type ); + }; // namespace RifEclipseSummaryAddressDefines diff --git a/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp b/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp index 2c035d566a..96131b8593 100644 --- a/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp @@ -773,6 +773,7 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() menuBuilder << "RicClearSourceSteppingEnsembleCurveSetFeature"; menuBuilder << "Separator"; menuBuilder << "RicNewEnsembleCurveFilterFeature"; + menuBuilder << "RicCreateRegressionAnalysisCurveFeature"; } else if ( dynamic_cast( firstUiItem ) ) { diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCrossPlotStatisticsCase.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCrossPlotStatisticsCase.cpp index 7afe579324..7a922b53e7 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCrossPlotStatisticsCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCrossPlotStatisticsCase.cpp @@ -21,6 +21,8 @@ #include "RiaSummaryTools.h" #include "RiaTimeHistoryCurveResampler.h" +#include "RifEclipseSummaryAddressDefines.h" + #include "RigStatisticsMath.h" #include "RimEnsembleCurveSet.h" @@ -49,13 +51,13 @@ bool RimEnsembleCrossPlotStatisticsCase::values( const RifEclipseSummaryAddress& auto quantityName = resultAddress.ensembleStatisticsVectorName(); - if ( quantityName == ENSEMBLE_STAT_P10_QUANTITY_NAME ) + if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP10() ) *values = m_p10Data; - else if ( quantityName == ENSEMBLE_STAT_P50_QUANTITY_NAME ) + else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP50() ) *values = m_p50Data; - else if ( quantityName == ENSEMBLE_STAT_P90_QUANTITY_NAME ) + else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP90() ) *values = m_p90Data; - else if ( quantityName == ENSEMBLE_STAT_MEAN_QUANTITY_NAME ) + else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameMean() ) *values = m_meanData; return true; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp index 99c3e4786d..db67fced3d 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp @@ -473,6 +473,14 @@ bool RimEnsembleCurveSet::isXAxisSummaryVector() const return m_xAxisType() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiaDefines::HorizontalAxisType RimEnsembleCurveSet::xAxisType() const +{ + return m_xAxisType(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -2093,13 +2101,13 @@ void RimEnsembleCurveSet::updateStatisticsCurves( const std::vectorshowP10Curve() && m_ensembleStatCaseXY->hasP10Data() ) - addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P10_QUANTITY_NAME, dataAddressX, dataAddressY ) ); + addresses.push_back( getStatisticsAddress( RifEclipseSummaryAddressDefines::statisticsNameP10(), dataAddressX, dataAddressY ) ); if ( m_statistics->showP50Curve() && m_ensembleStatCaseXY->hasP50Data() ) - addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P50_QUANTITY_NAME, dataAddressX, dataAddressY ) ); + addresses.push_back( getStatisticsAddress( RifEclipseSummaryAddressDefines::statisticsNameP50(), dataAddressX, dataAddressY ) ); if ( m_statistics->showP90Curve() && m_ensembleStatCaseXY->hasP90Data() ) - addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P90_QUANTITY_NAME, dataAddressX, dataAddressY ) ); + addresses.push_back( getStatisticsAddress( RifEclipseSummaryAddressDefines::statisticsNameP90(), dataAddressX, dataAddressY ) ); if ( m_statistics->showMeanCurve() && m_ensembleStatCaseXY->hasMeanData() ) - addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_MEAN_QUANTITY_NAME, dataAddressX, dataAddressY ) ); + addresses.push_back( getStatisticsAddress( RifEclipseSummaryAddressDefines::statisticsNameMean(), dataAddressX, dataAddressY ) ); } else { @@ -2114,13 +2122,13 @@ void RimEnsembleCurveSet::updateStatisticsCurves( const std::vectorshowP10Curve() && m_ensembleStatCaseY->hasP10Data() ) - addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P10_QUANTITY_NAME, dataAddressY ) ); + addresses.push_back( getStatisticsAddress( RifEclipseSummaryAddressDefines::statisticsNameP10(), dataAddressY ) ); if ( m_statistics->showP50Curve() && m_ensembleStatCaseY->hasP50Data() ) - addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P50_QUANTITY_NAME, dataAddressY ) ); + addresses.push_back( getStatisticsAddress( RifEclipseSummaryAddressDefines::statisticsNameP50(), dataAddressY ) ); if ( m_statistics->showP90Curve() && m_ensembleStatCaseY->hasP90Data() ) - addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_P90_QUANTITY_NAME, dataAddressY ) ); + addresses.push_back( getStatisticsAddress( RifEclipseSummaryAddressDefines::statisticsNameP90(), dataAddressY ) ); if ( m_statistics->showMeanCurve() && m_ensembleStatCaseY->hasMeanData() ) - addresses.push_back( getStatisticsAddress( ENSEMBLE_STAT_MEAN_QUANTITY_NAME, dataAddressY ) ); + addresses.push_back( getStatisticsAddress( RifEclipseSummaryAddressDefines::statisticsNameMean(), dataAddressY ) ); } } @@ -2453,11 +2461,15 @@ void RimEnsembleCurveSet::updateLegendMappingMode() //-------------------------------------------------------------------------------------------------- RiuPlotCurveSymbol::PointSymbolEnum statisticsCurveSymbolFromAddress( const RifEclipseSummaryAddress& address ) { - auto qName = QString::fromStdString( address.vectorName() ); + auto qName = address.vectorName(); + + if ( qName.find( RifEclipseSummaryAddressDefines::statisticsNameP10() ) != std::string::npos ) + return RiuPlotCurveSymbol::SYMBOL_DOWN_TRIANGLE; + if ( qName.find( RifEclipseSummaryAddressDefines::statisticsNameP50() ) != std::string::npos ) + return RiuPlotCurveSymbol::SYMBOL_DIAMOND; + if ( qName.find( RifEclipseSummaryAddressDefines::statisticsNameP90() ) != std::string::npos ) + return RiuPlotCurveSymbol::SYMBOL_TRIANGLE; - if ( qName.contains( ENSEMBLE_STAT_P10_QUANTITY_NAME ) ) return RiuPlotCurveSymbol::SYMBOL_DOWN_TRIANGLE; - if ( qName.contains( ENSEMBLE_STAT_P90_QUANTITY_NAME ) ) return RiuPlotCurveSymbol::SYMBOL_TRIANGLE; - if ( qName.contains( ENSEMBLE_STAT_P50_QUANTITY_NAME ) ) return RiuPlotCurveSymbol::SYMBOL_DIAMOND; return RiuPlotCurveSymbol::SYMBOL_ELLIPSE; } diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h index c3c230409c..7843672dd1 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h @@ -189,10 +189,12 @@ class RimEnsembleCurveSet : public caf::PdmObject, public RimEnsembleCurveSetInt std::vector generateColorsForCases( const std::vector& summaryCases ) const; - RiuPlotAxis axisY() const; - RiuPlotAxis axisX() const; - void setLeftOrRightAxisY( RiuPlotAxis plotAxis ); - void setBottomOrTopAxisX( RiuPlotAxis plotAxis ); + RiuPlotAxis axisY() const; + RiuPlotAxis axisX() const; + void setLeftOrRightAxisY( RiuPlotAxis plotAxis ); + void setBottomOrTopAxisX( RiuPlotAxis plotAxis ); + bool isXAxisSummaryVector() const; + RiaDefines::HorizontalAxisType xAxisType() const; protected: void initAfterRead() override; @@ -237,7 +239,6 @@ class RimEnsembleCurveSet : public caf::PdmObject, public RimEnsembleCurveSetInt void onColorTagClicked( const SignalEmitter* emitter, size_t index ); void setSummaryAddressX( RifEclipseSummaryAddress address ); - bool isXAxisSummaryVector() const; private: caf::PdmField m_showCurves; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp index 6c2496d4c6..6321372cb4 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp @@ -77,13 +77,13 @@ bool RimEnsembleStatisticsCase::values( const RifEclipseSummaryAddress& resultAd { auto quantityName = resultAddress.ensembleStatisticsVectorName(); - if ( quantityName == ENSEMBLE_STAT_P10_QUANTITY_NAME ) + if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP10() ) *values = m_p10Data; - else if ( quantityName == ENSEMBLE_STAT_P50_QUANTITY_NAME ) + else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP50() ) *values = m_p50Data; - else if ( quantityName == ENSEMBLE_STAT_P90_QUANTITY_NAME ) + else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP90() ) *values = m_p90Data; - else if ( quantityName == ENSEMBLE_STAT_MEAN_QUANTITY_NAME ) + else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameMean() ) *values = m_meanData; return true; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp index c7e571195d..4a0f0089df 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp @@ -689,53 +689,55 @@ void RimSummaryCurve::onLoadDataAndUpdate( bool updateParentPlot ) { if ( plot->timeAxisProperties()->timeMode() == RimSummaryTimeAxisProperties::DATE ) { - auto reader = summaryCaseY()->summaryReader(); - if ( reader ) + RifEclipseSummaryAddress errAddress; + std::vector errValues; + + if ( summaryCaseY() && summaryCaseY()->summaryReader() ) { - auto errAddress = reader->errorAddress( summaryAddressY() ); - if ( errAddress.isValid() ) - { - std::vector errValues; - reader->values( errAddress, &errValues ); + auto reader = summaryCaseY()->summaryReader(); + errAddress = reader->errorAddress( summaryAddressY() ); + reader->values( errAddress, &errValues ); + } - auto timeSteps = RiuQwtPlotCurve::fromTime_t( curveTimeStepsY ); + if ( errAddress.isValid() ) + { + auto timeSteps = RiuQwtPlotCurve::fromTime_t( curveTimeStepsY ); - if ( !errValues.empty() ) - { - setSamplesFromXYErrorValues( timeSteps, curveValuesY, errValues, useLogarithmicScale ); - } - else - { - setSamplesFromXYValues( timeSteps, curveValuesY, useLogarithmicScale ); - } + if ( !errValues.empty() ) + { + setSamplesFromXYErrorValues( timeSteps, curveValuesY, errValues, useLogarithmicScale ); } else { - if ( m_yValuesResampling() != RiaDefines::DateTimePeriod::NONE ) - { - auto [resampledTimeSteps, resampledValues] = - RiaSummaryTools::resampledValuesForPeriod( m_yValuesSummaryAddress->address(), - curveTimeStepsY, - curveValuesY, - m_yValuesResampling() ); - - if ( !resampledValues.empty() && !resampledTimeSteps.empty() ) - { - // When values are resampled, each time step value is reported at the end of each - // resampling period. Insert a duplicate of the first value at the start of the time - // series to make curve start at the very first reported time step. - - resampledTimeSteps.insert( resampledTimeSteps.begin(), curveTimeStepsY.front() ); - resampledValues.insert( resampledValues.begin(), resampledValues.front() ); - - setSamplesFromTimeTAndYValues( resampledTimeSteps, resampledValues, useLogarithmicScale ); - } - } - else + setSamplesFromXYValues( timeSteps, curveValuesY, useLogarithmicScale ); + } + } + else + { + if ( m_yValuesResampling() != RiaDefines::DateTimePeriod::NONE ) + { + auto [resampledTimeSteps, resampledValues] = + RiaSummaryTools::resampledValuesForPeriod( m_yValuesSummaryAddress->address(), + curveTimeStepsY, + curveValuesY, + m_yValuesResampling() ); + + if ( !resampledValues.empty() && !resampledTimeSteps.empty() ) { - setSamplesFromTimeTAndYValues( curveTimeStepsY, curveValuesY, useLogarithmicScale ); + // When values are resampled, each time step value is reported at the end of each + // resampling period. Insert a duplicate of the first value at the start of the time + // series to make curve start at the very first reported time step. + + resampledTimeSteps.insert( resampledTimeSteps.begin(), curveTimeStepsY.front() ); + resampledValues.insert( resampledValues.begin(), resampledValues.front() ); + + setSamplesFromTimeTAndYValues( resampledTimeSteps, resampledValues, useLogarithmicScale ); } } + else + { + setSamplesFromTimeTAndYValues( curveTimeStepsY, curveValuesY, useLogarithmicScale ); + } } } else @@ -993,6 +995,14 @@ RiaDefines::PhaseType RimSummaryCurve::phaseType() const return m_yValuesSummaryAddress->addressPhaseType(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSummaryCurve::isRegressionCurve() const +{ + return false; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.h index aaa9ed95b9..73f9a037c7 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.h @@ -105,6 +105,8 @@ class RimSummaryCurve : public RimStackablePlotCurve RiaDefines::PhaseType phaseType() const override; + virtual bool isRegressionCurve() const; + protected: // RimPlotCurve overrides QString createCurveAutoName() override; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.cpp index dc5b86bef0..0a38dff05a 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.cpp @@ -1958,6 +1958,19 @@ void RimSummaryPlot::onLoadDataAndUpdate() curve->loadDataAndUpdate( false ); } + // Load data for regression curves, as they depend on data loaded by curves updated previously in this function + if ( m_summaryCurveCollection ) + { + auto curves = m_summaryCurveCollection->curves(); + for ( auto c : curves ) + { + if ( c->isRegressionCurve() ) + { + c->loadDataAndUpdate( false ); + } + } + } + if ( plotWidget() ) { plotWidget()->setInternalLegendVisible( m_showPlotLegends && !isSubPlot() ); diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp index 5df22e404a..fc9da67264 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp @@ -23,6 +23,9 @@ #include "RiaRegressionTextTools.h" #include "RiaTimeTTools.h" +#include "RimEnsembleCurveSet.h" +#include "RimEnsembleCurveSetCollection.h" +#include "RimSummaryCaseCollection.h" #include "RimSummaryPlot.h" #include "RimTimeAxisAnnotation.h" @@ -56,7 +59,7 @@ void caf::AppEnum::setUp() addItem( RimSummaryRegressionAnalysisCurve::RegressionType::POWER_FIT, "POWER_FIT", "Power Fit" ); addItem( RimSummaryRegressionAnalysisCurve::RegressionType::EXPONENTIAL, "EXPONENTIAL", "Exponential" ); addItem( RimSummaryRegressionAnalysisCurve::RegressionType::LOGARITHMIC, "LOGARITHMIC", "Logarithmic" ); - setDefault( RimSummaryRegressionAnalysisCurve::RegressionType::LINEAR ); + setDefault( RimSummaryRegressionAnalysisCurve::RegressionType::POLYNOMIAL ); } template <> @@ -68,6 +71,22 @@ void caf::AppEnum::setUp() setDefault( RimSummaryRegressionAnalysisCurve::ForecastUnit::YEARS ); } +template <> +void caf::AppEnum::setUp() +{ + addItem( RimSummaryRegressionAnalysisCurve::DataSource::SUMMARY_ADDRESS, "SUMMARY_ADDRESS", "Summary Address" ); + addItem( RimSummaryRegressionAnalysisCurve::DataSource::ENSEMBLE, "ENSEMBLE", "Ensemble" ); + setDefault( RimSummaryRegressionAnalysisCurve::DataSource::SUMMARY_ADDRESS ); +} + +template <> +void caf::AppEnum::setUp() +{ + addItem( RimSummaryRegressionAnalysisCurve::RangeType::FULL_RANGE, "FULL_RANGE", "Full Range" ); + addItem( RimSummaryRegressionAnalysisCurve::RangeType::USER_DEFINED_RANGE, "USER_DEFINED_RANGE", "Custom Range" ); + setDefault( RimSummaryRegressionAnalysisCurve::RangeType::FULL_RANGE ); +} + }; // namespace caf //-------------------------------------------------------------------------------------------------- @@ -77,12 +96,17 @@ RimSummaryRegressionAnalysisCurve::RimSummaryRegressionAnalysisCurve() { CAF_PDM_InitObject( "Regression Analysis Curve", ":/regression-curve.svg" ); + CAF_PDM_InitFieldNoDefault( &m_dataSourceForRegression, "DataSourceForRegression", "Data Source" ); + CAF_PDM_InitFieldNoDefault( &m_ensembleCurveSet, "SourceCurveSet", "Source Curve Set" ); + CAF_PDM_InitFieldNoDefault( &m_ensembleStatisticsType, "EnsembleStatisticsType", "Ensemble Statistics Type" ); + CAF_PDM_InitFieldNoDefault( &m_regressionType, "RegressionType", "Type" ); CAF_PDM_InitField( &m_forecastForward, "ForecastForward", 0, "Forward" ); CAF_PDM_InitField( &m_forecastBackward, "ForecastBackward", 0, "Backward" ); CAF_PDM_InitFieldNoDefault( &m_forecastUnit, "ForecastUnit", "Unit" ); CAF_PDM_InitField( &m_polynomialDegree, "PolynomialDegree", 3, "Degree" ); + CAF_PDM_InitFieldNoDefault( &m_timeRangeSelection, "TimeRangeSelection", "Time Range" ); CAF_PDM_InitFieldNoDefault( &m_minTimeStep, "MinTimeStep", "From" ); m_minTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiSliderEditor::uiEditorTypeName() ); @@ -97,9 +121,11 @@ RimSummaryRegressionAnalysisCurve::RimSummaryRegressionAnalysisCurve() m_expressionText.uiCapability()->setUiReadOnly( true ); m_expressionText.xmlCapability()->disableIO(); + CAF_PDM_InitFieldNoDefault( &m_xRangeSelection, "XRangeSelection", "X Value Range" ); CAF_PDM_InitField( &m_valueRangeX, "ValueRangeX", std::make_pair( 0.0, 0.0 ), "Value Range X" ); m_valueRangeX.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); + CAF_PDM_InitFieldNoDefault( &m_yRangeSelection, "YRangeSelection", "Y Value Range" ); CAF_PDM_InitField( &m_valueRangeY, "ValueRangeY", std::make_pair( 0.0, 0.0 ), "Value Range Y" ); m_valueRangeY.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); } @@ -113,15 +139,33 @@ RimSummaryRegressionAnalysisCurve::~RimSummaryRegressionAnalysisCurve() if ( plot && m_timeRangeAnnotation ) plot->removeTimeAnnotation( m_timeRangeAnnotation ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryRegressionAnalysisCurve::setEnsembleCurveSet( RimEnsembleCurveSet* ensembleCurveSet ) +{ + m_dataSourceForRegression = DataSource::ENSEMBLE; + m_ensembleCurveSet = ensembleCurveSet; + m_ensembleStatisticsType = RifEclipseSummaryAddressDefines::StatisticsType::P10; + + setSummaryAddressY( {} ); + setSummaryAddressX( {} ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryRegressionAnalysisCurve::onLoadDataAndUpdate( bool updateParentPlot ) { - auto xValues = RimSummaryCurve::valuesX(); - auto yValues = RimSummaryCurve::valuesY(); - auto timeStepsX = RimSummaryCurve::timeStepsX(); - auto timeStepsY = RimSummaryCurve::timeStepsY(); + extractSourceCurveData(); + updateDefaultValues(); + + std::vector xValues = m_sourceValuesX; + std::vector yValues = m_sourceValuesY; + std::vector timeStepsX = m_sourceTimeStepsX; + std::vector timeStepsY = m_sourceTimeStepsY; + + if ( yValues.empty() ) return; if ( axisTypeX() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ) { @@ -135,11 +179,12 @@ void RimSummaryRegressionAnalysisCurve::onLoadDataAndUpdate( bool updateParentPl "Differences in time steps for X and Y axis detected. This is currently not supported. Make sure that the same " "case is used for both axis." ); } + } - // NB! Assume that time stamps for X and Y are the same - std::vector indicesToRemove; + std::vector indicesToRemove; - // Step 1: Find indices of values which are outside the specified range + if ( axisTypeX() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ) + { for ( size_t i = 0; i < xValues.size(); i++ ) { if ( xValues[i] < m_valueRangeX().first || xValues[i] > m_valueRangeX().second ) @@ -147,31 +192,35 @@ void RimSummaryRegressionAnalysisCurve::onLoadDataAndUpdate( bool updateParentPl indicesToRemove.push_back( i ); } } + } - for ( size_t i = 0; i < yValues.size(); i++ ) + for ( size_t i = 0; i < yValues.size(); i++ ) + { + if ( yValues[i] < m_valueRangeY().first || yValues[i] > m_valueRangeY().second ) { - if ( yValues[i] < m_valueRangeY().first || yValues[i] > m_valueRangeY().second ) - { - indicesToRemove.push_back( i ); - } + indicesToRemove.push_back( i ); } + } - // Step 2: Sort indices in descending order - std::sort( indicesToRemove.rbegin(), indicesToRemove.rend() ); + // Sort indices in descending order + std::sort( indicesToRemove.rbegin(), indicesToRemove.rend() ); - // There might be duplicates, remove them - indicesToRemove.erase( std::unique( indicesToRemove.begin(), indicesToRemove.end() ), indicesToRemove.end() ); + // There might be duplicates, remove them + indicesToRemove.erase( std::unique( indicesToRemove.begin(), indicesToRemove.end() ), indicesToRemove.end() ); - // Step 3: Remove elements at the specified indices - for ( auto index : indicesToRemove ) + // Remove elements at the specified indices + for ( auto index : indicesToRemove ) + { + if ( index < yValues.size() ) { - if ( index < xValues.size() ) - { - xValues.erase( xValues.begin() + index ); - yValues.erase( yValues.begin() + index ); - timeStepsX.erase( timeStepsX.begin() + index ); - timeStepsY.erase( timeStepsY.begin() + index ); - } + yValues.erase( yValues.begin() + index ); + timeStepsY.erase( timeStepsY.begin() + index ); + } + + if ( index < xValues.size() ) + { + xValues.erase( xValues.begin() + index ); + timeStepsX.erase( timeStepsX.begin() + index ); } } @@ -186,6 +235,80 @@ void RimSummaryRegressionAnalysisCurve::onLoadDataAndUpdate( bool updateParentPl RimSummaryCurve::onLoadDataAndUpdate( updateParentPlot ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryRegressionAnalysisCurve::extractSourceCurveData() +{ + std::vector xValues; + std::vector yValues; + std::vector xTimeSteps; + std::vector yTimeSteps; + + if ( m_dataSourceForRegression() == DataSource::ENSEMBLE ) + { + auto findStatisticsCurve = []( RimEnsembleCurveSet* curveSet, const QString& statisticsCurveName ) -> RimSummaryCurve* + { + if ( curveSet == nullptr ) return nullptr; + + auto allCurves = curveSet->curves(); + for ( auto curve : allCurves ) + { + auto yAddr = curve->summaryAddressY(); + + if ( yAddr.category() == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_ENSEMBLE_STATISTICS ) + { + auto statisticsName = QString::fromStdString( yAddr.ensembleStatisticsVectorName() ); + if ( statisticsName == statisticsCurveName ) + { + return curve; + } + } + } + return nullptr; + }; + + auto curve = findStatisticsCurve( m_ensembleCurveSet(), m_ensembleStatisticsType().uiText() ); + if ( curve ) + { + yValues = curve->valuesY(); + xValues = curve->valuesX(); + + auto curveTimeY = curve->timeStepsY(); + if ( curveTimeY.size() == yValues.size() ) + { + yTimeSteps = curveTimeY; + } + else + { + // Fallback to use time steps from summary case + // The time steps are used for reference, not used when computing the regression curve + auto summaryCase = m_ensembleCurveSet->summaryCaseCollection()->allSummaryCases().back(); + auto allTimeSteps = summaryCase->summaryReader()->timeSteps( {} ); + yTimeSteps = allTimeSteps; + + yTimeSteps.resize( yValues.size() ); + } + + if ( xValues.size() == yValues.size() ) xTimeSteps = yTimeSteps; + } + } + else + { + // Get curve data from the summary data defined by X and Y axis data + + xValues = RimSummaryCurve::valuesX(); + yValues = RimSummaryCurve::valuesY(); + xTimeSteps = RimSummaryCurve::timeStepsX(); + yTimeSteps = RimSummaryCurve::timeStepsY(); + } + + m_sourceTimeStepsX = xTimeSteps; + m_sourceTimeStepsY = yTimeSteps; + m_sourceValuesX = xValues; + m_sourceValuesY = yValues; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -310,6 +433,14 @@ void RimSummaryRegressionAnalysisCurve::defineUiOrdering( QString uiConfigName, { RimPlotCurve::updateFieldUiState(); + uiOrdering.add( &m_dataSourceForRegression ); + + if ( m_dataSourceForRegression() == DataSource::ENSEMBLE ) + { + uiOrdering.add( &m_ensembleCurveSet ); + uiOrdering.add( &m_ensembleStatisticsType ); + } + caf::PdmUiGroup* regressionCurveGroup = uiOrdering.addNewGroup( "Regression Analysis" ); regressionCurveGroup->add( &m_regressionType ); @@ -320,18 +451,32 @@ void RimSummaryRegressionAnalysisCurve::defineUiOrdering( QString uiConfigName, regressionCurveGroup->add( &m_expressionText ); - caf::PdmUiGroup* timeSelectionGroup = uiOrdering.addNewGroup( "Time Selection" ); - timeSelectionGroup->add( &m_minTimeStep ); - timeSelectionGroup->add( &m_maxTimeStep ); - timeSelectionGroup->add( &m_showTimeSelectionInPlot ); + caf::PdmUiGroup* valueRangeYGroup = uiOrdering.addNewGroup( "Value Range Y" ); + valueRangeYGroup->add( &m_yRangeSelection ); + if ( m_yRangeSelection() == RangeType::USER_DEFINED_RANGE ) + { + valueRangeYGroup->add( &m_valueRangeY ); + } if ( axisTypeX() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ) { caf::PdmUiGroup* valueRangeXGroup = uiOrdering.addNewGroup( "Value Range X" ); - valueRangeXGroup->add( &m_valueRangeX ); - - caf::PdmUiGroup* valueRangeYGroup = uiOrdering.addNewGroup( "Value Range Y" ); - valueRangeYGroup->add( &m_valueRangeY ); + valueRangeXGroup->add( &m_xRangeSelection ); + if ( m_xRangeSelection() == RangeType::USER_DEFINED_RANGE ) + { + valueRangeXGroup->add( &m_valueRangeX ); + } + } + else + { + caf::PdmUiGroup* timeSelectionGroup = uiOrdering.addNewGroup( "Time Selection" ); + timeSelectionGroup->add( &m_timeRangeSelection ); + if ( m_timeRangeSelection() == RangeType::USER_DEFINED_RANGE ) + { + timeSelectionGroup->add( &m_minTimeStep ); + timeSelectionGroup->add( &m_maxTimeStep ); + } + timeSelectionGroup->add( &m_showTimeSelectionInPlot ); } caf::PdmUiGroup* forecastingGroup = uiOrdering.addNewGroup( "Forecasting" ); @@ -339,7 +484,22 @@ void RimSummaryRegressionAnalysisCurve::defineUiOrdering( QString uiConfigName, forecastingGroup->add( &m_forecastBackward ); forecastingGroup->add( &m_forecastUnit ); - RimSummaryCurve::defineUiOrdering( uiConfigName, uiOrdering ); + if ( m_dataSourceForRegression() == DataSource::ENSEMBLE ) + { + caf::PdmUiGroup* appearanceGroup = uiOrdering.addNewGroup( "Appearance" ); + RimPlotCurve::appearanceUiOrdering( *appearanceGroup ); + + caf::PdmUiGroup* nameGroup = uiOrdering.addNewGroup( "Curve Name" ); + nameGroup->setCollapsedByDefault(); + nameGroup->add( &m_showLegend ); + RimPlotCurve::curveNameUiOrdering( *nameGroup ); + } + else + { + RimSummaryCurve::defineUiOrdering( uiConfigName, uiOrdering ); + } + + uiOrdering.skipRemainingFields(); } //-------------------------------------------------------------------------------------------------- @@ -349,6 +509,8 @@ void RimSummaryRegressionAnalysisCurve::fieldChangedByUi( const caf::PdmFieldHan const QVariant& oldValue, const QVariant& newValue ) { + RimSummaryCurve::fieldChangedByUi( changedField, oldValue, newValue ); + if ( &m_minTimeStep == changedField && m_minTimeStep > m_maxTimeStep ) { m_maxTimeStep = m_minTimeStep; @@ -359,17 +521,10 @@ void RimSummaryRegressionAnalysisCurve::fieldChangedByUi( const caf::PdmFieldHan m_minTimeStep = m_maxTimeStep; } - RimSummaryCurve::fieldChangedByUi( changedField, oldValue, newValue ); - if ( changedField == &m_regressionType || changedField == &m_polynomialDegree || changedField == &m_forecastBackward || - changedField == &m_forecastForward || changedField == &m_forecastUnit || changedField == &m_minTimeStep || - changedField == &m_maxTimeStep || changedField == &m_showTimeSelectionInPlot || changedField == &m_valueRangeX || - changedField == &m_valueRangeY ) - { - loadAndUpdateDataAndPlot(); + loadAndUpdateDataAndPlot(); - auto plot = firstAncestorOrThisOfTypeAsserted(); - if ( plot ) plot->zoomAll(); - } + auto plot = firstAncestorOrThisOfTypeAsserted(); + if ( plot ) plot->zoomAll(); } //-------------------------------------------------------------------------------------------------- @@ -401,7 +556,7 @@ void RimSummaryRegressionAnalysisCurve::defineEditorAttribute( const caf::PdmFie { if ( auto* myAttr = dynamic_cast( attribute ) ) { - auto timeSteps = RimSummaryCurve::timeStepsY(); + auto timeSteps = m_sourceTimeStepsY; if ( !timeSteps.empty() ) { myAttr->m_minimum = *timeSteps.begin(); @@ -431,7 +586,7 @@ void RimSummaryRegressionAnalysisCurve::defineEditorAttribute( const caf::PdmFie attr->m_decimals = 2; attr->m_sliderTickCount = 100; - auto values = RimSummaryCurve::valuesX(); + auto values = m_sourceValuesX; if ( !values.empty() ) { attr->m_minimum = *std::min_element( values.begin(), values.end() ); @@ -446,7 +601,7 @@ void RimSummaryRegressionAnalysisCurve::defineEditorAttribute( const caf::PdmFie attr->m_decimals = 2; attr->m_sliderTickCount = 100; - auto values = RimSummaryCurve::valuesY(); + auto values = m_sourceValuesY; if ( !values.empty() ) { attr->m_minimum = *std::min_element( values.begin(), values.end() ); @@ -456,12 +611,52 @@ void RimSummaryRegressionAnalysisCurve::defineEditorAttribute( const caf::PdmFie } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RimSummaryRegressionAnalysisCurve::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) +{ + if ( fieldNeedingOptions == &m_ensembleCurveSet ) + { + QList options; + options.append( { "None", nullptr } ); + + auto plot = firstAncestorOrThisOfType(); + if ( plot ) + { + auto curveSets = plot->ensembleCurveSetCollection()->curveSets(); + + for ( auto curveSet : curveSets ) + { + options.append( { curveSet->name(), curveSet } ); + } + } + + return options; + } + + return RimSummaryCurve::calculateValueOptions( fieldNeedingOptions ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimSummaryRegressionAnalysisCurve::createCurveAutoName() { - return RimSummaryCurve::createCurveAutoName() + " " + m_regressionType().uiText() + " Regression"; + QString sourceCurveName; + if ( m_dataSourceForRegression() == DataSource::ENSEMBLE ) + { + if ( m_ensembleCurveSet() ) + { + sourceCurveName = m_ensembleCurveSet()->name(); + } + } + else + { + sourceCurveName = RimSummaryCurve::createCurveAutoName(); + } + + return sourceCurveName + " " + m_regressionType().uiText() + " Regression"; } //-------------------------------------------------------------------------------------------------- @@ -562,6 +757,14 @@ std::vector RimSummaryRegressionAnalysisCurve::getOutputTimeSteps( const return outputTimeSteps; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSummaryRegressionAnalysisCurve::isRegressionCurve() const +{ + return true; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -650,25 +853,22 @@ void RimSummaryRegressionAnalysisCurve::updateTimeAnnotations() //-------------------------------------------------------------------------------------------------- void RimSummaryRegressionAnalysisCurve::updateDefaultValues() { - auto timeSteps = RimSummaryCurve::timeStepsY(); - if ( !timeSteps.empty() ) + if ( !m_sourceTimeStepsY.empty() && m_timeRangeSelection() == RangeType::FULL_RANGE ) { - m_minTimeStep = timeSteps.front(); - m_maxTimeStep = timeSteps.back(); + m_minTimeStep = m_sourceTimeStepsY.front(); + m_maxTimeStep = m_sourceTimeStepsY.back(); } - auto allValuesX = RimSummaryCurve::valuesX(); - if ( !allValuesX.empty() ) + if ( !m_sourceValuesX.empty() && m_xRangeSelection() == RangeType::FULL_RANGE ) { - m_valueRangeX = std::make_pair( *std::min_element( allValuesX.begin(), allValuesX.end() ), - *std::max_element( allValuesX.begin(), allValuesX.end() ) ); + m_valueRangeX = std::make_pair( *std::min_element( m_sourceValuesX.begin(), m_sourceValuesX.end() ), + *std::max_element( m_sourceValuesX.begin(), m_sourceValuesX.end() ) ); } - auto allValuesY = RimSummaryCurve::valuesY(); - if ( !allValuesY.empty() ) + if ( !m_sourceValuesY.empty() && m_yRangeSelection() == RangeType::FULL_RANGE ) { - m_valueRangeY = std::make_pair( *std::min_element( allValuesY.begin(), allValuesY.end() ), - *std::max_element( allValuesY.begin(), allValuesY.end() ) ); + m_valueRangeY = std::make_pair( *std::min_element( m_sourceValuesY.begin(), m_sourceValuesY.end() ), + *std::max_element( m_sourceValuesY.begin(), m_sourceValuesY.end() ) ); } } diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h index 5fd94148de..1b5a6cadaa 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h @@ -36,6 +36,7 @@ class PowerFitRegression; } // namespace regression class RimTimeAxisAnnotation; +class RimEnsembleCurveSet; //================================================================================================== /// @@ -63,9 +64,23 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve YEARS, }; + enum class DataSource + { + SUMMARY_ADDRESS, + ENSEMBLE + }; + + enum class RangeType + { + FULL_RANGE, + USER_DEFINED_RANGE + }; + RimSummaryRegressionAnalysisCurve(); ~RimSummaryRegressionAnalysisCurve() override; + void setEnsembleCurveSet( RimEnsembleCurveSet* ensembleCurveSet ); + // Y Axis functions std::vector valuesY() const override; std::vector timeStepsY() const override; @@ -76,7 +91,7 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve static std::vector getOutputTimeSteps( const std::vector& timeSteps, int forecastBackward, int forecastForward, ForecastUnit forecastUnit ); - void updateDefaultValues(); + bool isRegressionCurve() const override; protected: void updateTimeAnnotations() override; @@ -84,6 +99,9 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve private: void onLoadDataAndUpdate( bool updateParentPlot ) override; + void extractSourceCurveData(); + void updateDefaultValues(); + QString createCurveAutoName() override; QString curveExportDescription( const RifEclipseSummaryAddress& address ) const override; @@ -91,6 +109,7 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override; + QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; std::tuple, std::vector, QString> computeRegressionCurve( const std::vector& timeSteps, const std::vector& values ) const; @@ -115,10 +134,16 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve static void appendTimeSteps( std::vector& destinationTimeSteps, const std::set& sourceTimeSteps ); private: + caf::PdmField> m_dataSourceForRegression; + caf::PdmPtrField m_ensembleCurveSet; + caf::PdmField> m_ensembleStatisticsType; + caf::PdmField> m_regressionType; - caf::PdmField m_minTimeStep; - caf::PdmField m_maxTimeStep; - caf::PdmField m_showTimeSelectionInPlot; + + caf::PdmField> m_timeRangeSelection; + caf::PdmField m_minTimeStep; + caf::PdmField m_maxTimeStep; + caf::PdmField m_showTimeSelectionInPlot; caf::PdmField m_polynomialDegree; caf::PdmField m_expressionText; @@ -126,7 +151,10 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve caf::PdmField m_forecastBackward; caf::PdmField> m_forecastUnit; + caf::PdmField> m_xRangeSelection; caf::PdmField> m_valueRangeX; + + caf::PdmField> m_yRangeSelection; caf::PdmField> m_valueRangeY; caf::PdmPointer m_timeRangeAnnotation; @@ -134,4 +162,10 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve std::vector m_timeStepsX; std::vector m_valuesY; std::vector m_timeStepsY; + + // Source curve data + std::vector m_sourceValuesX; + std::vector m_sourceTimeStepsX; + std::vector m_sourceValuesY; + std::vector m_sourceTimeStepsY; };