diff --git a/ApplicationCode/Commands/RicImportSummaryCasesFeature.cpp b/ApplicationCode/Commands/RicImportSummaryCasesFeature.cpp index e163c9eee9..c015a25d42 100644 --- a/ApplicationCode/Commands/RicImportSummaryCasesFeature.cpp +++ b/ApplicationCode/Commands/RicImportSummaryCasesFeature.cpp @@ -165,8 +165,9 @@ bool RicImportSummaryCasesFeature::createSummaryCasesFromFiles( const QStringLis std::vector* newCases, bool ensembleOrGroup ) { - RiaApplication* app = RiaApplication::instance(); - RimProject* proj = app->project(); + RiaApplication* app = RiaApplication::instance(); + RimProject* proj = app->project(); + RimSummaryCaseMainCollection* sumCaseColl = proj->activeOilField() ? proj->activeOilField()->summaryCaseMainCollection() : nullptr; diff --git a/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryPlotFeatureImpl.cpp b/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryPlotFeatureImpl.cpp index b608f034c5..5417b4ff72 100644 --- a/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryPlotFeatureImpl.cpp +++ b/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryPlotFeatureImpl.cpp @@ -178,43 +178,11 @@ RimSummaryCurve* createHistoryCurve( const RifEclipseSummaryAddress& addr, RimSu return nullptr; } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -class RigGridCellResultAddress -{ -public: - RigGridCellResultAddress() - : gridIndex( -1 ) - , i( -1 ) - , j( -1 ) - , k( -1 ) - { - } - - RigGridCellResultAddress( - size_t gridIndex, size_t i, size_t j, size_t k, const RigEclipseResultAddress& eclipseResultAddress ) - : gridIndex( gridIndex ) - , i( i ) - , j( j ) - , k( k ) - , eclipseResultAddress( eclipseResultAddress ) - { - } - - // Using zero based ijk - - size_t gridIndex; - size_t i; - size_t j; - size_t k; - RigEclipseResultAddress eclipseResultAddress; -}; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -std::vector createGridCellAddressesFromFilter( const QString& text ) +std::vector RigGridCellResultAddress::createGridCellAddressesFromFilter( const QString& text ) { std::vector addresses; QStringList addressParts = text.split( ":" ); @@ -252,6 +220,9 @@ std::vector createGridCellAddressesFromFilter( const Q return addresses; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- std::vector openEclipseCasesForCellPlotting( QStringList gridFileNames ) { std::vector openedCases; @@ -424,53 +395,31 @@ void RicSummaryPlotFeatureImpl::createSummaryPlotsFromArgumentLine( const QStrin { RicImportSummaryCasesFeature::createSummaryCasesFromFiles( summaryFileNames, &summaryCasesToUse, isEnsembleMode ); RicImportSummaryCasesFeature::addSummaryCases( summaryCasesToUse ); + RiaApplication::instance()->setLastUsedDialogDirectory( RiaDefines::defaultDirectoryLabel( RiaDefines::ECLIPSE_SUMMARY_FILE ), QFileInfo( summaryFileNames[0] ).absolutePath() ); } - // Sort in summary and grid curve addresses - - QStringList gridResultAddressFilters; - QStringList summaryAddressFilters; if ( summaryCasesToUse.size() ) { - const std::set& addrs = summaryCasesToUse[0]->summaryReader()->allResultAddresses(); - std::vector usedFilters; - std::set setToInsertFilteredAddressesIn; - filteredSummaryAdressesFromCase( allCurveAddressFilters, addrs, &setToInsertFilteredAddressesIn, &usedFilters ); + // Sort in summary and grid curve addresses + QStringList gridResultAddressFilters; + QStringList summaryAddressFilters; - QRegularExpression gridAddressPattern( "^[A-Z]+:[0-9]+,[0-9]+,[0-9]+$" ); + splitAddressFiltersInGridAndSummary( summaryCasesToUse[0], + allCurveAddressFilters, + &summaryAddressFilters, + &gridResultAddressFilters ); - for ( int filterIdx = 0; filterIdx < allCurveAddressFilters.size(); ++filterIdx ) - { - const QString& address = allCurveAddressFilters[filterIdx]; - if ( usedFilters[filterIdx] ) - { - summaryAddressFilters.push_back( address ); - } - else - { - if ( gridAddressPattern.match( address ).hasMatch() ) - { - gridResultAddressFilters.push_back( address ); - } - else - { - RiaLogging::warning( "No summary or restart vectors matched \"" + address + "\"" ); - } - } - } - } - - if ( summaryCasesToUse.size() ) - { if ( summaryAddressFilters.size() ) { RimSummaryPlotCollection* sumPlotColl = RiaApplication::instance()->project()->mainPlotCollection()->summaryPlotCollection(); + RimSummaryPlot* lastPlotCreated = nullptr; RimSummaryCaseCollection* ensemble = nullptr; + if ( isEnsembleMode ) ensemble = RicCreateSummaryCaseCollectionFeature::groupSummaryCases( summaryCasesToUse, "Ensemble", true ); @@ -482,6 +431,7 @@ void RicSummaryPlotFeatureImpl::createSummaryPlotsFromArgumentLine( const QStrin { std::set filteredAdressesFromCases = applySummaryAddressFiltersToCases( summaryCasesToUse, summaryAddressFilters ); + for ( const auto& addr : filteredAdressesFromCases ) { RimEnsembleCurveSet* curveSet = new RimEnsembleCurveSet(); @@ -490,16 +440,19 @@ void RicSummaryPlotFeatureImpl::createSummaryPlotsFromArgumentLine( const QStrin { curveSet->setColorMode( RimEnsembleCurveSet::BY_ENSEMBLE_PARAM ); curveSet->setEnsembleParameter( ensembleColoringParameter ); + if ( ensembleColoringStyle == LOG_PARAMETER ) { curveSet->legendConfig()->setMappingMode( RimRegularLegendConfig::LOG10_CONTINUOUS ); } } + newPlot->ensembleCurveSetCollection()->addCurveSet( curveSet ); if ( addHistoryCurves ) { RimSummaryCurve* historyCurve = createHistoryCurve( addr, summaryCasesToUse[0] ); + if ( historyCurve ) newPlot->addCurveNoUpdate( historyCurve ); } } @@ -600,6 +553,7 @@ void RicSummaryPlotFeatureImpl::createSummaryPlotsFromArgumentLine( const QStrin } } } + sumPlotColl->updateConnectedEditors(); if ( lastPlotCreated ) @@ -628,8 +582,9 @@ void RicSummaryPlotFeatureImpl::createSummaryPlotsFromArgumentLine( const QStrin int curveColorIndex = 0; for ( const QString& gridAddressFilter : gridResultAddressFilters ) { - std::vector cellResAddrs = createGridCellAddressesFromFilter( - gridAddressFilter ); + std::vector cellResAddrs = + RigGridCellResultAddress::createGridCellAddressesFromFilter( gridAddressFilter ); + for ( RigGridCellResultAddress cellResAddr : cellResAddrs ) { for ( RimEclipseCase* eclCase : gridCasesToPlotFrom ) @@ -682,8 +637,8 @@ void RicSummaryPlotFeatureImpl::createSummaryPlotsFromArgumentLine( const QStrin for ( const QString& gridAddressFilter : gridResultAddressFilters ) { - std::vector cellResAddrs = createGridCellAddressesFromFilter( - gridAddressFilter ); + std::vector cellResAddrs = + RigGridCellResultAddress::createGridCellAddressesFromFilter( gridAddressFilter ); for ( RigGridCellResultAddress cellResAddr : cellResAddrs ) { std::vector createdCurves; @@ -742,6 +697,45 @@ void RicSummaryPlotFeatureImpl::createSummaryPlotsFromArgumentLine( const QStrin } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicSummaryPlotFeatureImpl::splitAddressFiltersInGridAndSummary( RimSummaryCase* summaryBaseCases, + const QStringList& allCurveAddressFilters, + QStringList* summaryAddressFilters, + QStringList* gridResultAddressFilters ) +{ + if ( summaryBaseCases ) + { + const std::set& addrs = summaryBaseCases->summaryReader()->allResultAddresses(); + std::vector usedFilters; + std::set setToInsertFilteredAddressesIn; + filteredSummaryAdressesFromCase( allCurveAddressFilters, addrs, &setToInsertFilteredAddressesIn, &usedFilters ); + + QRegularExpression gridAddressPattern( "^[A-Z]+:[0-9]+,[0-9]+,[0-9]+$" ); + + for ( int filterIdx = 0; filterIdx < allCurveAddressFilters.size(); ++filterIdx ) + { + const QString& address = allCurveAddressFilters[filterIdx]; + if ( usedFilters[filterIdx] ) + { + summaryAddressFilters->push_back( address ); + } + else + { + if ( gridAddressPattern.match( address ).hasMatch() ) + { + gridResultAddressFilters->push_back( address ); + } + else + { + RiaLogging::warning( "No summary or restart vectors matched \"" + address + "\"" ); + } + } + } + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryPlotFeatureImpl.h b/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryPlotFeatureImpl.h index f5cea7677c..8a28bbfc95 100644 --- a/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryPlotFeatureImpl.h +++ b/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryPlotFeatureImpl.h @@ -39,16 +39,59 @@ public: static void createSummaryPlotsFromArgumentLine( const QStringList& arguments ); + static void filteredSummaryAdressesFromCase( const QStringList& curveFilters, + const std::set& allAddressesInCase, + std::set* setToInsertFilteredAddressesIn, + std::vector* usedFilters ); + private: static std::vector addCurvesFromAddressFiltersToPlot( const QStringList& curveFilters, RimSummaryPlot* plot, RimSummaryCase* summaryCase, bool addHistoryCurves ); + static std::set - applySummaryAddressFiltersToCases( const std::vector& summaryCasesToUse, - const QStringList& summaryAddressFilters ); - static void filteredSummaryAdressesFromCase( const QStringList& curveFilters, - const std::set& allAddressesInCase, - std::set* setToInsertFilteredAddressesIn, - std::vector* usedFilters ); + applySummaryAddressFiltersToCases( const std::vector& summaryCasesToUse, + const QStringList& summaryAddressFilters ); + + static void splitAddressFiltersInGridAndSummary( RimSummaryCase* summaryBaseCases, + const QStringList& allCurveAddressFilters, + QStringList* summaryAddressFilters, + QStringList* gridResultAddressFilters ); +}; + +#include "RigEclipseResultAddress.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +class RigGridCellResultAddress +{ +public: + RigGridCellResultAddress() + : gridIndex( -1 ) + , i( -1 ) + , j( -1 ) + , k( -1 ) + { + } + + RigGridCellResultAddress( + size_t gridIndex, size_t i, size_t j, size_t k, const RigEclipseResultAddress& eclipseResultAddress ) + : gridIndex( gridIndex ) + , i( i ) + , j( j ) + , k( k ) + , eclipseResultAddress( eclipseResultAddress ) + { + } + + static std::vector createGridCellAddressesFromFilter( const QString& text ); + // Using zero based ijk + + size_t gridIndex; + size_t i; + size_t j; + size_t k; + RigEclipseResultAddress eclipseResultAddress; }; diff --git a/ApplicationCode/ProjectDataModel/RimEclipseGeometrySelectionItem.cpp b/ApplicationCode/ProjectDataModel/RimEclipseGeometrySelectionItem.cpp index 798a0c0388..4aac6cc689 100644 --- a/ApplicationCode/ProjectDataModel/RimEclipseGeometrySelectionItem.cpp +++ b/ApplicationCode/ProjectDataModel/RimEclipseGeometrySelectionItem.cpp @@ -127,3 +127,22 @@ size_t RimEclipseGeometrySelectionItem::cellIndex() const { return m_cellIndex; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +cvf::Vec3st RimEclipseGeometrySelectionItem::cellIJK() const +{ + cvf::Vec3st IJK( -1, -1, -1 ); + + if ( m_cellIndex != cvf::UNDEFINED_SIZE_T ) + { + if ( m_eclipseCase && m_eclipseCase->eclipseCaseData() && m_eclipseCase->eclipseCaseData()->grid( m_gridIndex ) ) + { + m_eclipseCase->eclipseCaseData()->grid( m_gridIndex )->ijkFromCellIndex( m_cellIndex, &IJK[0], &IJK[1], &IJK[2] ); + } + } + + return IJK; +} + diff --git a/ApplicationCode/ProjectDataModel/RimEclipseGeometrySelectionItem.h b/ApplicationCode/ProjectDataModel/RimEclipseGeometrySelectionItem.h index 72e6d2df30..03d2e1fc19 100644 --- a/ApplicationCode/ProjectDataModel/RimEclipseGeometrySelectionItem.h +++ b/ApplicationCode/ProjectDataModel/RimEclipseGeometrySelectionItem.h @@ -46,6 +46,7 @@ public: RimEclipseCase* eclipseCase() const; size_t gridIndex() const; size_t cellIndex() const; + cvf::Vec3st cellIJK() const; private: caf::PdmPtrField m_eclipseCase; diff --git a/ApplicationCode/ProjectDataModel/RimGridTimeHistoryCurve.cpp b/ApplicationCode/ProjectDataModel/RimGridTimeHistoryCurve.cpp index 8236ee0987..a5258bd1f9 100644 --- a/ApplicationCode/ProjectDataModel/RimGridTimeHistoryCurve.cpp +++ b/ApplicationCode/ProjectDataModel/RimGridTimeHistoryCurve.cpp @@ -45,6 +45,7 @@ #include "RiuQwtPlotCurve.h" #include "qwt_plot.h" +#include "SummaryPlotCommands/RicSummaryPlotFeatureImpl.h" CAF_PDM_SOURCE_INIT( RimGridTimeHistoryCurve, "GridTimeHistoryCurve" ); @@ -169,6 +170,30 @@ void RimGridTimeHistoryCurve::setFromEclipseCellAndResult( geomSelectionItem->setFromCaseGridAndIJK( eclCase, gridIdx, i, j, k ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigGridCellResultAddress RimGridTimeHistoryCurve::resultAddress() +{ + RimEclipseGeometrySelectionItem* eclipseGeomSelectionItem = dynamic_cast( + m_geometrySelectionItem.value() ); + + if ( m_eclipseResultDefinition && eclipseGeomSelectionItem ) + { + cvf::Vec3st IJK = eclipseGeomSelectionItem->cellIJK(); + + return RigGridCellResultAddress( eclipseGeomSelectionItem->gridIndex(), + IJK[0], + IJK[1], + IJK[2], + m_eclipseResultDefinition->eclipseResultAddress() ); + } + + // Todo: support geomech stuff + + return RigGridCellResultAddress(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -310,6 +335,26 @@ QString RimGridTimeHistoryCurve::caseName() const return ""; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimCase* RimGridTimeHistoryCurve::gridCase() const +{ + RimEclipseGeometrySelectionItem* eclTopItem = eclipseGeomSelectionItem(); + if ( eclTopItem && eclTopItem->eclipseCase() ) + { + return eclTopItem->eclipseCase(); + } + + RimGeoMechGeometrySelectionItem* geoMechTopItem = geoMechGeomSelectionItem(); + if ( geoMechTopItem && geoMechTopItem->geoMechCase() ) + { + return geoMechTopItem->geoMechCase(); + } + + return nullptr; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ProjectDataModel/RimGridTimeHistoryCurve.h b/ApplicationCode/ProjectDataModel/RimGridTimeHistoryCurve.h index 21f7331376..3053e1ab13 100644 --- a/ApplicationCode/ProjectDataModel/RimGridTimeHistoryCurve.h +++ b/ApplicationCode/ProjectDataModel/RimGridTimeHistoryCurve.h @@ -26,6 +26,7 @@ #include "cafPdmPtrField.h" #include +#include "SummaryPlotCommands/RicSummaryPlotFeatureImpl.h" class RigMainGrid; class RimEclipseCase; @@ -37,6 +38,7 @@ class RimGeometrySelectionItem; class RiuFemTimeHistoryResultAccessor; class RiuSelectionItem; class RigEclipseResultAddress; +class RimCase; //================================================================================================== /// @@ -61,8 +63,11 @@ public: std::vector timeStepValues() const; std::vector daysSinceSimulationStart() const; + RigGridCellResultAddress resultAddress(); + QString quantityName() const; QString caseName() const; + RimCase* gridCase() const; protected: QString createCurveAutoName() override; diff --git a/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake b/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake index 8a1eb85a22..27f61f9f4e 100644 --- a/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake +++ b/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake @@ -36,6 +36,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatistics.h ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatisticsCase.h ${CMAKE_CURRENT_LIST_DIR}/RimDerivedEnsembleCase.h ${CMAKE_CURRENT_LIST_DIR}/RimDerivedEnsembleCaseCollection.h +${CMAKE_CURRENT_LIST_DIR}/RimSummaryPlotFilterTextCurveSetEditor.h ) set (SOURCE_GROUP_SOURCE_FILES @@ -75,6 +76,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatistics.cpp ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatisticsCase.cpp ${CMAKE_CURRENT_LIST_DIR}/RimDerivedEnsembleCase.cpp ${CMAKE_CURRENT_LIST_DIR}/RimDerivedEnsembleCaseCollection.cpp +${CMAKE_CURRENT_LIST_DIR}/RimSummaryPlotFilterTextCurveSetEditor.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlot.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlot.cpp index e3328327d0..40297c3ee4 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlot.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlot.cpp @@ -41,6 +41,7 @@ #include "RimSummaryPlotCollection.h" #include "RimSummaryPlotNameHelper.h" #include "RimSummaryTimeAxisProperties.h" +#include "RimSummaryPlotFilterTextCurveSetEditor.h" #include "RiuPlotMainWindowTools.h" #include "RiuSummaryQwtPlot.h" @@ -138,7 +139,7 @@ RimSummaryPlot::RimSummaryPlot() { CAF_PDM_InitObject( "Summary Plot", ":/SummaryPlotLight16x16.png", "", "" ); - CAF_PDM_InitField( &m_userDefinedPlotTitle, "PlotDescription", QString( "Summary Plot" ), "Name", "", "", "" ); + CAF_PDM_InitField( &m_userDefinedPlotTitle, "PlotDescription", QString( "Summary Plot" ), "Title", "", "", "" ); CAF_PDM_InitField( &m_showPlotTitle, "ShowPlotTitle", true, "Plot Title", "", "", "" ); m_showPlotTitle.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); CAF_PDM_InitField( &m_showLegend, "ShowLegend", true, "Legend", "", "", "" ); @@ -147,7 +148,7 @@ RimSummaryPlot::RimSummaryPlot() CAF_PDM_InitField( &m_legendFontSize, "LegendFontSize", 11, "Legend Font Size", "", "", "" ); m_showLegend.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); - CAF_PDM_InitField( &m_useAutoPlotTitle, "IsUsingAutoName", true, "Auto Name", "", "", "" ); + CAF_PDM_InitField( &m_useAutoPlotTitle, "IsUsingAutoName", true, "Auto Title", "", "", "" ); m_useAutoPlotTitle.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); CAF_PDM_InitField( &m_normalizeCurveYValues, "normalizeCurveYValues", false, "Normalize all curves", "", "", "" ); @@ -198,6 +199,10 @@ RimSummaryPlot::RimSummaryPlot() CAF_PDM_InitFieldNoDefault( &m_plotTemplate, "PlotTemplate", "Template", "", "", "" ); + CAF_PDM_InitFieldNoDefault( &m_textCurveSetEditor, "SummaryPlotFilterTextCurveSetEditor", "Text Filter Curve Creator", "", "", "" ); + m_textCurveSetEditor.uiCapability()->setUiTreeHidden( true ); + m_textCurveSetEditor = new RimSummaryPlotFilterTextCurveSetEditor; + m_isCrossPlot = false; m_nameHelperAllCurves.reset( new RimSummaryPlotNameHelper ); @@ -1188,6 +1193,28 @@ void RimSummaryPlot::addGridTimeHistoryCurve( RimGridTimeHistoryCurve* curve ) } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryPlot::addGridTimeHistoryCurveNoUpdate(RimGridTimeHistoryCurve* curve) +{ + CVF_ASSERT( curve ); + + m_gridTimeHistoryCurves.push_back( curve ); + if ( m_qwtPlot ) + { + curve->setParentQwtPlotNoReplot( m_qwtPlot ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimSummaryPlot::gridTimeHistoryCurves() const +{ + return m_gridTimeHistoryCurves.childObjects(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -1510,6 +1537,14 @@ std::set RimSummaryPlot::allPlotAxes() const return {m_timeAxisProperties, m_bottomAxisProperties, m_leftYAxisProperties, m_rightYAxisProperties}; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryPlot::deleteAllGridTimeHistoryCurves() +{ + m_gridTimeHistoryCurves.deleteAllChildObjects(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -1563,21 +1598,28 @@ void RimSummaryPlot::setAsCrossPlot() //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { - uiOrdering.add( &m_showPlotTitle ); - uiOrdering.add( &m_useAutoPlotTitle ); - uiOrdering.add( &m_userDefinedPlotTitle ); - uiOrdering.add( &m_showLegend ); + caf::PdmUiGroup* mainOptions = uiOrdering.addNewGroup("General Plot Options"); - if ( m_showLegend() ) + mainOptions->add( &m_showPlotTitle ); + if ( m_showPlotTitle ) { - uiOrdering.add( &m_legendFontSize ); + mainOptions->add(&m_useAutoPlotTitle); + mainOptions->add(&m_userDefinedPlotTitle); } - - uiOrdering.add( &m_normalizeCurveYValues ); - m_userDefinedPlotTitle.uiCapability()->setUiReadOnly( m_useAutoPlotTitle ); - uiOrdering.add( &m_plotTemplate ); + mainOptions->add( &m_showLegend ); + if ( m_showLegend() ) + { + mainOptions->add( &m_legendFontSize ); + } + + mainOptions->add( &m_normalizeCurveYValues ); + mainOptions->add( &m_plotTemplate ); + + caf::PdmUiGroup* textCurveFilterGroup = uiOrdering.addNewGroup("Text-Based Curve Creation"); + + m_textCurveSetEditor->uiOrdering(uiConfigName, *textCurveFilterGroup); uiOrdering.skipRemainingFields( true ); } diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlot.h b/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlot.h index 967e904fc1..aa3350b05b 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlot.h +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlot.h @@ -52,6 +52,7 @@ class RimPlotAxisProperties; class RiuSummaryQwtPlot; class RimSummaryPlotNameHelper; class RimPlotTemplateFileItem; +class RimSummaryPlotFilterTextCurveSetEditor; class QwtInterval; class QwtPlotCurve; @@ -82,10 +83,14 @@ public: void deleteCurves( const std::vector& curves ); void deleteCurvesAssosiatedWithCase( RimSummaryCase* summaryCase ); + void deleteAllGridTimeHistoryCurves(); RimEnsembleCurveSetCollection* ensembleCurveSetCollection() const; void addGridTimeHistoryCurve( RimGridTimeHistoryCurve* curve ); + void addGridTimeHistoryCurveNoUpdate( RimGridTimeHistoryCurve* curve ); + + std::vector gridTimeHistoryCurves() const; void addAsciiDataCruve( RimAsciiDataCurve* curve ); @@ -231,6 +236,8 @@ private: caf::PdmPtrField m_plotTemplate; + caf::PdmChildField m_textCurveSetEditor; + QPointer m_qwtPlot; std::unique_ptr m_plotInfoLabel; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlotFilterTextCurveSetEditor.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlotFilterTextCurveSetEditor.cpp new file mode 100644 index 0000000000..94505ef109 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlotFilterTextCurveSetEditor.cpp @@ -0,0 +1,486 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 "RimSummaryPlotFilterTextCurveSetEditor.h" + +#include "RiaApplication.h" +#include "RiaLogging.h" +#include "RiaSummaryCurveDefinition.h" + +#include "RifSummaryReaderInterface.h" +#include "RigCaseCellResultsData.h" +#include "RigEclipseCaseData.h" +#include "RimEclipseCase.h" +#include "RimEnsembleCurveSet.h" +#include "RimEnsembleCurveSetCollection.h" +#include "RimGridSummaryCase.h" +#include "RimGridTimeHistoryCurve.h" +#include "RimObservedDataCollection.h" +#include "RimObservedSummaryData.h" +#include "RimOilField.h" +#include "RimProject.h" +#include "RimSummaryCalculationCollection.h" +#include "RimSummaryCase.h" +#include "RimSummaryCaseCollection.h" +#include "RimSummaryCaseMainCollection.h" +#include "RimSummaryCurve.h" +#include "RimSummaryCurveCollection.h" +#include "RimSummaryPlot.h" +#include "RiuSummaryCurveDefSelection.h" + +#include "SummaryPlotCommands/RicSummaryPlotFeatureImpl.h" +#include "WellLogCommands/RicWellLogPlotCurveFeatureImpl.h" + +#include "cafPdmUiTextEditor.h" +#include "cafPdmUiTreeSelectionEditor.h" + +#include + +#define FILTER_TEXT_OUTDATED_TEXT "" + +CAF_PDM_SOURCE_INIT( RimSummaryPlotFilterTextCurveSetEditor, "SummaryPlotFilterTextCurveSetEditor" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryPlotFilterTextCurveSetEditor::RimSummaryPlotFilterTextCurveSetEditor() + : m_isFieldRecentlyChangedFromGui( false ) +{ + CAF_PDM_InitObject( "Curve Set Filter Text", "", "", "" ); + + // clang-format off + QString filterTextToolTip = + "A space separated list of vector addresses in the syntax: [:[:[:i,j,k]]]\n" + "Wildcards can also be used. Examples:\n" + " \"WOPT:*\" One total oil production curve for each well.\n" + " \"FOPT FWPT\" Two curves with oil and water total production.\n" + " \"BPR:15,28,*\" (no space) Oil phase pressure for all blocks along k as separate curves.\n"; + // clang-format on + + CAF_PDM_InitFieldNoDefault( &m_curveFilterText, "CurveFilterText", "Curve Filter Text", "", filterTextToolTip, "" ); + m_curveFilterText.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); + // m_curveFilterText.uiCapability()->setUiEditorTypeName( caf::PdmUiTextEditor::uiEditorTypeName() ); + + CAF_PDM_InitFieldNoDefault( &m_selectedSources, "SummaryCases", "Sources", "", "", "" ); + m_selectedSources.uiCapability()->setAutoAddingOptionFromValue( false ); + m_selectedSources.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); + m_selectedSources.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryPlotFilterTextCurveSetEditor::~RimSummaryPlotFilterTextCurveSetEditor() {} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryPlotFilterTextCurveSetEditor::fieldChangedByUi( const caf::PdmFieldHandle* changedField, + const QVariant& oldValue, + const QVariant& newValue ) +{ + RimSummaryPlot* parentPlot; + this->firstAncestorOrThisOfType( parentPlot ); + + if ( parentPlot ) + { + // Remove all curves, Create new ones + + parentPlot->ensembleCurveSetCollection()->deleteAllCurveSets(); + parentPlot->deleteAllSummaryCurves(); + parentPlot->deleteAllGridTimeHistoryCurves(); + + std::set curveDefinitions; + + QStringList allCurveAddressFilters = curveFilterTextWithoutOutdatedLabel().split( QRegExp( "\\s+" ), + QString::SkipEmptyParts ); + std::vector accumulatedUsedFilters( allCurveAddressFilters.size(), false ); + + for ( SummarySource* currSource : selectedSummarySources() ) + { + RimSummaryCaseCollection* ensemble = dynamic_cast( currSource ); + RimSummaryCase* sumCase = dynamic_cast( currSource ); + + std::set allAddressesFromSource; + + if ( ensemble ) + { + auto addresses = ensemble->ensembleSummaryAddresses(); + allAddressesFromSource.insert( addresses.begin(), addresses.end() ); + } + else if ( sumCase ) + { + RifSummaryReaderInterface* reader = sumCase ? sumCase->summaryReader() : nullptr; + if ( reader ) + { + allAddressesFromSource.insert( reader->allResultAddresses().begin(), + reader->allResultAddresses().end() ); + } + } + + std::vector usedFilters; + std::set filteredAddressesFromSource; + RicSummaryPlotFeatureImpl::filteredSummaryAdressesFromCase( allCurveAddressFilters, + allAddressesFromSource, + &filteredAddressesFromSource, + &usedFilters ); + + for ( size_t fIdx = 0; fIdx < accumulatedUsedFilters.size(); ++fIdx ) + { + accumulatedUsedFilters[fIdx] = accumulatedUsedFilters[fIdx] || usedFilters[fIdx]; + } + + for ( const auto& filteredAddress : filteredAddressesFromSource ) + { + curveDefinitions.insert( RiaSummaryCurveDefinition( sumCase, filteredAddress, ensemble ) ); + } + } + + // Find potensial grid result addresses + + QRegularExpression gridAddressPattern( "^[A-Z]+:[0-9]+,[0-9]+,[0-9]+$" ); + QStringList gridResultAddressFilters; + + for ( int filterIdx = 0; filterIdx < allCurveAddressFilters.size(); ++filterIdx ) + { + if ( !accumulatedUsedFilters[filterIdx] ) + { + const QString& unusedAddressFilter = allCurveAddressFilters[filterIdx]; + if ( gridAddressPattern.match( unusedAddressFilter ).hasMatch() ) + { + gridResultAddressFilters.push_back( unusedAddressFilter ); + } + else + { + RiaLogging::warning( "No summary or restart vectors matched \"" + unusedAddressFilter + "\"" ); + } + } + } + + // Create Summary curves and Ensemble curvesets: + + for ( const RiaSummaryCurveDefinition& curveDef : curveDefinitions ) + { + if ( curveDef.isEnsembleCurve() ) + { + RimEnsembleCurveSet* curveSet = new RimEnsembleCurveSet(); + + curveSet->setSummaryCaseCollection( curveDef.ensemble() ); + curveSet->setSummaryAddress( curveDef.summaryAddress() ); + + parentPlot->ensembleCurveSetCollection()->addCurveSet( curveSet ); + } + else + { + RimSummaryCurve* newCurve = new RimSummaryCurve(); + parentPlot->addCurveNoUpdate( newCurve ); + if ( curveDef.summaryCase() ) + { + newCurve->setSummaryCaseY( curveDef.summaryCase() ); + } + newCurve->setSummaryAddressYAndApplyInterpolation( curveDef.summaryAddress() ); + } + } + + // create Grid time history curves + { + std::vector gridCasesToPlotFrom; + + for ( SummarySource* currSource : selectedSummarySources() ) + { + RimGridSummaryCase* gridSumCase = dynamic_cast( currSource ); + if ( gridSumCase ) + { + RimEclipseCase* eclCase = gridSumCase->associatedEclipseCase(); + + if ( eclCase ) + { + gridCasesToPlotFrom.push_back( eclCase ); + } + } + } + + bool isEnsembleMode = gridCasesToPlotFrom.size() > 1; + int curveColorIndex = 0; + + for ( const QString& gridAddressFilter : gridResultAddressFilters ) + { + std::vector cellResAddrs = + RigGridCellResultAddress::createGridCellAddressesFromFilter( gridAddressFilter ); + + for ( RigGridCellResultAddress cellResAddr : cellResAddrs ) + { + for ( RimEclipseCase* eclCase : gridCasesToPlotFrom ) + { + RigCaseCellResultsData* gridCellResults = eclCase->eclipseCaseData()->results( + RiaDefines::MATRIX_MODEL ); + if ( !( gridCellResults && gridCellResults->resultInfo( cellResAddr.eclipseResultAddress ) ) ) + { + RiaLogging::warning( "Could not find a restart result property with name: \"" + + cellResAddr.eclipseResultAddress.m_resultName + "\"" ); + continue; + } + + RimGridTimeHistoryCurve* newCurve = new RimGridTimeHistoryCurve(); + newCurve->setFromEclipseCellAndResult( eclCase, + cellResAddr.gridIndex, + cellResAddr.i, + cellResAddr.j, + cellResAddr.k, + cellResAddr.eclipseResultAddress ); + newCurve->setLineThickness( 2 ); + newCurve->setColor( RicWellLogPlotCurveFeatureImpl::curveColorFromTable( curveColorIndex ) ); + + if ( !isEnsembleMode ) ++curveColorIndex; + + parentPlot->addGridTimeHistoryCurveNoUpdate( newCurve ); + } + if ( isEnsembleMode ) ++curveColorIndex; + } + } + } + + parentPlot->applyDefaultCurveAppearances(); + parentPlot->loadDataAndUpdate(); + + m_isFieldRecentlyChangedFromGui = true; + + parentPlot->updateConnectedEditors(); + } + + if ( changedField == &m_curveFilterText ) + { + m_curveFilterText = curveFilterTextWithoutOutdatedLabel(); + } + + m_isFieldRecentlyChangedFromGui = true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryPlotFilterTextCurveSetEditor::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) +{ + if ( !m_isFieldRecentlyChangedFromGui ) + { + // Sync gui from existing curves + + RimSummaryPlot* parentPlot; + this->firstAncestorOrThisOfType( parentPlot ); + std::set sourcesFromExistingCurves; + std::set addressesInUse; + std::vector gridaddressesInUse; + + if ( parentPlot ) + { + std::vector ensembleCurveSets = parentPlot->ensembleCurveSetCollection()->curveSets(); + for ( auto ensCurvSet : ensembleCurveSets ) + { + sourcesFromExistingCurves.insert( ensCurvSet->summaryCaseCollection() ); + addressesInUse.insert( ensCurvSet->summaryAddress() ); + } + + std::vector sumCurves = parentPlot->summaryCurveCollection()->curves(); + for ( auto sumCurve : sumCurves ) + { + sourcesFromExistingCurves.insert( sumCurve->summaryCaseY() ); + addressesInUse.insert( sumCurve->summaryAddressY() ); + } + + std::vector gridTimeHistoryCurves = parentPlot->gridTimeHistoryCurves(); + for ( auto grCurve : gridTimeHistoryCurves ) + { + RimEclipseCase* eclCase = dynamic_cast( grCurve->gridCase() ); + if ( eclCase ) + { + sourcesFromExistingCurves.insert( eclCase ); + gridaddressesInUse.push_back( grCurve->resultAddress() ); + } + } + } + + std::vector> usedSources( sourcesFromExistingCurves.begin(), + sourcesFromExistingCurves.end() ); + + m_selectedSources.clear(); + m_selectedSources.setValue( usedSources ); + + // Check if existing filtertext matches all the curves + // Todo: possibly check grid time history curves also + + QStringList allCurveAddressFilters = curveFilterTextWithoutOutdatedLabel().split( QRegExp( "\\s+" ), + QString::SkipEmptyParts ); + std::vector accumulatedUsedFilters( allCurveAddressFilters.size(), false ); + + std::vector usedFilters; + std::set filteredAddressesFromSource; + RicSummaryPlotFeatureImpl::filteredSummaryAdressesFromCase( allCurveAddressFilters, + addressesInUse, + &filteredAddressesFromSource, + &usedFilters ); + + if ( filteredAddressesFromSource != addressesInUse ) + { + m_curveFilterText = FILTER_TEXT_OUTDATED_TEXT + curveFilterTextWithoutOutdatedLabel(); + } + else + { + m_curveFilterText = curveFilterTextWithoutOutdatedLabel(); + } + } + + m_isFieldRecentlyChangedFromGui = false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryPlotFilterTextCurveSetEditor::setupBeforeSave() +{ + m_curveFilterText = curveFilterTextWithoutOutdatedLabel(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList + RimSummaryPlotFilterTextCurveSetEditor::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, + bool* useOptionsOnly ) +{ + QList options; + if ( fieldNeedingOptions == &m_selectedSources ) + { + appendOptionItemsForSources( options, false, false ); + } + + return options; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryPlotFilterTextCurveSetEditor::appendOptionItemsForSources( QList& options, + bool hideSummaryCases, + bool hideEnsembles ) +{ + RimProject* proj = RiaApplication::instance()->project(); + + std::vector oilFields; + + proj->allOilFields( oilFields ); + for ( RimOilField* oilField : oilFields ) + { + RimSummaryCaseMainCollection* sumCaseMainColl = oilField->summaryCaseMainCollection(); + if ( sumCaseMainColl ) + { + if ( !hideSummaryCases ) + { + // Top level cases + for ( const auto& sumCase : sumCaseMainColl->topLevelSummaryCases() ) + { + options.push_back( caf::PdmOptionItemInfo( sumCase->caseName(), sumCase ) ); + } + } + + // Ensembles + if ( !hideEnsembles ) + { + bool ensembleHeaderCreated = false; + for ( const auto& sumCaseColl : sumCaseMainColl->summaryCaseCollections() ) + { + if ( !sumCaseColl->isEnsemble() ) continue; + + if ( !ensembleHeaderCreated ) + { + options.push_back( caf::PdmOptionItemInfo::createHeader( "Ensembles", true ) ); + ensembleHeaderCreated = true; + } + + auto optionItem = caf::PdmOptionItemInfo( sumCaseColl->name(), sumCaseColl ); + optionItem.setLevel( 1 ); + options.push_back( optionItem ); + } + } + + if ( !hideSummaryCases ) + { + // Grouped cases + for ( const auto& sumCaseColl : sumCaseMainColl->summaryCaseCollections() ) + { + if ( sumCaseColl->isEnsemble() ) continue; + + options.push_back( caf::PdmOptionItemInfo::createHeader( sumCaseColl->name(), true ) ); + + for ( const auto& sumCase : sumCaseColl->allSummaryCases() ) + { + auto optionItem = caf::PdmOptionItemInfo( sumCase->caseName(), sumCase ); + optionItem.setLevel( 1 ); + options.push_back( optionItem ); + } + } + + // Observed data + auto observedDataColl = oilField->observedDataCollection(); + if ( observedDataColl->allObservedSummaryData().size() > 0 ) + { + options.push_back( caf::PdmOptionItemInfo::createHeader( "Observed Data", true ) ); + + for ( const auto& obsData : observedDataColl->allObservedSummaryData() ) + { + auto optionItem = caf::PdmOptionItemInfo( obsData->caseName(), obsData ); + optionItem.setLevel( 1 ); + options.push_back( optionItem ); + } + } + } + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimSummaryPlotFilterTextCurveSetEditor::selectedSummarySources() const +{ + std::vector sources; + + for ( const auto& source : m_selectedSources ) + { + sources.push_back( source ); + } + + // Always add the summary case for calculated curves as this case is not displayed in UI + sources.push_back( RiaApplication::instance()->project()->calculationCollection()->calculationSummaryCase() ); + + return sources; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimSummaryPlotFilterTextCurveSetEditor::curveFilterTextWithoutOutdatedLabel() const +{ + QString filterText = m_curveFilterText(); + + if ( filterText.startsWith( FILTER_TEXT_OUTDATED_TEXT ) ) + { + return filterText.right( filterText.length() - QString( FILTER_TEXT_OUTDATED_TEXT ).length() ); + } + + return filterText; +} diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlotFilterTextCurveSetEditor.h b/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlotFilterTextCurveSetEditor.h new file mode 100644 index 0000000000..f7e4869593 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryPlotFilterTextCurveSetEditor.h @@ -0,0 +1,55 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 "cafPdmObject.h" + +#include "RiaSummaryCurveDefinition.h" +#include "cafPdmField.h" +#include "cafPdmPtrArrayField.h" + +using SummarySource = caf::PdmObject; + +class RimSummaryPlotFilterTextCurveSetEditor : public caf::PdmObject +{ + CAF_PDM_HEADER_INIT; + +public: + RimSummaryPlotFilterTextCurveSetEditor(); + ~RimSummaryPlotFilterTextCurveSetEditor() override; + +protected: + QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, + bool* useOptionsOnly ) override; + void fieldChangedByUi( const caf::PdmFieldHandle* changedField, + const QVariant& oldValue, + const QVariant& newValue ) override; + void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; + virtual void setupBeforeSave() override; + +private: + static void appendOptionItemsForSources( QList& options, + bool hideSummaryCases, + bool hideEnsembles ); + + std::vector selectedSummarySources() const; + QString curveFilterTextWithoutOutdatedLabel() const; + + caf::PdmPtrArrayField m_selectedSources; + caf::PdmField m_curveFilterText; + + bool m_isFieldRecentlyChangedFromGui; +};